Adapter的封装

简述

先前在一微信讨论组里讨论起adapter和viewholder的抽取,有的说他们项目里用的是抽得他妈都不认识…他妈都不认识那还怎么用? 也看了github上几个开源出来的抽取方法,有的抽取过度,有的不便使用,还要自己记id什么的,感觉都不是很满意,于是回头看看自己项目里封装的,感觉还是比较合理的,兼顾了可读性和重用性,封装也适度,于是把它放出来,与大家交流交流.

抽取封装BaseAdapter的基本原理(以listview为例)

convertview:

listview本身提供的复用机制,缓存的是item的rootview,避免了每次getview都去将整个xml解析成一个view对象.

内部是若干个View数组,数组个数等于getItemTypeCount(),每次getview方法的参数里传入的convertview对象就是根据getItemType(pisition)在指定的数组中取缓存的view.

viewholder

缓存了itemview内部需要用到的各个子view对象,避免了每次getview时都从rootview对象中findviewById,减少了xml解析的时间.
viewholder的复用是通过与converview绑定,借用convertview的复用机制来达到复用的效果.绑定与取出是通过view.settag(obj)和view.gettag()来实现的.

封装技巧

viewholder

不仅封装了各子view对象,也同时封装rootview对象,便于设定整个item的点击事件,长按事件,这样就避开了对listview设置onItemClickListener时可能发生的无焦点等坑爹事件.

提供设置布局的xml的方法,让子类实现,用@LayoutRes注解限定返回值.

使用Butterkinfe结合其对应的android studio插件来快速生成view对象的代码.

将数据和事件设置到各view上封装成统一的方法,让adapter里直接调用holder的方法,而无多余代码. 数据ben采用泛型,让子类实现时直接指定.

代码:

public abstract class SuperLvHolder<T> {
    public View rootView;

    public SuperLvHolder(Activity context){
        rootView = View.inflate(context,setLayoutRes(),null);
        ButterKnife.bind(this,rootView);
    }

    protected abstract  @LayoutRes  int setLayoutRes();

    /**
     * 一般情况下,实现这个方法就足够了
     * @param context
     * @param bean
     */
    public  abstract void assingDatasAndEvents(Activity context, T bean);

    /**
     * 如果有需要,才实现这个方法
     * @param context activity实例,用于一些点击事件
     * @param bean 该条目的数据
     * @param position 该条目所在的位置
     * @param isLast 是否为最后一条,有些情况下需要用到
     * @param isListViewFling listview是不是在惯性滑动,备用.一般图片加载框架会提供全局暂停和恢复的方法,无需此参数
     *  @param datas 整个listview对应的数据
     * @param superAdapter adapter对象引用,可用于触发notifydatesetChanged()方法刷新整个listview,比如更改的单选按钮
     */
    public void assingDatasAndEvents(Activity context, T bean, int position ,boolean isLast,
                                     boolean isListViewFling,List datas, SuperLvAdapter superAdapter){
        assingDatasAndEvents(context,bean);
    }
}

adapter

继承BaseAdapter,四个抽象方法都用重写,getview里方法使用上面封装好的viewholder,只需要提供一个抽象方法generateNewHolder(int itemtype).而且,即使是多种type的item,也无需更改getview里的逻辑.

adapter 对外提供添加数据,刷新数据,删除数据等功能,这些功能抽取成接口并让adapter实现。

adapter不指定数据类型,因为多种类型时,可能会有多种数据类型。

/**
* 单一的item
* Created by Administrator on 2016/4/15 0015.
*/
public abstract class SuperLvAdapter extends BaseAdapter implements Refreshable {
    List datas;
    Activity context;
    boolean isListViewFling;

    public boolean isListViewFling() {
        return isListViewFling;
    }

    public void setListViewFling(boolean listViewFling) {
        isListViewFling = listViewFling;
    }


    public SuperLvAdapter(@NonNull List datas, Activity context){
        if (datas == null){
            throw new RuntimeException("datas cannot be null");
        }
        this.datas = datas;
        this.context = context;
    }
    @Override
    public int getCount() {
        if (datas == null )
        return 0;
        return datas.size();
    }



    @Override
    public Object getItem(int position) {
        if (datas == null)
        return null;
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        if (datas == null){
            return 0;
        }
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        SuperLvHolder holder = null;
        if (convertView == null){
            holder = generateNewHolder(context,getItemViewType(position));//考虑多种类型的item
            convertView = holder.rootView;
            convertView.setTag(holder);
        }else {
            holder = (SuperLvHolder) convertView.getTag();
        }
        holder.assingDatasAndEvents(context,datas.get(position),position,position == getCount() -1,isListViewFling,datas,this);
        return convertView;
    }

    protected abstract SuperLvHolder generateNewHolder(Activity context, int itemViewType);//子类需要实现的唯一方法

