深入理解使用ListView时ArrayAdapter、SimpleAdapter、BaseAdapter的原理

在使用ListView的时候,我们传给setAdapter方法的Adapter通常是ArrayAdapter、SimpleAdapter、BaseAdapter,但是这几个Adapter内部究竟是什么样子如果我们不搞清楚的话,在使用的时候就会感觉有些混乱,概括的说这三个Adapter之间的差异主要是由他们各自的getView方法的差异造成的,接下来我们一起看一下这几个Adapter的getView的源码

1.ArrayAdapter的getView方法源码如下:

    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(mInflater, position, convertView, parent, mResource);
    }

    private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
            ViewGroup parent, int resource) {
        View view;
        TextView text;

        if (convertView == null) {
            view = inflater.inflate(resource, parent, false);
        } else {
            view = convertView;
        }

        try {
            if (mFieldId == 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = (TextView) view;
            } else {
                //  Otherwise, find the TextView field within the layout
                text = (TextView) view.findViewById(mFieldId);
            }
        } catch (ClassCastException e) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
            throw new IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e);
        }

        T item = getItem(position);
        if (item instanceof CharSequence) {
            text.setText((CharSequence)item);
        } else {
            text.setText(item.toString());
        }

        return view;
    }

  可以看到ArrayAdapter的getView方法直接调用了createViewFromResource方法,在这个方法里面用到了一个成员变量mFieldId ,我们往上翻一下源码可以看到他的定义如下:

private int mFieldId = 0;

  再接着翻源码可以看到mFieldId的值只在构造函数中修改:

    public ArrayAdapter(Context context, @LayoutRes int resource, @IdRes int textViewResourceId,
            @NonNull List<T> objects) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mResource = mDropDownResource = resource;
        mObjects = objects;
        mFieldId = textViewResourceId;
    }

  此时我们可以明白mFieldId的值就是在构造ArrayAdapter时传入的textViewResourceId,也就是布局文件中TextView的android:id属性的值,弄明白mFieldId后,我们接着分析,可以看到接下来对mFieldId进行了判断,如果mFieldId的值是0,那么传入整个布局文件的根节点就是一个TextView,如果mFieldId的值不为0,就在传入的布局文件中查找android:id为mFieldId的TextView,之后用getItem方法获取TextView的文字内容,然后用text的setText方法设置标题,至此我们可以明白ArrayAdapter只能对TextView及TextView的子类进行定制,ListView的每一项可以仅仅是一个TextView,也可以是一个布局文件,但是这个布局文件里面必须且只能包含一个android:id属性为textViewResourceId的TextView(TextView的子类当然也可以,因为他的子类也属于TextView)。

2.接下来分析SimpleAdapter,他的getView方法源码如下:

    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(mInflater, position, convertView, parent, mResource);
    }

    private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
            ViewGroup parent, int resource) {
        View v;
        if (convertView == null) {
            v = inflater.inflate(resource, parent, false);
        } else {
            v = convertView;
        }

        bindView(position, v);

        return v;
    }

  可以看到,在他的getView方法里面依然是调用了createViewFromResource方法,只是createViewFromResource方法和ArrayAdapter的createViewFromResource不同,在SimpleAdapter的createViewFromResource方法里面又调用了bindView方法,我们看一下bindView的源码:

    private void bindView(int position, View view) {
        final Map dataSet = mData.get(position);
        if (dataSet == null) {
            return;
        }

        final ViewBinder binder = mViewBinder;
        final String[] from = mFrom;
        final int[] to = mTo;
        final int count = to.length;

        for (int i = 0; i < count; i++) {
            final View v = view.findViewById(to[i]);
            if (v != null) {
                final Object data = dataSet.get(from[i]);
                String text = data == null ? "" : data.toString();
                if (text == null) {
                    text = "";
                }

                boolean bound = false;
                if (binder != null) {
                    bound = binder.setViewValue(v, data, text);
                }

                if (!bound) {
                    if (v instanceof Checkable) {
                        if (data instanceof Boolean) {
                            ((Checkable) v).setChecked((Boolean) data);
                        } else if (v instanceof TextView) {
                            // Note: keep the instanceof TextView check at the bottom of these
                            // ifs since a lot of views are TextViews (e.g. CheckBoxes).
                            setViewText((TextView) v, text);
                        } else {
                            throw new IllegalStateException(v.getClass().getName() +
                                    " should be bound to a Boolean, not a " +
                                    (data == null ? "<unknown type>" : data.getClass()));
                        }
                    } else if (v instanceof TextView) {
                        // Note: keep the instanceof TextView check at the bottom of these
                        // ifs since a lot of views are TextViews (e.g. CheckBoxes).
                        setViewText((TextView) v, text);
                    } else if (v instanceof ImageView) {
                        if (data instanceof Integer) {
                            setViewImage((ImageView) v, (Integer) data);
                        } else {
                            setViewImage((ImageView) v, text);
                        }
                    } else {
                        throw new IllegalStateException(v.getClass().getName() + " is not a " +
                                " view that can be bounds by this SimpleAdapter");
                    }
                }
            }
        }
    }

