zoukankan      html  css  js  c++  java
  • 深入理解使用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。

  • 相关阅读:
    osg编译日志
    MFC加载大型osg模型
    osg gdal加载tif数据文件
    osg创建灯光
    ubuntu挂载新硬盘
    MFC加载osg模型
    nginx中Geoip_module模块的使用
    centos中释放缓存的方法
    python连接kafka生产者,消费者脚本
    python初始化环境记录
  • 原文地址:https://www.cnblogs.com/xxNote/p/5435876.html
Copyright © 2011-2022 走看看