基于Recyclerview的万能适配器封装-结合实际开发项目碰到的诸多问题的总结

         对于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;
    }
}

 

你可能感兴趣的:(android)