共计 7365 个字符,预计需要花费 19 分钟才能阅读完成。
传统的适配器
在 Android 我的项目中,基本上都会有列表性能,而当初的列表性能都是通过 RecyclerView 实现的,当我的项目中列表性能比拟多的时候,每一个 RecyclerView 都须要一个 Adapter 适配器,这样会使得我的项目中的 Adapter 类十分的多。所以,封装一个万能的 RecyclerView 适配器是能够进步咱们的开发效率的。在这之前,咱们先来看一下传统适配器配合 RecyclerView 是怎么应用的。
咱们先来看一下咱们要实现的例子的样子,如下所示:
适配器 Adapter 的代码如下所示:
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
private OnItemClickListener onItemClickListener;
private RecyclerView mRv;
private List<String> dataSource;
private Context mContext;
private int addDataPosition = -1;
public MyRecyclerViewAdapter(Context context, RecyclerView recyclerView) {
this.mContext = context;
this.dataSource = new ArrayList<>();
this.mRv = recyclerView;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.onItemClickListener = onItemClickListener;}
public void setDataSource(List<String> dataSource) {
this.dataSource = dataSource;
notifyDataSetChanged();}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {return new MyViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_layout, viewGroup, false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, final int position) {myViewHolder.mIv.setImageResource(getIcon(position));
myViewHolder.mTv.setText(dataSource.get(position));
// 只在瀑布流布局中应用随机高度
if (mRv.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
myViewHolder.mTv.setLayoutParams(params);
} else {LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
myViewHolder.mTv.setLayoutParams(params);
}
// 扭转 ItemView 背景色彩
if (addDataPosition == position) {myViewHolder.mItemView.setBackgroundColor(Color.RED);
} else {myViewHolder.mItemView.setBackgroundColor(Color.parseColor("#A4D3EE"));
}
myViewHolder.mItemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 调用接口的回调办法
if (onItemClickListener != null) {onItemClickListener.onItemClick(position);
}
}
});
}
@Override
public int getItemCount() {return dataSource.size();
}
private int getIcon (int position) {switch (position % 5) {
case 0:
return R.mipmap.a;
case 1:
return R.mipmap.b;
case 2:
return R.mipmap.c;
case 3:
return R.mipmap.d;
case 4:
return R.mipmap.e;
}
return 0;
}
private int getRandomHeight () {return (int)(Math.random() * 1000);
}
public void addData (int position) {
addDataPosition = position;
dataSource.add(position, "插入的数据");
notifyItemInserted(position);
// 刷新 ItemView
notifyItemRangeChanged(position, dataSource.size() - position);
}
public void removeData (int position) {
addDataPosition = -1;
dataSource.remove(position);
notifyItemRemoved(position);
// 刷新 ItemView
notifyItemRangeChanged(position, dataSource.size() - position);
}
class MyViewHolder extends RecyclerView.ViewHolder {
View mItemView;
ImageView mIv;
TextView mTv;
public MyViewHolder(@NonNull View itemView) {super(itemView);
mIv = itemView.findViewById(R.id.iv);
mTv = itemView.findViewById(R.id.tv);
mItemView = itemView;
}
}
public interface OnItemClickListener {void onItemClick(int position);
}
}
下面这个适配器尽管增加了点击事件、插入数据等性能,然而还是比较简单的,Adapter 适配器的实现次要就是三个步骤:
- 继承 RecyclerView.Adapter
- 创立 ViewHolder
- 绑定数据
有了这个适配器后,RecyclerView 只有绑定这个适配器就能够显示列表了,具体代码如下所示:
mRecyclerView = findViewById(R.id.recycler_view);
// 线性布局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mAdapter = new MyRecyclerViewAdapter(this, mRecyclerView);
mRecyclerView.setAdapter(mAdapter);
// itemView 点击事件监听
mAdapter.setOnItemClickListener(new MyRecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {Toast.makeText(MainActivity2.this, "第" + position + "条数据被点击", Toast.LENGTH_SHORT).show();}
});
这样一个列表性能就实现了,然而下面这个 Adapter 只能适配这个列表,无奈做到通用性。当初咱们来实现通用的 RecyclerView 适配器。
封装通用 Adapter
因为 RecyclerView 的 Adapter 必须继承自 RecyclerView.Adapter,并且指定咱们写的 ViewHolder 为泛型,为了达到万能的成果,咱们把须要传入的 Java Bean 属性间接用一个泛型 T 指代。这里我是利用接口来封装,没有应用抽象类,并且还实现了多 type 类型的适配器的封装,具体代码如下所示:
public class CommonAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> {
private final List<T> mList;
private OnBindDataListener<T> onBindDataListener;
private OnMoreBindDataListener<T> onMoreBindDataListener;
// 单类型
public CommonAdapter(List<T> mList, OnBindDataListener<T> onBindDataListener) {
this.mList = mList;
this.onBindDataListener = onBindDataListener;
}
// 多类型
public CommonAdapter(List<T> mList, OnMoreBindDataListener<T> onMoreBindDataListener) {
this.mList = mList;
this.onBindDataListener = onMoreBindDataListener;
this.onMoreBindDataListener = onMoreBindDataListener;
}
// 绑定数据
public interface OnBindDataListener<T> {void onBindViewHolder(T model, CommonViewHolder viewHolder, int type, int position);
int getLayoutId(int type);
}
// 绑定多类型的数据
public interface OnMoreBindDataListener<T> extends OnBindDataListener<T> {int getItemType(int position);
}
@Override
public int getItemViewType(int position) {if (onMoreBindDataListener != null) {return onMoreBindDataListener.getItemType(position);
}
return 0;
}
@Override
public CommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {int layoutId = onBindDataListener.getLayoutId(viewType);
View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return CommonViewHolder.getCommonViewHolder(parent.getContext(), view);
}
@Override
public void onBindViewHolder(CommonViewHolder holder, int position) {onBindDataListener.onBindViewHolder(mList.get(position), holder, getItemViewType(position), position);
}
@Override
public int getItemCount() {return mList == null ? 0 : mList.size();
}
// 插入一项数据
public void insert(T item, int position) {mList.add(position, item);
notifyItemInserted(position);
}
// 删除一项数据
public void remove(int position) {mList.remove(position);
notifyItemRemoved(position);
}
}
这样一个通用 Adapter 适配器就写好了, 核心思想就是通过接口将 onCreateViewHolder() 和 onBindViewHolder() 交给调用者解决 。
封装通用 ViewHolder
咱们都晓得 ListView 中 ViewHolder 是须要自定义的,在 RecyclerView 中 ViewHolder 是曾经封装好的,所以咱们还须要封装一个通用的 ViewHolder,具体代码如下所示:
public class CommonViewHolder extends RecyclerView.ViewHolder {
// 子 View 的汇合
private SparseArray<View> mViews;
private Context context;
public CommonViewHolder(Context context, View itemView) {super(itemView);
this.context = context;
mViews = new SparseArray<>();}
public static CommonViewHolder getCommonViewHolder(Context context, View itemView) {return new CommonViewHolder(context, itemView);
}
/**
* 提供给内部拜访 View 的办法
*
* @param viewId id
* @param <T> 泛型
* @return
*/
public <T extends View> T getView(int viewId) {View view = mViews.get(viewId);
if (view == null) {view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* 设置文本
*
* @param viewId id
* @param text 文本
* @return this
*/
public CommonViewHolder setText(int viewId, String text) {TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 设置图片
*
* @param viewId id
* @param resId 图片 id
* @return this
*/
public CommonViewHolder setImageResource(int viewId, int resId) {ImageView iv = getView(viewId);
iv.setImageResource(resId);
return this;
}
}
应用万能的 RecyclerView 适配器
咱们曾经将通用的 Adapter 和通用的 ViewHolder 都封装好了,当初咱们来看一下是如何应用的,具体代码如下所示:
mRecyclerView = findViewById(R.id.recycler_view);
// 线性布局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mCommonAdapter = new CommonAdapter<>(mList, new CommonAdapter.OnBindDataListener<String>() {
@Override
public void onBindViewHolder(String model, CommonViewHolder viewHolder, int type, final int position) {viewHolder.setText(R.id.tv, model);
}
@Override
public int getLayoutId(int type) {return R.layout.item_layout;}
});
mRecyclerView.setAdapter(mCommonAdapter);
这个应用是非常简单的,通过 getLayoutId() 传入 Item 的布局,通过 onBindViewHolder() 办法设置数据。当前咱们我的项目中 RecyclerView 的适配器只须要上述两个类就能够了。
源码
源码 曾经上传到 github,因为下面并没有贴出所有代码,所以大家能够去 github 上下载。