万能适配器CommonAdapter和ViewHolder

对于我们Android程序猿来说,listview算是最常见的控件之一了,当然listview是要和adapter配套使用的,下面我们就来看看常规的adapter操作 

@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (convertView == null) {
			holder = new ViewHolder();
			convertView = inflater.inflate(R.layout.child_item, parent, false);
			holder.title = (TextView) convertView.findViewById(R.id.title);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		holder.title.setText(list.get(position).getPName());

		return convertView;
	}

	public static class ViewHolder {
		public TextView title;
	}
其他的方法就不说了,主要代码都是集中在getView方法中的,一个完整的项目里面应该也不会是只有一个listview和adapter,这样一来我们就需要重复性的写getview方法里面的代码,既然viewholder和findview,settag这些在adapter里面都要用到,我们能不能把这些都要用到的代码抽取出来呢?下面就说一下如何抽取。

@Override
	public int getCount() {
		return mList == null ? 0 : mList.size();
	}

	@Override
	public T getItem(int position) {
		return mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}


下面就一步步教你如何写一个万能的适配器CommonAdapter

首先我们写一个类继承BaseAdapter

public class CommonAdapter<T> extends BaseAdapter 

因为不能确定数据源是什么,所以我们用List<T>

public CommonAdapter(Context context, List<T> list, int itemLayoutId) {
		this.mItemLayoutId = itemLayoutId;
		this.context = context;
		this.mList = list;
		this.mInflater = LayoutInflater.from(context);
	}

我们通过实例化adapter的时候将item布局文件的id传进来,接下来要用到


在常规的操作中每个adapter我们需要一个ViewHolder,一个converView

接下来我们写一个ViewHolder

item中的控件可以是各种各样的,所以我们需要一个集合用来存放这些控件,考虑到效率问题,我们用SparseArray来存放这些组件

private CommonViewHolder(Context context) {
		this.mViews = new SparseArray<View>();
		
	}

既然是用来存放item中的组件的,那么我们就需要先拿到这些组件,那么就需要item的布局文件

@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (convertView == null) {
			holder = new ViewHolder();
			convertView = inflater.inflate(R.layout.child_item, parent, false);
			holder.title = (TextView) convertView.findViewById(R.id.title);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		holder.title.setText(list.get(position).getPName());

		return convertView;
	}
可以看到,我们需要三个参数,第一个是布局文件id,第二个是getview中的ViewGroup,第三个可以不用去管,

所以在我们的CommonViewHolder中就需要这些参数,我们将构造方法改成

private CommonViewHolder(Context context, ViewGroup parent, int layoutId) {
		this.mViews = new SparseArray<View>();
		this.mConverView = LayoutInflater.from(context).inflate(layoutId,
				parent, false);
		this.mConverView.setTag(this);
	}
这样一来我们就可以获取item的布局文件,接下来就是拿到item中的组件,并保存到集合中

public <T extends View> T getView(int viewId) {
		View view = mViews.get(viewId);
		if (view == null) {
			view = mConverView.findViewById(viewId);
			mViews.put(viewId, view);
		}
		return (T) view;
	}

接下来我们就可以给这些组件设置各自的值了

public CommonViewHolder setText(int viewId, String text) {
		TextView tv = getView(viewId);
		tv.setText(text);
		return this;
	}

这只是其中的一个,大家可以根据不同的需求后续添加


接下来就是getview方法了


既然getview方法里面需要我们进行不同的操作,那么我们就在adapter里面写一个抽象的方法,并在getview方法里面实现这个抽象,
@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		CommonViewHolder viewHolder = CommonViewHolder.get(context,
				convertView, parent, mItemLayoutId, position);
		convert(viewHolder, position, getItem(position));
		return viewHolder.getConvertView();
	}

	public abstract void convert(CommonViewHolder holder, int position, T item);

