preferenceActivity源码解析与简单用例

  • PreferenceActivity可以显示一系列Header,每一个Header可以关联一个Fragment或者Activity。此外,它还可以直接显示Preference条目。
  • PreferenceActivity显示Header的时候有两种模式:single pane和two panes;如果是Fragment,那么在two panes模式下,也就是大屏模式下,它可以同时显示Header和Fragment,这充分利用了屏幕的空间。而在singlepane模式下只会显示Header,无论如何,我们都可以在Header关联的Fragment中再显示preference条目。

如上描述可用示意图标示:

在介绍它的使用方法之前,为了更好的理解PreferenceActivity,我会先对源码做一个简单的分析,分析结束后再介绍它的用法,包括显示Header和显示preference,这样更容易理解为什么会这么使用。

一.源码分析

1.继承关系:

可以看到PreferenceActivity继承自ListActivity,而ListActivity是一个封装了ListView的Activity,在ListActivity中给ListView设置了事件监听器:

mList.setOnItemClickListener(mOnClickListener);

这个事件监听器是这样的:

    private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, int position, long id)
        {
            onListItemClick((ListView)parent, v, position, id);
        }
    };

可以看到在监听器中,又简单调用了onListItemClick方法,这个方法的定义如下:

    protected void onListItemClick(ListView l, View v, int position, long id) {
    }

它是一个空的方法,如果你的Activity继承自ListActivity,想处理ListView的事件的话,只需要重写这个方法。

ListActivity中提供了给ListView设置适配器的接口,但是适配器还得自己去写,因此可以说ListActivity功能有限。PreferenceActivity继承了ListActivity,那么它当然主要是一个ListView了,那么它会怎样给ListView设置适配器呢?它又怎样处理按键事件?

2.Header

Header是PreferenceActivity一个非常重要的概念,PreferenceActivity可以显示一系列的Header,每一个Header可以给它关联一个fragment,这样当你点击这个Header时,就会打开它关联的fragment,但是不局限于此,从源码来看,如果没有关联Fragment,那么也可以设置Intent,此时,可以通过Intent打开对应的Activity。

这个类还有两种模式,singlepane和two panes,分别针对小屏幕设备和大屏幕设备。对小屏幕设备而言,屏幕只会显示header,而大屏则会同时显示它关联的frament。

Header是一个定义在PreferenceActivity中的内部类:

    public static final class Header implements Parcelable {

它实现了Parcelable接口,表明它可以被序列化。它无非就是一个容器,里面保存了一个条目的所有相关的内容,比如这个条目的title-标题,summary-描述,fragment-关联的fragment等等。当我们要显示它的时候,我们只需要构造好Header,然后把它提交给PreferenceActivity就可以了,提交的方法是重写onBuildHeaders方法,这个方法的参数是一个List<Header >,只需要把自己构造的Header添加到这个List就可以了。此外,还需要覆写isValidFragment方法,后面会有介绍。

3.给ListView设置适配器

            setListAdapter(new HeaderAdapter(this, mHeaders, mPreferenceHeaderItemResId,
                    mPreferenceHeaderRemoveEmptyIcon));

可以看到它给ListView设置的适配器是一个HeaderAdapter.那么这个Adapter是什么呢?

    private static class HeaderAdapter extends ArrayAdapter<Header> {
        private static class HeaderViewHolder {
            ImageView icon;
            TextView title;
            TextView summary;
        }

        private LayoutInflater mInflater;
        private int mLayoutResId;
        private boolean mRemoveIconIfEmpty;

        public HeaderAdapter(Context context, List<Header> objects, int layoutResId,
                boolean removeIconBehavior) {
            super(context, 0, objects);
            mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mLayoutResId = layoutResId;
            mRemoveIconIfEmpty = removeIconBehavior;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            HeaderViewHolder holder;
            View view;

            if (convertView == null) {
                view = mInflater.inflate(mLayoutResId, parent, false);
                holder = new HeaderViewHolder();
                holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
                holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
                holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
                view.setTag(holder);
            } else {
                view = convertView;
                holder = (HeaderViewHolder) view.getTag();
            }

            // All view fields must be updated every time, because the view may be recycled
            Header header = getItem(position);
            if (mRemoveIconIfEmpty) {
                if (header.iconRes == 0) {
                    holder.icon.setVisibility(View.GONE);
                } else {
                    holder.icon.setVisibility(View.VISIBLE);
                    holder.icon.setImageResource(header.iconRes);
                }
            } else {
                holder.icon.setImageResource(header.iconRes);
            }
            holder.title.setText(header.getTitle(getContext().getResources()));
            CharSequence summary = header.getSummary(getContext().getResources());
            if (!TextUtils.isEmpty(summary)) {
                holder.summary.setVisibility(View.VISIBLE);
                holder.summary.setText(summary);
            } else {
                holder.summary.setVisibility(View.GONE);
            }

            return view;
        }
    }

从定义中可以看到,HeaderAdapter是一个ArrayAdapter的子类,并且复写了getView方法。在getView方法中,根据header中的内容,填充icon,title.summary等。

4.PreferenceActivity的事件处理

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if (!isResumed()) {
            return;
        }
        super.onListItemClick(l, v, position, id);

        if (mAdapter != null) {
            Object item = mAdapter.getItem(position);
            if (item instanceof Header) onHeaderClick((Header) item, position);
        }
    }

