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)这里事实上就是一个动态更新,不同系统版本号用户权限不一样,呈现出来的设置界面也不一样。




        

  • 相关阅读:
    AX ERROR: Could not find my mock parent, most likely I am stale 不及格的程序员
    利用Segue在视图控制器间传值的问题 不及格的程序员
    Creating a Singleton Instance 不及格的程序员
    iPad 通知 UIKeyboardWillShowNotification 不会在keyBoard处在Undock状态下接到通知 不及格的程序员
    Why RootViewController's view is rotated Automatically by System when the app first loaded? 不及格的程序员
    如何弹出UIDatePicker最好 不及格的程序员
    jQuery开始做恶了 不及格的程序员
    what is the SEL,id and IMP,Class ,Method? 不及格的程序员
    Objectivec 字符串比较的陷井 不及格的程序员
    Unable to create any keyboard shortcuts after the iOS 6.1.3 update on iPad. 不及格的程序员
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6854782.html
Copyright © 2011-2022 走看看