我想先说一下我们设计通用适配器的设计原理:
我们的通用适配器和普通适配器不同:首先我们不知道我们要展现什么样的布局,其次,我们也不知道布局中的控件类型,更不知道控件的id,在这种三不知的情况下,唯一能让我们实现的,只有 通过回调方法,我们通过在适配器中添加抽象类或接口,把让控件和数据连接的过程通过回调方法实现。
而为了获取我们的控件,我们加大了ViewHolder的作用空间,让他不单单存储控件,直接用来存储布局,反正布局和控件是我们通过参数传入的,布局中控件的id我们自己知道,在回调方法中自行操作就好了。这就是通用适配器的原理。
在正式写代码之前,我们首先写一下我们的基础代码,包括控件布局和bean类:
private RecyclerView mRv; private List<Book> bookList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); beans(); initViews(); } private void beans() { bookList = new ArrayList<>(); for (int i = 0; i < 5; i++) { bookList.add(new Book("我是第" + (i + 1) + "本书", "10" + i, R.drawable.a, true)); } for (int i = 5; i < 10; i++) { bookList.add(new Book("我是第" + (i + 1) + "本书", "10" + i, R.drawable.b, false)); } for (int i = 10; i < 15; i++) { bookList.add(new Book("我是第" + (i + 1) + "本书", "10" + i, R.drawable.c, true)); } for (int i = 15; i < 20; i++) { bookList.add(new Book("我是第" + (i + 1) + "本书", "10" + i, R.drawable.d, false)); } } private void initViews() { mRv = (RecyclerView) findViewById(R.id.mrv);
}
1.创建适配器:
1).我们要让适配器继承RecyclerView的Adapter,
public class RvAdapter extends RecyclerView.Adapter {}
2).我们需要创建一个类MyViewHolder去继承RecyclerView的ViewHolder,这个RecyclerView跟我们的ListView有一点不太一样,他有自己的ViewHolder类,所以我们需要去继承他。
public static class MyViewHolder extends RecyclerView.ViewHolder{ public MyViewHolder(View itemView) { super(itemView); } }由于等会儿要把ViewHolder写成单例模式,所以我这里就直接写成静态内部类啦。
3).添加泛型和指定泛型
添加泛型我们不用多解释,因为我们要做的是万能适配器,适配的数据类型不确定,所以要用泛型来表示。
public class RvAdapter<T> extends RecyclerView.Adapter<RvAdapter.MyViewHolder>指定泛型是在我们RecyclerView.Adapter后面,指定成我们刚刚继承RecyclerView.Adapter的类名。
4).重写方法:
我们RecyclerView需要重写三个方法,onCreateViewHolder、onBindViewHolder和getItemCount
在这之前我们先把属性添加到adapter中。我们需要两个属性,一个是我们布局id,另一个是我们的数据集合。
private int mLayRes; private List<T> list;由于我们发现RecyclerView中没有convertView这个属性,所以我们要创建一个View 属性,用于优化。
private View convertView;
最简单的就是getItemCount方法了,我们只需要返回list的大小就好。
@Override public int getItemCount() { return list.size(); }然后是onCreateViewHolder。
在这之前我们先完善一下我们的MyViewHolder类吧。
完善MyViewHolder类:
首先我们把构造方法改为私有构造方法,我们发现他构造方法中有一个View参数,那我们在MyViewHolder中添加一个属性来接收他。
private View itemView; private MyViewHolder(View itemView) { super(itemView); this.itemView = itemView; }然后我们要创建一个静态方法来bind,用来获取ViewHolder对象:
public static MyViewHolder bind(ViewGroup parent,int viewType,int LayRes,View convertView){ MyViewHolder holder; if(convertView == null){ convertView = LayoutInflater.from(parent.getContext()).inflate(LayRes,parent,false); holder = new MyViewHolder(convertView); }else holder = (MyViewHolder) convertView.getTag(); return holder; }里面的几个参数大家都可以看懂的,这段代码是不是跟ListView中getView的优化很像呢?
我们的MyViewHolder还需要一个必要的属性:
private SparseArray<View> views = new SparseArray<>();我们用SparseArray来存储控件,指定泛型为View,他可以直接根据我们控件的id来查找对应id的控件。
现在我们的MyViewHolder还需要几个方法,用来辅助我们操作:
①.获取指定控件
public <T extends View> T getView(int id) { T t = (T) views.get(id); if (t == null) { t = itemView.findViewById(id); views.put(id, t); } return t; }我们通过getView方法,通过id获取到对应控件。
②.添加文本数据
public MyViewHolder setText(int id, CharSequence text) { TextView view = getView(id); view.setText(text); return this; }③.添加图片
public MyViewHolder setImgRes(int id, int resId) { ImageView view = getView(id); view.setImageResource(resId); return this; }④.。。。。。其实到现在这个适配器的ViewHolder已经基本写好了,我们只添加了两个最基本的,文字和图片,如果想要拓展就可以自己添加方法了。好了。现在我们在onCreateViewHolder中通过bind方法得到一个MyViewHolder对象。
@Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return MyViewHolder.bind(parent,viewType,mLayRes,convertView); }然后我们的onBindViewHolder方法也很简单,由于我们不知道什么样的控件,什么样的布局,所以我们没有办法把onBindViewHolder写死,所以我们写一个抽象类用于回调。
public abstract void bindView(MyViewHolder holder,int position);同时在onBindViewHolder中调用这个方法:
@Override public void onBindViewHolder(MyViewHolder holder, int position) { bindView(holder,position); }
好了,到现在为止我们的适配器就已经全部写完了,我们来检验一下看一下效果如何:
RvAdapter<Book> adapter = new RvAdapter<Book>(android.R.layout.activity_list_item, bookList) { @Override public void bindView(MyViewHolder holder, int position) { holder.setText(android.R.id.text1, bookList.get(position).getName()); holder.setImgRes(android.R.id.icon, bookList.get(position).getPic()); } }; LinearLayoutManager manager = new LinearLayoutManager(this); mRv.setLayoutManager(manager); mRv.setAdapter(adapter);
为了方便,我直接使用系统内置的布局了哈哈哈哈,别说我懒。
看来是可以使用的,现在大家知道如何配置RecyclerView的通用适配器了吗?