    @Override
    public void refresh(List newData){
        if (newData == null){
            datas.clear();
            notifyDataSetChanged();
            return;
        }
        if (datas == null){
            datas = newData;
            notifyDataSetChanged();
        }else {
            datas.clear();
            datas.addAll(newData);
            notifyDataSetChanged();
        }
    }
    @Override
    public void addAll(List newData){
        if (newData == null){
            return;
        }
        if (datas == null){
            datas = newData;
            notifyDataSetChanged();
        }else {
            datas.addAll(newData);
            notifyDataSetChanged();
        }
    }
    @Override
    public void clear(){
        if (datas != null){
            datas.clear();
            notifyDataSetChanged();
        }
    }
    @Override
    public void delete(int position){
        if (datas != null && position < getCount()){
            datas.remove(position);
            notifyDataSetChanged();
        }
    }
    public List getListData(){
        return datas;
    }

    @Override
    public void add(Object object) {
        if (object ==null)
            return;
        try {
            datas.add(object);
            notifyDataSetChanged();
        }catch (Exception e){

        }
    }
}

RecycleView的adapter封装形式类似

有一点不同是ViewHolder必须继承RecyView.ViewHolder,构造函数被限定只能传一个itemview对象进来,所以layout文件id不能封装到holder内部,只能从外部指定并inflate成view后传递进来.

public abstract  class SuperRcvHolder<T> extends RecyclerView.ViewHolder {
    public  View rootView;//用于外部对itemview的引用操作

    public SuperRcvHolder(View itemView) {
        super(itemView);
        rootView = itemView;
        ButterKnife.bind(this,rootView);
    }
....
}

使用

使用技巧:

listview,recycleview的数据与界面达到完全的一一对应,如果服务器返回的数据不对应,那么重新组合,如果有一个item无需数据,那么在datas里插入null或无意义的数据,holder多一种类型来处理即可.

SuperLvAdapter:

单一类型item时,使用匿名实现类即可,多种类型时,重写getItemViewTypeCount和getItemViewType(position) 即可,无需更改getview内部的逻辑.

SuperLvHolder:

写子类的时候在子类内部指定layout文件,一般情况下,实现assingDatasAndEvents(Activity context, String bean)就可以,如果要用到int position ,boolean isLast,就实现更多参数的同名方法,此时,上面那个简化的方法空实现即可.

如果该holder在多个地方使用,那么可以作为单独的类,达到复用的目的.

SuperRcvAdapter和SuperRcvHolder

adapter一般情况下都使用匿名子类.多个item时分别指定类型和对应的hoder即可.
holder一般也使用匿名子类.如果在其他页面需要复用,那么可以写成单独的子类.其layout文件需要在构造函数前传入,已封装好方法.

示例代码

AbstractListview 的 SuperLvAdapter:

adapter:

 ListView listView = new ListView(this);
    ArrayList<String> datas = new ArrayList<>();

    SuperLvAdapter adapter = new SuperLvAdapter(datas, this) {
        @Override
        protected SuperLvHolder generateNewHolder(Activity context,int viewType) {
            return new CustomHolder(context);
        }
    };

    listView.setAdapter(adapter);


    adapter.add("hhhh");

viewholder的实现:

  class CustomHolder extends SuperLvHolder<String> {

        @Bind(R.id.tv_text)
        TextView mTvText;

        public CustomHolder(Activity context) {
            super(context);
        }

        @Override
        protected int setLayoutRes() {
            return R.layout.holder_demo_list;
        }

        @Override
        public void assingDatasAndEvents(Activity context, String bean) {
            mTvText.setText(bean);
        }
}

RecycleView 的 SuperRcvAdapter:

SuperRcvAdapter,多种类型下的使用

 mAdapter = new SuperRcvAdapter(datas, mActivity) {

        public static final int TYPE_0 = 0;
        public static final int TYPE_1 = 1;

        @Override
        protected SuperRcvHolder generateCoustomViewHolder(int viewType) {

            switch (viewType) {
                case TYPE_0:
                    return new CustomHolder(inflate(R.layout.holder_demo_list));
                case TYPE_1:
                    return new CustomHolder2(inflate(R.layout.holder_demo_list_2));
                default:
                    return new SuperRcvHolder<String>(inflate(R.layout.holder_demo_list_2)) {//匿名子类
                        private TextView tv_text;

                        @Override
                        public void assignDatasAndEvents(Activity context, String data) {
                            super.assignDatasAndEvents(context, data);
                            tv_text.setText(data);
                        }
                    };
            }

        }


        @Override
        public int getItemViewType(int position) {
            if (position % 2 == 0) {//偶数位
                return TYPE_0;
            } else {//奇数位
                return TYPE_1;
            }

        }
    };

    mRecyclerView.setAdapter(mAdapter);

holder的实现:

class CustomHolder extends SuperRcvHolder<String> {

    @Bind(R.id.tv_text)
    TextView mTvText;

    public CustomHolder(View itemView) {
        super(itemView);
    }


    @Override
    public void assignDatasAndEvents(Activity context, String data) {
        mTvText.setText(data);
    }

     //备用
    @Override
    public void assignDatasAndEvents(Activity context, String data, int position, boolean isLast, 
                                     boolean isListViewFling, List datas, SuperRcvAdapter superRecyAdapter) {
        super.assignDatasAndEvents(context, data, position, isLast, isListViewFling, datas, superRecyAdapter);
    }
}

代码地址

SuperAdapter : https://github.com/glassLake/SuperAdapter

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值