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
首先介绍下一些常见的抽象。
列表的数据集合
//列表的数据集合
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.DetailListBean, ButterKnife绑定控件,setData中写业务逻辑
MyCommonAdapter.ApaterParent<AlongSceneryDetailBean.MileageSceneryList> dataApaterParent = getFather();
if(dataApaterParent!=null){
mileageSceneryList = dataApaterParent.getBean();
//取得父adapter 中的bean,获取父类位置。做你想做的事
}
}
一个通用的适配器就算完成,项目中还是要多抽象,多复用,这样对于机械的任务,编码越来越轻松,且不易出错,可以有更多时间挑战有难度的事情。