This commit is contained in:
2018-04-13 15:47:23 +08:00
parent d60fd6b2df
commit 8c51f52a2c
208 changed files with 8975 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.lemon.view">
</manifest>

View File

@@ -0,0 +1,137 @@
package cn.lemon.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import cn.lemon.view.adapter.Action;
import cn.lemon.view.adapter.RecyclerAdapter;
/**
* Created by linlongxin on 2016/1/24.
*/
public class RefreshRecyclerView extends FrameLayout {
private final String TAG = "RefreshRecyclerView";
private SwipeRefreshLayout mSwipeRefreshLayout;
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapter;
private boolean loadMoreAble;
public RefreshRecyclerView(Context context) {
this(context, null);
}
public RefreshRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RefreshRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
View view = inflate(context, R.layout.view_refresh_recycler, this);
mRecyclerView = view.findViewById(cn.lemon.view.R.id.recycler_view);
mSwipeRefreshLayout = view.findViewById(R.id.refresh_layout);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RefreshRecyclerView);
boolean refreshAble = typedArray.getBoolean(R.styleable.RefreshRecyclerView_refresh_able, true);
loadMoreAble = typedArray.getBoolean(R.styleable.RefreshRecyclerView_load_more_able, true);
if (!refreshAble) {
mSwipeRefreshLayout.setEnabled(false);
}
}
public void setAdapter(RecyclerAdapter adapter) {
mRecyclerView.setAdapter(adapter);
mAdapter = adapter;
mAdapter.loadMoreAble = loadMoreAble;
}
public void setLayoutManager(final RecyclerView.LayoutManager layoutManager) {
mRecyclerView.setLayoutManager(layoutManager);
if (layoutManager instanceof GridLayoutManager) {
((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
int type = mAdapter.getItemViewType(position);
if (type == RecyclerAdapter.HEADER_TYPE
|| type == RecyclerAdapter.FOOTER_TYPE
|| type == RecyclerAdapter.STATUS_TYPE) {
return ((GridLayoutManager) layoutManager).getSpanCount();
} else {
return 1;
}
}
});
}
}
public void setRefreshAction(final Action action) {
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
action.onAction();
}
});
}
public void setLoadMoreAction(final Action action) {
Log.d(TAG, "setLoadMoreAction");
if (mAdapter.isShowNoMore || !loadMoreAble) {
return;
}
mAdapter.loadMoreAble = true;
mAdapter.setLoadMoreAction(action);
}
public void showNoMore() {
mAdapter.showNoMore();
}
public void setItemSpace(int left, int top, int right, int bottom) {
mRecyclerView.addItemDecoration(new SpaceItemDecoration(left, top, right, bottom));
}
public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) {
mRecyclerView.addItemDecoration(itemDecoration);
}
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
public SwipeRefreshLayout getSwipeRefreshLayout() {
return mSwipeRefreshLayout;
}
public TextView getNoMoreView() {
return mAdapter.mNoMoreView;
}
public void setSwipeRefreshColorsFromRes(@ColorRes int... colors) {
mSwipeRefreshLayout.setColorSchemeResources(colors);
}
/**
* 8位16进制数 ARGB
*/
public void setSwipeRefreshColors(@ColorInt int... colors) {
mSwipeRefreshLayout.setColorSchemeColors(colors);
}
public void showSwipeRefresh() {
mSwipeRefreshLayout.setRefreshing(true);
}
public void dismissSwipeRefresh() {
mSwipeRefreshLayout.setRefreshing(false);
}
}

View File

@@ -0,0 +1,31 @@
package cn.lemon.view;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by linlongxin on 2015/12/19.
*/
public class SpaceItemDecoration extends RecyclerView.ItemDecoration {
private int top;
private int left;
private int right;
private int bottom;
public SpaceItemDecoration(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.left = left;
outRect.top = top;
outRect.right = right;
outRect.bottom = bottom;
}
}

View File

@@ -0,0 +1,8 @@
package cn.lemon.view.adapter;
/**
* Created by linlongxin on 2016/1/25.
*/
public interface Action {
void onAction();
}

View File