可以看到它重写了onListItemClick方法,用于处理事件,在这个方法中调用了onHeaderClick方法:

   public void onHeaderClick(Header header, int position) {
        if (header.fragment != null) {
            if (mSinglePane) {
                int titleRes = header.breadCrumbTitleRes;
                int shortTitleRes = header.breadCrumbShortTitleRes;
                if (titleRes == 0) {
                    titleRes = header.titleRes;
                    shortTitleRes = 0;
                }
                startWithFragment(header.fragment, header.fragmentArguments, null, 0,
                        titleRes, shortTitleRes);
            } else {
                switchToHeader(header);
            }
        } else if (header.intent != null) {
            startActivity(header.intent);
        }
    }

这个方法中判断header中是否设置了frament,如果设置了,又会判断是不是处于singpane模式,如果是,就会启动fragment,如果不是singlepane,那么肯定就是two panes模式了,这个时候则切换到Header关联的Fragment中显示Fragment中的preference。如果没有设置fragment,那么又会判断有没有设置Intent,如果设置了Intent,那么会启动Intent所指向的Activity。

关于Header的显示,我们可以自定义适配器,这时候需要重写setListAdapter方法,比如:

    @Override
    public void setListAdapter(ListAdapter adapter) {
        if (adapter == null) {
            super.setListAdapter(null);
        } else {

        }
    }

这个方法是ListActivity中提供的用于设置适配器的方法,这个方法在PreferenceActivity的onCreate方法中调用,用来给ListView设置适配器。

构造Header则需要重写onBuildHeaders方法,比如:

    @Override
    public void onBuildHeaders(List<Header> headers) {

    }

这个方法在PreferenceActivity的onCreate方法中调用。它的调用在setListAdapter之前,用来构造Header。

二.简单的使用举例:

1.代码构造Header

写一个Activity,继承PreferenceActivity,然后覆写onBuildHeaders方法即可:

public class MainActivity extends PreferenceActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
    }

    @Override
    public void onBuildHeaders(List<Header> target) {
        for(int i=0;i<5;i++){
            Header header = new Header();
            header.title = "hello"+i;
            header.summary = "hehe"+i;
            header.extras = new Bundle();
            header.fragment = MyFrament.class.getName();

            target.add(header);
        }
        super.onBuildHeaders(target);
    }

}

注意:

1.setContentView要注释掉,因为如果重新设置ContentView,那么ListActivity中设置的ListView就找不到了。

2.onBuildHeaders中,构造的header要添加到target列表中。

3.fragment一定要设置,不设置就不会显示的,因此你需要自己实现一个与Header关联的Fragment,这个Fragment会在点击Header的时候被打开。

4.因为PreferenceActivity中已经提供了默认的适配器,所以,如果不需要自定义,则不需重写setListAdapter方法。

这样只是单纯的可以显示一些Header,但是点击Header还是会报错,我们还必须要覆写一个方法:

    @Override
    protected boolean isValidFragment(String fragmentName) {
        return true;
    }

即明确告诉PrefercenceActivity我们准备了一个合法的Fragment。

