ListView通用泛型适配器
作者:Zy_JiBai 发布时间:2022-06-30 11:52:04
还记得我们之前说的ListView吗,(这个难用的控件-。+)我们在用他的同时也用到了一个叫做适配器Adapter的东西。一般我们用一个类继承BaseAdapter,来进行数据和控件的适配。
但是我们每一种适配器都只是为了适配一种数据源和一种布局,如果用到的少还好,如果要用到十几种,我们是不是要写十几个适配器呢?这个想法真的是太蠢了!
有一种适配器写法,可以做到一个适配器与多种类型数据和布局进行适配,这个东西叫做通用适配器(因为他是用到泛型实现的,我称他为泛型适配器),今天我们来看一下这种适配器的写法:
在写之前呢,我们首先回忆一下之前所用到的BaseAdapter适配器:
我们通过继承BaseAdapter,实现了他的四个方法:getCount,getPosition,getItem,和getView。其中最难写的就是getView了,然后我们还对他进行了优化:通过写一个叫做ViewHolder的类,在里面放入对应的控件。
现在我们首先来说一下通用适配器和一般的适配器的区别和相同点:
接下来我们正式来看一下通用适配器的写法:
1.先创建好我们今天需要的控件、源数据以及Bean类。
控件只有一个ListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.MainActivity">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mlv"/>
</LinearLayout>
beans方法模拟了一个假数据
private void beans() {
list = new ArrayList<>();
for (int i = 0; i < 16; i += 4) {
list.add(new Student("同学" + i, "男", 15 + i, R.drawable.a, true));
list.add(new Student("同学" + (i + 1), "男", 15 + i, R.drawable.b, false));
list.add(new Student("同学" + (i + 2), "男", 15 + i, R.drawable.c, false));
list.add(new Student("同学" + (i + 3), "男", 15 + i, R.drawable.d, true));
}
}
这是bean类
package zy.pers.homework_20.bean;
public class Student {
private String name;
private String sex;
private int age;
private int imgId;
private boolean isOver;
public Student(String name, String sex, int age, int imgId,boolean isOver) {
this.name = name;
this.sex = sex;
this.age = age;
this.imgId = imgId;
this.isOver = isOver;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getImgId() {
return imgId;
}
public void setImgId(int imgId) {
this.imgId = imgId;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", imgId=" + imgId +
'}';
}
public boolean isOver() {
return isOver;
}
public void setOver(boolean over) {
isOver = over;
}
}
2.创建MyBaseAdapter继承BaseAdapter
public class MyBaseAdapter<T> extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
3.我们说通用适配器传入布局id和源数据,所以我们定义这两个量接收传入的数据。
private List<Student> list;
private int mLayRes;
public MyBaseAdapter(List<Student> list, int mLayRes) {
this.list= list;
this.mLayRes = mLayRes;
}
4.重写我们的前三个方法
前三个方法应该算是比较简单的了,
@Override
public int getCount() {
return list != null ? list.size() : 0;
}
@Override
public T getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
第一个一个简单的判断,返回list的大小。第二个跟第三个和以前适配器一样,只是getItem的返回值写成了泛型。
5.写Viewholder类,这个是很麻烦的,我们先创建出来Viewholder,之后的方法我们一步一步添加。
public static class ViewHolder {
private SparseArray<View> mViews = new SparseArray<>();
private Context mContext;
private int position;
private int layRes;
private View itemView;
private ViewHolder(Context context, ViewGroup parent, int layRes) {
this.mContext = context;
this.layRes= layRes;
this.itemView = LayoutInflater.from(context).inflate(layRes, parent, false);
this.itemView.setTag(this);
}
public static ViewHolder bind(int position, View convertView, ViewGroup parent, int layRes, Context context) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder(context, parent, layRes);
} else {
holder = (ViewHolder) convertView.getTag();
holder.itemView = convertView;
}
holder.position = position;
return holder;
}
东西有点多,我们顺着逻辑慢慢看:
1)首先是通过单例来实现,所以我们需要一个私有化构造方法,里面有三个参数,分别是上下文,ViewGroup和布局id,这三个属性是我们必须要用到的,我们传入上下文获取inflater,把布局id传进去,然后把holder传入我们的itemView中。
这一步我们应该比较熟悉吧,我们以前是在getView中实现这一步的。
2)然后我们看下面的bind方法,他的参数有五个。其实有三个参数我们很熟悉,就是我们getView中的三个参数。在这基础上我们又添加了两个参数,布局id和上下文。
然后为了优化我们先判断当前的convertView是否为空,如果为空就新建一个Viewholder,让convertView在私有构造器中加载;如果不为空,直接通过getTag拿到。
注意我们要对holder中的两个参数进行修改,一个是itemView,一个是position。因为我们优化过后,如果convertView不为空,他里面是有之前的数据的,其他的几个属性我们不用管,但是这两个还是储存着上一个的内容。我们需要让他重新指向当前的convertView和position,给大家画一张图就很明白了:
索引什么的画的可能不准确,但是主要就是这么个意思,大家领会精神哈。
最后返回holder。
3)我们还需要返回我们加载完成的convertView,
public View getItemView() {
return itemView;
}
现在我们Viewholder基本框架写完了,我们暂时不管他了,去写getView。
6.重写方法getView:
我们刚才说了,在adapter中写一个抽象方法,然后通过回调方法,实现多类型适配,也就是说这个抽象方法是写我们给具体控件添加数据的,我们在这里面传递两个参数,一个是我们的Viewholder,另一个是对应位置的数据,类型为泛型。
public abstract void bindView(ViewHolder holder,T obj);
因为我们出现了抽象方法,所以我们的MyBaseAdapter需要变成抽象类,
public abstract class MyBaseAdapter<T> extends BaseAdapter {
这是我们的getView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.bind(position,convertView,parent,mLayRes,parent.getContext());
bindView(holder,list.get(position));
return holder.getItemView();
}
现在我们的适配器已经完成百分之九十了,还差一点,我们需要写几个辅助方法,为了方便我们等会进行适配。
1.获取指定控件
public <T extends View> T getView(int id){
T t = (T) mViews.get(id);
if(t == null){
t = itemView.findViewById(id);
mViews.put(id,t);
}
return t;
}
在Viewholder中写一个getView方法,通过控件id来获取指定控件。
2.TextView控件输入数据
public ViewHolder setText(int id,CharSequence text){
View view = getView(id);
if(view instanceof View){
((TextView)view).setText(text);
}
return this;
}
3.ImageView输入图片
public ViewHolder setImg(int id,int resId){
View view = getView(id);
if(view instanceof View){
((ImageView)view).setImageResource(resId);
}else
view.setBackgroundResource(resId);
return this;
}
4.复选框输入选定状态
public ViewHolder setCheckable(int id,boolean checkable){
View view = getView(id);
if(view instanceof View){
((CheckBox)view).setChecked(checkable);
}
return this;
}
好啦,先在我们的适配器完全写完了,我们来看一下效果吧。
private void initTools() {
ListView mLv = (ListView) findViewById(R.id.mlv);
adapter = new MyBaseAdapter<Student>(list,R.layout.item_one) {
@Override
public void bindView(ViewHolder holder, Student obj) {
holder.setText(R.id.name,obj.getName())
.setText(R.id.age,obj.getAge() + "")
.setText(R.id.sex,obj.getSex())
.setImg(R.id.head,obj.getImgId())
.setCheckable(R.id.mc,obj.isOver());
}
};
mLv.setAdapter(adapter);
}
虽然效果有点丑,但是功能我们实现了哈哈,大家如果不信可以在创建一个新的bean类和新的layout布局试验一下,同样可以。
来源:https://blog.csdn.net/zy_jibai/article/details/80216694


