Android中的万能适配器——base-adapter-helper解析

在Android开发中,我们经常会用到ListView、GridView,每次编码的时候都需要为他们写对应的Adapter,写多了就感觉很烦躁了,因为基本的编程思想都是一样的,但是每次都要重复去写,所以我们能不能把它们抽象成一个通用的模板,这样就不用每次都重复写相同的代码了,直接重复使用,这样不是更好,下面我们就来介绍介绍一个开源项目base-adapter-helper。

传统Adapter的编码思路,主要看Adapter中的getView方法。

public View getView(int pos, View convertView, ViewGroup parent){
    ViewHolder holder;
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.list_item, null);
        holder = new ViewHolder();
        holder.text = (TextView) convertView.findViewById(R.id.text));
        holder.icon = (ImageView) convertView.findViewButId(R.id.icon));
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    holder.text.setText(DATA[pos]);
    holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2);
    return convertView;
}

static class ViewHolder {
    TextView text;
    ImageView icon;
}

上面使用了一个ViewHolder用来缓存对应Item中的view,并且重用移出的Item,它对应的就是convertView。这样注意为了节省资源,提高效率。这种写法大家都应该很熟悉了。

下面来看看base-adapter-helper是怎样对其进行抽象封装的。首先来看看它的类继承图。

github链接:base-adapter-helper

可以看到BaseQuickAdapter继承自BaseAdapter,同样我们重点关注它的getView函数。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (getItemViewType(position) == 0) {
        final H helper = getAdapterHelper(position, convertView, parent);
        T item = getItem(position);
        helper.setAssociatedObject(item);
        convert(helper, item);
        return helper.getView();
    }

    return createIndeterminateProgressView(convertView, parent);
}

private View createIndeterminateProgressView(View convertView, ViewGroup parent) {
    if (convertView == null) {
        FrameLayout container = new FrameLayout(context);
        container.setForegroundGravity(Gravity.CENTER);
        ProgressBar progress = new ProgressBar(context);
        container.addView(progress);
        convertView = container;
    }
    return convertView;
}

下面我们分析分析getView的代码:

  • 第3行代码就是获取该postion的Item类型,上面定义了两种类型的Item,一种是我们需要显示的View的Item,一种是底部的加载的View的Item。当Item类型为0是就为需要显示的Item,当Item类型为1是就为底部加载的Item,上面的第11行代码的createIndeterminateProgressView就是创建底部用来的加载的Item,可以看到它是一个ProgressBar。另外可以通过showIndeterminateProgress(boolean)来显示或者隐藏这个item。

  • 第4行代码的getAdapterHelper获取一个BaseAdapterHelper对象,我们可以看到,在BaseQuickAdapter类中,getAdapterHelper是一个抽象函数,所以我们来看看BaseQuickAdapter的子类QuickAdapter,在这个类中它实现了getAdapterHelper方法,执行的实质是BaseAdapterHelper的静态get方法。另外需要提到的是,QuickAdapter类的构造函数有一个参数为layoutResId,它就是传入我们要显示Item的布局文件。
static BaseAdapterHelper get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
    if (convertView == null) {
        return new BaseAdapterHelper(context, parent, layoutId, position);
    }

    // Retrieve the existing helper and update its position
    BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
    existingHelper.position = position;
    return existingHelper;
}

从上面可以看到它也对convertView进行了重用,当convertView为null,那么我们需要创建一个BaseAdapterHelper,传入的就是要显示的位置position以及layoutId布局文件,这个position为前面getView中的那个position参数,layoutId为创建QuickAdapter传入的参数,就是Item的布局文件。

protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
    this.context = context;
    this.position = position;
    this.views = new SparseArray<View>();
    convertView = LayoutInflater.from(context) //
            .inflate(layoutId, parent, false);
    convertView.setTag(this);
}

在BaseAdapterHelper的构造函数里面,定义了一个views,它其实就是传统Adapter里面的那个ViewHolder用来存放Item里面的各个view。convertView为我们要显示的Item的View,接着通过setTag函数将BaseAdapterHelper对象本身关联到convertView上面,所以我们知道每个Item对象都关联了一个BaseAdapterHelper对象。

当convertView不为null的时候,我们就直接可以复用这个convertView,首先从convertView中取出它关联的BaseAdapterHelper对象,这样就可以复用这个convertView对应的BaseAdapterHelper对象。

  • 接着分析最上面的代码,第5行代码getItem用来得到需要显示的数据,这里需要说明的是,我们应该把需要传入的数据放在一个List中,我们可以看看QuickAdapter的构造函数
public QuickAdapter(Context context, int layoutResId, List<T> data) {
    super(context, layoutResId, data);
}

可以看到我们传入Item布局的layoutResId和要显示的数据data,data是List类型的。

  • 第6行代码是将我们向显示的数据项与BaseAdapterHelper对象关联起来。
  • 第7行代码convert函数同样是一个抽象函数,它需要我们进行实现,来具体设置对应的Item里面的内容。
  • 第8行代码是当BaseAdapterHelper对象设置为Item里面的内容之后,然后就可以得到这个Item的View对象进行返回。

下面我们来具体画个图来说明。假如我们要显示的是一个ListView。

当刚开始显示的时候,因为对应的convertView为null,所以会创建BaseAdapterHelper,核心代码如下:

protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
    this.context = context;
    this.position = position;
    this.views = new SparseArray<View>();
    convertView = LayoutInflater.from(context) //
            .inflate(layoutId, parent, false);
    convertView.setTag(this);
}

因为在BaseAdapterHelper对象中包含有对应的Item对应的convertView和对应的position以及convertView里面的子view集合,因此我们可以直接通过BaseAdapterHelper对象来完成各项操作。