在Fragment中可以显示preference了,这里暂时什么都不显示,这样打开的是一个空页面:

public class MyFrament extends PreferenceFragment {
    public void onCreate(Bundle b) {
        super.onCreate(b);
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return null;
    }
}

代码效果如下:

2.xml构造Header

XML构造Header更加简单方便。

2.1构建xml文件:

<preference-headers
    xmlns:android="http://schemas.android.com/apk/res/android">

    <header android:fragment="com.konka.preferenceactivitytest1.MyFrament"
        android:icon="@mipmap/ic_launcher"
        android:title="Prefs 1"
        android:summary="An example of some preferences." />

    <header android:fragment="com.konka.preferenceactivitytest1.MyFrament"
        android:icon="@mipmap/ic_launcher"
        android:title="Prefs 2"
        android:summary="Some other preferences you can see.">
        <!-- Arbitrary key/value pairs can be included with a header as
             arguments to its fragment. -->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>

    <header android:icon="@mipmap/ic_launcher"
        android:title="Intent"
        android:summary="Launches an Intent.">
        <intent android:action="android.intent.action.VIEW"
            android:data="http://www.baidu.com" />
    </header>

</preference-headers>

2.2加载XML文件:

    public void onBuildHeaders(List<Header> target) {
//        for(int i=0;i<5;i++){
//            Header header = new Header();
//            header.title = "hello"+i;
//            header.summary = "hehe"+i;
//            header.fragment = MyFrament.class.getName();
//            target.add(header);
//        }
//        super.onBuildHeaders(target);
        loadHeadersFromResource(R.xml.preference_header, target);
    }

也就是说还是要重写onBuilderHeaders方法,只不过这次试用loadHeadersFromResource方法从xml中加载header了。

代码效果如下:

3.PreferenceActivity显示preference

PreferenceActivity除了可以显示Header和它关联的fragment之外,还可以直接显示preference,这些prefercence可以直接从XML文件中加载。PreferenceActivity显示preference的Api大都可以在PreferenceFragment中找到,而且更推荐使用PreferenceFragment来显示preference,不过在很多比较旧的的代码中,还保留着很多使用PreferenceActivity显示preference的代码。

使用PreferenceActivity显示preference只需要两步:

3.1在xml文件下创建xml配置文件:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >

    <Preference
        android:icon="@drawable/ic_settings_wifi_4"
        android:key="network"
        android:title="@string/connectivity_network" >
        <intent
            android:targetClass="com.android.tv.settings.connectivity.NetworkActivity"
            android:targetPackage="com.android.tv.settings" />
    </Preference>
    <Preference
        android:icon="@drawable/ic_settings_cast"
        android:key="cast"
        android:title="@string/system_cast" >
        <intent android:action="com.google.android.settings.CAST_RECEIVER_SETTINGS" />
    </Preference>
    <Preference
        android:icon="@drawable/ic_settings_apps"
        android:key="apps"
        android:title="@string/device_apps" >
        <intent
            android:targetClass="com.android.tv.settings.device.apps.AppsActivity"
            android:targetPackage="com.android.tv.settings" />
    </Preference>
    <Preference
        android:icon="@drawable/ic_settings_storage"
        android:key="storagereset"
        android:title="@string/device_storage_reset" >
        <intent
            android:targetClass="com.android.tv.settings.device.StorageResetActivity"
            android:targetPackage="com.android.tv.settings" />
    </Preference>
    <Preference
        android:icon="@drawable/ic_settings_about"
        android:key="about_device"
        android:title="@string/about_preference">
        <intent
            android:targetClass="com.android.tv.settings.about.AboutActivity"
            android:targetPackage="com.android.tv.settings" />
    </Preference>
</PreferenceScreen>

PreferenceScreen中的内容会显示在一个新的屏幕中,PreferenceCategory标示一个分组,它会成为一组条目的头目,而且它是不能获取焦点的。其他的配置选项这里就不啰嗦了。

3.2在代码中加载XML文件

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        addPreferencesFromResource(R.xml.item);
    }

这样两步就可以构造一个设置界面了。

时间: 2024-08-27 21:57:25

preferenceActivity源码解析与简单用例的相关文章

PreferenceActivity源码分析与简单应用

