ListView通用泛型适配器

作者:Zy_JiBai 时间:2022-06-30 11:52:04 

还记得我们之前说的ListView吗,(这个难用的控件-。+)我们在用他的同时也用到了一个叫做适配器Adapter的东西。一般我们用一个类继承BaseAdapter,来进行数据和控件的适配。

但是我们每一种适配器都只是为了适配一种数据源和一种布局,如果用到的少还好,如果要用到十几种,我们是不是要写十几个适配器呢?这个想法真的是太蠢了!

有一种适配器写法,可以做到一个适配器与多种类型数据和布局进行适配,这个东西叫做通用适配器(因为他是用到泛型实现的,我称他为泛型适配器),今天我们来看一下这种适配器的写法:

在写之前呢,我们首先回忆一下之前所用到的BaseAdapter适配器:

我们通过继承BaseAdapter,实现了他的四个方法:getCount,getPosition,getItem,和getView。其中最难写的就是getView了,然后我们还对他进行了优化:通过写一个叫做ViewHolder的类,在里面放入对应的控件。

现在我们首先来说一下通用适配器和一般的适配器的区别和相同点:

ListView通用泛型适配器

接下来我们正式来看一下通用适配器的写法:

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,给大家画一张图就很明白了:

ListView通用泛型适配器

索引什么的画的可能不准确,但是主要就是这么个意思,大家领会精神哈。

最后返回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);
}

ListView通用泛型适配器

虽然效果有点丑,但是功能我们实现了哈哈,大家如果不信可以在创建一个新的bean类和新的layout布局试验一下,同样可以。

来源:https://blog.csdn.net/zy_jibai/article/details/80216694

标签:android,listview,适配器
0
投稿

猜你喜欢

  • Spring Boot 2结合Spring security + JWT实现微信小程序登录

    2022-07-14 08:25:54
  • datatables 带查询条件java服务端分页处理实例

    2023-12-24 08:48:16
  • Spring Boot应用监控的实战教程

    2022-03-02 18:17:09
  • C#发送HttpPost请求来调用WebService的方法

    2021-06-11 03:36:30
  • springboot+vue部署按照及运行方法

    2023-07-15 06:16:09
  • Spring Security 控制授权的方法

    2023-08-06 19:21:08
  • C#使用linq对数组进行筛选排序的方法

    2023-12-06 06:21:21
  • Java套接字(Socket)网络编程入门

    2022-09-22 16:19:56
  • 微信公众号开发之设置自定义菜单实例代码【java版】

    2022-09-02 00:52:22
  • C#中比较常用的DateTime结构的使用方法

    2023-01-06 21:33:11
  • Java中的javaBean、vo、entity、domain和pojo

    2023-03-30 21:41:20
  • C++实现leetcode(3.最长无重复字符的子串)

    2023-06-25 03:17:22
  • java并发包JUC同步器框架AQS框架原文翻译

    2022-08-22 22:17:23
  • spring异步service中处理线程数限制详解

    2021-09-02 23:20:12
  • springmvc4+hibernate4分页查询功能实现

    2021-08-16 02:28:08
  • Spring Boot 详细分析Conditional自动化配置注解

    2021-11-25 21:56:14
  • 详解Java编程中向量(Vector)的应用

    2021-05-31 18:14:27
  • Java实现解出世界最难九宫格问题

    2022-06-14 19:47:10
  • Java常用类String的面试题汇总(java面试题)

    2023-11-23 20:40:45
  • Java多线程yield心得分享

    2023-11-29 05:58:15
  • asp之家 软件编程 m.aspxhome.com