@@ -0,0 +1,51 @@
package cn.lemon.view.adapter;
import android.support.annotation.IdRes;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* BaseViewHolder 顶级父类
* Created by linlongxin on 2015/12/19.
*/
public class BaseViewHolder<T> extends RecyclerView.ViewHolder{
private final String TAG = "BaseViewHolder";
public BaseViewHolder(View itemView) {
super(itemView);
}
public BaseViewHolder(ViewGroup parent, int layoutId) {
super(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false));
onInitializeView();
}
public void onInitializeView() {
}
public <T extends View> T findViewById(@IdRes int resId) {
return (T) itemView.findViewById(resId);
}
public void setData(final T data) {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemViewClick(data);
}
});
}
public void onItemViewClick(T data) {
}
public void setTag(Object tag){
itemView.setTag(tag);
}
}

View File

@@ -0,0 +1,144 @@
package cn.lemon.view.adapter;
import android.content.Context;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 复杂的数据类型列表 Adapter , 没有 Header , Footer 的概念,所有的 item 都对应一个 ViewHolder
* Created by linlongxin on 2016/8/22.
*/
public class MultiTypeAdapter extends RecyclerAdapter {
private final String TAG = "MultiTypeAdapter";
private List<Object> mViewsData;
private SparseIntArray mPositionViewType; //position --> ViewType
private ViewHolderManager mViewHolderManager;
public MultiTypeAdapter(Context context) {
super(context);
mViewsData = new ArrayList<>();
mPositionViewType = new SparseIntArray();
mViewHolderManager = new ViewHolderManager();
}
public <T> void add(Class<? extends BaseViewHolder<T>> viewHolder, T data) {
if (isShowNoMore) {
return;
}
mViewsData.add(data);
mViewHolderManager.addViewHolder(viewHolder);
int viewType = mViewHolderManager.getViewType(viewHolder);
mPositionViewType.put(mViewCount - 1, viewType);//mViewCount从1开始
int positionStart = mViewCount - 1;
mViewCount++;
notifyItemRangeInserted(positionStart, 1);
}
public <T> void addAll(Class<? extends BaseViewHolder<T>> viewHolder, T[] data) {
addAll(viewHolder, Arrays.asList(data));
}
public <T> void addAll(Class<? extends BaseViewHolder<T>> viewHolder, List<T> data) {
int size = data.size();
if (isShowNoMore || size == 0) {
return;
}
mViewsData.addAll(data);
mViewHolderManager.addViewHolder(viewHolder);
int viewType = mViewHolderManager.getViewType(viewHolder);
int positionStart = mViewCount - 1;
for (int i = 0; i < size; i++) {
mPositionViewType.put(mViewCount - 1, viewType); //mViewCount从1开始
mViewCount++;
}
notifyItemRangeInserted(positionStart, size);
}
public void clear() {
if (mViewsData == null) {
log("clear() mData is null");
return;
}
mViewsData.clear();
mViewCount = 1;
isShowNoMore = false;
mLoadMoreView.setVisibility(View.GONE);
mNoMoreView.setVisibility(View.GONE);
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (position == mViewCount - 1) {
return STATUS_TYPE;
}
return mPositionViewType.get(position);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
log("onCreateViewHolder -- viewType : " + viewType);
if (viewType == STATUS_TYPE) {
return new BaseViewHolder(mStatusView);
}
Class clazzViewHolder = mViewHolderManager.getViewHolder(viewType);
try {
//这里只适配了ViewHolder构造函数只有ViewGroup.class参数或者无参情况的构造函数
BaseViewHolder holder;
Constructor constructor = clazzViewHolder.getDeclaredConstructor(new Class[]{ViewGroup.class});
constructor.setAccessible(true);
holder = (BaseViewHolder) constructor.newInstance(new Object[]{parent});
if (holder == null) {
constructor = clazzViewHolder.getDeclaredConstructor();
holder = (BaseViewHolder) constructor.newInstance();
}
return holder;
} catch (NoSuchMethodException e) {
e.printStackTrace();
Log.e(TAG, "onCreateBaseViewHolder : " + e.getMessage());
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e(TAG, "onCreateBaseViewHolder : " + e.getMessage());
} catch (InstantiationException e) {
e.printStackTrace();
Log.e(TAG, "onCreateBaseViewHolder : " + e.getMessage());
} catch (InvocationTargetException e) {
e.printStackTrace();
Log.e(TAG, "onCreateBaseViewHolder : " + e.getMessage());
}
return null;
}
@Override
public BaseViewHolder onCreateBaseViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
log("onBindViewHolder -- position : " + position);
if (position == 0 && mViewCount == 1) {
} else if (position == mViewCount - 1) {
// 显示加载更多
if (loadMoreAble && mLoadMoreAction != null && !isShowNoMore) {
mLoadMoreView.setVisibility(View.VISIBLE);
mLoadMoreAction.onAction();
}
} else {
holder.setData(mViewsData.get(position));
}
}
}

