精华内容
下载资源
问答
  • Android Settings

    2013-07-17 10:16:46
    Android源码之Android Settings代码分享
  • Android 8.0 系统自带 Settings Android 8.0 系统自带 Settings
  • 安卓 logcat设置 Android logcat Settings.pdf
  • android 4.4 Settings

    2018-04-11 08:29:39
    android 4.4 Settings 源码
  • Android settings源码

    2017-09-13 15:07:52
    Android settings源码 有需要自己写settings的可以参考下。Android 4.4的 很有用的。支持大家看 Android 4.4的
  • Android N Settings模块与Android M Settings模块差异

    千次阅读 热门讨论 2016-11-03 21:16:15
    android N settings 模块与android M settings 模块的差异

    Android N Settings 与之前的Android版本Settings模块在UI上右较大的改变,最直观的差异在于Android N 设置界面增加了向右滑动的抽屉效果。

    先回想下android M设置的主界面时怎么显示和交互的?

    android M设置的主界面是定义在res/xml/dashboard_categories.xml这个文件中,其SettingsActivity.java继承与Activity,在其onCreate方法中,通过mIsShowingDashboard变量,控制加载不同的布局R.layout.settings_main_dashboard或者 R.layout.settings_main_prefsSettings的主界面或者二级三级菜单),mIsShowingDashboard Boolear类型的变量是直接判断当前要启动的Activity的类是不是Settings.class,因为Settings这个类才是Settings的入口类。若是加载Settings的主界面,则是加载R.layout.settings_main_dashboard布局,这个布局里面仅仅只是一个FrameLayoutViewContainer

    SettingsActivity.javabuildDashboardCategories()->loadCategoriesFromResource(R.xml.dashboard_categories, categories, this) ->updateTilesList()去解析这个布局文件,动态添加或删除一级菜单的item,然后将主界面加载到FrameLayoutViewContainer中,这样一级菜单就显示出来了。

    对于二级菜单,在设置模块中有一个Settings.java的类,该类中定义了许多直接继承与SettingsActivity的空实现Activity,如果细心点还会发现,这些空实现的ActivityAndroidMainifest.xml中都有<meta-data/>属性,这是为什么呢?原因很简单,上面说过,既然这些Activity都是继承与SettingsActivity,那么要启动这些Activity时必然会走SettingsActivityonCreate()方法,刚在还提到SettingsActivityonCreate()方法是需要加载一级菜单还是加载二级菜单,取决于mIsShowingDashboard变量,而现在要确定加载的是二级菜单的具体哪个菜单?则需要用到AndroidMainifest.xml中声明该Activity时定义的<meta-data/>属性。

     

    当然,并不是所有的设置项都是这样,例如若要在Settings中添加一个选项,跳转到其他应用中,那么也可以直接在dashboard_categories.xml中添加相应的item,直接用intent跳转过去也时可以的。

     

    那么,Android N中的设置主界面又时怎么显示和交互的呢?

    Android N 在代码架构上和Android M最大的区别是Android N设置代码分为了两部分,一部分是在packages/apps/Settings,另一部分是在frameworks/base/packages/SettingsLib

    Android N Settings增加了向右滑动的抽屉效果,即无论在一级二级还是三级菜单界面下,只要向右滑动就可以调出Settings的主界面(事实上是无论哪一级菜单,只要改级菜单继承了SettingsActivity,那么就可以向右滑动调出设置的主界面)。接下来分析Android N Settings的启动流程。

    先看设置的入口类,Settings.java,该类和之前一样,没什么变化,一样继承与SettingsActivity.java,内部也定义了很多空实现的内部类,空Activity继承与SettingsActivity。那么应该和android M一样,真正的入口还是在SettingsActivity中。直接看SettingsActivity类,发现SettingsActivity不再是继承与Activity类,而是继承与rameworks/base/packages/SettingsLib下面的SettingsDrawerActivity,看一下SettingsDrawerActivity是什么鬼??

    SettingsDrawerActivity从名字来看,好像和实现滑动效果有关,到该类一看究竟,发现它加载的布局时settings_with_drawer.xml,这个布局中用到android.support.v4.widget.DrawerLayoutDrawerLayout 布局就具有左右滑动的抽屉效果。在看settings_with_drawer.xml布局中有一个listView控件,猜想该控件就是用来显示抽屉布局中的设置的主界面(后面在分析)。

    继续看SettingsActivityonCreate方法,其加载布局的地方和之前没什么区别,可是却发现有区别的地方在从DashboardSummary.class中的获取要加载item的数据部分,DashboardSummary.javarebuildUI()中,调用了SettingsDrawerActivitygetDashboardCategories()获取数据。也就是说在设置中主界面上要显示那些选项也是rameworks/base/packages/SettingsLib中的SettingsDrawerActivitygetDashboardCategories()获取来的,而不是像Android M一样将布局定义在res/xml/dashboard_categories.xml,现在在Settings模块下面已经没有了dashboard_categories.xml文件,那么数据到底是怎么获取出来的呢?

    现在来分析SettingsDrawerActivity类,在SettingsDrawerActivityonCreate方法中,如上面提到,它加载的布局是settings_with_drawer.xml,它是外层是DrawerLayout,所以能左右滑动(可自行百度DrawerLayout用法),该布局中有listView,这个listView就是我们左右滑动看到的DrawerLayout的主界面,listViewAdapterSettingsDrawerAdapter的实例,SettingsDrawerAdapter继承于BaseAdapter,SettingsDrawerActivity中通过getDashboardCategories()   >TileUtils.getCategories(this, sTileCache),获取的数据,它将数据也是封装成DashboardCategory类型。

    再来看 TileUtils类的getCategories()是如何返回DashboardCategory对象的List?先看代码:

     

    getCategories()方法差不多可分为三步:

    1.先调用getTilesForAction() ->getTilesForInten()从AndroidMainifast.xml中声明的activity信息中解析,并将解析的数据以Tile类型数据结构封装。

    2,遍历存储所有Tile的集合,将通过tile.category属性,将其分类,并构建成category,并将category存储在对应的categoryMap中。

    3.构建categories,并且排序,排序是按照DashboardCategorypriority排序的。

    先看一下Title数据类型,它里面封装的每一个变量都是和AndroidMainifast.xml中声明Activity<meta-data/>标签一一对应的。

     

     

    Title  : 对应每个item显示的标题

    icon   :对应每个Item显示的图标

    summary  : 对应每个item显示的详细信息,summary

    intent  :item 对应的intent

    cetagory :item 属于哪一个catagory

    priority  : item 的位置有关,排序会用到

    extras  Optional additional data for use by subclasses of the activity,从启动它的类携带过来的信息

    meta-data The metaData from the activity that defines this tile,定义的meta-data

     

    通过这样的方法获取到了Settings主菜单和左后滑动抽屉效果需要显示的数据,然后将数据抛给SettingsDrawerAdapter,在它的getView方法中去创建对应的view,就将界面展示出来了。

    总结一下:到了Android N设置中没有主界面显示的布局文件,而是直接解析AndroidManifast.xml文件,从该文件中获得并处理要显示的信息。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • simple-settingsAndroid的:gear:Simple Settings
  • android settings 源码

    2014-03-27 17:39:19
    android settings 源码,这是修改过的settings源码,有兴趣可以看看
  • 安卓 logcat设置 Android logcat Settings   作者:韩梦飞沙 Author:han_meng_fei_sha   邮箱:313134555@qq.com E-mail: 313134555 @qq.com

    安卓 logcat设置

    Android logcat Settings

     

    作者:韩梦飞沙

    Author:han_meng_fei_sha

     

    邮箱:313134555@qq.com

    E-mail: 313134555 @qq.com

     

     

    展开全文
  • Android Settings 源码

    2017-01-20 14:38:21
    原生态Android settings 源码
  • Android 8.1 、Android Settings 谷歌原生,安卓系统设置应用,源码下载。。。。。。。。。
  • Android5.0 Settings源码

    热门讨论 2015-03-23 09:38:34
    Android5.0 Settings源码
  • androidN settings模块

    2017-07-10 17:16:00
    settings模块UI介绍
  • Android Settings总结

    千次阅读 2016-05-07 10:19:07
    1.Settings简介Settings,包括手机各项属性的基本调整和功能的开关,是用户根据个人喜好对手机进行定制的最方便的入口,也是用户在日常生活中使用频率最高的模块之一。因此,它的稳定性、修改定制,对于开发者来说尤...

    1.Settings简介

    Settings,包括手机各项属性的基本调整和功能的开关,是用户根据个人喜好对手机进行定制的最方便的入口,也是用户在日常生活中使用频率最高的模块之一。因此,它的稳定性、修改定制,对于开发者来说尤为重要。

    在目前的移动设备中,Settings界面除过主题定制的颜色图标等差别外,存在两种形式:单页形式和分页形式。单页形式为主要形式,而在平板等大屏设备中,则会采用分页形式。
    单页
    分页
    图1 单页(左)和分页(右)

    原生的Android4.0以后的系统中,将设置分为四个部分:

    WIRELESS&NETWORKS:SIM卡管理,流量使用情况,飞行模式,VPN,网络共享等。
    DEVICE:情景模式,显示,存储,电池,应用程序。
    PERSONAL:账户与同步,位置服务,安全,语言和输入法,备份和重置。
    SYSTEM:日期和时间,定时开关及,辅助功能,开发人员选项,关于手机。
    

    2.Settings代码结构

    Settings其实是以应用Settings.apk的形式存在于手机系统中的。在Google源码中的路径为:

    /packages/apps/Settings/src

    具体代码结构图如下:
    Settings代码结构图
    图2 Settings代码结构图

    Settings第一级菜单的显示主要由包com.android.settings下面的Settings.java来负责控制。在该包下面,还包含了其他一些功能设置项的控制类,比如DisplaySettings.java等。其他包从包名基本可以看出,具体负责对应功能模块的控制。各个功能模块封装相对独立,这样,我们只需要进入具体模块,一般就可以完成对其的修改。

    3.Settings配置文件

    既然是APK,我们进入AndroidManifest.xml文件中可以看到它的配置信息。在该文件中,有相当多的权限使用声明,这正是因为Settings包含众多的模块,不同的模块可能需要不同的权限所致。

    <application android:label="@string/settings_label"
                android:icon="@mipmap/ic_launcher_settings"
                android:taskAffinity=""
                android:theme="@style/Theme.Settings"
                android:hardwareAccelerated="true"
                android:requiredForAllUsers="true"
                android:supportsRtl="true">
    
            <!-- Settings -->
    
            <activity android:name="Settings"
                    android:label="@string/settings_label_launcher"
                    android:taskAffinity="com.android.settings"
                    android:configChanges="keyboardHidden|screenSize|mcc|mnc"
                    android:launchMode="singleTask">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <action android:name="android.settings.SETTINGS" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.LAUNCHER" />
                    <category android:name="android.intent.category.APP_SETTINGS" />
                </intent-filter>
            </activity>
    
            <activity android:name=".SubSettings"
                    android:taskAffinity="com.android.settings"
                    android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc"
                    android:parentActivityName="Settings">
            </activity>
    ……

    第一个标签中,”android.intent.action.MAIN”的action配合”android.intent.category.DEFUALT”的category,决定了整个Settings.APK默认从Settings这个Activity进入。Settings在Launcher进入时,启动的是Settings.java,由”android.intent.category.LAUNCHER”决定。

    而整个APK在Launcher中的图标,目标进程,主题,硬件加速,是否面向所有用户,是否支持阿拉伯语等属性在标签下进行定义。

    在上面的代码最后,还有一个SubSettings的activity,这也是比较重要的一个类,在小分辨率(未分页)的时候,Settings绝大部分二级菜单都是在SubSettings这个activity中负责控制的。这个后面再讲。

    4.Settings实现原理

    Settings第一级菜单,是一个ListView,每一个item都是由一个Header构成,整个列表由HeaderAdapter来进行适配。在适配的时候,会取出Header的icon以及title,summary等并放入HeaderViewHolder中,这些就是我们在图一左中看到的外在信息。

    然后是对各item的监听,当点击一个item的时候,跳转到具体的模块对应的Fragment中去。

    分页模式和单页模式在基本实现上是一致的,区别在于分页模式Header和对应的Fragment将同时显示,因此,在对应模块的Fragment的显示的时候有区别,这个后面再讲。

    以上,是Settings实现的基本流程,出现的几个词汇分别是Header、Fragment、HeaderAdapter、HeaderViewHolder,后面代码遇到的时候会讲。这里知道大概流程以及需要这些组件就可以了。

    5.Settings代码分析

    5.1 父类PreferenceActivity.java

    我们首先进入Settings.java,它的注释说得很清楚,这个类是用来处理Settings单页和双页的UI布局的顶级Activity。

    /**
     * Top-level settings activity to handle single pane and double pane UI layout.
     */
    public class Settings extends PreferenceActivity
            implements ButtonBarHandler, OnAccountsUpdateListener {
    

    它继承于PreferenceActivity,并实现了ButtonBarHandler和onAccountsUpdateListener接口。PreferencActivity以下简称PA,需要重点分析,因为在当前Settings.java中的部分方法就是重写PA的,有很多重要的代码,单单在Settings.java中是无法理解的,必须进入PA中,才能发现根本原理。而两个接口,只是为了增加按钮栏的处理和账户更新处理的功能,我们不去深入讲。

    5.2 布局文件preference_content_list.xml

    在PA的onCreate()方法中,通过setContentView()设置了preference_content_list的布局,该布局文件定义了Settings的主要界面表现形式。代码如下。

    <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:layout_width="match_parent">
    
        <LinearLayoutandroid:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="0px"
            android:layout_weight="1">
    
            <LinearLayoutstyle="?attr/preferenceHeaderPanelStyle"
                android:id="@+id/headers"
                android:orientation="vertical"
                android:layout_width="0px"
                android:layout_height="match_parent"
                android:layout_weight="@integer/preferences_left_pane_weight">
    
                <ListView android:id="@android:id/list"
                    style="?attr/preferenceListStyle"
                    android:layout_width="match_parent"
                    android:layout_height="0px"
                    android:layout_weight="1"
                    android:clipToPadding="false"
                    android:drawSelectorOnTop="false"
                    android:cacheColorHint="@android:color/transparent"
                    android:listPreferredItemHeight="48dp"
                    android:scrollbarAlwaysDrawVerticalTrack="true" />
    
                <FrameLayout android:id="@+id/list_footer"android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="0" /> /④
    
            </LinearLayout> /③
    
            <LinearLayoutandroid:id="@+id/prefs_frame"
                    style="?attr/preferencePanelStyle"
                    android:layout_width="0px"
                    android:layout_height="match_parent"
                    android:layout_weight="@integer/preferences_right_pane_weight"
                    android:orientation="vertical"
                    android:visibility="gone" >
    
                <!-- Breadcrumb inserted here, in certain screen sizes. In others, it will be an
                    empty layout or just padding, and PreferenceActivity will put the breadcrumbs in
                    the action bar. -->
                <include layout="@layout/breadcrumbs_in_fragment" />
    
                <android.preference.PreferenceFrameLayout android:id="@+id/prefs"
                        android:layout_width="match_parent"
                        android:layout_height="0dip"
                        android:layout_weight="1"
                    />
            </LinearLayout> /⑤
        </LinearLayout> /②
    
        <RelativeLayout android:id="@+id/button_bar"android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:layout_weight="0"
            android:visibility="gone">
    
            <Button android:id="@+id/back_button"
                android:layout_width="150dip"
                android:layout_height="wrap_content"
                android:layout_margin="5dip"
                android:layout_alignParentStart="true"
                android:text="@string/back_button_label"
            />
            <LinearLayoutandroid:orientation="horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true">
    
                <Button android:id="@+id/skip_button"
                    android:layout_width="150dip"
                    android:layout_height="wrap_content"
                    android:layout_margin="5dip"
                    android:text="@string/skip_button_label"
                    android:visibility="gone"
                />
    
                <Button android:id="@+id/next_button"
                    android:layout_width="150dip"
                    android:layout_height="wrap_content"
                    android:layout_margin="5dip"
                    android:text="@string/next_button_label"
                />
            </LinearLayout> /⑦
        </RelativeLayout> /⑥
    </LinearLayout> /①
    

    布局图如下:
    PreferenceActivity布局图
    图3 PreferenceActivity布局图

    从上面布局图,很容易看出,id为headers的LinearLayout即放置HeaderList的地方,右侧则为放置Fragment的地方。单页的时候,只显示左侧LinearLayout③;分页后,右侧的LinearLayout⑤由默认的不显示变为显示,就成为了图1(右)分页后的效果。至于最下方的RelativeLayout⑥,为返回、跳过、前进按钮,默认是不显示的。

    5.3分页相关代码块

    5.3.1 判断是否是分页模式的方法onIsMultiPane()

    在PA中,有个方法onIsMultiPane()来判断是否需要进行分页显示。代码如下,而它其实是通过读取系统属性preferences_prefer_dual_pane的值来判定的。该布尔值位于/frameworks/base/core/res/res/values/bools.xml中。

    /**
         * Called to determine if the activity should run in multi-pane mode.
         * The default implementation returns true if the screen is large
         * enough.
         */
        public boolean onIsMultiPane() {
            boolean preferMultiPane = getResources().getBoolean(
                    com.android.internal.R.bool.preferences_prefer_dual_pane);
            return preferMultiPane;
        }
    

    5.3.2 是否是单页模式的标志mSinglePane

    在PA中,有一个布尔值mSinglePane专门用来标识是否是单页还是分页显示。

    private boolean mSinglePane;

    它在onCreate()方法中获得具体值,如果HeaderList被隐藏了(意味着此时只会显示具体模块的内容)或者非多页模式,那么mSinglePane即为true,表示单页模式。在PA中,涉及到切换到双页模式的几处关键代码,都和这个值有关。下面接着看其他地方。

    boolean hidingHeaders = onIsHidingHeaders();
            mSinglePane = hidingHeaders || !onIsMultiPane();
    

    5.3.3 控制Fragment的显示

    下面的代码仍然在onCreate()方法中,重点看else分支,这个分支即表示切换到分页模式,如果是分页模式且initialFragment为空,也就是暂时没有要显示的Fragment,则通过onGetInitialHeader()方法获取一个初始Header,然后通过switchHeader(h)方法将Header(此时为分页模式,在显示该Header的时候会同样会将整个HeaderList显示出来)和对应的Fragment显示出来。如果initialFragment本来就不为空,则通过switchHeader(initialFragment,initialArgument)方法将此Fragment显示出来。

     if (initialFragment != null && mSinglePane) {
                    Log.d(TAG, "    Show a fragment from EXTRA_SHOW_FRAGMENT.");
    
                    // If we are just showing a fragment, we want to run in
                    // new fragment mode, but don't need to compute and show
                    // the headers.
                    switchToHeader(initialFragment, initialArguments);
                    if (initialTitle != 0) {
                        CharSequence initialTitleStr = getText(initialTitle);
                        CharSequence initialShortTitleStr = initialShortTitle != 0
                                ? getText(initialShortTitle) : null;
                        showBreadCrumbs(initialTitleStr, initialShortTitleStr);
                    }
    
                } else {
                    // We need to try to build the headers.
                    onBuildHeaders(mHeaders);
    
                    // If there are headers, then at this point we need to show
                    // them and, depending on the screen, we may also show in-line
                    // the currently selected preference fragment.
                    if (mHeaders.size() > 0) {
                        Log.d(TAG, "    Build headers successfully.");
    
                        if (!mSinglePane) {
                            if (initialFragment == null) {
                                Header h = onGetInitialHeader();
                                switchToHeader(h);
                            } else {
                                switchToHeader(initialFragment, initialArguments);
                            }
                        }
                    }
    

    在上面代码中,出现几个重要方法:switchToHeader(initialFragment, initialArguments)、showBreadCrumbs(initialTitleStr, initialShortTitleStr)、switchToHeader(h)。可以说,这几个方法决定了分页显示的最终结果。下面将代码贴出来。

    /**
         * When in two-pane mode, switch the fragment pane to show the given
         * preference fragment.
         *
         * @param fragmentName The name of the fragment to display.
         * @param args Optional arguments to supply to the fragment.
         */
        public void switchToHeader(String fragmentName, Bundle args) {
            setSelectedHeader(null);
            switchToHeaderInner(fragmentName, args, 0);
        }
    
        /**
         * When in two-pane mode, switch to the fragment pane to show the given
         * preference fragment.
         *
         * @param header The new header to display.
         */
        public void switchToHeader(Header header) {
            if (mCurHeader == header) {
                // This is the header we are currently displaying.  Just make sure
                // to pop the stack up to its root state.
                getFragmentManager().popBackStack(BACK_STACK_PREFS,
                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
            } else {
                if (header.fragment == null) {
                    throw new IllegalStateException("can't switch to header that has no fragment");
                }
                int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
                switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
                setSelectedHeader(header);
            }
        }
    

    可以看到,这两个为switchToHeader()的参数重载方法。它们最终,都调用了方法switchToHeaderInner(),这个方法中对即将要显示的Fragment进行了初始化,并通过FragmentTransaction的方式启动。

     private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
            getFragmentManager().popBackStack(BACK_STACK_PREFS,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
            if (!isValidFragment(fragmentName)) {
                throw new IllegalArgumentException("Invalid fragment for this activity: "
                        + fragmentName);
            }
            Fragment f = Fragment.instantiate(this, fragmentName, args);
            FragmentTransaction transaction = getFragmentManager().beginTransaction();
            transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            transaction.replace(com.android.internal.R.id.prefs, f);
            transaction.commitAllowingStateLoss();
        }
    

    而showBreadCrumbs()为两个参数重载方法。

    void showBreadCrumbs(Header header) {
            if (header != null) {
                CharSequence title = header.getBreadCrumbTitle(getResources());
                if (title == null) title = header.getTitle(getResources());
                if (title == null) title = getTitle();
                showBreadCrumbs(title, header.getBreadCrumbShortTitle(getResources()));
            } else {
                showBreadCrumbs(getTitle(), null);
            }
        }
    

    上面这个单参数方法,最终其实也是调用了它的另外一个重载方法。它的功能。。。

    /**
         * Change the base title of the bread crumbs for the current preferences.
         * This will normally be called for you.  See
         * {@link android.app.FragmentBreadCrumbs} for more information.
         */
        public void showBreadCrumbs(CharSequence title, CharSequence shortTitle) {
            if (mFragmentBreadCrumbs == null) {
                View crumbs = findViewById(android.R.id.title);
                // For screens with a different kind of title, don't create breadcrumbs.
                try {
                    mFragmentBreadCrumbs = (FragmentBreadCrumbs)crumbs;
                } catch (ClassCastException e) {
                    setTitle(title);
                    return;
                }
                if (mFragmentBreadCrumbs == null) {
                    if (title != null) {
                        setTitle(title);
                    }
                    return;
                }
                if (mSinglePane) {
                    mFragmentBreadCrumbs.setVisibility(View.GONE);
                    // Hide the breadcrumb section completely for single-pane
                    View bcSection = findViewById(com.android.internal.R.id.breadcrumb_section);
                    if (bcSection != null) bcSection.setVisibility(View.GONE);
                    setTitle(title);
                }
                mFragmentBreadCrumbs.setMaxVisible(2);
                mFragmentBreadCrumbs.setActivity(this);
            }
            if (mFragmentBreadCrumbs.getVisibility() != View.VISIBLE) {
                setTitle(title);
            } else {
                mFragmentBreadCrumbs.setTitle(title, shortTitle);
                mFragmentBreadCrumbs.setParentTitle(null, null, null);
            }
        }
    

    5.3.4 控制Fragment在其他配置下的显示

    重新回到PA的onCreate()方法中,继续向下看。

      // The default configuration is to only show the list view.  Adjust
            // visibility for other configurations.
            if (initialFragment != null && mSinglePane) {
                Log.d(TAG, "    Single pane, showing just a prefs fragment.");
    
                // Single pane, showing just a prefs fragment.
                findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
                mPrefsContainer.setVisibility(View.VISIBLE);
                if (initialTitle != 0) {
                    CharSequence initialTitleStr = getText(initialTitle);
                    CharSequence initialShortTitleStr = initialShortTitle != 0
                            ? getText(initialShortTitle) : null;
                    showBreadCrumbs(initialTitleStr, initialShortTitleStr);
                }
            } else if (mHeaders.size() > 0) {
                Log.d(TAG, "    Set list adapter created from headers.");
    
                setListAdapter(new HeaderAdapter(this, mHeaders));
                if (!mSinglePane) {
                    // Multi-pane.
                    getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
                    if (mCurHeader != null) {
                        setSelectedHeader(mCurHeader);
                    }
                    mPrefsContainer.setVisibility(View.VISIBLE);
                }
            } else {
                Log.d(TAG, "    In the old \"just show a screen of preferences\" mode.");
    
                // If there are no headers, we are in the old "just show a screen
                // of preferences" mode.
                setContentView(com.android.internal.R.layout.preference_list_content_single);
                mListFooter = (FrameLayout) findViewById(com.android.internal.R.id.list_footer);
                mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs);
                mPreferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
                mPreferenceManager.setOnPreferenceTreeClickListener(this);
            }
    

    5.3.5 Settings.java中设置Settings的label

    在Settings.java中,也有部分与分页有关的代码。这部分代码,主要是PreferenceActivity无法直接满足Settings具体的要求而进行修改定制时所用。下面这段代码在onCreate()方法中,作用是在分页模式下,将界面的标题设置为Settings的label。这样从Launcher一进入Settings第一级菜单,就会看到左上角的应用标题为Settings。没有这段代码,前面提到的在PA的onCreate()方法中的onGetInitialHeader()方法将会生效,那么第一次进入后将使用HeaderList的第一个Header(WifiSettings)的标题作为标题。

     if (!onIsHidingHeaders() && onIsMultiPane()) {
                highlightHeader(mTopLevelHeaderId);
                // Force the title so that it doesn't get overridden by a direct launch of
                // a specific settings screen.
                setTitle(R.string.settings_label);
            }
    

    5.3.6 Settings中禁用顶端Home返回键

    仍然在Settings的onCreate()方法中,下面的代码用于在分页的时候,禁用界面顶端的Home返回键。从这些代码看出,如果要对Settings的一级菜单进行定制,在onCreate()方法中增加相应的控制代码就可以。

      // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
            if (onIsMultiPane()) {
                getActionBar().setDisplayHomeAsUpEnabled(false);
                getActionBar().setHomeButtonEnabled(false);
            }
    

    5.3.7 Settings.java中的onNewIntent函数

    如果不是从历史栈中启动,将重置到一级菜单。如果是分页模式,将调用switchToHeaderLocal()方法,其最终调用的是PA的switchToHeader()方法,前面已经有介绍。

       @Override
        public void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
    
            // If it is not launched from history, then reset to top-level
            if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
                if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
                    switchToHeaderLocal(mFirstHeader);
                }
                getListView().setSelectionFromTop(0, 0);
            }
        }
    

    5.3.8 Settings.java中的getIntent函数

    下面getIntent()方法,作用是对传递过来的Intent作一下判断和处理,增加Extra信息。主要需要理解这个方法:getStartFragmentClass()。它将得到的superIntent中的组件名与Settings类的进行比对,如果相同则返回null;如果不同,则返回类名,使其能够以Fragment的形式进行加载。不难发现,这个方法对分页模式不会有任何影响。

     @Override
        public Intent getIntent() {
            Intent superIntent = super.getIntent();
            String startingFragment = getStartingFragmentClass(superIntent);
            // This is called from super.onCreate, isMultiPane() is not yet reliable
            // Do not use onIsHidingHeaders either, which relies itself on this method
            if (startingFragment != null && !onIsMultiPane()) {
                Intent modIntent = new Intent(superIntent);
                modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
                Bundle args = superIntent.getExtras();
                if (args != null) {
                    args = new Bundle(args);
                } else {
                    args = new Bundle();
                }
                args.putParcelable("intent", superIntent);
                modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
                return modIntent;
            }
            return superIntent;
        }
    

    5.4 Settings.java核心代码

    5.4.1 Settings.java的onCreate函数

    onCreate()方法在刚才已经有讲过,主要对PA进行进一步的定制,不再多说。

    5.4.2 Settings.java的onResume函数

    onResume()方法中,进行了多个BroadcastRecevier的注册。其中一个比较重要的地方,就是对【开发者选项】的监听器。在用户版本,默认【开发者选项】是被隐藏的。只有在第一级菜单先进入【关于手机】,然后连续按7次【Build Number】后,才能将其激活,从而在第一级菜单中显示出来。下面的代码就是这个监听器的创建和注册。

       mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
                @Override
                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                    invalidateHeaders();
                }
            };
            mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
                    mDevelopmentPreferencesListener);
    

    5.4.3 Settings.java的onPause函数

    在onPause()方法中,对在onResume()方法中注册的监听器进行unRegisterRecevier()操作。(代码略)

    5.4.4 Settings.java的onBuilderHeaders函数

    Settings一级菜单中几乎所有(账户相关的由代码中的具体方法控制增删,后面有讲)的Header均是通过onBuildHeaders()方法进行加载的。

      /**
         * Populate the activity with the top-level headers.
         */
        @Override
        public void onBuildHeaders(List<Header> headers) {
            if (!onIsHidingHeaders()) {
                PDebug.Start("loadHeadersFromResource");
                loadHeadersFromResource(R.xml.settings_headers, headers);
                PDebug.End("loadHeadersFromResource");
                updateHeaderList(headers);
            }
        }
    

    从上面代码中,可以看到一个非常重要的XML文件:settings_headers.xml。所有要显示的Header均在这个文件中以 < header>的形式进行定义。

    每一个< header>中,定义了id、icon、fragment、title属性,各自的作用分别为:id用来标识是哪个header;icon即Settings一级菜单上显示的每一个item前的图像;fragment用来指定具体启动的类,用完整的包名表示;title即一级菜单中每一个item的名称,例如Wifi、Bluetooth等。

    所以,我们要增加一个功能的话,只需要在这个文件中增加一个< header>,然后实现对应的功能类即可。

    <preference-headers
            xmlns:android="http://schemas.android.com/apk/res/android">
    
    
        <!-- WIRELESS and NETWORKS -->
        <header android:id="@+id/wireless_section"
            android:title="@string/header_category_wireless_networks" />
    
        <!-- Sim management -->
        <header
            android:id="@+id/sim_settings"
            android:icon="@drawable/ic_settings_dualsim"
            android:fragment="com.mediatek.gemini.SimManagement"
            android:title="@string/gemini_sim_management_title" />
        <!-- Wifi -->
        <header
            android:id="@+id/wifi_settings"
            android:fragment="com.android.settings.wifi.WifiSettings"
            android:title="@string/wifi_settings_title"
            android:icon="@drawable/ic_settings_wireless" />
    
        <!-- Bluetooth -->
        <header
            android:id="@+id/bluetooth_settings"
            android:fragment="com.android.settings.bluetooth.BluetoothSettings"
            android:title="@string/bluetooth_settings_title"
            android:icon="@drawable/ic_settings_bluetooth2" />
    
     ……(省略中间部分)……
    
        <header
            android:id="@+id/about_settings"
            android:fragment="com.android.settings.DeviceInfoSettings"
            android:icon="@drawable/ic_settings_about"
            android:title="@string/about_settings" />
    
    </preference-headers>
    

    5.4.5 Settings.java的updateHeaderList函数

    接着上面,在通过loadHeadersFromResource()方法加载settings_headers.xml后,紧跟着调用方法updateHeaderList(headers)对headers做具体的处理。这块的代码比较长,具体所做的事就是对各个具体功能根据需要进行控制,代码逻辑非常清晰,就不再贴出来了。

    5.4.6 Settings.java的HeaderAdapter内部类

    HeaderAdapter为整个Settings的一级菜单ListView的适配器,它声明为了Settings.java的静态内部类,继承自ArrayAdapter。与所有Adapter一样,它的主要内容是将我们之前得到的headersList如何显示在ListView中去。我讲一下需要特别理解的主要思路:如下面代码,预先定义了5种Header的类型,以满足对不同外观的Header的分别管控处理,比如Wifi和蓝牙这样的带有开关的,即HEADER_TYPE_SWITCH;DisplaySettings这样正常的,即HEADER_TYPE_NORMAL;快速启动这样带有CheckBox的,即HEADER_TYPE_CHECK;还有fragment和intent都为空的只是为了做区分的Header,即HEADER_TYPE_CHECK。

    不同的Header可以自定义不同的XML布局,这样,就使得一级菜单每一个item根据功能的不同表现出不同的外观。

       private static class HeaderAdapter extends ArrayAdapter<Header> implements
                CompoundButton.OnCheckedChangeListener {
            static final int HEADER_TYPE_CATEGORY = 0;
            static final int HEADER_TYPE_NORMAL = 1;
            static final int HEADER_TYPE_SWITCH = 2;
            static final int HEADER_TYPE_BUTTON = 3;
            static final int HEADER_TYPE_CHECK = 4;
            private static final int HEADER_TYPE_COUNT = HEADER_TYPE_CHECK + 1;
    

    在HeaderAdapter.java中,又定义了一个内部静态类HeaderViewHolder,它相当每一个Header在ListView中要表现的所有元素的数据类型集合。

          private static class HeaderViewHolder {
                ImageView icon;
                TextView title;
                TextView summary;
                Switch switch_;
                CheckBox check;//add by yangzhong.gong for FR581773
                ImageButton button_;
                View divider_;
            }
    

    而获取视图的方法为getView(),主要思路很简单,就是判断不同类型的HEADER_TYPE,然后根据不同的HEADER_TYPE做不同的处理,将所需要的title、icon等等信息装入事先定义好的HeaderViewHolder对象。然后用setTag(holder)方法传递给view对象,最后将view返回。(代码略)

    最后就是单击等操作的监听器设置。这里只是想强调一点,附加功能都可以通过接口来实现,例如在上个例子里,就实现了CompoundButton.OnCheckedChangeListener接口,而这个是我们在做定制时自己添加的。

    @Override
        public void onHeaderClick(Header header, int position) {
            boolean revert = false;
            if (header.id == R.id.account_add) {
                revert = true;
            }
    
            super.onHeaderClick(header, position);
    
            if (revert && mLastHeader != null) {
                highlightHeader((int) mLastHeader.id);
            } else {
                mLastHeader = header;
            }
    
            if (header.id == R.id.regulatory_safety) {
                Intent intent = new Intent();
                ComponentName comp = new ComponentName(
                        "com.eyelike.Elabel",
                        "com.eyelike.Elabel.SettingsRegulatoryActivity");
                intent.setComponent(comp);
                startActivity(intent);
            }
        }
    

    5.5 定制相关的代码块

    5.5.1 定制fragment

    在Settings.java中定义了一个字符串数组ENTY_FRAGMENTS,这个数组的声明与方法isValidFragment()关系甚大。而isValidFragment()方法是PA用来判断Fragment是否可用的,在Settings.java中做了复写。

    之前在讲Settings主要实现原理的时候有讲,每一个具体功能都由Fragment来实现。如果我们想要在第一级菜单中增加一个功能,只需要在

    private static final String[] ENTRY_FRAGMENTS = {
            WirelessSettings.class.getName(),
            WifiSettings.class.getName(),
            AdvancedWifiSettings.class.getName(),
            BluetoothSettings.class.getName(),
            TetherSettings.class.getName(),
            WifiP2pSettings.class.getName(),
            VpnSettings.class.getName(),
            DateTimeSettings.class.getName(),
            LocalePicker.class.getName(),
            InputMethodAndLanguageSettings.class.getName(),
            SpellCheckersSettings.class.getName(),
            UserDictionaryList.class.getName(),
            UserDictionarySettings.class.getName(),
            SoundSettings.class.getName(),
            DisplaySettings.class.getName(),
            DeviceInfoSettings.class.getName(),
            ManageApplications.class.getName(),
            ProcessStatsUi.class.getName(),
            NotificationStation.class.getName(),
            LocationSettings.class.getName(),
            SecuritySettings.class.getName(),
            PrivacySettings.class.getName(),
            DeviceAdminSettings.class.getName(),
            AccessibilitySettings.class.getName(),
            ToggleCaptioningPreferenceFragment.class.getName(),
            TextToSpeechSettings.class.getName(),
            Memory.class.getName(),
            DevelopmentSettings.class.getName(),
            UsbSettings.class.getName(),
            AndroidBeam.class.getName(),
            WifiDisplaySettings.class.getName(),
            PowerUsageSummary.class.getName(),
            AccountSyncSettings.class.getName(),
            CryptKeeperSettings.class.getName(),
            DataUsageSummary.class.getName(),
            DreamSettings.class.getName(),
            UserSettings.class.getName(),
            NotificationAccessSettings.class.getName(),
            ManageAccountsSettings.class.getName(),
            PrintSettingsFragment.class.getName(),
            PrintJobSettingsFragment.class.getName(),
            TrustedCredentialsSettings.class.getName(),
            PaymentSettings.class.getName(),
            KeyboardLayoutPickerFragment.class.getName(),
            //M@:
            SimManagement.class.getName(),
            SimInfoEditor.class.getName(),
            //Class name same as Activity name so use full name here
            com.mediatek.gemini.SimDataRoamingSettings.class.getName(),
            AudioProfileSettings.class.getName(),
            Editprofile.class.getName(),
            HDMISettings.class.getName(),
            SelectSimCardFragment.class.getName(),
            UsbSharingChoose.class.getName(),
            UsbSharingInfo.class.getName(),
            TetherWifiSettings.class.getName(),
            DrmSettings.class.getName(),
            NfcSettings.class.getName(),
            WifiGprsSelector.class.getName(),
            BeamShareHistory.class.getName(),
            CardEmulationSettings.class.getName(),
            MtkAndroidBeam.class.getName(),
            HotKnotSettings.class.getName(),
            MasterClear.class.getName()//add by eyelike
        };
    
        @Override
        protected boolean isValidFragment(String fragmentName) {
            // Almost all fragments are wrapped in this,
            // except for a few that have their own activities.
            for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
                if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
            }
            return false;
        }
    

    5.5.2 定制ActionBar

    在Settings的第二级菜单,也就是各个具体功能界面,大量应用了ActionBar(界面最上方的条状栏,右侧往往有开关等功能按钮)。而在Wifi、蓝牙等设置界面,有时候会看到界面下方也有按钮,如下图。
    这里写图片描述
    图4 ActionBar分离示例图
    这是由ActionBar的属性来控制的,对应于XML文件中的属性为:

    android:uiOptions="splitActionBarWhenNarrow"

    而在Settings.java中的控制在onBuildStartFragmentIntent()方法中,代码如下。如果要修改相关功能,只需在其中做增删即可。

     @Override
        public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
                int titleRes, int shortTitleRes) {
            Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
                    titleRes, shortTitleRes);
    
            // Some fragments want split ActionBar; these should stay in sync with
            // uiOptions for fragments also defined as activities in manifest.
            if (WifiSettings.class.getName().equals(fragmentName) ||
                    WifiP2pSettings.class.getName().equals(fragmentName) ||
                    BluetoothSettings.class.getName().equals(fragmentName) ||
                    DreamSettings.class.getName().equals(fragmentName) ||
                    LocationSettings.class.getName().equals(fragmentName) ||
                    BeamShareHistory.class.getName().equals(fragmentName) ||
                    MtkAndroidBeam.class.getName().equals(fragmentName) ||
                    AudioProfileSettings.class.getName().equals(fragmentName) ||                
                    ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
                    PrintSettingsFragment.class.getName().equals(fragmentName) ||
                    PrintServiceSettingsFragment.class.getName().equals(fragmentName) ||
                    HotKnotSettings.class.getName().equals(fragmentName)) {
                intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
            }
    
            intent.setClass(this, SubSettings.class);
            return intent;
        }
    

    5.6 Settings其他重要问题释疑

    以上通过代码段对主要实现进行了介绍,但是,如果跳出一小块一小块代码,从整体上来看,还是会有一些一时难以琢磨理解的疑问。下面,就将我曾经遇到的一些主要疑问列出来,并做一些解答。

    5.6.1 为什么使用Hierarchyviewer 工具查看时Settings中的很多界面显示的都是SubSettings?

    要解决这个问题我们先要清楚为什么会写一个SubSettings.java继承自Settings.java?SubSettings.java的内容非常简单,代码如下。

    /**
     * Stub class for showing sub-settings; we can't use the main Settings class
     * since for our app it is a special singleTask class.
     */
    public class SubSettings extends Settings {
    
        @Override
        public boolean onNavigateUp() {
            finish();
            return true;
        }
    
        @Override
        protected boolean isValidFragment(String fragmentName) {
            Log.d("SubSettings", "Launching fragment " + fragmentName);
            return true;
        }
    }
    

    SubSettings.java中的注释很清楚的告诉了我们原因:Stub class for showing sub-settings; we can’t use the main Settings class since for our app it is a special singleTask class。

    原来是因为Settings.java在声明时指定了android:launchMode=”singleTask”。

    要显示Fragment的内容,我们就必须为其指定一个Activity。而Settings中的很多设置界面是由PreferenceFragment来完成的,当然也需要我们指定Activity。PA中得onBuildStartFragmentIntent函数会为我们构造一个显示Fragment的Intent对象(该函数的注释写的非常明白)。Settings.java重写了这个函数(见4.2,重写时它调用了super的该方法),在为intent对象setClass时都使用SubSettings.java(注:在settings_headers.xml指定了intent的header是不会触发onBuildStartFragmentIntent的)。

    结果就是,Settings中大部分fragment都是使用的SubSettings这个Activity来显示。由于Hierarchyviewer只是显示当前界面使用的Activity(不能显示这个界面是由哪个Fragment构造的),所以我们使用Hierarchyviewer 对Settings进行观察时很多设置界面显示的是SubSettings。

    5.6.2 Hierarchyviewer 中显示SubSetting时如何确定我进入的是哪个fragment?

    在res/xml/settings_headers.xml中声明了各个header被点击后使用的fragment。我们可以根据这个文件确定我们进入的fragment。

    例如,当我们点击Display时Hierarchyviewer 中显示SubSetting。我们通过查找settings_headers就可知道使用的是哪个fragment(见5.1)。header中使用 android:fragment指明使用的fragment。由此可知,Display使用的是com.android.settings.DisplaySettings这个fragment。

    5.6.3 点击设置界面的某一个header时,设置界面是如何切换的?

    点击设置界面的header时,会触发Settings中onHeaderClick函数,主要的处理都在其父类PreferenceActivity的onHeaderClick中实现的。如果这个header指定了fragment,在mSinglePane(见5.3)为true时,会调用startWithFragment方法,在startWithFragment方法中将调用onBuildStartFragmentIntent方法来构造intent对象(重要),最后使用该intent对象启动一个activity来显示fragment。

    以点击Settings中的Display为例(Bluetooth同理,只不过启动的Activity变为BluetoothSettingsActivity(继承自Settings,但是没有实现重写任何方法,所以与SubSettings是一样的处理),fragment变为 com.android.settings.bluetooth.BluetoothSettings)。fragment是com.android.settings.DisplaySettings,activity是com.android.settings.SubSettings(fragment是由onHeaderClick函数传入的,activity是由onBuildStartFragmentIntent()指定的)。

    执行startActivity后将启动SubSettings.java。即我们将会再一次执行SubSettings和PreferenceActivity的onCreate方法(因为Settings.java的onCreate方法调用了super.onCreate()),但是这次并不会进入Settings的主界面,因为我们的使用的intent对象是有很大不同的。这一次onCreate函数(PreferenceActivity)中的initialFragment 将被初始化为com.android.settings.DisplaySettings,然后我们将进入switchToHeader(),最后switchToHeaderInner会取得FragmentTransaction对象(见5.6),然后执行了transaction.replace(com.android.internal.R.id.prefs, f)。就这样把我们的fragment显示出来了。在onCreate中会对其他view的visibility进行设置,以保证只显示prefs。如,将com.android.internal.R.id.headers的visibility设置为VIEW.GONE。

    5.6.4 Settings.java中getMetaData与getStartingFragmentClass这两个函数是否有点矛盾?

    这两个函数可以说是相辅相成的。getMetaData会从AndroidManifest.xml中读取Activity的节点的数据;getStartingFragmentClass则从启动Activity的intent中读取数据。这两个函数会对读取到的数据进行整合,getStartingFragmentClass依赖于getMetaData读取到的数据,但是它也可能对数据作出修改(为了兼容性,如对原有manage apps类进行特殊处理)。

    5.6.5 Settings的shortcut是如何创建的?从shortcut进入Settings的流程是什么?

    创建Settings的shortcut时Luancher将会启动CreateShortcut,创建shortcut所需的intent对象将会由CreateShortcut和其父类LuancherActivity共同构建(详见 CreateShortcut的onListItemClick),这时创建的Intent对象使用的就不是SubSettings了(LuancherActivity中intentForPosition函数执行setClassName()时使用的参数并不是SubSettings)。

     public Intent intentForPosition(int position) {
                if (mActivitiesList == null) {
                    return null;
                }
    
                Intent intent = new Intent(mIntent);
                ListItem item = mActivitiesList.get(position);
                intent.setClassName(item.packageName, item.className);
                if (item.extras != null) {
                    intent.putExtras(item.extras);
                }
                return intent;
            }
    

    CreateShortcut中列出了可以创建shortcut的设置项,这些设置项怎样检索出来的?
    原来,在创建LuancherActivity的ActivityAdapter对象时,其构造函数中执行了makeListItems函数,该函数将使用PackageManager的queryIntentActivities来根据intent对象查询符合条件的activity。使用的intent是从getTargetIntent函数返回的。不难发现,要想在CreateShortcut中显示,Activity在必须要有

    <category android:name="com.android.settings.SHORTCUT" />

    如果我们想将Security设置项添加到shortcut列表,我们只需要在androidmanifest.xml中声明Settings$SecuritySettingsActivity部分加上

    <category android:name="com.android.settings.SHORTCUT" />

    即可。

    回到正题,点击shortcut进入Settings时,传入的Intent对象中包含了目标fragment和目标activity以及其他信息。PreferenceActivity得到了足够多的信息,因此在onCreate中将依次执行switchToHeader()->setSelectedHeader(null)->switchToHeaderInner()->transaction.replace(com.android.internal.R.id.prefs, f);
    这样就完成了fragment的显示(使用的activity是从intent解析出来的。在switchToHeaderInner中执行Fragment.instantiate时使用的Context是this!!)。不像执行onHeaderClick那样会执行函数onBuildStartFragmentIntent(Settings中重写了该函数)来重新指定我们使用的Activity。

    5.6.6 为什么我从Settings的shortcut进入时,Hierarchyviewer显示的就不是SubSettings(如Data usage)?

    Hierarchyviewer中显示SubSettings是因为我们在onBuildStartFragmentIntent方法中做了特殊处理(详见问题二)。从shortcut进入Settings时不显示SubSettings是因为没有走这个函数,因此就不会显示为SubSettings了(详见问题六)。

    5.6.7 Settings.java中很多继承自它的内部类都是空实现,为什么要写这些类?

    空实现,使得他们虽然被声明,但仍然都将使用Settings.java中的函数(注意private的属性和方法的访问权限问题)。因此,这样的构造必定是为了其他的便利。注释讲了一点原因:声明的这些类都将作为Settings的子类,为的是在启动的时候保持独立性。这样能够提高各个设置项、整个Settings的灵活性,方便开发者进行扩展。

    /*
         * Settings subclasses for launching independently.
         */
    

    除此之外,和整个Settings的设计结构也由一定关系:

    ①这样声明非常清晰明朗,易于维护;
    ②可以让我们为单独的设置项添加 shortcut(如data usage),因为创建shortcut使用queryIntentActivities查询使用的activity;
    ③允许其它程序访问单独的设置项;
    ④结构设计需要,启动Activity会读取meta-data信息;
    ⑤使得某些设置项可以不使用SubSettings的属性。如,在Settings中点击Bluetooth时使用BluetoothSettingsActivity,启动Bluetooth时将使用BluetoothSettingsActivity的属性,如 android:clearTaskOnLaunch=”true”。
    等等。

    • eyelike@2014-06-11
    展开全文
  • android_8.1 Settings源码

    2018-07-25 15:37:46
    android_8.1源码 、android go Settings谷歌原生代码。。。
  • Android Settings添加选项

    千次阅读 2016-01-29 17:35:09
    Android中,设置Settings一直都是header+fragment的形式出现。例如设置里面的“时间和日期”项,在settings_hesder.xml中其布局大概是这样: android:id="@+id/date_time_settings" android:fragment=...

    在Android中,设置Settings一直都是header+fragment的形式出现。例如设置里面的“时间和日期”项,在settings_hesder.xml中其布局大概是这样:

    <!-- Date & Time -->
        <header
            android:id="@+id/date_time_settings"
            android:fragment="com.android.settings.DateTimeSettings"
            android:icon="@drawable/ic_settings_date_time"
            android:title="@string/date_and_time">
            <extra android:name="setting_component_key" android:value="date_time" />
            <intent android:targetPackage="com.android.settings"
                    android:targetClass="com.android.settings.Settings$DateTimeSettingsActivity"/>
        </header>

     
    

    这是典型的一个设置中的选项,DateTimeSettings.java 继承了Fragment。

         所以要添加一个选项,模仿即可。

         本例中以添加一个“老人模式”选项为例,为连线到一个APP中,即点击此选项将跳转到相对应的APP。只需这个应用的包名和类名,我们就可以把它嵌入到设置Settings中。

         1、首先在settings_hesder.xml中添加选项

    <header
            android:id="@+id/aged_mode_setting_new"
            android:icon="@drawable/ic_settings_aged_mode"
            android:title="@string/aged_mode_setting_title" >
            <intent android:action="com.hll.haolauncher"/>
        </header>

         其中,com.hll.haolauncher是目标应用的包名。


         2、在SettingsActivity.java中添加该选项显示

    <pre name="code" class="java">// Show only these settings for restricted users
    private int[] SETTINGS_FOR_RESTRICTED = {
                R.id.wireless_section,
                R.id.wifi_settings,
    			 ......
                R.id.aged_mode_setting_new
        };

          3、找到updateHeaderList方法,更新Settings选项的状态,代码大概如下: 
    

    private void updateHeaderList(List<Header> target) {
            PDebug.Start("updateHeaderList");
            int i = 0;      
            mHeaderIndexMap.clear();
            while (i < target.size()) {
                Header header = target.get(i);
                int id = (int) header.id;
                 if (id == R.id.bluetooth_settings) {
                    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
                        target.remove(i);
                    }          
                } else if (id == R.id.battery_settings) {
                    if (!mBatteryPresent) {
                        target.remove(i);
                    }          
                }
    		else if(id==R.id.aged_mode_setting) {
             	   if (enable){
             	  	  	target.remove(header);
             	  	}
             }
            PDebug.End("updateHeaderList");
    }
     一些选项的显示或隐藏将被写到这里面。


           4、设置Settings中的点击事件被写在onHeaderClick方法中,如下:

    if (header.id ==R.id.aged_mode_setting_new)
    	       {
    	         Intent mIntent = new Intent();
    	         mIntent.setClassName("com.hll.haolauncher","com.hll.elauncher.StartActivity");
    		  mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    	         startActivity(mIntent);
                 return;
            }

    过程基本上是如此,由于笔者手上的代码不是原生系统的源码,不能把多一些的源码贴上,其中可能与原生代码有些出入,但大同小异,大都数地方还是相同的。




    展开全文
  • 安卓7.0Settings源码

    2017-10-30 09:24:06
    安卓7.0Settings源码,可供大家参考,实属居家旅行必备。
  • Android 7.0 Settings分析

    千次阅读 2018-07-26 15:13:01
    最近参加了settings的开发,由于代码还比较新, Android7.0 对Settings进行了重构,相比5.0,6.0而言,7.0的Settings有很大的不同,所以将开过程中的一些点点滴滴记录下来.此篇文章主要给大家介绍Settings相关的基础...
  • adb命令行打开Android settings

    千次阅读 2019-12-02 14:32:56
    adb shell am start com.android.settings/com.android.settings.Settings 安全 adb shell am start com.android.settings/com.android.settings.SecuritySettings 手机无线信息 adb shell am start ...
  • 本篇文章主要介绍了Android 7.0 Settings 加载选项,Android 7.0 Settings顶部多了一个建议选项,多了个侧边栏,操作更加便捷了,有兴趣的可以了解一下。
  • Android L Settings实现

    千次阅读 2015-10-28 17:47:49
    概述Android L的settings界面实现和Android4.4版本有很大的不同,在Android L中,setting是使用dashboard等控件进行了重新实现。具体流程如下。初始化流程Android L Settings模块首界面为Settings,继承自...
  • Android_Settings_4.0

    2016-01-02 20:35:58
    Android_Settings_4.0
  • Android 7.1 Settings详解

    万次阅读 2018-01-08 09:28:46
    Android N 上Settings是带有侧拉菜单的,我们先从界面的角度大致看下Settings是怎么显示出来,然后再看下view对应的数据是如何加载而来的,先来看看设置的界面如下: 从图片可以看出主界面有点类似listview的...
  • android 2.3 settings apk 源码
  • Android CTS SettingsProvider日志Android CTS SettingsProvider日志
  • Android_Settings源码_4.4.4

    2016-08-04 14:33:52
    Android_Settings源码_4.4.4
  • 原生android系统的源码路径:/packages/apps/Settings。但MTK厂商的源码包中对该应用进行了重构其源码路径:/vendor/mediatek/proprietary/packages/apps/MtkSettings。一、Setting1、Setting函数入口android应用...
  • Android Studio settings

    2018-01-29 17:07:12
    这是一套android studio的设置,包括主题、字体、logcat颜色等等
  • 该补丁用在rk3288android5.1上,在设置->显示 里添加了一个"永久隐藏状态栏和虚拟按键"的选项,文件名:0001-Android-Settings-displaySettings.patch

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 135,156
精华内容 54,062
关键字:

settings安卓