前言
对于一个Android应用来说,列表是经常需要使用的一个控件,而实现列表对于Android系统来说一般就是使用ListView和RecycleView这两个,由于RecycleView更为灵活高效,所以大部分Android开发者更倾向于RecycleView。大家都知道如果要使用RecycleView必须要有一个与之相伴的Adapter。但是大部分简单的列表的Adapter功能都一致,那可不可以只使用一个BaseAdapter来控制RecyclerView呢,这样可能减少很多重复性的代码,我最近正好写了一个简单的项目。整个项目只使用了一个BaseAdapter。下面我来给读者分享一下我的尝试。
内容
其实这个东西很简单,就是一个抽象的过程。把大部分列表都需要的功能都抽象出来做成一个通用的类,然后剩下的数据加载。View控制这些多态性的东西留给使用者自己去实现就可以了,总之就是用最少的代码实现相同的功能。
那么那些东西是通用的呢,来举例子吧
- 设置一个List数据集合
- 重写getItemCount()
- 重写onCreateViewHolder(单布局)
- 追加数据到集合末尾,适用于分页加载
- 设置数据源并更新界面,适用于下啦刷新
- 删除某个子项数据并更新界面
- 通过某个下标获取数据
- 设置item的点击事件监听
- 通过反射加载ViewHolder
上述的都是可以抽象出来的。这个时候我们就可以自己先实现一个BaseAdapter去实现这些基础的功能,然后有那么东西是不可以抽象出来的呢?
同样举例
- onBindViewHolder()
这个接口是留给Adapter用户将数据加载到对应的View上的,每个列表的控件和数据都不一样,自然是不可以复用的。所以这个要作为一个接口留出来给使用者重写。那么明白了就来看看代码吧
public class BaseRecycleAdapter<T, V extends BaseViewHolder<T>> extends RecyclerView.Adapter<V> {
protected List<T> dataList;
private int layoutId;
private OnItemClickListener<T> onItemClickListener;
private OnItemLongClickListener<T> onItemLongClickListener;
private Context mContext;
private Class<V> vClass;
public BaseRecycleAdapter(List<T> dataList, int layoutId, Class<V> vClass) {
this.dataList = dataList;
this.layoutId = layoutId;
this.vClass = vClass;
}
public BaseRecycleAdapter(int layoutId, Class<V> vClass) {
this(null, layoutId, vClass);
}
@NonNull
@Override
public V onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (mContext == null) mContext = viewGroup.getContext();
try {
Constructor<V> constructor = vClass.getConstructor(View.class);
return constructor.newInstance(newItemView(viewGroup, layoutId));
} catch (NoSuchMethodException | IllegalAccessException
| InstantiationException | InvocationTargetException e) {
e.printStackTrace();
LogHelper.log("反射失败");
}
return null;
}
private View newItemView(ViewGroup viewGroup, int resId) {
return LayoutInflater.from(viewGroup.getContext()).inflate(resId, viewGroup, false);
}
@Override
public void onBindViewHolder(@NonNull V baseViewHolder, int i) {
baseViewHolder.setData(dataList.get(i));
baseViewHolder.loadItemData(mContext, dataList.get(i), i);
if (onItemClickListener != null) {
baseViewHolder.setOnClickListener(v
-> onItemClickListener.onItemClick(dataList.get(i), v, i));
}
if (onItemLongClickListener != null) {
baseViewHolder.itemView.setOnLongClickListener(v ->
onItemLongClickListener.onItemLongClick(dataList.get(i), i));
}
}
@Override
public int getItemCount() {
return dataList == null ? 0 : dataList.size();
}
public void appendDataToList(T data) {
if (dataList == null) {
dataList = new ArrayList<>();
}
dataList.add(data);
notifyItemInserted(getItemCount() - 1);
}
/**
* 用于上拉加载更多更新界面
*/
public void appendDataToList(List<T> datas) {
int firstPosition = getItemCount();
dataList.addAll(datas);
int lastPosition = getItemCount();
for (int i = firstPosition; i < lastPosition; i++) {
notifyItemInserted(i);
}
}
public T getItemData(int position) {
if (0 <= position && position < getItemCount()) {
return dataList.get(position);
}
return null;
}
/**
* 用于下啦
*/
public void setDataList(List<T> dataList) {
this.dataList = dataList;
notifyDataSetChanged();
}
public void removeItemFormList(int position) {
if (position < getItemCount()) {
dataList.remove(position);
notifyItemRemoved(position);
for (int i = position; i < dataList.size(); i++) {
notifyItemChanged(i);
}
}
}
public List<T> getData() {
return dataList == null ? new ArrayList<>(0) : dataList;
}
public void refreshItemData(T data, int position) {
dataList.set(position, data);
notifyItemChanged(position);
}
public void refreshRangeData(int start, List<T> datas) {
int end = start + datas.size();
if (start < 0 || end > getItemCount()) {
return;
}
for (int i = start; i < end; i++) {
dataList.set(i, datas.get(i - start));
}
notifyItemRangeChanged(start, datas.size());
}
public void setOnItemClickListener(OnItemClickListener<T> onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public void setOnItemLongClickListener(
OnItemLongClickListener<T> onItemLongClickListener) {
this.onItemLongClickListener = onItemLongClickListener;
}
}
下面是点击和长按事件监听代码:
//点击事件监听
public interface OnItemClickListener<D> {
void onItemClick(D itemData, View view,int position);
}
//长按事件监听
public interface OnItemLongClickListener<D> {
boolean onItemLongClick(D data, int position);
}
这是我实现的一个通用的单布局Adapter,从这里看,是不是只有一些必须或者通用方法的实现。因为onBindViewHolder这个方法里面item的布局更新是需要留给使用者重写的,所以把这个留给继承BaseViewHolder并重写loadItemData的子ViewHolder来实现
看一下BaseViewHolder的代码
public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder {
public int currentPosition;
private T data;
public BaseViewHolder(@NonNull View itemView) {
super(itemView);
initItemView(itemView);
}
protected abstract void initItemView(View view);
public abstract void loadItemData(Context context, T data, int position);
public void onViewRecycled() {
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public void setOnClickListener(View.OnClickListener onClickListener) {
itemView.setOnClickListener(onClickListener);
}
}
使用者只需要继承BaseViewHolder并重写抽象方法,在initItemView中findView在loadItemData中重写布局更新,然后在Activity直接服用BaseAdapter即可
如下所示
先实现一个ViewHolder
public class CourseDataViewHolder extends BaseViewHolder<Boolean> {
@BindView(R.id.tv_number)
TextView tvNumber;
@BindView(R.id.cv_date)
CardView cvDate;
public CourseDataViewHolder(@NonNull View itemView) {
super(itemView);
}
@Override
protected void initItemView(View view) {
ButterKnife.bind(this, view);
}
@Override
public void loadItemData(Context context, Boolean data, int position) {
if (data) {
cvDate.setCardBackgroundColor(UiHelper.getColor(R.color.colorPrimary));
} else {
cvDate.setCardBackgroundColor(UiHelper.getColor(R.color.bg_no));
}
tvNumber.setText(String.valueOf(position + 1));
}
}
然后这样复用即可
new BaseRecycleAdapter<>(data, R.layout.item_course_date, CourseDataViewHolder.class);
同理当需要使用Adapter的通用方法时,直接调用即可。比如给item添加点击监听
代码如下所示
baseRecycleAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(Object itemData, View view, int position) {
//重写item被点击后要处理的事情
}
});
总结
其实这只是一个简单的抽象过程。使用多了,也就明白了
网友评论