View File

@@ -0,0 +1,386 @@
package cn.lemon.view.adapter;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cn.lemon.view.R;
/**
* Created by linlongxin on 2015/12/19.
*/
public abstract class RecyclerAdapter<T> extends RecyclerView.Adapter<BaseViewHolder<T>> {
private static final String TAG = "RecyclerAdapter";
private boolean allowLog = false; //改成false关闭日志
public static final int HEADER_TYPE = 111;
public static final int FOOTER_TYPE = 222;
public static final int STATUS_TYPE = 333;
int mViewCount = 0;
private boolean hasHeader = false;
private boolean hasFooter = false;
public boolean isShowNoMore = false; //停止加载
public boolean loadMoreAble = false; //是否可加载更多
protected Action mLoadMoreAction;
private List<T> mData = new ArrayList<>();
private View headerView;
private View footerView;
protected View mStatusView;
protected LinearLayout mLoadMoreView;
public TextView mNoMoreView;
private Context mContext;
public void colseLog() {
allowLog = false;
}
public RecyclerAdapter(Context context) {
mContext = context;
initStatusView(context);
}
public RecyclerAdapter(Context context, T[] data) {
this(context, Arrays.asList(data));
}
public RecyclerAdapter(Context context, List<T> data) {
mContext = context;
initStatusView(context);
this.mData = data;
mViewCount += data.size();
notifyDataSetChanged();
}
public void initStatusView(Context context) {
mStatusView = LayoutInflater.from(context).inflate(R.layout.view_status_last, null);
mStatusView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mLoadMoreView = (LinearLayout) mStatusView.findViewById(R.id.load_more_view);
mNoMoreView = (TextView) mStatusView.findViewById(R.id.no_more_view);
mViewCount++;
}
@Override
public BaseViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == HEADER_TYPE) {
return new BaseViewHolder<>(headerView);
} else if (viewType == FOOTER_TYPE) {
return new BaseViewHolder<>(footerView);
} else if (viewType == STATUS_TYPE) {
return new BaseViewHolder<>(mStatusView);
} else
return onCreateBaseViewHolder(parent, viewType);
}
public abstract BaseViewHolder<T> onCreateBaseViewHolder(ViewGroup parent, int viewType);
/* ViewHolder 绑定数据,这里的 position 和 getItemViewType() 方法的 position 不一样
这里的 position 指当前可见的 item 的 position 的位置。
注意 :每个 ViewHolder 绑定数据时值调用此方法一次
*/
@Override
public void onBindViewHolder(BaseViewHolder<T> holder, int position) {
// log("onBindViewHolder() viewCount : " + mViewCount + " position : " + position);
if (position == 0) {
// 最先加载 mStatusView 时不需要绑定数据
if (mViewCount == 1 || hasHeader) {
return;
} else {
if (mData.size() > 0)
holder.setData(mData.get(0));
else
return;
}
} else if (!hasHeader && !hasFooter && position < mData.size()) { //没有Header和Footer
holder.setData(mData.get(position));
} else if (hasHeader && !hasFooter && position > 0 && position < mViewCount - 1) { //有Header没有Footer
holder.setData(mData.get(position - 1));
} else if (!hasHeader && position < mViewCount - 2) { //没有Header有Footer
holder.setData(mData.get(position));
} else if (position > 0 && position < mViewCount - 2) { //都有
holder.setData(mData.get(position - 1));
}
// 最后一个可见的 item 时 加载更多。解决 remove 时 bug
int positionEnd;
if ((hasHeader && hasFooter) || (!hasHeader && hasFooter)) {
positionEnd = mViewCount - 3;
} else {
positionEnd = mViewCount - 2;
}
if (loadMoreAble && !isShowNoMore && position == positionEnd) {
mLoadMoreView.setVisibility(View.VISIBLE);
if (mLoadMoreAction != null) {
mLoadMoreAction.onAction();
}
}
}
/**
* ViewHolder 更新 Item 的位置选择 ViewType , 和 UI 是同步的
*/
@Override
public int getItemViewType(int position) {
if (hasHeader && position == 0) { //header
return HEADER_TYPE;
}
if (hasFooter && position == mViewCount - 2) { //footer
return FOOTER_TYPE;
}
if (position == mViewCount - 1) { //最后View的状态
return STATUS_TYPE;
}
return super.getItemViewType(position);
}
/**
* 包含了 Header , Footer , 状态显示 Item
*/
@Override
public int getItemCount() {
return mViewCount;
}
public void showNoMore() {
isShowNoMore = true;
handler.post(new Runnable() {
@Override
public void run() {
mLoadMoreView.setVisibility(View.GONE);
mNoMoreView.setVisibility(View.VISIBLE);
}
});
/*mLoadMoreView.post(new Runnable() {
@Override
public void run() {
mLoadMoreView.setVisibility(View.GONE);
mNoMoreView.setVisibility(View.VISIBLE);
Log.i("调试","载入View"+(mLoadMoreView.getVisibility()));
}
});*/
}
private android.os.Handler handler=new android.os.Handler();
public void openLoadMore() {
isShowNoMore = false;
mLoadMoreView.post(new Runnable() {
@Override
public void run() {
mLoadMoreView.setVisibility(View.VISIBLE);
mNoMoreView.setVisibility(View.GONE);
}
});
}
public void setLoadMoreAction(Action action) {
mLoadMoreAction = action;
}
public void add(T object) {
if (!isShowNoMore) {
mData.add(object);
int position;
if (hasFooter) {
position = mViewCount - 2;
} else {
position = mViewCount - 1;
}
mViewCount++;
notifyItemInserted(position);
}
}
public void insert(T object, int itemPosition) {
if (mData != null && itemPosition < mViewCount) {
int dataPosition;
if (hasHeader) {
dataPosition = itemPosition - 1;
} else {
dataPosition = itemPosition;
}
mData.add(dataPosition, object);
mViewCount++;
notifyItemInserted(itemPosition);
}
}
public void addAll(List<T> data) {
int size = data.size();
if (!isShowNoMore && size > 0) {
mData.addAll(data);
int positionStart;
if (hasFooter) {
positionStart = mViewCount - 2;
} else {
positionStart = mViewCount - 1;
}
mViewCount += size;
notifyItemRangeInserted(positionStart, size);
log("addAll() startPosition : " + positionStart + " itemCount : " + size);
}
}
public void addAll(T[] objects) {
addAll(Arrays.asList(objects));
}
public void replace(T object, int itemPosition) {
if (mData != null) {
int dataPosition;
if (hasHeader) {
dataPosition = itemPosition - 1;
} else {
dataPosition = itemPosition;
}
if (dataPosition < mData.size()) {
mData.set(dataPosition, object);
mViewCount++;
notifyItemChanged(itemPosition);
}
}
}
//position start with 0
public void remove(T object) {
if (!mData.contains(object)) {
Toast.makeText(getContext(), "删除失败", Toast.LENGTH_SHORT).show();
log("remove() without the object : " + object.getClass().getName());
return;
}
int dataPosition = mData.indexOf(object);
int itemPosition;
if (hasHeader) {
itemPosition = dataPosition + 1;
} else {
itemPosition = dataPosition;
}
remove(itemPosition);
}
//positionItem start with 0
public void remove(int itemPosition) {
int dataPosition;
int dataSize = mData.size();
if (hasHeader) {
dataPosition = itemPosition - 1;
if (dataPosition >= 0 && dataPosition < dataSize) {
mData.remove(dataPosition);
notifyItemRemoved(itemPosition);
mViewCount--;
} else if (dataPosition >= dataSize) {
Toast.makeText(getContext(), "删除失败", Toast.LENGTH_SHORT).show();
} else {
throw new IndexOutOfBoundsException("RecyclerView has header,position is should more than 0 ." +
"if you want remove header , pleasure user removeHeader()");
}
} else {
dataPosition = itemPosition;
if (dataPosition >= dataSize) {
Toast.makeText(getContext(), "删除失败", Toast.LENGTH_SHORT).show();
} else {
mData.remove(dataPosition);
notifyItemRemoved(itemPosition);
mViewCount--;
}
}
}
public void clear() {
if (mData == null) {
log("clear() mData is null");
return;
}
mData.clear();
mViewCount = 1;
if (hasHeader) {
mViewCount++;
}
if (hasFooter) {
mViewCount++;
}
isShowNoMore = false;
mLoadMoreView.setVisibility(View.GONE);
mNoMoreView.setVisibility(View.GONE);
notifyDataSetChanged();
}
public void setHeader(View header) {
hasHeader = true;
headerView = header;
mViewCount++;
}
public void setHeader(@LayoutRes int res) {
setHeader(LayoutInflater.from(mContext).inflate(res, null));
}
public View getHeader() {
return headerView;
}
public View getFooter() {
return footerView;
}
public void setFooter(View footer) {
hasFooter = true;
footerView = footer;
mViewCount++;
}
public void setFooter(@LayoutRes int res) {
setFooter(LayoutInflater.from(mContext).inflate(res, null));
}
public void removeHeader() {
if (hasHeader) {
hasHeader = false;
notifyItemRemoved(0);
}
}
public void removeFooter() {
if (hasFooter) {
hasFooter = false;
notifyItemRemoved(mViewCount - 2);
}
}
public List<T> getData() {
return mData;
}
public Context getContext() {
return mContext;
}
public void log(String content) {
if (allowLog) {
Log.d(TAG, content);
}
}
}

