http://bj007.blog.51cto.com/1701577/643568

对于手机开发,我一直坚持的是“用iPhone的方式开发iPhone应用,用Android的方式开发Android应用”。但如何去把握iPhone 或者Android的方式呢?在iPhone开发时可能需要看大量Apple文档。而Android在看文档的同时还可以看看源码。这源码中有时可以发现 一些内部实现加深理解,有时可以受到某些可以在应用中使用的启发。总之源码是一个需要去发掘的“矿场”,时而不时地都会发现一些“金子”。

在Android开发的时候经常会有一些列表数据分组显示的需求,对于iPhone来说修改下模型数据并设置下风格就可以轻松搞定(如下图)。但对于 Android这种需求就会有些痛苦了。因为系统提供的ListAdapter无法提供这种支持,因此只能自定义Adapter。为了实现该功 能,Adapter通常就开始充斥很多特殊处理以及各种硬编码。如何用Android的方式设计和实现一个自定义Adapter呢?源码中的 HeaderViewListAdapter可能会提供些答案或者启发。

图1:Group分隔的iPhone表格

注释版本的HeaderViewListAdapter.java如下:

/*如果List需要有标题就会使用该ListAdapter。该ListAdapter包装了另一个ListAdapter并保存着
标题视图和相关数据对象。该类是作为基类,在代码中可能不需要直接使用。
*/
// 类名有点不确切,其实是支持Header和Footter的
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.
    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;
        /// 很好的处理,这里实际是使用了一种Null对象模式
        if (headerViewInfos == null) {
            mHeaderViewInfos = EMPTY_INFO_LIST;
        } else {
            mHeaderViewInfos = headerViewInfos;
        }
        if (footerViewInfos == null) {
            mFooterViewInfos = EMPTY_INFO_LIST;
        } else {
            mFooterViewInfos = footerViewInfos;
        }

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

/// 支持多个标题栏哦
    public int getHeadersCount() {
        return mHeaderViewInfos.size();
    }

/// 支持多个尾注栏哦
    public int getFootersCount() {
        return mFooterViewInfos.size();
    }

public boolean isEmpty() {
        return mAdapter == null || mAdapter.isEmpty();
    }

/// FixedViewInfo名字很贴切,这是相对于包装Adapter中的可变数据命名的
    private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
        if (infos != null) {
            for (ListView.FixedViewInfo info : infos) {
                if (!info.isSelectable) {
                    return false;
                }
            }
        }
        return true;
    }

/// 支持标题删减
    public boolean removeHeader(View v) {
        for (int i = 0; i < mHeaderViewInfos.size(); i++) {
            ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
            if (info.view == v) {
                mHeaderViewInfos.remove(i);
                mAreAllFixedViewsSelectable =
                        areAllListInfosSelectable(mHeaderViewInfos)
                        && areAllListInfosSelectable(mFooterViewInfos);
                return true;
            }
        }

return false;
    }

/// 支持尾注删减
    public boolean removeFooter(View v) {
        for (int i = 0; i &lt; mFooterViewInfos.size(); i++) {
            ListView.FixedViewInfo info = mFooterViewInfos.get(i);
            if (info.view == v) {
                mFooterViewInfos.remove(i);

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

return true;
            }
        }

return false;
    }

/// 标题视图、尾注视图和普通的数据视图,一视同仁
    public int getCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

public boolean areAllItemsEnabled() {
        if (mAdapter != null) {
            return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
        } else {
            return true;
        }
    }

public boolean isEnabled(int position) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position &lt; numHeaders) {
            return mHeaderViewInfos.get(position).isSelectable;
        }

// Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition &lt; adapterCount) {
                return mAdapter.isEnabled(adjPosition);
            }
        }

// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
    }

/// 需要分段处理
    public Object getItem(int position) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position &lt; numHeaders) {
            return mHeaderViewInfos.get(position).data;
        }

// Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition &lt; adapterCount) {
                return mAdapter.getItem(adjPosition);
            }
        }

// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).data;
    }

/// 这可不包括Header和Footer
    public long getItemId(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemId(adjPosition);
            }
        }
        return -1;
    }

public boolean hasStableIds() {
        if (mAdapter != null) {
            return mAdapter.hasStableIds();
        }
        return false;
    }

/// 需要分段处理,其中Header和Footer视图已经事先存储在ListView.FixedViewInfo对象中了
    public View getView(int position, View convertView, ViewGroup parent) {
        // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position &lt; numHeaders) {
            return mHeaderViewInfos.get(position).view;
        }

// Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition &lt; adapterCount) {
                return mAdapter.getView(adjPosition, convertView, parent);
            }
        }

// Footer (off-limits positions will throw an ArrayIndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).view;
    }

/// 支持同一个List中不同类型的视图
    public int getItemViewType(int position) {
        int numHeaders = getHeadersCount();
        if (mAdapter != null && position >= numHeaders) {
            int adjPosition = position - numHeaders;
            int adapterCount = mAdapter.getCount();
            if (adjPosition &lt; adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }

return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
    }

public int getViewTypeCount() {
        if (mAdapter != null) {
            return mAdapter.getViewTypeCount();
        }
        return 1;
    }

public void registerDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(observer);
        }
    }