猜你喜欢
- 废话不多说,直接上代码Main代码package processdemo.example.administrator.processbard
- 在一个项目中我们可能会需要用到相同的布局设计,如果都写在一个xml文件中,代码显得很冗余,并且可读性也很差,所以我们可以把相同布局的代码单独
- 为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平。你的一个决定会影响团队未来的几年。要考虑方面太多:
- 大家好,这是 [C#.NET 拾遗补漏] 系列的第 07 篇文章。在 C# 中,大多数方法都是通过 return 语句立即把程序的控制权交回
- 前导:发过程中经常会使用java将office系列文档转换为PDF, 一般都使用微软提供的openoffice+jodconverter 实
- 在编程过程中一定要注意代码命名的规范性,否则在使用和维护过程中将造成很大的麻烦,这也是一种良好的编码习惯。看下面代码,除了userPass命
- 功能介绍大家都知道在Spring boot开发过程中,需要在配置文件里配置许多信息,如数据库的连接信息等,如果不加密,传明文,数据库就直接暴
- 一、什么是递归方法调用自己的行为就是递归,递归必须要有终止条件,不然它会无限递归。1.先来看一下一个递归的例子此程序的Fact方法从大到小地
- 相信对于一名JAVA开发者开说properties文件一定再熟悉不过了,比如一下配置:config.properties会经常存放一些系统常
- 目录效果图实现自定义软键盘1、通过xml定义键盘2、将xml文件与keyboardview绑定起来3、处理点击事件onKey附赠一些实用的效
- 一.前言:CentOS7.0虽然自带JDK1.7和1.8,运行“java -version”命令也可以看到版本信息,但是jdk的安装环境不全
- 详解Java读取Jar中资源文件及实现代码 &
- 每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联。Handler一般有两种用途:1)执行计划任务,
- 正确使用并行流,避免共享可变状态错用并行流而产生错误的首要原因,就是使用的算法改变了某些共享状态。下面是另一种实现对前n个自然数求和的方法,
- 一、前言什么是多渠道打包以及多渠道打包可以做什么,这里就不做介绍了,相信看到这篇文章的你已经了解了,多渠道打包的方式比较多,这里我们用Gra
- 前言碎语今天博主安利一个国产开源的无服务器容器云平台,关注它已经有一年多了,虽然其迭代到现在很多功能还是一直处于测试验证中,但是其设计理念以
- 在wpf中实现treeview的功能,可能看到很多分享的都是简单的绑定,仅此记录自己完成的功能。前台<TreeView x:Name=
- 所谓的浮动工具栏,效果图如下:也就是说,可以将工具栏拖出其原先的停靠位置,而且可以将拖出来的工具栏再拖放回去。实现的基本思路如下1、拖动出来
- 依然使用IE9的捕获参数,做了一个12306的登录功能。参照了网上童鞋们的做法。其他都和前面几篇读取余票、票价一样,不过登录要用到证书的问题
- launch 是 CoroutineScope 的一个扩展函数,该方法在不阻塞当前线程的情况下启动新的协程,launch 里面的代码虽然有挂