对于Android移动端来说,大部分列表型界面都需要使用适配器。如果不做好封装,每个界面都定义一个适配器的话,那么不但会使代码变得臃肿,而且以后维护起来也不方便,因此封装一个通用的基类适配器还是挺有必要的,这样不仅可以减少很多很多冗余代码,更重要的是能提高开发效率。网上也有类似的Demo,但是大都是实现复用功能的封装,如果真正开发,还会碰到很多很多问题,此适配器特色:
1.实现最基本的ContentView的复用功能,提高性能;
2.可以自定义View的每个条目的动画;
3.解决了单个子条目中包还EditText时,由于控件复用引发的字体错乱问题;
4.解决了单个子条目包还RatingBar(五星评论条)时,由于控件复用,当滑动列表时引发的评论星数条错乱问题;
5.适配主流三方上拉下拉控件库CommonPullToRefresh(github stars 1000+的开元项目);
6.VM之间的解耦,控件的点击事件传递的数据可以通过传入的集合里的泛型携带,解决传统的通过接口回调设置点击事件;
7.支持ItemView里任意一个子View设置点击事件监听。
8.待添加的功能-实际开发中碰到的其他问题,EditText高度固定,会导致字体滚动与Recyclerview滚动冲突、
同时支持上拉下拉刷新+一级标题粘性悬停效果等。
功能实现简单介绍:
功能1的实现:在ViewHolder里的构造函数里:
public ViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
this.itemView = itemView;
this.itemView.setOnClickListener(v -> onItemClick(getAdapterPosition()));
setTextChangedListener(this, itemView);
setRatingBarListener(this, itemView);
}
public T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
SparseArray底层实现参考地址:https://www.cnblogs.com/RGogoing/p/5095168.html
查找效率更高,可以提高性能。
功能2的实现:
protected void itemAnimation(View itemView, int position) {
if (!mAnimateItems) return;
if (position > mLastAnimatedPosition) {
mLastAnimatedPosition = position;
itemView.setTranslationY(getScreenHeight());
itemView.animate()
.translationY(50)
.setStartDelay(100)
.setInterpolator(new DecelerateInterpolator(3.f))
.setDuration(300)
.start();
}
}
在 onBindViewHolder()方法中调用即可。
功能3,4的实现:EditText的输入监听,RatingBar的触摸监听需要放在创建ViewHolder里,而不是绑定ViewHolder方法里。同时考虑到只有个别界面有用到这两个控件的需求,可以在适配器添加调用逻辑,真正的处理逻辑放到子类里:
/**
* 为edittext添加文本监听(如果需要,重写此方法就行)
*
* @param holder
* @param itemView
*/
protected void setTextChangedListener(ViewHolder holder, View itemView) {
}
protected void setRatingBarListener(ViewHolder holder, View itemView) {
}
功能5的实现:为了适配CommonPullToRefresh,万能适配器需要继承sdk里的Recyclerview.Adapter,注意泛型也需要是sdk里的
RecyclerView.ViewHolder,重写的ViewHolder需要继承它即可,否则可能会报类型转换异常。
功能6的实现:这个功能替代了传统的接口回调功能。可以通过
BaseRecyclerViewAdapter
里的泛型传递展示数据和传递数据,甚至可以通过泛型里的参数指定ItemView的类型及控件是否可点击等功能。
功能7的实现:如果是列表子项目整体点击,子类重写此方法:
/**
* ItemView的单击事件(如果需要,重写此方法就行)
*
* @param position
*/
protected void onItemClick(int position) {
}
如果指定子项目里的某个控件的点击事件,子类需要重写此方法:
/**
* ItemView里的某个子控件的单击事件(如果需要,重写此方法就行)
* 只有在{@link #onBindDataToView}注册了{@link ViewHolder#setClickListener}才有效果
*
* @param position
*/
protected void onSingleViewClick(View view,int position) {
}
以下是此适配器的所有代码:
public abstract class BaseRecyclerViewAdapter
extends RecyclerView.Adapter {
protected List mBeans;
protected Context mContext;
/**
* 是否开启item动画
*/
protected boolean mAnimateItems = false;
protected int mLastAnimatedPosition = -1;
public BaseRecyclerViewAdapter(Context context, List beans) {
mContext = context;
mBeans = beans;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(getItemLayoutId(viewType), parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
itemAnimation(holder.itemView, position);
ViewHolder viewHolder= (ViewHolder) holder;
onBindDataToView(viewHolder, mBeans.get(position));
}
@Override
public int getItemCount() {
return mBeans.size();
}
public void add(T bean) {
mBeans.add(bean);
notifyDataSetChanged();
}
public void addAll(List beans) {
mBeans.addAll(beans);
notifyDataSetChanged();
}
public void clear() {
mBeans.clear();
notifyDataSetChanged();
}
/**
* item动画
*
* @param itemView
* @param position
*/
protected void itemAnimation(View itemView, int position) {
if (!mAnimateItems) return;
if (position > mLastAnimatedPosition) {
mLastAnimatedPosition = position;
itemView.setTranslationY(getScreenHeight());
itemView.animate()
.translationY(50)
.setStartDelay(100)
.setInterpolator(new DecelerateInterpolator(3.f))
.setDuration(300)
.start();
}
}
private int getScreenHeight() {
return mContext.getResources().getDisplayMetrics().heightPixels;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private final SparseArray mViews;
private View itemView;
public ViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
this.itemView = itemView;
this.itemView.setOnClickListener(v -> onItemClick(getAdapterPosition()));
setTextChangedListener(this, itemView);
setRatingBarListener(this, itemView);
}
public T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public void setText(int viewId, String text) {
TextView tv = getView(viewId);
tv.setText(text);
}
public void setImage(int viewId, Object params) {
ImageView iv = getView(viewId);
if (params instanceof String) {
//自己写加载图片的逻辑
} else if (params instanceof Integer) {
iv.setImageResource((Integer) params);
} else if (params instanceof Bitmap) {
iv.setImageBitmap((Bitmap) params);
} else if (params instanceof Drawable) {
iv.setImageDrawable((Drawable) params);
} else {
try {
throw new Exception("params is wrong!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void setClickListener(int viewId) {
View v = getView(viewId);
v.setOnClickListener(v1 -> onSingleViewClick(v1,getAdapterPosition()));
}
public int getCurrentPosition() {
return getAdapterPosition();
}
}
/**
* 绑定布局id
*
* @param viewType
* @return
*/
protected abstract int getItemLayoutId(int viewType);
/**
* 绑定数据
*
* @param holder
* @param t
*/
protected abstract void onBindDataToView(ViewHolder holder, T t);
/**
* 支持多种ItemType
*
* @param position
* @return
*/
@Override
public abstract int getItemViewType(int position);
/**
* ItemView里的某个子控件的单击事件(如果需要,重写此方法就行)
* 只有在{@link #onBindDataToView}注册了{@link ViewHolder#setClickListener}才有效果
*
* @param position
*/
protected void onSingleViewClick(View view,int position) {
}
/**
* ItemView的单击事件(如果需要,重写此方法就行)
*
* @param position
*/
protected void onItemClick(int position) {
}
/**
* 为edittext添加文本监听(如果需要,重写此方法就行)
*
* @param holder
* @param itemView
*/
protected void setTextChangedListener(ViewHolder holder, View itemView) {
}
protected void setRatingBarListener(ViewHolder holder, View itemView) {
}
}
子类必须要重写的方法:
class TestAdapter extends BaseRecyclerViewAdapter{
public TestAdapter(Context context, List beans) {
super(context, beans);
}
@Override
protected int getItemLayoutId(int viewType) {
return 0;
}
@Override
protected void onBindDataToView(ViewHolder holder, Universal_Cell_Class choseMajorBean) {
}
@Override
public int getItemViewType(int position) {
return 0;
}
}