· PreferenceActivity可以显示一系列Header,每一个Header可以关联一个Fragment或者Activity.此外,它还可以直接显示Preference条目. · PreferenceActivity显示Header的时候有两种模式:single pane和two panes:如果是Fragment,那么在two panes模式下,也就是大屏模式下,它可以同时显示Header和Fragment,这充分利用了屏幕的空间.而在singlepane模式下只会显示Header,

Redis源码解析01: 简单动态字符串SDS

Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple  dynamic  string, SDS)的抽象类型,SDS设计API实现对字符串的各种修改. 1:SDS的定义 在sds.h中,定义了结构体sdshdr表示SDS,其定义如下: struct sdshdr { unsigned int len; unsigned int free; char buf[]; }; len记录SDS保存的字符串的长度(不包括末尾的'\0'):free记录

设计模式-简单工厂Coding+jdk源码解析

前面的软件设计七大原则,目前只有理论这块,因为最近参与项目重构,暂时没有时间把Coding的代码按照设计思路一点点写出来.本周周末会花时间整理出来,然后现在想的是白天更新一点并发编程,晚上回家学习设计模式.非科班出身,脑子也比较笨.博文都是自己根据学习的时候所想的思路,如果能有帮到各位的地方,那十分荣幸.如果有欠缺之处,希望能在评论中指出一起进步.好啦,开始正文了. 本套设计模式的博文,包含各种设计模式的定义.类型.适用场景及优缺点分析.并通过Coding去实际加深理论理解. 简单工厂: 该模式

设计模式课程 设计模式精讲 4-3 简单工厂源码解析

1 源码解析 1.1 Calendar源码解析 1.2 DriverManager源码解析 1 源码解析 1.1 Calendar源码解析 /** * Gets a calendar using the specified time zone and default locale. * The <code>Calendar</code> returned is based on the current time * in the given time zone with the d

Android EventBus源码解析, 带你深入理解EventBus

上一篇带大家初步了解了EventBus的使用方式,详见:Android EventBus实战 没听过你就out了,本篇博客将解析EventBus的源码,相信能够让大家深入理解该框架的实现,也能解决很多在使用中的疑问:为什么可以这么做?为什么这么做不好呢? 1.概述 一般使用EventBus的组件类,类似下面这种方式: [java] view plain copy public class SampleComponent extends Fragment { @Override public vo

Spring IoC源码解析——Bean的创建和初始化

Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,MyBatis框架等组合使用. IoC介绍 IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控

Android EventBus3.0使用及源码解析

叨了个叨 最近因为换工作的一些琐事搞的我一个头两个大,也没怎么去学新东西,实在是有些愧疚.新项目用到了EventBus3.0,原来只是听说EventBus的鼎鼎大名,一直没仔细研究过.趁着周末有些时间,研究下代码,也算没有虚度光阴. EventBus GitHub : https://github.com/greenrobot/EventBus EventBus3.0简介 EventBus是greenrobot出品的一个用于Android中事件发布/订阅的库.以前传递对象可能通过接口.广播.文件

Andfix热修复框架原理及源码解析-下篇

热补丁介绍及Andfix的使用 Andfix热修复框架原理及源码解析-上篇 Andfix热修复框架原理及源码解析-下篇 如果没有看过上篇的建议从上篇看起.先大概回忆下,上一篇分析了mPatchManager.init("1.0"),addPatch()方法.还有通过分析打补丁工具,了解补丁文件是怎么生成的.下面就来讲讲我们如何去读它.思绪回到Application的loadPatch()方法. 这个方法就是遍历mPatchs,就是上篇介绍的存储patch的一个集合.根据补丁名找到对应的

Android 热修复Nuwa的原理及Gradle插件源码解析

现在,热修复的具体实现方案开源的也有很多,原理也大同小异,本篇文章以Nuwa为例,深入剖析. Nuwa的github地址 https://github.com/jasonross/Nuwa 以及用于hotpatch生成的gradle插件地址 https://github.com/jasonross/NuwaGradle 而Nuwa的具体实现是根据QQ空间的热修复方案来实现的.安卓App热补丁动态修复技术介绍.在阅读本篇文章之前,请先阅读该文章. 从QQ空间终端开发团队的文章中可以总结出要进行热更