kaikeba

Android 自定义 BaseAdapter 最佳实践

虽然现在很多新的项目都在使用RecyclerView,但是很多开发者在一些场景中还是倾向使用ListView或者GridView,然后就是需要写许多的Adapter。一次项目组在新启动一个新项目的时候,有个同事拿来了一个网上说的万能Adapter,在使用的时候发现即使在单个视图类型一旦逻辑判断比较复杂情况下非常不方便,更不用说在适配器Adapter中使用多视图类型了,这里仅是个人观点,也许没有掌握到精华,这是有关万能适配器Adapter的一片博文 Android 快速开发系列 打造万能的ListView GridView 适配器 

当然了随着RecyclerView的使用,网上也有很多有关对RecyclerView多视图类型Adapter封装的博客,MultiType 3.0是一个大神写的比较全面的Adapter,这篇博客Android 复杂的多类型列表视图新写法:MultiType 3.0有详细的用法。万能适配器Adapter自己使用不是很方便,于是就参看RecyclerView中Adapter的实现方式进行对BaseAdapter进行了简单的封装,封装的目一是为了少写代码,另外一个就是让逻辑看上去更清晰一些。我们知道在RecyclerView的Adapter实现中它将视图创建与数据绑定进行了分离,同时将对View的查找创建也剥离开来了,本文就主要介绍如何将BaseAdapter的使用封装为跟RecyclerView的Adapter使用方式一致。由于很多时候在Adapter中我们都是使用的简单的视图类型,即单类型视图,因此本文将单视图类型的Adapter单独封装了一下,比使用多视图类型的Adapter使用了更严格的数据类型检查,同时在使用上也方便了许多。

adapter_simpleadapter_multi

RecyclerView中Adapter的使用

在使用RecyclerView的Adapter的时候我们首先需要继承RecyclerView的一个静态内部类Adapter,然后重写三个方法,实际上下面三个方法是必须要重写的,因为都是抽象方法。

  • getItemCount()
  • onBindViewHolder(VH holder, int position)
  • onCreateViewHolder(ViewGroup parent, int viewType)

一般情况下重写上面三个方法就可以,但是如果存在多视图类型,在第三个方法
onCreateViewHolder()方法中我们也可以看到有一个参数是viewType,该参数作用就是针对不同的viewType需要创建不同的ViewHolder,因此还需要重写一个方法getItemViewType(int position),针对多视图类型同BaseAdapter实现方式倒是很像,在BaseAdapter中这是需要除此之外还要重写一个方法getViewTypeCount(),但是在RecyclerView的Adapter中不需要该方法。

简单类型Adapter

private class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {

	@Override
	public int getItemCount() {
		return COUNT;
	}

	@Override
	public void onBindViewHolder(MyViewHolder holder, int position) {
		holder.textView.setText("TEXT_" + position);
	}

	@Override
	public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
		View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_text, parent, false);
		MyViewHolder holder = new MyViewHolder(view);
		return holder;
	}

}

private static class MyViewHolder extends RecyclerView.ViewHolder {
	private TextView textView;

	public MyViewHolder(View itemView) {
		super(itemView);
		textView = (TextView) itemView.findViewById(R.id.textView);
	}
}

复杂类型Adapter

private class MyAdapter extends RecyclerView.Adapter<ViewHolder> {

	@Override
	public int getItemCount() {
		return COUNT;
	}

	@Override
	public int getItemViewType(int position) {
		return position % 2 == 0 ? TYPE_IMAGE : TYPE_TEXT;
	}

	@Override
	public void onBindViewHolder(ViewHolder holder, int position) {
		int type = getItemViewType(position);
		switch (type) {
		case TYPE_TEXT:
			((MyTextHolder) holder).textView.setText("TEXT_" + position);
			break;
		case TYPE_IMAGE:
			((MyImageHolder) holder).imageView.setImageResource(R.drawable.image);
			break;
		}
	}

