ListView通用泛型适配器

还记得我们之前说的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布局试验一下,同样可以。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • MVPXlistView展示上拉下拉效果

    本文实例为大家分享了MVPXlistView上拉下拉展示的具体代码,供大家参考,具体内容如下 抽基类 package com.gs.gg.day8.back; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; public abstract class BackActivity extends AppCompa

  • Android解决ScrollView下嵌套ListView和GridView中内容显示不全的问题

    最近为公司做的一个Demo里面用到了ScrollView嵌套了GridView和ListView,然而在嵌套的时候我发现GridView和ListView都是不能完全显示,显示的基本上都是单行的数据,最后查找资料和翻阅文档看到原因是ListView和GridView的绘制过程中在ScrollView中无法准确的测量自身的高度,而且listVIew和GridView抢占了焦点,使得ListView和GrideView具有自身的显示的效果,这样就测量出显示一行条目即可的距离,其他的条目根据自身的滑动

  • Android ListView和Adapter数据适配器的简单介绍

    ListView 显示大量相同格式数据 常用属性: listSelector listView每项在选中.按下等不同状态时的Drawable divider ListView每项间的间隔Drawable dividerHeight ListView每项间间隔的间隔高度 常用方法: setAdapter() 设置数据适配器 setOnItemClickListener() 设置每项点击事件监听 addHeaderView() 添加头视图 addFooterView() 添加脚视图 setEmpty

  • Android ListView适配器(Adapter)优化方法详解

    Android ListView的优化,在做Android项目的时候,在用到ListView 界面及数据显示,这个时候如果资源过大,对项目来说,用户体验肯定是不好的,这里就对如何优化做了详细介绍: Adapter的作用就是ListView界面与数据之间的桥梁,当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View.想过没有? 在我们的列表有1000000项时会是什么样的?是不是会占用极大的系统资源? ListView的Adapter的作用如下图所示: 先看看下面

  • Android ListView万能适配器实例代码

    ListView是开发中最常用的控件了,但是总是会写重复的代码,浪费时间又没有意义. 最近参考一些资料,发现一个万能ListView适配器,代码量少,节省时间,总结一下分享给大家. 首先有一个自定义的Adapter继承于BaseAdapter,下面是自定义的Adapter,精华在getView()方法中 package com.example.mylistview.util; import java.util.List; import android.content.Context; impor

  • Android通过代码控制ListView上下滚动的方法

    本文将介绍一种通过代码控制ListView上下滚动的方法. 先上图: 按下按钮会触发ListView滚动或停止. 实现该功能并不难,下面给出主要代码MainActivity.java package cn.guet.levide; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View;

  • 详解xamarin Android 实现ListView万能适配器

    详解xamarin Android 实现ListView万能适配器 早些时候接触xamarin Android 的列表,写了很多ListView的Adapter,建一个ListView就写一个Adapter,每一个Adapter里面还有去写一个ViewHolder的类来优化,自从看了hongyang博客的listview万能适配器的文章,学习良多,所以就写篇关于xamarin android ListView通用适配器的文章. 本章主要分为以下三点: 打造通用的ViewHolder优化ListV

  • android使用flutter的ListView实现滚动列表的示例代码

    现如今打开一个 App,比如头条.微博,都会有长列表,随着我们不断地滑动,视窗内的内容也会不断地更新.今天就用 Flutter 实现一下这种效果. 这里的表现其实就相当于有一个固定长度的容器,然后超出的内容是不可见的,只有当你向上或向下滑动屏幕时,视窗外看不见的内容才会出现在视窗中.如果在 web 开发时,是需要容器加上样式 overflow: auto; 要想用 Flutter 实现,其实也是很简单的,因为 Flutter 为我们提供了 ListView 组件. ListView 主要有以下几

  • Android巧用XListView实现万能下拉刷新控件

    摘要:想必大家做开发的时候都会用到下拉刷新的控件,现在各种第三方的下拉刷新控件不胜枚举.当然最NB的还是XListView.其他也有针对GridView,ScrollView,LinearLayout进行重写的下拉刷新控件.本文针对xListView采取一种巧用办法,可以实现各种控件的下拉刷新. 这种巧用思路有人可能已经想到,因为ListView本身就有addHeaderView方法,用该方法我们可以添加任何布局的View.因此本文的思路就是往xListView的头部添加我们自定义写的布局文件.

  • Android自定义控件ListView下拉刷新的代码

    ListView在实际实用中,一般都会有下新刷新和上拉加载的动态效果,今天要学的就是如何自定义带下拉刷新的ListView. 原理解析:一般将有下拉刷新的listview分成四种不同的状态来进行不同的显示效果. 1.完成状态done:listview正常显示状态 2.下拉状态pull:listview正在下拉时的状态 3.释放状态release:listview下拉后松开的状态 4.更新状态refreshing:listview下拉后加载数据时的状态 实现步骤: 自定义CustomListVie

随机推荐