Adapter的封装

来源:互联网 发布:东华软件股份公司官网 编辑:程序博客网 时间:2024/06/06 21:38

简述

先前在一微信讨论组里讨论起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
原创粉丝点击