	@Override
	public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
		View view;
		ViewHolder holder = null;
		switch (viewType) {
		case TYPE_TEXT:
			view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_text, parent, false);
			holder = new MyTextHolder(view);
			break;
		case TYPE_IMAGE:
			view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item_image, parent, false);
			holder = new MyImageHolder(view);
			break;
		}
		return holder;
	}
}

private class MyTextHolder extends RecyclerView.ViewHolder {
	private TextView textView;

	public MyTextHolder(View itemView) {
		super(itemView);
		textView = (TextView) itemView.findViewById(R.id.textView);
	}
}

private class MyImageHolder extends ViewHolder {
	private ImageView imageView;

	public MyImageHolder(View itemView) {
		super(itemView);
		imageView = (ImageView) itemView.findViewById(R.id.imageView);
	}
}

自定义BaseAdapter

在自定义基类之前,先简单分析一下,我们需要自定义一个支持单种视图的Adapter,还要自定义一个支持多种视图类型的Adapter,两个类都要继承BaseAdapter,先将两个类都公用的部分抽取出来定义为MyAdapter。

public abstract class MyAdapter<T> extends BaseAdapter {

	protected List<T> dataList = new ArrayList<>();
	protected Context context;
	protected LayoutInflater inflater;

	public MyAdapter(Context context) {
		this.context = context;
		inflater = LayoutInflater.from(context);
	}

	public void setDataList(List<T> dataList) {
		this.dataList = dataList;
		notifyDataSetChanged();
	}

	@Override
	public int getCount() {
		if (null == dataList) {
			return 0;
		}
		return dataList.size();
	}

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

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

}

在RecyclerView的Adapter实现中是没有getView()方法的,下面我们就分析一下getView()方法如何拆分,一般情况下我们在实现getView()方法都是如下流程。

public View getView(int position, View convertView, ViewGroup parent) {
	ViewHolder holder = null;
	if (null == convertView) {
		//填充布局
		convertView=inflater.inflate(R.layout.item_layout, parent,false);
		holder = new ViewHolder();
		//通过ID查询控件
		holder.textView=(TextView)convertView.findViewById(R.id.textView);
		holder.imageView=(ImageView)convertView.findViewById(R.id.imageView);
		convertView .setTag(holder);
	} else {
		holder = (ViewHolder) convertView.getTag();
	}
	//赋值逻辑
	return convertView;
}
//一个空的ViewHolder
public static class ViewHolder{
	TextView textView;
	ImageView imageView;
}

Java编程比较流行的一种编程方式不是说面向接口编程吗,在Android开发中也有一个开发方式叫做面向Holder的编程,上面代码是传统的实现ViewHolder的方式,说句实现话就没做什么事,就是作为一个载体承载着我们需要的控件。我们让ViewHolder多做一些事情,让它在convertView==null情况下需要做的多数逻辑都放到ViewHolder中去。

public class ViewHolder {
	private final View itemView;

	public ViewHolder(View itemView) {
		if (null == itemView) {
			throw new IllegalArgumentException("itemView must not be null");
		} else {
			this.itemView = itemView;
			itemView.setTag(this);
		}
	}

	public View getItemView() {
		return itemView;
	}
}

在ViewHolder中的itemView就是getView()方法中的convertView,这里刚好是条目的根View,类似RecyclerView中ViewHolder构造方法中itemView。由于不同的视图需要创建不同的ViewHolder,因此我们可以将创建ViewHolder的方法设置为抽象的方法暴露出去,另外赋值的时候我们也需要根据具体的业务进行赋值,同样设置一个抽象方法。

public abstract class SimpleAdapter<T,VH extends ViewHolder> extends MyAdapter<T> {

	public SimpleAdapter(Context context) {
		super(context);
	}

	public View getView(int position, View convertView, ViewGroup parent) {
        VH holder = null;
        if (null == convertView) {
            holder = onCreateViewHolder(parent);
            convertView = holder.getItemView();
        } else {
            holder = (VH) convertView.getTag();
        }
        onBindViewHolder(holder, position);
        return convertView;
    }

