Android RecyclerView设计通用Adapter
来源:互联网 发布:java 获得项目根目录 编辑:程序博客网 时间:2024/05/22 00:40
RecylerView 的使用频率现在也算做是很高了吧?使用起来的确是挺方便的,也容易实现一些比较好看的效果
一、一般步骤
一般的设计流程都是如下所示
首先是需要一个 JavaBean 来承载数据,包含的内容分别是标题还有内容
public class Data { private String title; private String content; public Data(String title, String content) { this.title = title; this.content = content; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}
然后继承 RecyclerView.Adapter ,并实现几个固定方法
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> { private List<Data> dataList; private LayoutInflater layoutInflater; public MyRecyclerAdapter(Context context, List<Data> dataList) { this.dataList = dataList; layoutInflater = LayoutInflater.from(context); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = layoutInflater.inflate(R.layout.item_left, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.tv_title.setText(dataList.get(position).getTitle()); holder.tv_content.setText(dataList.get(position).getContent()); } @Override public int getItemCount() { return dataList.size(); } class MyViewHolder extends RecyclerView.ViewHolder { private TextView tv_title; private TextView tv_content; MyViewHolder(View itemView) { super(itemView); tv_title = (TextView) itemView.findViewById(R.id.tv_title); tv_content = (TextView) itemView.findViewById(R.id.tv_content); } }}
使用到的布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="left" android:textSize="15sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="15sp" /> <View android:layout_width="match_parent" android:layout_height="1px" android:background="#abc" /></LinearLayout>
然后再来为 RecycleView 填充 Adapter
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView); recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); List<Data> dataList = new ArrayList<>(); for (int i = 0; i < 40; i++) { Data data = new Data("叶应是叶", "我也不知道说什么好,我也不知道说什么好,我也不知道说什么好,我也不知道说什么好"); dataList.add(data); } MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList); recyclerView.setAdapter(adapter); }}
运行效果如下
使用多了会发现,其实 MyRecyclerAdapter 中几个需要实现的方法其实都是步骤都是类似的,如果每次都要来重复这样的步骤,那真的是在做无用的工作了
这里,来为 RecycleView 设计一个通用 Adapter,取代掉那些重复的步骤,让代码更加简洁
二、通用ViewHolder
首先,继承 RecyclerView.ViewHolder 实现一个通用的 ViewHolder
当中,使用 SparseArray 来存放 View 以减少 findViewById 的次数,SparseArray 比 HashMap 更省内存,在某些条件下性能会更好,不过只能存储 key 为 int 类型的数据,正好用来存放资源ID
因为列表项中一般都是使用 TextView 和 ImageView 两个控件,所以这里提供两个控件的操作方法。此外,为了监听列表项单击和双击事件,这里再来自定义一个接口 onItemCommonClickListener ,用于点击事件回调
public class CommonViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener { // SparseArray 比 HashMap 更省内存,在某些条件下性能更好,只能存储 key 为 int 类型的数据, // 用来存放 View 以减少 findViewById 的次数 private SparseArray<View> viewSparseArray; private onItemCommonClickListener commonClickListener; public CommonViewHolder(View itemView) { super(itemView); itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); viewSparseArray = new SparseArray<>(); } /** * 根据 ID 来获取 View * * @param viewId viewID * @param <T> 泛型 * @return 将结果强转为 View 或 View 的子类型 */ public <T extends View> T getView(int viewId) { // 先从缓存中找,找打的话则直接返回 // 如果找不到则 findViewById ,再把结果存入缓存中 View view = viewSparseArray.get(viewId); if (view == null) { view = itemView.findViewById(viewId); viewSparseArray.put(viewId, view); } return (T) view; } public CommonViewHolder setText(int viewId, CharSequence text) { TextView tv = getView(viewId); tv.setText(text); return this; } public CommonViewHolder setViewVisibility(int viewId, int visibility) { getView(viewId).setVisibility(visibility); return this; } public CommonViewHolder setImageResource(int viewId, int resourceId) { ImageView imageView = getView(viewId); imageView.setImageResource(resourceId); return this; } protected interface onItemCommonClickListener { void onItemClickListener(int position); void onItemLongClickListener(int position); } public void setCommonClickListener(onItemCommonClickListener commonClickListener) { this.commonClickListener = commonClickListener; } @Override public void onClick(View v) { if (commonClickListener != null) { commonClickListener.onItemLongClickListener(getAdapterPosition()); } } @Override public boolean onLongClick(View v) { if (commonClickListener != null) { commonClickListener.onItemClickListener(getAdapterPosition()); } return false; }}
三、通用RecyclerAdapter
再来实现一个通用的 RecyclerView.Adapter
因为不知道要使用到的数据类型是哪一种,也为了更好的适配各种数据类型,所以这里需要用到泛型
当中,onBindViewHolder(CommonViewHolder holder, int position) 需要我们自己来操作,所以这里再来声明一个抽象方法 bindData(CommonViewHolder holder, T data) ,由子类来负责实现绑定操作
public abstract class CommonRecycleAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> { protected LayoutInflater layoutInflater; protected List<T> dataList; protected int layoutId; public CommonRecycleAdapter(Context context, List<T> dataList, int layoutId) { this.layoutInflater = LayoutInflater.from(context); this.dataList = dataList; this.layoutId = layoutId; } @Override public int getItemViewType(int position) { return super.getItemViewType(position); } @Override public CommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = layoutInflater.inflate(layoutId, parent, false); return new CommonViewHolder(itemView); } @Override public void onBindViewHolder(CommonViewHolder holder, int position) { bindData(holder, dataList.get(position)); } @Override public int getItemCount() { return dataList.size(); } abstract void bindData(CommonViewHolder holder, T data);}
这样,就有了一个实现了基本操作的通用 Adapter
这里再来看看如何使用它
四、使用通用 Adapter
需要先来继承 CommonRecycleAdapter ,只需要实现一个方法即可,看起来简洁多了吧
代码中声明了两个构造函数,根据是否需要用到点击事件监听来选择
public class MyAdapter extends CommonRecycleAdapter<Data> { private CommonViewHolder.onItemCommonClickListener commonClickListener; public MyAdapter(Context context, List<Data> dataList) { super(context, dataList, R.layout.item_left); } public MyAdapter(Context context, List<Data> dataList, CommonViewHolder.onItemCommonClickListener commonClickListener) { super(context, dataList, R.layout.item_left); this.commonClickListener = commonClickListener; } @Override void bindData(CommonViewHolder holder, Data data) { holder.setText(R.id.tv_title, data.getTitle()) .setText(R.id.tv_content, data.getContent()) .setCommonClickListener(commonClickListener); }}
这里选择要监听点击事件
public class MainActivity extends AppCompatActivity implements CommonViewHolder.onItemCommonClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView); recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); List<Data> dataList = new ArrayList<>(); for (int i = 0; i < 40; i++) { Data data = new Data("叶应是叶", "我也不知道说什么好,我也不知道说什么好,我也不知道说什么好,我也不知道说什么好"); dataList.add(data); } //MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList); MyAdapter adapter = new MyAdapter(this, dataList, this); recyclerView.setAdapter(adapter); } @Override public void onItemClickListener(int position) { Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClickListener(int position) { Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show(); }}
运行效果
五、布局多样化
前面,CommonRecycleAdapter 已经可以为我们节省很多代码了,免去了一些重复性操作
这里,可以再来更进一步使 CommonRecycleAdapter 得以更加通用
举个例子,类似微信App的聊天界面那样,自己发送的信息和对方发送来的信息位置是一左一右的,也就是说,这个聊天列表使用了两个不同的布局文件
那么,可以也为 CommonRecycleAdapter 加入支持多布局的功能
为了标识数据的不同,在 Data 类中加入一个新的变量 location
/** * Created by 叶应是叶 on 2017/2/25. */public class Data { private String title; private String content; private String location; public Data(String title, String content) { this.title = title; this.content = content; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; }}
之前使用的布局中标题是靠左的,这里再定义一个布局,使标题靠右
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:textSize="20sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" /> <View android:layout_width="match_parent" android:layout_height="1px" android:background="#abc" /></LinearLayout>
然后,需要有一个方法来判断哪种数据类型需要使用哪种布局,所以再来定义一个接口,getLayoutId() 用于返会布局文件ID
public interface MultiTypeSupport<T> { int getLayoutId(T item, int position);}
修改 CommonRecycleAdapter
如果 multiTypeSupport 不为 null,意思就是要使用到不同的布局文件了,则调用 getLayoutId() 方法,将其返回值作为 ItemViewType
public abstract class CommonRecycleAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> { protected LayoutInflater layoutInflater; protected List<T> dataList; protected int layoutId; protected MultiTypeSupport<T> multiTypeSupport; public CommonRecycleAdapter(Context context, List<T> dataList, int layoutId) { this.layoutInflater = LayoutInflater.from(context); this.dataList = dataList; this.layoutId = layoutId; } @Override public int getItemViewType(int position) { if (multiTypeSupport != null) { return multiTypeSupport.getLayoutId(dataList.get(position), position); } return super.getItemViewType(position); } @Override public CommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (multiTypeSupport != null) { layoutId = viewType; } View itemView = layoutInflater.inflate(layoutId, parent, false); return new CommonViewHolder(itemView); } @Override public void onBindViewHolder(CommonViewHolder holder, int position) { bindData(holder, dataList.get(position)); } @Override public int getItemCount() { return dataList.size(); } abstract void bindData(CommonViewHolder holder, T data);}
修改 MyAdapter 类,实现 MultiTypeSupport 接口,根据 Data 对象的 location 字段的值,来决定返回哪个布局文件的ID
public class MyAdapter extends CommonRecycleAdapter<Data> implements MultiTypeSupport<Data> { private CommonViewHolder.onItemCommonClickListener commonClickListener; public MyAdapter(Context context, List<Data> dataList) { super(context, dataList, R.layout.item_left); } public MyAdapter(Context context, List<Data> dataList, CommonViewHolder.onItemCommonClickListener commonClickListener) { super(context, dataList, R.layout.item_left); this.commonClickListener = commonClickListener; this.multiTypeSupport = this; } @Override void bindData(CommonViewHolder holder, Data data) { holder.setText(R.id.tv_title, data.getTitle()) .setText(R.id.tv_content, data.getContent()) .setCommonClickListener(commonClickListener); } @Override public int getLayoutId(Data item, int position) { if (item.getLocation().equals("left")) { return R.layout.item_left; } return R.layout.item_right; }}
再来修改 Activity 中的代码
public class MainActivity extends AppCompatActivity implements CommonViewHolder.onItemCommonClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView); recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); List<Data> dataList = new ArrayList<>(); boolean bool = false; for (int i = 0; i < 40; i++) { Data data = new Data("叶应是叶", "我也不知道说什么好,我也不知道说什么好,我也不知道说什么好,我也不知道说什么好"); if (!bool) { data.setLocation("left"); } else { data.setLocation("right"); } bool = !bool; dataList.add(data); } //MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList); MyAdapter adapter = new MyAdapter(this, dataList, this); recyclerView.setAdapter(adapter); } @Override public void onItemClickListener(int position) { Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClickListener(int position) { Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show(); }}
运行效果如下
可以看到,每隔一行使用到的布局都不一样
这样一来,通用的 Adapter 也设计完成了,以后即使需要使用到很多种不同的数据类型,只要继承 CommonRecycleAdapter ,实现一个或两个方法,就可以搞定 Adapter 了
这里提供示例代码下载:Android RecyclerView设计通用Adapter
- Android RecyclerView设计通用Adapter
- Android RecyclerView通用Adapter封装
- Android RecyclerView通用Adapter及item拖拽、滑动删除
- RecyclerView通用Adapter与ViewHolder
- 通用的RecyclerView Adapter适配器
- 打造 RecyclerView的 通用Adapter
- 实现RecyclerView 的通用Adapter
- 通用的recyclerview adapter 适配
- 为RecyclerView打造通用adapter
- 为RecyclerView打造通用Adapter
- 为RecyclerView打造通用Adapter
- 通用RecyclerView.Adapter和RecyclerView.ViewHolder
- android------通用ViewHolder 通用Adapter
- 打造AbsListView、RecyclerView的通用Adapter
- RecyclerView快速通用适配Adapter
- 打造通用的RecyclerView的Adapter
- 打造通用的Adapter为RecyclerView服务
- RecyclerView自定义基础通用Adapter和ViewHolder
- IOS开发学习笔记(五)
- Stanford Parser使用
- 源码安装nagios-4.2 (centos 6.8)
- springMVC前台时间字符串到后台Date的转换
- 446. Arithmetic Slices II
- Android RecyclerView设计通用Adapter
- 多线程-线程间的通信
- Linux下 ch340 插入后没有/dev/ttyUSB0设备的解决方法
- CentOS自动安装Maven
- MySQL数据库如何解决大数据量存储问题
- LeetCode Algorithms 2. Add Two Numbers 题解
- android 教程
- socket句柄泄漏问题的定位: losf和strace的联合使用!
- SpringMVC对日期类型的转换