当List向上滑动的时候,第一个Item移出List,底部就需要再显示一个Item,这个时候getView里面的convertView就是第一个移出的View,我们可以直接对它重用来显示下一个Item,核心代码为:

BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;

他直接得到BaseAdapterHelper对象,然后重新设置它对应的位置postion,因为BaseAdapterHelper对象中引用到了重用的convertView,这样就可以直接使用这个view的Item了。

下面来举个简单的例子:

public class MainActivity extends AppCompatActivity {
    private ListView listView;

    private List<String> data = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        data.add("text1");
        data.add("text2");
        data.add("text3");
        data.add("text4");
        data.add("text5");
        data.add("text6");

        listView = (ListView) findViewById(R.id.listview);
        QuickAdapter adapter = new QuickAdapter<String>(this, R.layout.item, data) {
            @Override
            protected void convert(BaseAdapterHelper helper, String item) {
                helper.setText(R.id.textView, item);
            }
        };
        listView.setAdapter(adapter);
    }

}

也就是说我们只需要定义一个QuickAdapter重写convert方法就可以了,在convert方法里面我们直接使用BaseAdapterHelper对象完成对数据内容item的设置就可以了。

时间: 2024-10-23 08:51:50

Android中的万能适配器——base-adapter-helper解析的相关文章

Android开发之万能适配器

ListView.GridView等等非常多的东西都需要适配器.而如果开发一个app每一个listview都有写一个Adapter的话,那还怎么愉快的玩游戏.. 什么是ViewHolider以及的用法和为什么要用? 这位博主写的非常好. http://www.cnblogs.com/lichenwei/p/4085107.html 所谓的万能适配器,无非是将适配器的重复代码抽取出来进行封装.不同功能的代码则留写一个方法留给用户复写,则每个listview的适配器就只变成几句话就够了. 这是一般适

Android中利用ViewHolder优化自定义Adapter的典型写法

public class MarkerItemAdapter extends BaseAdapter { private Context mContext = null; private List<MarkerItem> mMarkerData = null; public MarkerItemAdapter(Context context, List<MarkerItem> markerItems) { mContext = context; mMarkerData = mark

Android中使用WebView与JS交互全解析

1.概述首先,需要提出一个概念,那就是hybrid,主要意思就是native原生Android和h5混合开发.为什么要这样做呢?大家可以想象一下针对于同一个活动,如果使用纯native的开发方式,Android和iOS两边都要维护同一套界面甚至是逻辑,这样开发和维护的成本会很大,而使用hybrid的开发方式的话,让前端的同学去写一套界面和逻辑,对于native端来说只要使用对应的容器去展示就可以了(对于Android来说这个容器当然就是WebView).那为什么不所有的页面都使用这种方式开发呢?

Android中IntentService的使用及其源码解析

为什么我们需要IntentService ? Android中的IntentService是继承自Service类的,在我们讨论IntentService之前,我们先想一下Service的特点: Service的回调方法(onCreate.onStartCommand.onBind.onDestroy)都是运行在主线程中的.当我们通过startService启动Service之后,我们就需要在Service的onStartCommand方法中写代码完成工作,但是onStartCommand是运行

Android中Activity与Task相关的属性解析

与Task相关的属性解析 android:allowTaskReparenting 用来标记Activity能否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)--"true",表示能移动,"false",表示它必须呆在启动时呆在的那个Task里.    如果这个特性没有被设定,设定到<application>元素上的allowTaskReparenting特性的值会应用到Activity上.默认值为"fals

Android 中Canvas的save(),saveLayer()和restore()解析

1.save()方法 : 用来保存Canvas的状态,save()方法之后的代码,可以调用Canvas的平移.放缩.旋转.裁剪等操作! 2.restore()方法: 用来恢复Canvas之前保存的状态(可以想成是保存坐标轴的状态),防止save()方法代码之后对Canvas执行的操作,继续对后续的绘制会产生影响,通过该方法可以避免连带的影响 总结:就是在save之前绘制的状态会保存下来,在restore方法之后绘制的不会再因为状态而改变 示例说明: 例如:我们想在画布上绘制一个向右的三角箭头,当

Android中自定义常用的三个对象解析(Paint,Color,Canvas)

Paint,Color,Canvas Paint:画笔对象,画图用的"笔" Color:颜色,相当于调料 Canvas:画布,现实中的纸板 Paint 画笔 常用的方法就是设置和获取到画笔的样式: paint.setStyle(); 设置画笔的风格,空心的或者是实心的 paint.setColor(); 设置画笔的颜色 paint.setStrokeWidth(); 设置边框线的宽度 paint.setAlpha(); 设置画笔的Alpha值 paint.setAntiAlias();

Android中XML文件的序列化生成与解析

首先,我把Person的实体类 package net.loonggg.test; public class Person { private int id; private String age; private String name; private String sex; private String address; public int getId() { return id; } public void setId(int id) { this.id = id; } public

[Android]GOF23种设计模式 &amp; Android中的设计模式

GOF23种设计模式 设计原则: 1. 单一职责原则(SRP):就一个类而言,应该仅有一个引起它变化的原因 2. 开放-封闭原则(OCP):软件实体(类.模块.函数等)应该可以扩展,但是不可修改.即对于扩展是开放的, 对于修改是封闭的. 3. 依赖倒转原则: A. 高层模块不应该依赖低层模块,两个都应该依赖抽象.B.抽象不应该依赖细节,细节应该依赖抽象.说白了,就是要针对接口编程,不要对实现编程. 4. 迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应该发生直接的相互作用.如