源码分析-ListView组件addHeaderView()方法的源码解析

分析动力

最近在写自己的项目,使用到RecyclerView这控件替代我之前常用的ListView。使用起来当然感觉比之前的ListView功能强大太多。但是目前RecyclerView却没有添加addHeadView()和addFooterView()这两个列表组件常用的功能。网络上有很多博客都有介绍他们的方法给RecyclerView添加这两个功能,主要就是在onCreateViewHolderonBindViewHolder上做文章。作为一个有追求的Android开发者不能直接照抄他们。也写出一些自己的想法。所以我就回到ListView的源码看Google是怎么实现这功能的

addHeadView()源码分析

源码入口

两个add重载方法最后来到这里,先看英文注释

/** * Add a fixed view to appear at the top of the list. If this method is * called more than once, the views will appear in the order they were * added. Views added using this call can take focus if they want. * <p> * Note: When first introduced, this method could only be called before * setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with * {@link android.os.Build.VERSION_CODES#KITKAT}, this method may be * called at any time. If the ListView's adapter does not extend * {@link HeaderViewListAdapter}, it will be wrapped with a supporting * instance of {@link WrapperListAdapter}. * * @param v The view to add. * @param data Data to associate with this view * @param isSelectable whether the item is selectable */
    public void addHeaderView(View v, Object data, boolean isSelectable) {}

先看方法的注释部分,英文大概意思是:这是给adapter添加一个顶级视图的方法,有使用提示和参数说明。

ListView采用MVC模式 模型(model)-视图(view)-控制器(controller)
ListView负责UI显示是View层
构造Adapter时传入的Data数据是Model层,是数据的来源。
Adapter适配器负责控制View层需要显示什么样的UI,是最关键的Controller层。

所以给ListView添加head其实就是在adapter上做修改。note:提示这个方法要在绑定适配器setAdapter()之前调用。

源码

再看里面的源码,代码不多。主要分三步操作

 public void addHeaderView(View v, Object data, boolean isSelectable) {
         //第一步 数据的初始化和赋值 并添加到头部视图list中
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mHeaderViewInfos.add(info);
        mAreAllItemsSelectable &= isSelectable;

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            //第二步 关键点
            //给mAdapter赋值成HeaderViewListAdapter对象 并把head、foot视图列表和原来的adapter传进去 
            //所以这里就是关键 从这里进入看HeaderViewListAdapter类的源码
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
            //第三步 被观察者发出修改通知
            // In the case of re-adding a header view, or adding one later on,
            // we need to notify the observer.
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }

FixedViewInfo类

是对HeadView视图的包装,里面封装了三个字段。

 /** * A class that represents a fixed view in a list, for example a header at the top * or a footer at the bottom. */
    public class FixedViewInfo {
        /** The view to add to the list */
        public View view;
        /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */
        public Object data;
        /** <code>true</code> if the fixed view should be selectable in the list */
        public boolean isSelectable;
    }

HeaderViewListAdapter的源码分析

内部对象

类中保存有3个关键内部对象

public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
    //关键对象-->开始
    private final ListAdapter mAdapter;

    // These two ArrayList are assumed to NOT be null.
    // They are indeed created when declared in ListView and then shared.
    ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
    ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
    //关键对象-->结束
    // Used as a placeholder in case the provided info views are indeed null.
    // Currently only used by some CTS tests, which may be removed.
    //处理空 ArrayList<ListView.FixedViewInfo>的静态不可变对象 作用是内存优化 值得学习
    static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
        new ArrayList<ListView.FixedViewInfo>();

    boolean mAreAllFixedViewsSelectable;

    private final boolean mIsFilterable;

构造函数

三个参数的构造函数,负责初始化和赋值

public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
                                 ArrayList<ListView.FixedViewInfo> footerViewInfos,
                                 ListAdapter adapter) {
        mAdapter = adapter;
        mIsFilterable = adapter instanceof Filterable;

        if (headerViewInfos == null) {
        //如果为null 内部对象被静态不可变量赋值 避免new出新对象和非空处理 值得学习
            mHeaderViewInfos = EMPTY_INFO_LIST;
        } else {
            mHeaderViewInfos = headerViewInfos;
        }

        if (footerViewInfos == null) {
            mFooterViewInfos = EMPTY_INFO_LIST;
        } else {
            mFooterViewInfos = footerViewInfos;
        }

        mAreAllFixedViewsSelectable =
                areAllListInfosSelectable(mHeaderViewInfos)
                && areAllListInfosSelectable(mFooterViewInfos);
    }

添加head和foot视图的关键

在getView上做修改

public View getView(int position, View convertView, ViewGroup parent) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        //从position从0开始取view 先进入这里从FixedViewInfo对象中直接取出View对象作为返回值
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).view;
        }

        //普通的itemView视图 从传入的mAdapter的getView()方法中取 这步一般是我们自己写的BaseAdapter.getView(),就是我们重写的部分,由我们的代码控制
        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getView(adjPosition, convertView, parent);
            }
        }
        //foot视图同Head原理
        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).view;
    }

其他的方法

和一般的adapter没有太大的差别,不是我们分析点。

总结

  1. 从这几步的源码分析,我们可以得知,ListView的addHeadView(),其实就是修改adapter的getView()方法。在使用时包装HeaderViewListAdapter类有专门处理有head和foot的方法。这种思路和网络上大多数的博客差不多,应该大家都是从源码中找到思路的吧。
  2. 从ListView源码上得到的思路,其实也可以运用在RecyclerView上,Google官方没有提供我们就自己动手。当然RecyclerView比ListView要复杂它在MVC的C层分离出 RecyclerView.Adapter和RecyclerView.LayoutManager,分别控制View的显示内容和布局位置。通过上面的分析得到的思路接下来我要写个有addHeadView和addFootView的RecyclerView。
  3. 这里有对RecyclerView使用addHeadView的分析整理

你可能感兴趣的:(源码,ListView,addHeadVie)