View File

@@ -0,0 +1,48 @@
package cn.lemon.view.adapter;
import android.util.Log;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;
/**
* Created by linlongxin on 2016/8/22.
*/
public class ViewHolderManager {
private final String TAG = "ViewHolderManager";
private int mViewType = 10;
private Map<Class<? extends BaseViewHolder>, Integer> mHolderType;
private Map<Integer,Class<? extends BaseViewHolder>> mTypeHolder;
public ViewHolderManager() {
mHolderType = new HashMap<>();
mTypeHolder = new HashMap<>();
}
public void addViewHolder(Class<? extends BaseViewHolder> viewHolder) {
if (!mHolderType.containsKey(viewHolder)) {
Class dataClass = (Class) ((ParameterizedType) viewHolder.getGenericSuperclass()).getActualTypeArguments()[0]; //获取ViewHolder的泛型数据class
mViewType++;
mHolderType.put(viewHolder, mViewType);
mTypeHolder.put(mViewType,viewHolder);
Log.d(TAG, "addViewHolder dataClassType : " + dataClass.getName());
}
}
public int getViewType(Class<? extends BaseViewHolder> holder){
if(!mHolderType.containsKey(holder)){
throw new NullPointerException("please invoke addViewHolder method");
}
return mHolderType.get(holder);
}
public Class<? extends BaseViewHolder> getViewHolder(int viewType){
if(!mTypeHolder.containsKey(viewType)){
throw new NullPointerException("please invoke addViewHolder method");
}
return mTypeHolder.get(viewType);
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/load_more_view"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="正在加载..."
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/no_more_view"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center"
android:text=""
android:textSize="14sp"
android:visibility="gone" />
</FrameLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RefreshRecyclerView">
<attr name="refresh_able" format="boolean"/>
<attr name="load_more_able" format="boolean"/>
</declare-styleable>
</resources>