这时我们发现终于找到关键代码了,bindView先是获取第一项的数据dataSet ,然后通过to.length获取ListView的每一个列表项里面有几个要填充的控件,接下来是一个for循环,判断列表项里面的每个要填充的控件是具体什么东东,是Checkable还是TextView,还是ImageView。至此我们明白了SimpleAdapter只能填充Checkable、TextView、ImageView三种控件,在ListView的每一个列表项里面可以包含1~N个上面的三种控件,可以只有一种,也可以有两种,也可以都有,我们也可以看出每一个列表项都只能是一样的,通过SimpleAdapter我们不能让某个列表项和其他列表项不一样,通过继承BaseAdapter来自己实现getView则可以让我们任意定制列表项,在getView里面我们可以根据position的值决定返回何种类型的view。

时间: 2024-10-27 11:47:40

深入理解使用ListView时ArrayAdapter、SimpleAdapter、BaseAdapter的原理的相关文章

Android train——ListView绑定ArrayAdapter、SimpleAdapter、SimpleCursorAdapter、BaseAdapter

ListView绑定ArrayAdapter res/layout/activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout

Android -- ListView与ArrayAdapter、SimpleAdapter

对于ArrayAdapter,里面虽然能添加图片,但只能是相同的图片. 废话不多说: 布局&&list的item布局                                                                 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.andro

为了灵活使用listView,封装了BaseAdapter

为了灵活使用listView,封装了BaseAdapter. 实现了类似iOS的tableView功能.不用事先创建好所有可能需要的控件,再在运行时动态判断显示或隐藏.极大的提高了效率和减少了代码量. 主要功能: 1.可对listView进行分组管理2.每组都可以设置头与尾3.每组的多行可以定义多种布局文件4.可加载多个布局文件,从而可定制头与尾的布局文件 源码请点击github地址下载. 效果图如下: 0C5CADD9B0F72C4A98C73866C4EABA34.png API使用非常简单

Android新手入门2016(8)--ListView之ArrayAdapter

本文来自肥宝传说之路,引用必须注明出处! ListView是Android中经常使用的控件. 什么是列表视图,让我们先看看图: 最常见的样例就是各种菜单的下啦列表. 要实现列表,须要完毕三个要素: 1.ListView 把全部的数据按指定的格式排成列表. 列表中每一项能够称为Item(如上图This is Title). 能够想象得出,要显示列表.就要先弄成相应的格式 2.adapter 适配器就是这样的ListView可以识别的格式,当然适配器有几种.以下再细说.适配器是指定格式的数据.可是我

深入理解自定义ListView

深入理解自定义ListView ListView原理 他是一个系统的原生控件,用列表的形式来显示内容.如果内容过过有1000条左右,我们可以通过手势的上下滑动来查看数据.ListView也不是爆出OOM(out of memery)错误.下面是类的继承机构 我们给ListView装配数据的时候,要给他定义一个适配器Adapter,为什么要定义呢? 我的理解是给ListView一个通道,在和我们的数据之间建立一个连接,这样当ListView需要展现什么的数据,什么样的布局的时候我们就可以通过自己定

SimpleAdapter &amp; BaseAdapter

[SimpleAdapter & BaseAdapter] 参考:http://blog.csdn.net/shakespeare001/article/details/7926783

深入理解Java运行时数据区

前情回顾 在本专栏的前12篇博客中, 我们主要大致介绍了什么是JVM, 并且详细介绍了class文件的格式. 对于深入理解Java, 或者深入理解运行于JVM上的其他语言, 深入理解class文件格式都是必须的. 如果读者对class文件的格式不是很熟悉, 在阅读本博客下面的文章之前, 建议先读一下前面的12篇博客, 或者参考其他资料, 熟悉class文件的格式. 在深入理解Java虚拟机到底是什么 这篇博客中, 我们有提到过, JVM就是一个特殊的进程, 我们执行的java程序, 都运行在一个

使用ListView 时,遇到了 Your content must have a ListView whose id attribute is &#39;android.R.id.list&#39; 错误

今天在开发Android小应用的时候,使用到了ListView,在使用自己创建的listview风格的时候,就出现了如标题的错误提示信息,这个就让我纳闷了,以前也不会出现这个问题啊,而且也可以运行,赶紧查资料找原因,原来是我的主Activity继承了ListActivity,而ListActivity中必须有一个ListView,因为ListActivity启动时会默认去寻找这个ListView的id,如果没有找到,就会报错. 解决的方法很简单,就是在主Activity布局文件中加个ListVi

ScrollView里面添加ListView时,解决ListView的显示问题

在ScrollView里面添加ListView时,看了很多其他人的讲述,好像ListView只显示一条信息,为此简单新写了一个ListView控件,在布局文件里调用就可以了,代码如下: 1:ScrollViewWithListView.java 1 package com.ghp.view; 2 3 import android.widget.ListView; 4 5 /** 6 * 7 * @Description: scrollview中内嵌listview的简单实现 8 * 9 * @F