    public abstract void onBindViewHolder(VH holder, int position);

    public abstract VH onCreateViewHolder(ViewGroup parent);

}

在设置多视图类型的Adapter的时候只需要在创建ViewHolder的时候多传入一个viewType的参数即可。

public abstract class MultiAdapter<T> extends MyAdapter<T> {

	public MultiAdapter(Context context) {
		super(context);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (null == convertView) {
			holder = onCreateViewHolder(parent, getItemViewType(position));
			convertView = holder.getItemView();
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		onBindViewHolder(holder, position);
		return convertView;
	}

	public abstract void onBindViewHolder(ViewHolder holder, int position);

	public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

}

自定义BaseAdapter的使用

单视图类型SimpleAdapter使用

public class TextAdapter extends SimpleAdapter<String, TextAdapter.TextHolder> {

	public TextAdapter(Context context) {
		super(context);
	}

	@Override
	public void onBindViewHolder(TextHolder holder, int position) {
		holder.textView.setText(getItem(position));
	}

	@Override
	public TextHolder onCreateViewHolder(ViewGroup parent) {
		View convertView=inflater.inflate(R.layout.item_text, parent, false);
		return new TextHolder(convertView);
	}

	static class TextHolder extends ViewHolder{

		public TextView textView;

		public TextHolder(View itemView) {
			super(itemView);
			textView=(TextView) itemView.findViewById(R.id.textView);
		}
	}
}

这里我们使用了两个泛型,一个是ViewHolder中支持的数据类型String,另外一个就是我们需要创建的ViewHolder,这样在onCreateViewHolder方法的返回值就会自动返回我们自定义的ViewHolder,有关泛型更多的知识可以参看Java泛型使用解析,单视图类型Adapter的使用比RecyclerView的Adapter还要方便许多。

多视图类型的使用

public class RichAdapter extends MultiAdapter<String> {

	private static final int TEXT = 0;
	private static final int PIC = 1;

	public RichAdapter(Context context) {
		super(context);
	}

	@Override
	public int getViewTypeCount() {
		return 2;
	}

	@Override
	public int getItemViewType(int position) {
		if (position % 3 == 0) {
			return PIC;
		} else {
			return TEXT;
		}
	}

	@Override
	public void onBindViewHolder(ViewHolder holder, int position) {
		switch (getItemViewType(position)) {
		case TEXT:
			TextHolder textHolder=(TextHolder) holder;
			textHolder.textView.setText(getItem(position));
			break;
		case PIC:
			ImageHolder imageHolder=(ImageHolder) holder;
			imageHolder.imageView.setImageResource(R.drawable.image);
			break;
		}
	}

	@Override
	public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
		View itemView = null;
		ViewHolder holder = null;
		switch (viewType) {
		case TEXT:
			itemView = inflater.inflate(R.layout.item_text, parent, false);
			holder = new TextHolder(itemView);
			break;
		case PIC:
			itemView = inflater.inflate(R.layout.item_image, parent, false);
			holder = new ImageHolder(itemView);
			break;
		}
		return holder;
	}

	private static class TextHolder extends ViewHolder {
		TextView textView;

		public TextHolder(View itemView) {
			super(itemView);
			textView = (TextView) itemView.findViewById(R.id.textView);
		}

	}

	private static class ImageHolder extends ViewHolder {
		ImageView imageView;

		public ImageHolder(View itemView) {
			super(itemView);
			imageView = (ImageView) itemView.findViewById(R.id.imageView);
		}
	}
}

这里的使用情况跟RecyclerView的使用几乎是一模一样,唯一不一样的地方就是多写了一个getViewTypeCount()方法,在ListView或者GridView使用BaseAdapter实现多种类型视图的时候该方法必须要重写。

发表我的评论

取消评论
表情 插代码

Hi,您需要填写昵称和邮箱!

  • 必填项
  • 必填项