下面有demo的链接地址,大家可以下下来参考一下
Demo <a target=_blank href="http://download.csdn.net/detail/wrm0013/9001377">点击打开链接</a>




  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过封装BaseAdapter和RecyclerView.Adapter得到的通用的,简易的Adapter。项目地址:https://github.com/tianzhijiexian/CommonAdapter 效果图:已解决的问题 提升item的独立性,完美支持item被多处复用 item会根据type来做自动复用 支持多种类型的item 一个item仅会调用一次setViews(),避免重复建立监听器 一个item仅会触发一次绑定视图的操作,提示效率 支持dataBinding和其他第三方注入框架 提供了getView()方法来简化findViewById 支持通过item的构造方法来传入Activity对象 支持通过item的构造方法来传入item中事件的回调 提供了getConvertedData(data, type)方法来对item传入的数据做转换,方便拆包和提升item的复用性 支持viewpager的正常加载模式和懒加载 支持快速将listview适配器切换为recyclerView适配器 viewpager的notifyDataSetChanged可以正常更新界面 支持recyclerView的添加头部和底部 支持适配器的数据自动绑定,只用操作数据便可,adapter会自动notify界面零、重要接口adapter的item必须实现此接口,接口源码如下:public interface AdapterItem<T> {     /**      * @return item布局文件的layoutId      */     @LayoutRes     int getLayoutResId();     /**      * 初始化views      */     void bindViews(final View root);     /**      * 设置view的参数      */     void setViews();     /**      * 根据数据来设置item的内部views      *      * @param model    数据list内部的model      * @param position 当前adapter调用item的位置      */     void handleData(T model, int position); }例子:public class TextItem implements AdapterItem<DemoModel> {     @Override     public int getLayoutResId() {         return R.layout.demo_item_text;     }     TextView textView;     @Override     public void bindViews(View root) {         textView = (TextView) root.findViewById(R.id.textView);     }     @Override     public void setViews() { }     @Override     public void handleData(DemoModel model, int position) {         textView.setText(model.content);     } }一、ListView GridView的通用适配器——CommonAdapter只需继承CommonAdapter便可实现适配器:listView.setAdapter(new CommonAdapter<DemoModel>(data, 1) {     public AdapterItem<DemoModel> createItem(Object type) {         return new TextItem();     } });二、RecyclerView的通用适配器——CommonRcvAdapter通过继承CommonRcvAdapter来实现适配器:mAdapter = new CommonRcvAdapter<DemoModel>(data) {  public AdapterItem createItem(Object type) {         return new TextItem();   } };三、ViewPager的通用适配器——CommonPagerAdapter通过继承CommonPagerAdapter来实现适配器viewPager.setAdapter(new CommonPagerAdapter<DemoModel>() {     public AdapterItem createItem(Object type) {         return new TextItem();     } });设计思路1. Adapter如果用adapter常规写法,你会发现代码量很大,可读性低。如果adapter中有多个类型的Item,我们还得在getView()中写很多if-else语句,很乱。 而现在我让adapter的代码量减少到一个8行的内部类,如果你需要更换item只需要动一行代码,真正实现了可插拔化。最关键的是item现在作为了一个独立的对象,可以方便的进行复用。2. AdapterItem和原来方式最为不同的一点就是我把adapter的item作为了一个实体,这种方式借鉴了RecyclerViewViewHolder的设计。把item作为实体的好处有很多,比如复用啊,封装啊,其余的就不细说了。3. 分层在使用过程中,我发现如果adapter放在view层,那就会影响到view层的独立性。此外adapter中经常有很多数据处理的操作,比如通过type选择item,数据的拆包、转换等操作。于是我还是推荐把adapter放在mvp的p层,或者是mvvm的m层。通过在实际的项目中使用来看,放在m或p层的效果较好,view的复用也比较好做。
CommonAdapter 一个适用于ListView/GridView/RecyclerViewAdapter库,简化大量重复代码,支持多种布局,可自定义图片加载的实现。功能特点:简化大量重复代码支持多布局自定义图片加载常用数据操作view复用RecyclerView item 点击和长按事件gradle依赖dependencies {     compile 'com.classic.adapter:commonadapter:1.0'     //项目中使用到RecyclerView,需要添加依赖     compile 'com.android.support:recyclerview-v7:23.2.0'}ListView/GridView 使用示例:List<News> newsList = ...; //单布局文件 listView = (ListView) findViewById(R.id.listview); listView.setAdapter(new CommonAdapter<News>(context,     //item布局文件     R.layout.item_none_picture, newsList ) {     @Override public void onUpdate(BaseAdapterHelper helper, News item) {         //BaseAdapterHelper详细用法,见下方         helper.setText(R.id.xxx, item.getTitle())                //可实现ImageLoad接口,进行图片自定义加载方式,demo里面使用的Glide               .setImageLoad(new GlideImageLoad())               .setImageUrl(R.id.xxx,item.getCoverUrl());     } }); //多布局文件 private final class MultipleLayoutAdapter extends CommonAdapter<News>{     public MultipleLayoutAdapter(Context context, int layoutResId, List<News> data) {         super(context, layoutResId, data);     }     //多种布局重写此方法即可     @Override public int getLayoutResId(News item) {         int layoutResId = -1;         switch (item.getNewsType()){             case News.TYPE_NONE_PICTURE: //布局样式一                 layoutResId = R.layout.item_none_picture;                 break;             case News.TYPE_SINGLE_PICTURE: //布局样式二                 layoutResId = R.layout.item_single_picture;                 break;             case News.TYPE_MULTIPLE_PICTURE: //布局样式三                 layoutResId = R.layout.item_multiple_picture;                 break;             更多的布局样式 ...         }         return layoutResId;     }     @Override public void onUpdate(BaseAdapterHelper helper, News item) {         helper.setImageLoad(new GlideImageLoad());         switch (item.getNewsType()){             case News.TYPE_NONE_PICTURE: //布局样式一                 helper.setText(R.id.xxx, item.getTitle())                       .setImageUrl(R.id.xxx,item.getCoverUrl());                 break;             case News.TYPE_SINGLE_PICTURE: //布局样式二                 helper.setText(R.id.xxx, item.getTitle())                       .setImageUrl(R.id.xxx,item.getCoverUrl());                 break;             case News.TYPE_MULTIPLE_PICTURE: //布局样式三                 helper.setText(R.id.xxx, item.getTitle())                       .setImageUrl(R.id.xxx,item.getCoverUrl());                 break;             更多的布局样式 ...         }     } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值