這個封裝適用於一個數據結構/通過數據參數的不同展示不同想過的情況.
適配器
次適配器使用是不需要對該適配器進行操作修改
/**
* 使用時 無需都是此 Adapter 進行修改
*/
public class BaseAdapterRecycler<DATA> extends RecyclerView.Adapter<ViewHolder> {
protected Context mContext;
//.數據源
protected List<DATA> mDatas;
//.點擊監聽
private OnItemClickListener mOnItemClickListener;
//.模版管理 SparseArrayCompat 使用方法見末尾
private SparseArrayCompat<ItemTemplate<DATA>> mTemplates;
/**
* 構造方法
*
* @param context
*/
public BaseAdapterRecycler(Context context) {
this.mContext = context;
mTemplates = new SparseArrayCompat<>();
}
/**
* 構造方法
*
* @param context
* @param datas
*/
public BaseAdapterRecycler(Context context, List<DATA> datas) {
this(context);
this.setmDatas(datas);
}
/**
* 設置數據源
*
* @param datas
*/
public void setmDatas(List<DATA> datas) {
this.mDatas = datas;
notifyDataSetChanged();
}
/**
* 獲取佈局類型
*
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
//如果數組越界 執行默認操作
if (isOutOfBounds(position)) {
return super.getItemViewType(position);
} else {
//循環遍歷 佈局類型
for (int i = mTemplates.size() - 1; i >= 0; i--) {
ItemTemplate<DATA> itemTemplate = mTemplates.valueAt(i);
if (itemTemplate.isForViewType(mDatas.get(position))) {
//如果是當前模版樣式 , 返回當前位置的鍵 便於 onCreateViewHolder()時獲取 模版
//當前位置的鍵 與當前位置 存在不相同的情況,當調用了 addItemTemplate()雙參數 的方法時可能會不同
return mTemplates.keyAt(i);
}
}
return super.getItemViewType(position);
}
}
/**
* 生成 ViewHolder
*
* @param viewGroup
* @param viewType
* @return
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
//獲取模版樣式
ItemTemplate itemTemplate = mTemplates.get(viewType);
//生成 ViewHolder
ViewHolder holder = ViewHolder.createViewHolder(mContext, viewGroup, itemTemplate.getLayoutID());
//設置監聽
setListener(viewGroup, holder, viewType);
return holder;
}
/**
* 數據綁定 ViewHolder
*
* @param viewHolder
* @param position
*/
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
DATA data = mDatas.get(position);
for (int i = 0; i < mTemplates.size(); i++) {
ItemTemplate<DATA> template = mTemplates.valueAt(i);
if (template.isForViewType(data)) {
template.convert(viewHolder, data, position);
}
}
}
/**
* 動態設置 佈局樣式爲 GridLayoutManager時,每類佈局列數
* recyclerView設置GridLayoutManager 時,SpanCount 設置爲幾類佈局列數的最小公倍數
* 例如:
* 佈局A:2列
* 佈局B:3列
* 佈局C:2列
* 佈局D:1列
* 則SpanCount 設置爲 6
* 對應的Bean類中設置爲
* A:3(6/2)
* B:2(6/3)
* C:3(6/2)
* D:6(6/1)
* <p>
* 其實此處就是把 RecyclerView當成了一個表格,
* 設置的是每一個 Item 所佔的列數
*
* @param recyclerView
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
if (lm instanceof GridLayoutManager) {
((GridLayoutManager) lm).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
DATA data = mDatas.get(position);
for (int i = 0; i < mTemplates.size(); i++) {
ItemTemplate<DATA> template = mTemplates.valueAt(i);
if (template.isForViewType(data)) {
return template.getSpanCount();
}
}
return 1;
}
});
}
}
/**
* 獲取 數據源數量
*
* @return
*/
@Override
public int getItemCount() {
return mDatas == null ? 0 : mDatas.size();
}
/**
* 獲取數據源
*
* @return
*/
public List<DATA> getDatas() {
return mDatas;
}
/**
* 添加數據源
*
* @param data
* @return
*/
public BaseAdapterRecycler addData(DATA data) {
if (mDatas == null) {
mDatas = new ArrayList<>();
}
if (data != null) {
mDatas.add(data);
} else {
log("data is null");
}
notifyDataSetChanged();
return this;
}
/**
* 添加多條數據源
*
* @param datas
* @return
*/
public BaseAdapterRecycler addData(List<DATA> datas) {
if (mDatas == null) {
mDatas = new ArrayList<>();
}
if (datas != null) {
mDatas.addAll(datas);
} else {
log("data is null");
}
notifyDataSetChanged();
return this;
}
/**
* 在指定位置添加數據源
*
* @param position
* @param data
* @return
*/
public BaseAdapterRecycler addData(@IntRange(from = 0) int position, DATA data) {
if (mDatas == null) {
mDatas = new ArrayList<>();
}
if (isOutOfBounds(position)) {
log("addData() position 越界 ,默認添加到最後");
position = getItemCount();
}
if (data != null) {
mDatas.add(position, data);
} else {
log("data is null");
}
notifyDataSetChanged();
return this;
}
/**
* 在指定位置添加多條數據源
*
* @param position
* @param datas
* @return
*/
public BaseAdapterRecycler addData(@IntRange(from = 0) int position, List<DATA> datas) {
if (mDatas == null) {
mDatas = new ArrayList<>();
}
if (isOutOfBounds(position)) {
log("addData() position 越界 ,默認添加到最後");
position = getItemCount();
}
if (datas != null) {
mDatas.addAll(position, datas);
} else {
log("datas is null");
}
notifyDataSetChanged();
return this;
}
/**
* 移除 position 位置的數據
*
* @param position
* @return
*/
public BaseAdapterRecycler deleteData(@IntRange(from = 0) int position) {
return removeData(position);
}
/**
* 移除 position 位置的數據
*
* @param position
* @return
*/
public BaseAdapterRecycler removeData(@IntRange(from = 0) int position) {
if (mDatas == null) {
mDatas = new ArrayList<>();
}
if (isOutOfBounds(position)) {
log("removeData() position 越界");
} else {
mDatas.remove(position);
}
notifyDataSetChanged();
return this;
}
/**
* 設置監聽
* 此方法設置監聽適用於統一處理的情況
* 不同模版監聽處理不同的情況 建議在各自模版中進行設置(holder.itemView.setOnclickListener(this))
*
* @param viewGroup
* @param holder
* @param viewType
*/
private void setListener(ViewGroup viewGroup, ViewHolder holder, int viewType) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(v, holder, holder.getAdapterPosition());
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemLongClick(v, holder, holder.getAdapterPosition());
}
return false;
}
});
}
/**
* 設置監聽
*
* @param onItemClickListener
*/
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
/**
* 判斷 position 是否越界
*
* @param position
* @return
*/
private boolean isOutOfBounds(int position) {
if (mDatas == null || mDatas.isEmpty()) {
return true;
} else if (position < 0 || position >= mDatas.size()) {
return true;
} else {
return false;
}
}
/**
* 添加 樣式模版
*
* @param itemTemplate
* @return
*/
public BaseAdapterRecycler addItemTemplate(ItemTemplate<DATA> itemTemplate) {
if (itemTemplate != null) {
mTemplates.put(mTemplates.size(), itemTemplate);
} else {
log("itemTemplate is null");
}
return this;
}
/**
* 添加 樣式模版
*
* @param viewType
* @param itemTemplate
* @return
*/
public BaseAdapterRecycler addItemTemplate(int viewType, ItemTemplate<DATA> itemTemplate) {
if (mTemplates.get(viewType) != null) {
log("模版樣式" + viewType + "已經存在," + "存在的模版樣式爲" + mTemplates.get(viewType));
}
if (itemTemplate != null) {
log("已將原模版" + mTemplates.get(viewType) + "替換");
mTemplates.put(viewType, itemTemplate);
}
return this;
}
/**
* 監聽接口
*/
public interface OnItemClickListener {
void onItemClick(View view, RecyclerView.ViewHolder holder, int position);
boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position);
}
/**
* 打印信息
*
* @param log
*/
private void log(String log) {
Log.e(getClass().getName(), log);
}
/**
* SparseArray(API >19 ) SparseArrayCompat(兼容)
* android 獨有的類
* 類似於 HashMap<Integer,Object> 但是性能更優化
*/
/*
增
put(int key, E value)
append(int key, E value)
查
public E get(int key)
public E get(int key, E valueIfKeyNotFound)
//查不到時的默認值
//查看第 index 個位置的鍵
public int keyAt(int index)
//查看第 index 個位置的值
public E valueAt(int index)
//查看鍵所在位置 鍵不存在 返回 <0(不一定是-1)
public int indexOfKey(int key)
//查看值所在位置 值不存在 返回 -1
public int indexOfValue(E value)
刪
public void delete(int key)
public void remove(int key)
public void removeAt(int index)
public void clear()
改
public void setValueAt(int index, E value)
public void put(int key, E value)
*/
}
複用器 ViewHolder
使用時 此類也無需修改.
如果項目中添加了插件或者jar包(Glide …),可以適當修改
/**
* 使用時此類可以不進行修改
*/
public class ViewHolder extends RecyclerView.ViewHolder {
private Context mContext;
private View mConvertView;
private SparseArray<View> mViews;
public ViewHolder(Context context, @NonNull View itemView) {
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<View>();
}
/**
* 創建 ViewHolder 實例
*
* @param context
* @param itemView
* @return
*/
public static ViewHolder createViewHolder(Context context, View itemView) {
return new ViewHolder(context, itemView);
}
/**
* 創建 ViewHolder 實例
*
* @param context
* @param parent
* @param layoutRes
* @return
*/
public static ViewHolder createViewHolder(Context context, ViewGroup parent, @LayoutRes int layoutRes) {
return new ViewHolder(context, LayoutInflater.from(context).inflate(layoutRes, null));
}
/**
* 獲取 弱引用 對象控件
*
* @param idRes
* @return
*/
public WeakReference<View> getWeakReference(@IdRes int idRes) {
if (mViews.get(idRes) == null) {
mViews.put(idRes, itemView.findViewById(idRes));
}
return new WeakReference<>(mViews.get(idRes));
}
/**
* 根據 控件 idRes 獲取 控件
*
* @param idRes
* @param <V>
* @return
*/
public <V extends View> V getView(@IdRes int idRes) {
if (mViews.get(idRes) == null) {
mViews.put(idRes, itemView.findViewById(idRes));
}
if (mViews.get(idRes) == null) {
throw new NullPointerException(idRes + "不存在");
}
return (V) mViews.get(idRes);
}
///********************以下爲輔助方法 使用方法一致*****************************************///
/**
* 設置 TextView 控件的值
*
* @param idRes
* @param sequence
* @return
*/
public ViewHolder setText(@IdRes int idRes, CharSequence sequence) {
TextView tv = getView(idRes);
if (tv == null) {
return this;
}
if (sequence == null || sequence.length() == 0) {
tv.setVisibility(View.INVISIBLE);
} else {
tv.setText(sequence);
}
return this;
}
/**
* 設置 TextView 字體顏色
*
* @param idRes
* @param color
* @return
*/
public ViewHolder setTextColor(@IdRes int idRes, @ColorInt int color) {
TextView tv = getView(idRes);
tv.setTextColor(color);
return this;
}
/**
* 設置 TextView 控件的值及字體顏色
*
* @param idRes
* @param sequence
* @param color
* @return
*/
public ViewHolder setText(@IdRes int idRes, CharSequence sequence, @ColorInt int color) {
TextView tv = getView(idRes);
tv.setTextColor(color);
setText(idRes, sequence);
return this;
}
/**
* 通過 Resources 資源 設置 TextView 的值
*
* @param idRes
* @param stringRes
* @return
*/
public ViewHolder setText(@IdRes int idRes, @StringRes int stringRes) {
return setText(idRes, mContext.getResources().getString(stringRes));
}
/**
* 設置 TextView 的值及顏色
*
* @param idRes
* @param stringRes
* @param color
* @return
*/
public ViewHolder setText(@IdRes int idRes, @StringRes int stringRes, @ColorInt int color) {
return setText(idRes, mContext.getResources().getString(stringRes), color);
}
public @interface LinkifyMask {
}
/**
* 設置 TextView 的超鏈接
*
* @param idRes
* @param linkify
* @return
*/
public ViewHolder setTextLinkify(@IdRes int idRes, @LinkifyMask int linkify) {
// Linkify.WEB_URLS 網址
// Linkify.EMAIL_ADDRESSES 郵箱地址
// Linkify.PHONE_NUMBERS 電話號碼
// Linkify.MAP_ADDRESSES map地址
// Linkify.ALL 全部
TextView tv = getView(idRes);
Linkify.addLinks(tv, linkify);
return this;
}
/**
* 設置 ImageView 的 濾鏡顏色
*
* @param idRes
* @param colorRes
* @return
*/
public ViewHolder setImageColor(@IdRes int idRes, @ColorRes int colorRes) {
return setImageColor(idRes, colorRes, PorterDuff.Mode.MULTIPLY);
}
/**
* 設置 ImageView 控件 濾鏡顏色 及 樣式
*
* @param idRes
* @param colorRes
* @param mode
* @return
*/
public ViewHolder setImageColor(@IdRes int idRes, @ColorRes int colorRes, PorterDuff.Mode mode) {
ImageView iv = getView(idRes);
iv.setColorFilter(colorRes, mode);
// PorterDuff.Mode.CLEAR;//在源圖像覆蓋的目標圖像的像素被清除爲0
// PorterDuff.Mode.SRC;//源圖
// PorterDuff.Mode.DST;//只繪製目標圖像
// PorterDuff.Mode.SRC_OVER;//將源圖像放到目標圖像的上方
// PorterDuff.Mode.DST_OVER;//在源圖像的上方繪製目標圖像
// PorterDuff.Mode.SRC_IN;//繪製源圖像和目標圖像相交的部分的源圖像,捨棄原圖像和目標圖像其他的部分
// PorterDuff.Mode.DST_IN;//繪製源圖像和目標圖像相交的部分的目標圖像,捨棄原圖像和目標圖像其他的部分
// PorterDuff.Mode.SRC_OUT;//只繪製目標圖像未覆蓋的源圖像的部分
// PorterDuff. Mode.DST_OUT;//只繪製目標圖像與源圖像不相交的部分
// PorterDuff.Mode.SRC_ATOP;//在源圖像和目標圖像相交的地方繪製源圖像,在不相交的地方繪製目標圖像
// PorterDuff.Mode.DST_ATOP;//在源圖像和目標圖像相交的地方繪製目標圖像,而在不相交的地方繪製源圖像
// PorterDuff.Mode.XOR;//源圖像和目標圖像相交的部分的像素清除爲0,然後繪製剩餘的源圖像
// PorterDuff.Mode.ADD;
// PorterDuff.Mode.MULTIPLY;//只繪製源圖像和目標圖像相交的部分,而混合後圖像像素的顏色值爲源圖像素顏色值乘以目標圖像素顏色值除以255即得
// PorterDuff.Mode.SCREEN;//濾色:在源圖像和目標圖像相交的部分添加源圖像和目標圖像像素,然後減去目標圖像與源像素相乘的。
// PorterDuff.Mode.OVERLAY;//根據目標圖像的顏色,對源圖片和目標圖片的像素地進行倍增或屏蔽。
// PorterDuff.Mode.DARKEN;//保留原圖像和目標圖像的像素的最小的分量,效果就是源圖像和目標圖像相交的部分變暗
// PorterDuff.Mode.LIGHTEN;//保留原圖像和目標圖像的像素的最大的分量,效果就是源圖像和目標圖像相交的部分變亮
return this;
}
/**
* 清楚 ImageView 控件的濾鏡顏色
*
* @param idRes
* @return
*/
public ViewHolder clearImageColor(@IdRes int idRes) {
ImageView iv = getView(idRes);
iv.clearColorFilter();
return this;
}
/**
* 設置 ImageView 控件
*
* @param idRes
* @param resId
* @return
*/
public ViewHolder setImageResources(@IdRes int idRes, @DrawableRes int resId) {
ImageView iv = getView(idRes);
iv.setImageResource(resId);
return this;
}
/**
* 設置控件背景色
*
* @param idRes
* @param color
* @return
*/
public ViewHolder setBackgroundColor(@IdRes int idRes, @ColorInt int color) {
getView(idRes).setBackgroundColor(color);
return this;
}
/**
* 設置控件背景色
*
* @param idRes
* @param resId
* @return
*/
public ViewHolder setBackgroundColorRes(@IdRes int idRes, @ColorRes int resId) {
setBackgroundColor(idRes, mContext.getResources().getColor(resId));
return this;
}
/**
* 設置控件背景
*
* @param idRes
* @param resId
* @return
*/
public ViewHolder setBackgroundRes(@IdRes int idRes, @DrawableRes int resId) {
getView(idRes).setBackgroundResource(resId);
return this;
}
/**
* 設置控件 隱藏
*
* @param idRes
* @return
*/
public ViewHolder setGone(@IdRes int idRes) {
getView(idRes).setVisibility(View.GONE);
return this;
}
/**
* 設置控件 顯示
*
* @param idRes
* @return
*/
public ViewHolder setVisible(@IdRes int idRes) {
getView(idRes).setVisibility(View.VISIBLE);
return this;
}
/**
* 設置控件 佔位隱藏
*
* @param idRes
* @return
*/
public ViewHolder setInVisible(@IdRes int idRes) {
getView(idRes).setVisibility(View.INVISIBLE);
return this;
}
/**
* 設置 ProgressBar 初始值
*
* @param idRes
* @param progress
* @return
*/
public ViewHolder setProgress(@IdRes int idRes, int progress) {
ProgressBar bar = getView(idRes);
bar.setProgress(progress);
return this;
}
/**
* 設置 ProgressBar 初始值和最大值
*
* @param idRes
* @param progress
* @param max
* @return
*/
public ViewHolder setProgress(@IdRes int idRes, int progress, int max) {
ProgressBar bar = getView(idRes);
bar.setProgress(progress);
bar.setMax(max);
return this;
}
/**
* 設置 ProgressBar 最大值
*
* @param idRes
* @param max
* @return
*/
public ViewHolder setProgressMax(@IdRes int idRes, int max) {
ProgressBar bar = getView(idRes);
bar.setMax(max);
return this;
}
/**
* 設置控件的 Tag
*
* @param idRes
* @param tag
* @return
*/
public ViewHolder setTag(@IdRes int idRes, Object tag) {
getView(idRes).setTag(tag);
return this;
}
/**
* 設置 控件多個 Tag
*
* @param idRes
* @param key
* @param tag
* @return
*/
public ViewHolder setTag(@IdRes int idRes, @StringRes int key, Object tag) {
getView(idRes).setTag(key, tag);
return this;
}
/**
* 設置 控件選中/不選中
*
* @param idRes
* @param checked
* @return
*/
public ViewHolder setChecked(@IdRes int idRes, boolean checked) {
Checkable checkable = getView(idRes);
checkable.setChecked(checked);
return this;
}
/**
* 設置控件點擊監聽
*
* @param idRes
* @param listener
* @return
*/
public ViewHolder setOnClickListener(@IdRes int idRes, View.OnClickListener listener) {
getView(idRes).setOnClickListener(listener);
return this;
}
/**
* 設置控件觸摸監聽
*
* @param idRes
* @param listener
* @return
*/
public ViewHolder setOnTouchListener(@IdRes int idRes, View.OnTouchListener listener) {
getView(idRes).setOnTouchListener(listener);
return this;
}
/**
* 設置控件長按監聽
*
* @param idRes
* @param listener
* @return
*/
public ViewHolder setOnLongClickListener(@IdRes int idRes, View.OnLongClickListener listener) {
getView(idRes).setOnLongClickListener(listener);
return this;
}
/**
* 設置控件選中監聽
*
* @param idRes
* @param listener
* @return
*/
public ViewHolder setOnListener(@IdRes int idRes, CompoundButton.OnCheckedChangeListener listener) {
CheckBox cb = getView(idRes);
cb.setOnCheckedChangeListener(listener);
return this;
}
/**
* 獲取控件的 Tag
*
* @param idRes
* @return
*/
public <Tag>Tag getTag(@IdRes int idRes) {
return (Tag) getView(idRes).getTag();
}
/**
* 獲取控件的 多個Tag
*
* @param idRes
* @param key
* @return
*/
public <Tag>Tag getTag(@IdRes int idRes, @StringRes int key) {
return (Tag) getView(idRes).getTag(key);
}
}
佈局模版接口
public interface ItemTemplate<DATA> {
/**
* 獲取模版佈局
*
* @return
*/
int getLayoutID();
/**
* 判斷當前 item 的模版是否爲本模版
*
* @param data
* @return
*/
boolean isForViewType(DATA data);
/**
* 使用 GridLayoutManager 時,不同的列數所佔據的比重
* 設置爲幾類佈局列數的最小公倍數
* 例如:
* 佈局A:2列
* 佈局B:3列
* 佈局C:2列
* 佈局D:1列
* 則SpanCount 設置爲 6
* 對應的Bean類中設置爲
* A:3(6/2)
* B:2(6/3)
* C:3(6/2)
* D:6(6/1)
* <p>
* 其實此處就是把 RecyclerView當成了一個表格,
* 設置的是每一個 Item 所佔的列數
* JAVA 8 方法
* @return
*/
default int getSpanCount(){
return 1;
}
/**
* 模版數據綁定
* @param holder
* @param data
* @param position
*/
void convert(ViewHolder holder, DATA data, int position);
}
特別注意getSpanCount()
此方法在RecyclerView 設置 佈局爲 GridLayoutManager 時纔會調用,對於所有的佈局模版列數均相同時可以實現此方法,其他情況均需複寫此方法.
使用
public class Template implements ItemTemplate<String> {
/**
* 模版佈局
*
* @return
*/
@Override
public int getLayoutID() {
return R.layout.layout_1;
}
/**
* 判斷是否爲該模版
*
* @param baseBean
* @return
*/
@Override
public boolean isForViewType(String baseBean) {
return true;
}
/**
* 數據綁定
*
* @param holder
* @param baseBean
* @param position
*/
@Override
public void convert(ViewHolder holder, String baseBean, int position) {
holder.setText(R.id.tv, baseBean);
}
}
xml 就不發了,都是最簡單的