RecyclerView.Adapter介面卡通用化改造
RecyclerView 是谷歌給開發者的福利,比以往的ListView更加強大,效能更大,具體原始碼分析,我們下次講。本篇主要介紹如何在專案中提煉程式碼,增強它的適配性。下面就介紹我的思路。
首先一般編碼有如下幾個問題。
1.列表資料,需要寫一個RecyclerView.Adapter和一個ViewHolder。ViewHolder是根據不同佈局而來,但RecyclerView.Adapter則包含大量的重複程式碼是否可以只對修改程式設計?
2.專案中常見的是列表巢狀列表,資料的傳遞,在子列表介面卡,或者孫子,重孫介面卡中編碼的複雜度是否有好的解決方案?
下面貼出我的方案程式碼
public class MyCommonAdapter<T> extends RecyclerView.Adapter implements FindFather<MyCommonAdapter.ApaterParent> {
ApaterParent apaterParent = null;
@Override
public ApaterParent getFather() {
return apaterParent;
}
@Override
public void admitFather( ApaterParent father) {
this.apaterParent = father;
}
public static abstract class ApaterParent<T> {
public ApaterParent father;
public abstract int getPosition();
public abstract T getBean();
public ApaterParent getFather() {
return father;
}
public void setFather(ApaterParent father) {
this.father = father;
}
}
public static final String TAG = MyCommonAdapter.class.getName();
/**
* 列表資料集合.
*/
protected List<T> dataList = new ArrayList<>();
/**
* 佈局填充.
*/
protected LayoutInflater mInflater;
/**
* 一個adapter 適配一個viewholder.
*/
private Class<T> viewHolder;
/**
* M context.
*/
protected Context mContext;
private Fragment fragment;
public MyCommonAdapter(Context mContext, Class<T> viewHolder) {
this.viewHolder = viewHolder;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
public MyCommonAdapter(Fragment fragment, Class<T> viewHolder) {
this(fragment.getActivity(), viewHolder);
this.fragment = fragment;
}
@NonNull
@Override
public MyCommonAdapter.DefineHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
UseLayout layout = viewHolder.getAnnotation(UseLayout.class);
if (layout == null) {
throw new RuntimeException("請設定佈局註解");
}
int layoutRes = 0;
if (layout != null) {
layoutRes = layout.layoutRes();
}
try {
Constructor<T> constructor = viewHolder.getConstructor(Context.class, View.class);
MyCommonAdapter.DefineHolder viewHolder = (MyCommonAdapter.DefineHolder) constructor.newInstance(mContext, mInflater.inflate(layoutRes, null, false));
viewHolder.admitFather(apaterParent);
viewHolder.setAdapter(this);
if (fragment != null) {
viewHolder.setFragment(fragment);
}
return viewHolder;
} catch (Exception e) {
LogUtils.d(TAG, "onCreateViewHolder 發生錯誤:", e.toString());
}
return null;
}
@Override
public void onBindViewHolder(final @NonNull RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof MyCommonAdapter.DefineHolder) {
((MyCommonAdapter.DefineHolder) holder).setCount(dataList.size());
final T itemBean = dataList.get(position);
if (itemBean != null) {
MyCommonAdapter.ApaterParent<T> myself = new MyCommonAdapter.ApaterParent<T>() {
@Override
public int getPosition() {
return position;
}
@Override
public T getBean() {
return itemBean;
}
};
myself.setFather(getFather());
((MyCommonAdapter.DefineHolder) holder).setData(itemBean, position, myself);
}
}
}
public static abstract class DefineHolder<T> extends RecyclerView.ViewHolder implements FindFather<ApaterParent> {
private RecyclerView.Adapter adapter;
ApaterParent parent = null;
private Fragment fragment;
private Context context;
private int count;
public DefineHolder(Context context, View itemView) {
super(itemView);
this.context = context;
ButterKnife.bind(this, itemView);
}
public RecyclerView.Adapter getAdapter() {
return adapter;
}
public void setAdapter(RecyclerView.Adapter adapter) {
this.adapter = adapter;
}
public void notifyDataSetChanged() {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
@Override
public void admitFather(ApaterParent father) {
parent = father;
}
@Override
public ApaterParent getFather() {
return parent;
}
public boolean isLastPosition() {
return count - 1 == getLayoutPosition();
}
public boolean isFirstPosition() {
return 0 == getLayoutPosition();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Fragment getFragment() {
return fragment;
}
public void setFragment(Fragment fragment) {
this.fragment = fragment;
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public abstract void setData(final T bean, final int position, final ApaterParent myself);
}
public boolean isEmpty() {
return dataList.isEmpty();
}
@Override
public int getItemCount() {
return dataList.size();
}
public void updateData(List<T> data, boolean isClearOld) {
if (isClearOld) {
dataList.clear();
}
if (data != null && !data.isEmpty()) {
dataList.addAll(data);
}
notifyDataSetChanged();
}
}
通用介面卡類繼承RecyclerView.Adapter 實現FindFather<MyCommonAdapter.ApaterParent>。
首先介紹下一些常見的抽象。
列表的資料集合
//列表的資料集合
protected List<T> dataList = new ArrayList<>();
viewHodler封裝易變的佈局和資料與控制元件的程式碼編寫
private Class<T> viewHolder;
保留Activity與Fragment,在建構函式中過載了兩種型別,按需使用
protected Context mContext;
private Fragment fragment;
常規操作判斷空,獲取資料集大小,更新列表資料
public boolean isEmpty() {
return dataList.isEmpty();
}
@Override
public int getItemCount() {
return dataList.size();
}
public void updateData(List<T> data, boolean isClearOld) {
if (isClearOld) {
dataList.clear();
}
if (data != null && !data.isEmpty()) {
dataList.addAll(data);
}
notifyDataSetChanged();
}
定義一個抽象類DefineHolder,這個類是需要自己繼承實現的,是我們完成業務的程式設計空間
public static abstract class DefineHolder<T> extends RecyclerView.ViewHolder implements FindFather<ApaterParent> {
我們以前類似於getView的程式碼都寫在此處
public abstract void setData(final T bean, final int position, final ApaterParent myself);
常規程式碼不做解釋
public void notifyDataSetChanged() {
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
public boolean isLastPosition() {
return count - 1 == getLayoutPosition();
}
public boolean isFirstPosition() {
return 0 == getLayoutPosition();
}
ApaterParent是用於解決巢狀adapter中的資料,位置資訊的傳遞,可以想象成一個子到父的連結串列,通過getFather方法可以取到自己的父adapter,可以依次逐級呼叫,getBean,getPosition可以取到需要的資料和位置
public static abstract class ApaterParent<T> {
public ApaterParent father;
public abstract int getPosition();
public abstract T getBean();
public ApaterParent getFather() {
return father;
}
public void setFather(ApaterParent father) {
this.father = father;
}
}
onCreateViewHolder 這個方法中可以看到首先根據執行時註解UseLayout取到對應的佈局,隨後通過反射建立一個MyCommonAdapter.DefineHolder例項,例項連結到父類介面卡,返回
@NonNull
@Override
public MyCommonAdapter.DefineHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
UseLayout layout = viewHolder.getAnnotation(UseLayout.class);
if (layout == null) {
throw new RuntimeException("請設定佈局註解");
}
int layoutRes = 0;
if (layout != null) {
layoutRes = layout.layoutRes();
}
try {
Constructor<T> constructor = viewHolder.getConstructor(Context.class, View.class);
MyCommonAdapter.DefineHolder viewHolder = (MyCommonAdapter.DefineHolder) constructor.newInstance(mContext, mInflater.inflate(layoutRes, null, false));
viewHolder.admitFather(apaterParent);
viewHolder.setAdapter(this);
if (fragment != null) {
viewHolder.setFragment(fragment);
}
return viewHolder;
} catch (Exception e) {
LogUtils.d(TAG, "onCreateViewHolder 發生錯誤:", e.toString());
}
return null;
}
onBindViewHolder 負責建立MyCommonAdapter.ApaterParent節點傳入並且將資料和位置傳給實現的setData方法。
@Override
public void onBindViewHolder(final @NonNull RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof MyCommonAdapter.DefineHolder) {
((MyCommonAdapter.DefineHolder) holder).setCount(dataList.size());
final T itemBean = dataList.get(position);
if (itemBean != null) {
MyCommonAdapter.ApaterParent<T> myself = new MyCommonAdapter.ApaterParent<T>() {
@Override
public int getPosition() {
return position;
}
@Override
public T getBean() {
return itemBean;
}
};
myself(getFather());
((MyCommonAdapter.DefineHolder) holder).setData(itemBean, position, myself);
}
}
}
現在需要展示一個九宮格的圖片列表。下面是示範程式碼。MyCommonAdapter建構函式,第一個引數,Fragment或者Activity, 第二個引數自己定義的業務ViewHolder,admitFather方法繫結父類(這段程式碼也是在一個adapter中,單層adapter不需要認爹)。 其他的就是常見程式碼
List<AlongSceneryDetailBean.DetailListBean> detailList = bean.getDetailList();
if (detailList != null && !detailList.isEmpty()) {
MyCommonAdapter imageAdapter = new MyCommonAdapter(getFragment(), AlongSceneryImageItemViewHolder.class);
imageAdapter.admitFather(myself);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(),3);
dataRv.setLayoutManager(gridLayoutManager);
dataRv.setAdapter(imageAdapter);
imageAdapter.updateData(detailList, true);
} else {
dataRv.setVisibility(View.GONE);
}
UseLayout註解中設定layout,設定業務資料AlongSceneryDetailBean.DetailListBean, ButterKnife繫結控制元件,setData中寫業務邏輯
@UseLayout(layoutRes = R.layout.item_alone_scenery_image)
public class AlongSceneryImageItemViewHolder extends MyCommonAdapter.DefineHolder<AlongSceneryDetailBean.DetailListBean> {
@Bind(R.id.content_iv)
ImageView contentIv;
public AlongSceneryImageItemViewHolder(Context context, View itemView) {
super(context, itemView);
}
@Override
public void setData(final AlongSceneryDetailBean.DetailListBean bean, int position,final MyCommonAdapter.ApaterParent myself) {
if(!TextUtils.isEmpty(bean.getImgUrl())){
Glide.with(getContext()).load(bean.getImgUrl()).into(contentIv);
}else{
Glide.with(getContext()).load(R.color.white_pure).into(contentIv);
}
contentIv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//do something
}
}
}
}
});
}
}
UseLayout註解中設定layout,設定業務資料AlongSceneryDetailBean.DetailListB