public void unregisterDataSetObserver(DataSetObserver observer) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(observer);
        }
    }

/// 这有点意思,有待发掘
    public Filter getFilter() {
        if (mIsFilterable) {
            return ((Filterable) mAdapter).getFilter();
        }
        return null;
    }
    public ListAdapter getWrappedAdapter() {
        return mAdapter;
    }
}

本文出自 “林家男孩” 博客,请务必保留此出处http://bj007.blog.51cto.com/1701577/643568

Android源码分析:HeaderViewListAdapter的更多相关文章

  1. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

  2. Android源码分析(六)-----蓝牙Bluetooth源码目录分析

    一 :Bluetooth 的设置应用 packages\apps\Settings\src\com\android\settings\bluetooth* 蓝牙设置应用及设置参数,蓝牙状态,蓝牙设备等 ...

  3. Android源码分析(十七)----init.rc文件添加脚本代码

    一:init.rc文件修改 开机后运行一次: chmod 777 /system/bin/bt_config.sh service bt_config /system/bin/bt_config.sh ...

  4. Android源码分析(十六)----adb shell 命令进行OTA升级

    一: 进入shell命令界面 adb shell 二:创建目录/cache/recovery mkdir /cache/recovery 如果系统中已有此目录,则会提示已存在. 三: 修改文件夹权限 ...

  5. Android源码分析(十五)----GPS冷启动实现原理分析

    一:原理分析 主要sendExtraCommand方法中传递两个参数, 根据如下源码可以知道第一个参数传递delete_aiding_data,第二个参数传递null即可. @Override pub ...

  6. Android源码分析(十四)----如何使用SharedPreferencce保存数据

    一:SharedPreference如何使用 此文章只是提供一种数据保存的方式, 具体使用场景请根据需求情况自行调整. EditText添加saveData点击事件, 保存数据. diff --git ...

  7. Android源码分析(十三)----SystemUI下拉状态栏如何添加快捷开关

    一:如何添加快捷开关 源码路径:frameworks/base/packages/SystemUI/res/values/config.xml 添加headset快捷开关,参考如下修改. Index: ...

  8. Android源码分析(十二)-----Android源码中如何自定义TextView实现滚动效果

    一:如何自定义TextView实现滚动效果 继承TextView基类 重写构造方法 修改isFocused()方法,获取焦点. /* * Copyright (C) 2015 The Android ...

  9. Android源码分析(十一)-----Android源码中如何引用aar文件

    一:aar文件如何引用 系统Settings中引用bidehelper-1.1.12.aar 文件为例 源码地址:packages/apps/Settings/Android.mk LOCAL_PAT ...

  10. Android源码分析(十)-----关机菜单中如何添加飞行模式选项

    一:关机菜单添加飞行模式选项 源码路径:frameworks/base/core/res/res/values/config.xml 增加<item>airplane</item&g ...

随机推荐

  1. 文件写操作--WriteLog

    private static void Write(string sMsg, string fileName) { if (sMsg != "") { try { var dir ...

  2. [九度OJ]1137.浮点数加法

    原题链接:http://ac.jobdu.com/problem.php?pid=1137 题目描述: 求2个浮点数相加的和题目中输入输出中出现浮点数都有如下的形式:P1P2...Pi.Q1Q2... ...

  3. swift Swauth install

    devAuth 是swift原生的认证中间层, Swauth是为了解决devAuth不能扩展的问题而开发的替代方案. Quick Install #git clone https://github.c ...

  4. git引用^和~的区别

    这篇git文章必转:解答我一直的疑惑 http://www.cnblogs.com/hutaoer/archive/2013/05/14/3078191.html 一. 引子 在git操作中,我们可以 ...

  5. 143. Sort Colors II

    最后更新 一刷 class Solution { public void sortColors2(int[] colors, int k) { // write your code here if ( ...

  6. JBoss 目录结构

    安装JBoss 会创建下列目录结构: 目录 描述  bin 启动和关闭JBoss 的脚本  client 客户端与JBoss 通信所需的Java 库(JARs)  docs 配置的样本文件(数据库配置 ...

  7. iOS 极光推送

    1.关于推送的几个证书.http://www.mobile-open.com/2016/931624.html 进入开发者中心:https://developer.apple.com/account/ ...

  8. $.post()请求 ation请求,jsp获取的处理结果

    public void write(String content, String charset) { getHttpResponse().setCharacterEncoding(charset); ...

  9. MyEclipse x.x各版本终极优化配置指南

    先说优化:随着myeclipse版本不断更新,其功能不断强大,更加智能及人性化,为开发人员提供了很多便利.提高了开发速度,但是也牺牲了性能,让很多机器配置稍差的开发人员头疼不已.其实我们平时常用的功能 ...

  10. cocos2d-x 卡牌翻牌效果的实现

    转自:http://blog.csdn.net/yanghuiliu/article/details/9115833 这个能实现翻牌的action就是CCOrbitCamera. static CCO ...