zoukankan      html  css  js  c++  java
  • 首选项框架PreferenceFragment部分源代码分析

         由于要改一些settings里面的bug以及之前在里面有做过勿扰模式,准备对勿扰模式做一个总结,那先分析一下settings的源代码,里面的核心应该就是android3.0 上面的首选项框架PreferenceFragment。

    由于在3.0之前都是这些东西放在PreferenceActivity的,可是3.0之后google建议把setting放在PreferenceFragment,可是PreferenceActivity也同一时候在用的,以下就此总结一下:

         PreferenceActivity作用是管理全部headers,而每个header相应一个PreferenceFragment,相应不同的设置种类界面所以我感觉PreferenceActivity偏向系统级的设置,就比方android系统的settings。对于一些简单的应用设置一个PreferenceFragment是能够搞定的。

         我们自己定义一个 activity extends PreferenceActivity必须重写onBuildHeaders()这个方法,在这里载入一个xml布局显示:

    @Override
    	public void onBuildHeaders(List<Header> target) {
    		// TODO Auto-generated method stub
    		loadHeadersFromResource(R.xml.activity_headers, target);
    	}

    自己写一个xml文件看下。R.xml.activity_headers:

    <?xml version="1.0" encoding="utf-8"?>
    <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
        <header
            android:id="@+id/wifi"
            android:title="wifi"
            android:icon="@drawable/ic_launcher"
            android:fragment="com.example.preferencefragmenttest.WifiFragment"/>
        
         <header
            android:id="@+id/net"
            android:title="移动网络"
            android:icon="@drawable/ic_launcher"
            android:fragment="com.example.preferencefragmenttest.NetFragment"/>
         
         <header
            android:id="@+id/ee"
            android:title="打开activity"
            android:icon="@drawable/ic_launcher"
            >
            <intent android:action="android.intent.action.NEW"></intent>
    </header>
    </preference-headers>
    

    每个header点击就会进入一个新的界面。有两种情况:要么是fragment要么是intent打开一个activity。看看源代码了解这里的原理,为什么点击就会进入对应界面。找到父类PreferenceActivity有个方法:

    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相应fragment和activity的两种情况,打开activity这个好理解。看看fragment是怎么打开的,进入switchToHeader(),再进去switchToHeaderInner():

    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();
        }

        非常明白的知道这个跳转的fragment被装载在当前的activity了。也是告诉我们不须要再new 自己的activity来装这个fragment,我们仅仅须要在header里面写好fragment的属性。

    那么为啥有的header还是用intent定activity呢。我分析是由于有些系统界面须要别的应用直接intent訪问。比方WIFI设置界面:

    startActivity(new Intent( android.provider.Settings.ACTION_WIRELESS_SETTINGS)); 

        那当然须要想自己再建一个activity了,接着就是PreferenceFragment的使用方法,也是自己定义fragment extends PreferenceFragment,而且载入xml布局:

    @Override
    	public void onCreate(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onCreate(savedInstanceState);
    		addPreferencesFromResource(R.xml.wifi_preference);
    	}

    看一下这个布局。简单的写几个系统自带的preference:

    <?

    xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <CheckBoxPreference android:key="isopen" android:title="wifi" android:defaultValue="true"/> <ListPreference android:key="list" android:title="声音大小" android:entries="@array/keys" android:entryValues="@array/values"/> <Preference android:key="click" android:title="单独的" /> </PreferenceScreen>

       
          还能够在里面增加PreferenceCategory标签,作用是分块吧,中间间隔略微大一些,区分同一页面不同设置类别

    <PreferenceCategory >
            <cn.com.demo.ota.MyButtonPreference 
                android:key="start_install"
                android:title="@string/update_install"/>
        </PreferenceCategory>

          PreferenceFragment里面有个方法onPreferenceTreeClick()能够重写。可是依照上面的布局发现此方法不起作用。看下这种方法源代码:

     /**
         * {@inheritDoc}
         */
        public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
                Preference preference) {
            if (preference.getFragment() != null &&
                    getActivity() instanceof OnPreferenceStartFragmentCallback) {
                return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment(
                        this, preference);
            }
            return false;
        }

         看的出来,必需要这个被点击的preference含有相应的fragment。否则返回false点击不起作用。那么一般我们用
    update_more = (Preference)findPreference("update_more");<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">
    
    </span></span></span><pre name="code" class="java">update_more.setOnPreferenceClickListener(new OnPreferenceClickListener() {
    				
    				@Override
    				public boolean onPreferenceClick(Preference preference) {
    					
    				}
    			});


    
    
           来设置preference的点击事件,最后一点怎么得到我们保存的数值呢?在该应用的文件夹下能够找到 包名_preference.xml的文件,里面就是我们保存的数值 调用getDefaultSharedPreferences就能够拿到这个SharedPreferences对象从而取到数值。

            当然上面说的都是最基础的使用方法。看settings的源码里面的preference都是自定义的,用listview显示不同类型的preference,并且看Settings.java的一个方法:

    /**
         * Populate the activity with the top-level headers.
         */
        @Override
        public void onBuildHeaders(List<Header> headers) {
            loadHeadersFromResource(R.xml.iradar_settings_headers, headers);
            updateHeaderList(headers);
        }

           在载入完xml文件后还调用了uodateHeadersList(headers)这里事实上就是一个动态更新,不同系统版本号用户权限不一样,呈现出来的设置界面也不一样。




        

  • 相关阅读:
    微信小程序开发9-宿主环境(2)
    微信小程序开发8-小程序的宿主环境(1)
    微信小程序开发7-JavaScript脚本
    微信小程序开发6-WXSS
    点击底部input输入框,弹出的软键盘挡住input(苹果手机使用第三方输入法 )
    极光推送能获取 registrationId,但是接收不到通知
    App 运行后屏幕顶部和底部各留黑边问题
    App 分辨率相关
    配置隐私协议
    极光推送小结
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6854782.html
Copyright © 2011-2022 走看看