一区二区三区电影_国产伦精品一区二区三区视频免费_亚洲欧美国产精品va在线观看_国产精品一二三四

聯(lián)系我們 - 廣告服務(wù) - 聯(lián)系電話:
您的當(dāng)前位置: > 關(guān)注 > > 正文

Android應(yīng)用Preference相關(guān)及源碼淺析 Preference相關(guān)基礎(chǔ)概念

來(lái)源:CSDN 時(shí)間:2022-12-20 15:03:50

1 前言


(資料圖)

前一篇(點(diǎn)我閱讀前一篇《Android應(yīng)用Preference相關(guān)及源碼淺析(SharePreferences篇)》)我們討論分析使用了Android的SharePreferences,相信看過(guò)的朋友都有了自己的感悟與理解,這一篇我們繼續(xù)乘熱打鐵來(lái)說(shuō)說(shuō)SharePreferences的衍生品—-Preference組件。

其實(shí)Preference組件大家一定不陌生,因?yàn)閍ndroid系統(tǒng)的Setting應(yīng)用及我們市面上一些符合Android設(shè)計(jì)思想的應(yīng)用的設(shè)置界面一般都會(huì)用它來(lái)實(shí)現(xiàn),而且Google原生Android代碼中大量的使用了Preference組件。

簡(jiǎn)單說(shuō),Preference組件其實(shí)就是Android常見(jiàn)UI組件與SharePreferences的組合封裝實(shí)現(xiàn)。

2 Preference組件家族基礎(chǔ)

2-1 Preference相關(guān)基礎(chǔ)概念

既然要先說(shuō)說(shuō)Preference組件家族基礎(chǔ),那不得不先簡(jiǎn)單說(shuō)說(shuō)這些Preference組件間的關(guān)系,如下一張圖是基于API 22繪制的一副Preference組件繼承關(guān)系圖:

怎么樣?相比以前低版本的API來(lái)說(shuō),谷歌官方又增加了一些實(shí)用的Preference組件,不過(guò)遺憾的是這里面有些是被hide掉的,有些是在com.android.internal.preference包下的,所以這些hide和com.android.internal.preference包的preference我們應(yīng)用層是不能直接使用的。

到此你指定會(huì)說(shuō),這些組件和我們平時(shí)用的EditText等組件很類似,而我們平時(shí)用的EditText等組件顯示是依賴于Activity和Fragment的,那么這里的Preference組件是怎么顯示的呢?

答案就是Preference組件也有自己依賴顯示的框架,最常見(jiàn)的就像PreferenceActivity等,具體看代碼我們會(huì)發(fā)現(xiàn)如下:

public abstract class PreferenceActivity extends ListActivity implements        PreferenceManager.OnPreferenceTreeClickListener,        PreferenceFragment.OnPreferenceStartFragmentCallback {......}public abstract class PreferenceFragment extends Fragment implements        PreferenceManager.OnPreferenceTreeClickListener {......}12345678

嘿嘿,明白了吧?其實(shí)Preference組件的使用及展示到Window的原理和普通EditText控件展示使用是類似的,因?yàn)镻reference組件的顯示框架PreferenceActivity及PreferenceFragment都是從基本的Activity及Fragment繼承而來(lái),只是針對(duì)Preference進(jìn)行了二次封裝而已。

到此對(duì)Preference組件已經(jīng)有一個(gè)基本的概念認(rèn)識(shí)了,接下來(lái)我們就一步一步往下看。

2-2 常用Preference相關(guān)基礎(chǔ)組件屬性說(shuō)明

我們可以發(fā)現(xiàn),Preference組件家族的控件還是比較豐富的,這里肯定不能一一介紹,所以還是代表性的說(shuō)幾個(gè)使用頻率最高作為指引就行了,其他的用到時(shí)參考相關(guān)官方API或者源碼即可。

2-2-1 獨(dú)立控件Preference

所有Preference組件的基類,類似常見(jiàn)控件的TextView,一個(gè)單純的item,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

如下是基類Preference的相關(guān)屬性介紹:

attrdescription

android:defaultValue默認(rèn)值。

android:dependency設(shè)置此元素附屬于另一個(gè)元素,依賴的可用則當(dāng)前元素也可用(enable),反之。

android:enabled設(shè)置是否可用。

android:fragment指定fragment。

android:icon指定左側(cè)的圖標(biāo)。

android:key選項(xiàng)的名稱,也是用來(lái)存儲(chǔ)時(shí)唯一的key。

android:layout給當(dāng)前元素指定一個(gè)自定義布局。

android:order偏好的順序。如果不指定,默認(rèn)的順序?qū)⒆帜浮?/p>

android:persistent是否將其值存儲(chǔ)到共享SharePreferences。

android:selectable設(shè)置是否可以選擇操作。

android:shouldDisableView當(dāng)enabled設(shè)置為false變暗,同時(shí)此屬性設(shè)置為false時(shí)disable但不變暗。

android:summary摘要,配置的簡(jiǎn)要說(shuō)明,顯示在標(biāo)題下面。

android:title選項(xiàng)的標(biāo)題,當(dāng)沒(méi)有設(shè)置summary時(shí)自動(dòng)垂直居中顯示。

android:widgetLayout控件可調(diào)小部件的布局。是為一個(gè)優(yōu)先選擇的布局,比如一個(gè)復(fù)選框選擇要指定一個(gè)自定義布局(注意:包括的只是復(fù)選框)在這里。

關(guān)于基類Preference提供的方法這里就不再詳細(xì)列出了,如需查看,具體翻墻點(diǎn)我。

2-2-2 獨(dú)立控件CheckPreference

CheckPreference類似常見(jiàn)控件的CheckBox,一個(gè)item,右側(cè)有一個(gè)CheckBox,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

如下是CheckPreference的相關(guān)屬性介紹:

attrdescription

android:disableDependentsState與android:dependency相反;B可用,則A不可用;B不可用,則A可用。

android:summaryOff選項(xiàng)未選中時(shí)顯示的摘要。

android:summaryOn選項(xiàng)被選中時(shí)顯示的摘要。

2-2-3 獨(dú)立控件EditTextPreference

EditTextPreference類似常見(jiàn)控件的EditText,一個(gè)item,點(diǎn)擊彈出一個(gè)EditText的對(duì)話框,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

該控件無(wú)自有屬性。具體不再介紹,相關(guān)方法查看官方API。

2-2-4 獨(dú)立控件ListPreference

ListPreference類似常見(jiàn)控件的ListView,一個(gè)item,點(diǎn)擊彈出一個(gè)ListView的Dialog,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

如下是ListPreference的相關(guān)屬性介紹:

attrdescription

android:entrieslist要顯示的item數(shù)組名字。

android:entryValueslist要顯示的item數(shù)組值。

2-2-5 獨(dú)立控件MultiSelectListPreference

MultiSelectListPreference類似常見(jiàn)控件的ListView,一個(gè)item,點(diǎn)擊彈出一個(gè)多選的ListView的Dialog,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

MultiSelectListPreference的相關(guān)屬性同上ListPreference。

2-2-6 獨(dú)立控件SwitchPreference

SwitchPreference類似常見(jiàn)控件的Switch,一個(gè)item,右側(cè)有一個(gè)Switch控件,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

如下是SwitchPreference的相關(guān)屬性介紹:

attrdescription

android:disableDependentsState與android:dependency相反;B可用,則A不可用;B不可用,則A可用。

android:summaryOff選項(xiàng)未選中時(shí)顯示的摘要。

android:summaryOn選項(xiàng)被選中時(shí)顯示的摘要。

android:switchTextOff關(guān)閉狀態(tài)的文字提示。

android:switchTextOn打開狀態(tài)的文字提示。

2-2-7 獨(dú)立控件RingtonePreference

RingtonePreference就是一個(gè)鈴聲選擇item,點(diǎn)擊彈出鈴聲選擇list的dialog,用于通過(guò)SharePreferences存儲(chǔ)操作的設(shè)置值,具體翻墻點(diǎn)我。

如下是RingtonePreference的相關(guān)屬性介紹:

attrdescription

android:ringtoneType鈴聲類型。ringtone/notification/alarm/all

android:showDefault選項(xiàng)中默認(rèn)的鈴聲。

android:showSilent是否顯示靜音項(xiàng)。

2-2-8 組合控件PreferenceScreen

PreferenceScreen就Preference hierarchy的root節(jié)點(diǎn),實(shí)例化他可以使用createPreferenceScreen(Context)方法;這個(gè)類可以依附于兩個(gè)地方,當(dāng)一個(gè)preferenceactivity指向他時(shí)用來(lái)作為根布局顯示偏好,當(dāng)他嵌套出現(xiàn)在另一個(gè)Preference hierarchy內(nèi)部時(shí)他會(huì)啟動(dòng)一個(gè)新的界面來(lái)顯示子項(xiàng)Preference或者設(shè)置的intent;綜上也就是說(shuō)它不僅可以作為設(shè)置界面顯示,而且還能夠啟動(dòng)activity,具體翻墻點(diǎn)我。

如下展示了作為根布局及子布局的兩種情況:

<--!>... other preferences here ...123456789101112131415

<--!>1234567

2-2-9 組合控件PreferenceCategory

PreferenceCategory類似于LinearLayout,用于組合一組可設(shè)置標(biāo)題的Preference,使布局更具備層次感,具體翻墻點(diǎn)我。

這個(gè)類也沒(méi)有啥特殊的東西介紹,詳細(xì)參考API。

到此常用的Preference組件xml屬性介紹完畢,對(duì)應(yīng)的Java方法就不再說(shuō)明了,還有就是他們的protect方法也不再詳細(xì)介紹,具體參見(jiàn)API。

2-3 新增Headers相關(guān)基礎(chǔ)組件屬性說(shuō)明

上面我們簡(jiǎn)單介紹了PreferenceScreen相關(guān)xml的屬性,這些其實(shí)是老版本的處理方式;自從Android 3.0引入Fragment之后,Preference相關(guān)的控件也有了變化。

由于PreferenceActivity在3.0開始也需要能夠處理多屏幕碎片化問(wèn)題,所以Android 3.0之前采用PreferenceScreen嵌套的方法來(lái)跳轉(zhuǎn)分類細(xì)則,而Android 3.0及之后使用了Preference Headers的方法來(lái)適配多屏幕碎片化問(wèn)題。

他的核心就是在主屏中通過(guò)headers的xml布局列出所有的主題設(shè)置項(xiàng),每個(gè)主題設(shè)置的詳細(xì)設(shè)置由各自指定的PreferenceFragment負(fù)責(zé),而各自的PreferenceFragment可以如傳統(tǒng)的PreferenceActivity 一樣布局自身的PreferenceScreen。

preference-headers就是他們的root,既然這樣,那我們就來(lái)看看Headers相關(guān)的組件及方法吧。

2-3-1 PreferenceActivity.Header相關(guān)屬性方法使用基礎(chǔ)

點(diǎn)我翻墻查看。Header繼承自O(shè)bject,實(shí)現(xiàn)了Parcelable,用來(lái)展示一個(gè)item的header。

相關(guān)屬性如下:

attrdescription

android:icon

android:breadCrumbShortTitle在fragment顯示的短標(biāo)題文字。

android:breadCrumbTitle在fragment顯示的標(biāo)題文字。

android:fragment當(dāng)選擇該頭文件時(shí),將顯示該fragment的全名稱。

android:id唯一識(shí)別id。

android:summaryitem描述信息。

android:titleitem頭名稱。

如下是一個(gè)簡(jiǎn)單展示:

......          ......    節(jié)點(diǎn),類似extra,此處省略 -->123456

關(guān)于Header的用法下面會(huì)詳細(xì)演示,基本情況就介紹到這里。

2-4 Preference相關(guān)組件顯示操作控制API解釋

有了上面Preference組件基本概念及屬性介紹以后就相當(dāng)于我們有了磚瓦,接下來(lái)就是咋蓋房子了,也就是如何組合這些組件顯示在屏幕上,我們現(xiàn)在就來(lái)看看這些常用的操作。

2-4-1 PreferenceActivity相關(guān)屬性方法使用基礎(chǔ)

翻墻點(diǎn)我查看。PreferenceActivity繼承自ListActivity,這個(gè)類是Preference相關(guān)控件展示的基類,在Android 3.0以前推薦直接使用,3.0以后推薦和preferencefragment一起使用,所以你可以看見(jiàn)PreferenceActivity中有些方法現(xiàn)在已經(jīng)是過(guò)時(shí)的了。

首先看下PreferenceActivity加載xml目錄下的文件使用的方法,如下:

public class DemoActivity extends PreferenceActivity {@Override    public void onBuildHeaders(List target) {        super.onBuildHeaders(target);        //當(dāng)大于等于3.0版本時(shí)推薦重寫該方法加載xml,headers+fragments模式        loadHeadersFromResource(R.xml.preference_header, target);    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {            //當(dāng)小于3.0版本時(shí)推薦重寫該方法加載xml,當(dāng)然大于時(shí)也可以用,只是不推薦而已            addPreferencesFromResource(R.xml.preference);        }    }}1234567891011121314

如下我們來(lái)看看PreferenceActivity相關(guān)的常用方法:

methoddescription

public void addPreferencesFromIntent(Intent intent)@deprecated,添加一個(gè)匹配intent的preferences activity。

public void addPreferencesFromResource(int preferencesResId)@deprecated,添加一個(gè)xml到activity。

public Preference findPreference(CharSequence key)@deprecated,查找一個(gè)指定key的Preference。

public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData)結(jié)束指定的fragment,參數(shù)返回類似activity。

public PreferenceManager getPreferenceManager()@deprecated,獲取activity使用的PreferenceManager實(shí)例。

public PreferenceScreen getPreferenceScreen()@deprecated,獲取當(dāng)前activity的根布局視圖。

public boolean hasHeaders()返回當(dāng)前activity是否顯示了header list。

public void invalidateHeaders()刷新已經(jīng)顯示的header list,會(huì)重新回調(diào)onBuildHeaders()。

public boolean isMultiPane()是否同時(shí)顯示headers和fragment。

public void loadHeadersFromResource(int resid, List target)解析一個(gè)headers的xml然后添加到target列表里。

public void onBuildHeaders(List target)一般需要重寫,注意!這個(gè)函數(shù)可能不是總會(huì)被調(diào)用,例如,如果該Activity已被要求顯示一個(gè)特定的Fragment而不需要頭文件,就不需要構(gòu)建Headers,所以不調(diào)運(yùn)。

public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, int titleRes, int shortTitleRes)構(gòu)造一個(gè)顯示Fragment的Intent對(duì)象。

public void onContentChanged()當(dāng)界面發(fā)生變化時(shí)回調(diào)。

public void onHeaderClick(PreferenceActivity.Header header, int position)當(dāng)選擇Headers列表項(xiàng)時(shí)調(diào)用,默認(rèn)實(shí)現(xiàn)調(diào)用startwithfragment或switchtoheader。

public boolean onIsMultiPane()大屏下默認(rèn)實(shí)現(xiàn)是true。

public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref)當(dāng)單擊某個(gè)具有與它相關(guān)聯(lián)的gragment類名稱時(shí)調(diào)用。

public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)@deprecated,當(dāng)Preference控件被點(diǎn)擊時(shí),觸發(fā)該方法。參數(shù)preference為點(diǎn)擊的對(duì)象,返回值true代表點(diǎn)擊事件已成功捕捉,無(wú)須執(zhí)行默認(rèn)動(dòng)作或者返回上層調(diào)用,例如,不跳轉(zhuǎn)至默認(rèn)Intent。

public void setListFooter(View view)給Headers list設(shè)置foot view。

public void startPreferenceFragment(Fragment fragment, boolean push)起一個(gè)fragment,push決定是否入棧。

public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode)依據(jù)是否multi-pane模式啟動(dòng)一個(gè)preference的fragment(如果是小屏?xí)貑⒁粋€(gè)activity顯示)。

public void startWithFragment(……)啟動(dòng)一個(gè)新的fragment。

public void switchToHeader(……)在大屏multi-pane模式下切換到fragment顯示給定參數(shù)的fragment。

2-4-2 PreferenceFragment相關(guān)屬性方法使用基礎(chǔ)

翻墻點(diǎn)我查看。PreferenceFragment繼承自Fragment,這個(gè)類是3.0以后推薦使用的,用來(lái)處理碎片化問(wèn)題。

該類的常用方法和上面PreferenceActivity的介紹差不多,這里不再詳細(xì)說(shuō)明,只是PreferenceActivity的@deprecated方法在PreferenceFragment中不是@deprecated的而已。

2-4-3 PreferenceManager相關(guān)方法使用基礎(chǔ)

翻墻點(diǎn)我查看。PreferenceManager繼承自O(shè)bject,這個(gè)類其實(shí)我們前一篇《Android應(yīng)用Preference相關(guān)及源碼淺析(SharePreferences篇)》獲取Preference實(shí)例就該說(shuō)明的,這里才說(shuō)而已。

Android中得到SharedPreference的方式有四種:

ContextWrapper.getSharedPreferences(String name, int mode) 可以自己設(shè)置SharedPreference的名字與模式。

Activity.getPreferences(int mode) name是Activity名字,不能設(shè)置。

PreferenceManager.getSharedPreferences() 通過(guò)PreferenceManager維護(hù)一個(gè)SharedPreference,我們可以調(diào)用PreferenceManager的API來(lái)設(shè)置name和mode,并且最終也是調(diào)用到ContextWrapper的getSharedPreferences。

PreferenceManager.getDefaultSharedPreferences(Context context) 得到的SharedPreference是某個(gè)包名下共享私有的,不能讓其他的包訪問(wèn),而且name和mode不能設(shè)置,最終也會(huì)調(diào)用到ContextWrapper的getSharedPreferences。

接下來(lái)簡(jiǎn)單看下PreferenceManager相關(guān)方法,如下:

methoddescription

PreferenceManager.OnActivityDestroyListener當(dāng)所依賴的activity銷毀時(shí)回調(diào)接口。

PreferenceManager.OnActivityResultListener當(dāng)所依賴的activity得到返回result時(shí)回調(diào)接口。

PreferenceManager.OnActivityStopListener當(dāng)所依賴的activity停止時(shí)回調(diào)接口。

public Preference findPreference(CharSequence key)通過(guò)key找到Preference。

public static SharedPreferences getDefaultSharedPreferences(Context context)每個(gè)應(yīng)用有一個(gè)默認(rèn)的preferences文件,通過(guò)該方法獲取。

public SharedPreferences getSharedPreferences()通過(guò)PreferenceManager維護(hù)一個(gè)SharedPreference,可以調(diào)用PreferenceManager的API來(lái)設(shè)置name和mode。

public int getSharedPreferencesMode()獲取當(dāng)前的mode。

public String getSharedPreferencesName()獲取當(dāng)前的name。

public static void setDefaultValues(Context context, String sharedPreferencesName, int sharedPreferencesMode, int resId, boolean readAgain)更加靈活的設(shè)置默認(rèn)值,注意readAgain參數(shù)。

public static void setDefaultValues(Context context, int resId, boolean readAgain)設(shè)置默認(rèn)值,注意readAgain參數(shù)。

public void setSharedPreferencesMode(int sharedPreferencesMode)設(shè)置當(dāng)前的mode。

public void setSharedPreferencesName(String sharedPreferencesName)設(shè)置當(dāng)前的name。

可以看見(jiàn),這個(gè)類其實(shí)也沒(méi)啥介紹的,重點(diǎn)關(guān)注下setDefaultValues的幾個(gè)核心參數(shù)就行。如果我們的設(shè)置項(xiàng)很多,而且每項(xiàng)在代碼中都需要設(shè)置默認(rèn)缺省值,那就推薦使用setDefaultValues方法。在應(yīng)用第一次運(yùn)行時(shí),從preference的xml中獲取缺省值,并生成文件保存(如果已經(jīng)有一個(gè)SharedPrefferences對(duì)象,也會(huì)進(jìn)行更新,就像下面代碼中三四行對(duì)調(diào));不是第一運(yùn)行就不會(huì)改現(xiàn)有保存值。

protected void onCreate(Bundle savedInstanceState) {    ......    PreferenceManager.setDefaultValues(this, R.xml.default_value, false);    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);     String option = prefs.getString("key", null); }12345

好了,控件使用就到這里了。

2-5 Preference控件家族實(shí)例

關(guān)于Preference控件家族的使用比較簡(jiǎn)單,自定義網(wǎng)上也一大把,所以不再給出例子。如果你想看例子可以參考如下:

官方Settings設(shè)計(jì)原理。

Settings源碼。

其他的相關(guān)用法參考API及網(wǎng)絡(luò)例子。

【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開始Android技術(shù)交流】

3 Preference組件源碼設(shè)計(jì)簡(jiǎn)單分析

扯蛋了這么多,唉,嘆個(gè)氣繼續(xù)吧,接下來(lái)就到了有意思的環(huán)節(jié),源碼結(jié)構(gòu)簡(jiǎn)介。這里只是針對(duì)Preference控件特性介紹分析,不會(huì)過(guò)多追究View及Activity和Fragment細(xì)節(jié),具體View及Activity和Fragment細(xì)節(jié)后面會(huì)寫文章分析的。

3-1 PreferenceFragment源碼淺析

首先還記得上面基礎(chǔ)說(shuō)了,PreferenceFragment使用第一步就是使用其內(nèi)部方法addPreferencesFromResource或者addPreferencesFromIntent設(shè)置源。所以這里我們以addPreferencesFromResource為例來(lái)說(shuō)明,如下源碼:

//PreferenceFragment的方法public void addPreferencesFromResource(int preferencesResId) {    //判斷異常說(shuō)明了該方法至少得在super.onCreate方法之后調(diào)運(yùn),以便初始化PreferenceManager    requirePreferenceManager();    //這個(gè)前面也介紹過(guò)的,設(shè)置根布局PreferenceScreen    setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(),            preferencesResId, getPreferenceScreen()));}1234567

接著我們看下setPreferenceScreen方法源碼,如下:

public void setPreferenceScreen(PreferenceScreen preferenceScreen) {    //設(shè)置根布局到PreferenceManager里    if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {        //空方法        onUnbindPreferences();        //設(shè)置標(biāo)記,在onActivityCreated方法中有用        mHavePrefs = true;        //決定是否重設(shè)bind布局,核心都是為了執(zhí)行bindPreferences方法        if (mInitDone) {            postBindPreferences();        }    }}123456789

到此接下來(lái)就是bind了,至于在這里通過(guò)Handler發(fā)消息bindPreferences還是在onActivityCreated自動(dòng)調(diào)bindPreferences方法取決于你把a(bǔ)ddPreferencesFromResource方法寫在那個(gè)生命周期方法里。如下我們直接來(lái)看bindPreferences方法,如下源碼:

//這個(gè)方法是搭建顯示的核心方法!!!!!!!!!private void bindPreferences() {    //拿到PreferenceManager中存的根視圖PreferenceScreen    final PreferenceScreen preferenceScreen = getPreferenceScreen();    if (preferenceScreen != null) {        //傳遞當(dāng)前ListView到preferenceScreen的bind方法        preferenceScreen.bind(getListView());    }    //PreferenceFragment的空方法    onBindPreferences();}12345

到此可以看見(jiàn)PreferenceFragment里bind最終是交給了PreferenceScreen的bind來(lái)關(guān)聯(lián)PreferenceFragment的ListView與PreferenceScreen的ListAdapter。我們現(xiàn)在就來(lái)看下PreferenceScreen的bind源碼,如下:

//PreferenceScreen類的方法public void bind(ListView listView) {    //設(shè)置listview的item監(jiān)聽    listView.setOnItemClickListener(this);    //PreferenceScreen中bind的重點(diǎn)核心!!!!!!!!!!!!!給listview設(shè)置adapter    listView.setAdapter(getRootAdapter());    //一些register操作,忽略    onAttachedToActivity();}123456

好了,我們還是來(lái)關(guān)注這個(gè)adapter咋來(lái)的吧,如下就是getRootAdapter方法源碼:

public ListAdapter getRootAdapter() {    if (mRootAdapter == null) {        mRootAdapter = onCreateRootAdapter();    }    return mRootAdapter;}protected ListAdapter onCreateRootAdapter() {    return new PreferenceGroupAdapter(this);}1234567891011

終于真相快要大白了,PreferenceFragment的listview設(shè)置的adapter原來(lái)是PreferenceGroupAdapter。哈哈,我們繼續(xù)來(lái)看看這個(gè)類,如下:

//hide類,專門用來(lái)Preference的list顯示的adapterpublic class PreferenceGroupAdapter extends BaseAdapter        implements OnPreferenceChangeInternalListener {//省略相關(guān)屬性定義    ......    //構(gòu)造方法,傳入的是PreferenceScreen根布局    public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {        ......        //sync設(shè)置相關(guān)list列表數(shù)據(jù)后通知listview刷新        syncMyPreferences();    }    private void syncMyPreferences() {        ......        //通知listview刷新當(dāng)前準(zhǔn)備的Preference列表        notifyDataSetChanged();        ......    }    //省略一堆方法    ......    //notifyDataSetChanged后和普通adapter一樣item繪制會(huì)回調(diào)getView方法    public View getView(int position, View convertView, ViewGroup parent) {        //拿到當(dāng)前item的Preference組件        final Preference preference = this.getItem(position);        ......        //調(diào)運(yùn)Preference的getView方法得到當(dāng)前item真正的view顯示,這是核心!!!!!!!!!!!!        //關(guān)于Preference的getView方法下面分析Preference源碼會(huì)說(shuō)到的,或者你可以直接跳到Preference源碼分析部分查看。        View result = preference.getView(convertView, parent);        ......        return result;    }    ......}1234567891011121314151617181920212223242526272829303132

到此你會(huì)發(fā)現(xiàn),其實(shí)無(wú)非就是ListView和Adapter的關(guān)系,而Adapter的getView所得到的View由Preference提供而已,而Adapter由PreferenceScreen管理而已。

3-2 PreferenceActivity源碼淺析

說(shuō)到PreferenceActivity現(xiàn)在不推薦的addPreferencesFromResource方法時(shí)其實(shí)是沒(méi)啥解釋的,這種模式現(xiàn)在被官方推薦通過(guò)PreferenceFragment的addPreferencesFromResource來(lái)實(shí)現(xiàn),所以也就是說(shuō)關(guān)于PreferenceActivity的addPreferencesFromResource方法(也就是在PreferenceActivity中直接添加Preference組件)其顯示原理和上面分析的PreferenceFragment是一樣的,所以這里就不再過(guò)多解釋了。

我們把重點(diǎn)放在loadHeadersFromResource方法上,也就是現(xiàn)在推薦的PreferenceActivity放置Headers模式。接下來(lái)就來(lái)分析分析吧。

public abstract class PreferenceActivity extends ListActivity implements        PreferenceManager.OnPreferenceTreeClickListener,        PreferenceFragment.OnPreferenceStartFragmentCallback {......    //省略一堆方法    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //設(shè)置基礎(chǔ)布局        setContentView(com.android.internal.R.layout.preference_list_content);        //獲取一些ContentView里的控件實(shí)例        ......        //判斷是啥模式,左右展示還是單頁(yè)        boolean hidingHeaders = onIsHidingHeaders();        mSinglePane = hidingHeaders || !onIsMultiPane();        //獲取fragment參數(shù)(其實(shí)是PreferenceActivity中點(diǎn)擊Header item重啟PreferenceActivity時(shí)傳遞的)        String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);        Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);        int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);        int initialShortTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_SHORT_TITLE, 0);        if (savedInstanceState != null) {            ......  //忽略,非重點(diǎn)主線        } else {            if (initialFragment != null && mSinglePane) {                //SinglePane時(shí)有參數(shù)則替換顯示Fragment                switchToHeader(initialFragment, initialArguments);                ......            } else {                //核心方法之一!!!!!!!!!!!!                //記得上面基礎(chǔ)使用介紹過(guò)嗎?新的實(shí)現(xiàn)重寫onBuildHeaders空方法,在其中                //調(diào)運(yùn)loadHeadersFromResource方法加載header list xml文件                onBuildHeaders(mHeaders);                //如果存在header list則走這里(上面onBuildHeaders里會(huì)組織生成mHeaders的list結(jié)構(gòu))                if (mHeaders.size() > 0) {                    //header-fragment左右各半屏模式                    if (!mSinglePane) {                        if (initialFragment == null) {                            //設(shè)置顯示header                            Header h = onGetInitialHeader();                            switchToHeader(h);                        } else {                            //設(shè)置顯示header及fragment                            switchToHeader(initialFragment, initialArguments);                        }                    }                }            }        }        if (initialFragment != null && mSinglePane) {            //當(dāng)SinglePane加載的是Fragment時(shí)隱藏header,顯示fragment            findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);            mPrefsContainer.setVisibility(View.VISIBLE);            ......        } else if (mHeaders.size() > 0) {            //重點(diǎn)!!!!!!!!!!!!!!!!!這就是要分析的header的listview的adapter放置地            setListAdapter(new HeaderAdapter(this, mHeaders));            ......        } else {            //這就是最原始的供已經(jīng)不推薦的addPreferencesFromResource方式加載Preference組件了            //具體原理同上PreferenceFragment的加載顯示原理了,不再分析            setContentView(com.android.internal.R.layout.preference_list_content_single);            ......        }        //其他初始設(shè)置        ......    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354

通過(guò)上面的分析可以看見(jiàn)其實(shí)對(duì)于Header的adapter核心就是setListAdapter(new HeaderAdapter(this, mHeaders));這句代碼。那我們就來(lái)看看這個(gè)內(nèi)部類HeaderAdapter,源碼如下:

//可以發(fā)現(xiàn)PreferenceActivity的內(nèi)部類HeaderAdapter是繼承自ArrayAdapter的,//這個(gè)Adapter就是用來(lái)給推薦的Header list的listview提供數(shù)據(jù)的。private static class HeaderAdapter extends ArrayAdapter {//Holder里只有最典型經(jīng)典的三個(gè)組件    private static class HeaderViewHolder {ImageView icon;        TextView title;        TextView summary;    }    private LayoutInflater mInflater;    //構(gòu)造方法,不解釋    public HeaderAdapter(Context context, List objects) {        super(context, 0, objects);        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);    }    //最最核心方法!!!!!!Header list被顯示到PreferenceActivity的listview關(guān)鍵點(diǎn)    @Override    public View getView(int position, View convertView, ViewGroup parent) {        HeaderViewHolder holder;        View view;        //再常見(jiàn)不過(guò)的Adapter數(shù)據(jù)加載ViewHolder寫法了        if (convertView == null) {            //加載header的item布局,都是用的preference_header_item文件,如下會(huì)介紹            view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,                    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();        }        //一堆顯示,通過(guò)getItem(position)拿到構(gòu)造里傳入的List類型objects的item        // All view fields must be updated every time, because the view may be recycled        Header header = getItem(position);        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;    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

可以看見(jiàn)這個(gè)adapter的getView中的item核心是加載了一個(gè)preference_header_item的xml文件,然后設(shè)置作為item的header。這個(gè)xml源碼如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546

哈哈,到此就不在解釋啥了,很直觀了,就是這么任性,就是這么簡(jiǎn)單的實(shí)現(xiàn)了Header List的顯示。

3-3 Preference源碼淺析

說(shuō)這個(gè)的原因是上面PreferenceFragemnt分析加載設(shè)置adapter的getView方法時(shí)留下的歷史問(wèn)題。我們先來(lái)看看這個(gè)文件的核心代碼,后面總結(jié)串起來(lái)你就明白了,如下源碼:

//可以看見(jiàn),他不是一個(gè)View,但是組合管理了一個(gè)View和PreferenceManagerpublic class Preference implements Comparable{......    //各種屬性    ......    private PreferenceManager mPreferenceManager;    //重點(diǎn)關(guān)注,和自定義及Preference顯示原理息息相關(guān),preference就是下面列出的xml資源    private int mLayoutResId = com.android.internal.R.layout.preference;    private int mWidgetLayoutResId;    ......    //各種getXXX及setXXX方法    ......    /**     * Gets the View that will be shown in the {@link PreferenceActivity}.     * 獲取Preference的item顯示view     */        public View getView(View convertView, ViewGroup parent) {                if (convertView == null) {                convertView = onCreateView(parent);                }                onBindView(convertView);                return convertView;        }    protected View onCreateView(ViewGroup parent) {        final LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        final View layout = layoutInflater.inflate(mLayoutResId, parent, false);         final ViewGroup widgetFrame = (ViewGroup) layout.findViewById(com.android.internal.R.id.widget_frame);        if (widgetFrame != null) {            //mWidgetLayoutResId有專門的set方法可以設(shè)置或者重寫            if (mWidgetLayoutResId != 0) {                //android:id/widget_frame為mWidgetLayoutResId所對(duì)應(yīng)的布局預(yù)留空間插入                layoutInflater.inflate(mWidgetLayoutResId, widgetFrame);            } else {                //默認(rèn)實(shí)現(xiàn)是null的                widgetFrame.setVisibility(View.GONE);            }        }        return layout;    }    /**         * Binds the created View to the data for this Preference.         ** This is a good place to grab references to custom Views in the layout and         * set properties on them.         *         */        protected void onBindView(View view) {        //設(shè)置子View相關(guān)屬性                final TextView titleView = (TextView) view.findViewById(com.android.internal.R.id.title);                if (titleView != null) {                    final CharSequence title = getTitle();                    if (!TextUtils.isEmpty(title)) {                            titleView.setText(title);                            titleView.setVisibility(View.VISIBLE);                    } else {                            titleView.setVisibility(View.GONE);                    }                }        ......        //類似的各種子View設(shè)置操作,不再列出        }    ......}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596

可以看見(jiàn),這個(gè)getView其實(shí)就是上面PreferenceFragment分析中Adapter中g(shù)etView調(diào)運(yùn)的Preference的getView。怎么樣,串起來(lái)吧。也就是說(shuō)Preference不是View,但是他提供View給ListView的每一個(gè)Item顯示,其提供的View的基類布局(上面Preference類中mLayoutResId屬性的值)如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

哈哈,不用解釋了吧,這下相信你可以將前面基礎(chǔ)使用和源碼分析幾部分完全串起來(lái)理解了吧。

3-4 Preference組件家族源碼分析總結(jié)

通過(guò)上面分析可以知道Preference其實(shí)不是View,但是其內(nèi)部創(chuàng)建管理了一個(gè)View(ListView的item,被Adapter的getView通過(guò)Preference.getView方法獲得顯示)。

可以看出來(lái),上面我們分析Preference的onCreateView、getView、onBindView這幾個(gè)方法其實(shí)是整個(gè)Preference組件顯示等的核心方法,所以正如系統(tǒng)提供的Preference的各種實(shí)用子類一樣,當(dāng)我們想自定義Preference的時(shí)候完全可以重寫這些方法來(lái)得到自己的各種自定義View,這樣就完美的解決了代碼的擴(kuò)展性,我們不用去修改ListAdapter的實(shí)現(xiàn)就能實(shí)現(xiàn)自定義的Preference,所以說(shuō)可見(jiàn)Google的工程師在設(shè)計(jì)Preference結(jié)構(gòu)時(shí)是多么的牛叉,不得不膜拜。

【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開始Android技術(shù)交流】

4 Preference組件家族總結(jié)

題外話:其實(shí)這篇文章是受我一個(gè)朋友邀請(qǐng)幫忙寫的。還記得去年年初我在上家公司(做Android盒子)負(fù)責(zé)修改一個(gè)項(xiàng)目的Settings源碼,添加一個(gè)屏幕縮放功能在Settings里面。后來(lái)做好以后維護(hù)轉(zhuǎn)手給了別人,當(dāng)時(shí)別人是個(gè)新手,各種問(wèn)。所以落下后遺癥,于是乎就在邀請(qǐng)之下打算寫了這一系列兩篇文章,以幫助快速上手原生Settings的修改。

其實(shí)沒(méi)啥總結(jié)的,還記得前段時(shí)間在網(wǎng)上看見(jiàn)有人吐槽Preference是google設(shè)計(jì)的一個(gè)失敗品,一點(diǎn)也不好用啥的。其實(shí)我想說(shuō)Preference的設(shè)計(jì)還是不錯(cuò)的,是值得借鑒的,隨便舉個(gè)例子如下:

在布局設(shè)計(jì)上可以保持統(tǒng)一預(yù)留差異區(qū)域供自定義動(dòng)態(tài)插入,達(dá)到復(fù)用的目的。

在實(shí)現(xiàn)listView各個(gè)item不同的Adapter的getView方法時(shí)不用像傳統(tǒng)那樣if-else或者switch操作,而是預(yù)留一個(gè)基類用于實(shí)現(xiàn)回調(diào),這樣更加靈活。

其他的慢慢體會(huì)就行了。

責(zé)任編輯:

標(biāo)簽:

相關(guān)推薦:

精彩放送:

新聞聚焦
Top 一区二区三区电影_国产伦精品一区二区三区视频免费_亚洲欧美国产精品va在线观看_国产精品一二三四
亚洲欧美国产毛片在线| 亚洲自拍16p| 欧美视频一区二| 国产精品夜夜夜一区二区三区尤| 国产精品综合不卡av| 黄色成人在线| 亚洲私人黄色宅男| 麻豆成人av| 国产精品色婷婷久久58| 亚洲国语精品自产拍在线观看| 亚洲无线一线二线三线区别av| 久久综合色天天久久综合图片| 欧美视频三区在线播放| 在线观看一区| 欧美亚洲日本网站| 欧美视频在线不卡| 91久久午夜| 老司机午夜免费精品视频| 国产精品欧美久久| 亚洲素人在线| 欧美日韩小视频| 亚洲伦理在线观看| 欧美va亚洲va日韩∨a综合色| 国产日韩欧美夫妻视频在线观看| 一本久久青青| 欧美色图五月天| 9人人澡人人爽人人精品| 欧美大片在线观看一区| 精品成人一区二区| 久久久亚洲精品一区二区三区 | 久久国产手机看片| 国产精品黄色| 亚洲综合好骚| 国产日韩成人精品| 欧美一区二区三区四区在线观看地址| 国产精品盗摄久久久| 亚洲视频免费| 国产精品男女猛烈高潮激情| 亚洲一区二区av电影| 欧美日韩一二三四五区| 在线视频精品一| 国产精品社区| 久久久久久噜噜噜久久久精品| 国产一区二区三区的电影 | 99国产精品自拍| 欧美另类在线播放| 亚洲视频自拍偷拍| 国产精品尤物| 卡通动漫国产精品| 日韩午夜电影| 国产精品综合视频| 免费成人av在线| 这里只有精品在线播放| 国产日本欧美在线观看| 免费黄网站欧美| 夜夜嗨一区二区三区| 国产精品久久久久久久一区探花| 亚洲欧美一区二区视频| 国产在线播放一区二区三区| 欧美va天堂va视频va在线| 亚洲一区欧美激情| 极品少妇一区二区三区| 欧美日韩国产成人在线免费| 亚洲欧美色一区| 亚洲欧洲一区二区三区久久| 欧美午夜片欧美片在线观看| 久久久.com| 一区二区三区国产精品| 狠狠色狠色综合曰曰| 欧美精品色一区二区三区| 亚洲欧美一区二区视频| 在线日韩av片| 国产精品中文字幕欧美| 欧美不卡高清| 久久精品99久久香蕉国产色戒| 亚洲黄色视屏| 国内精品久久久久影院薰衣草| 欧美精品1区2区| 欧美影院视频| 国产精品99久久久久久人| 亚洲成色777777女色窝| 国产农村妇女精品| 欧美日韩精品伦理作品在线免费观看| 老司机午夜精品| 性欧美xxxx视频在线观看| 一本不卡影院| 日韩视频精品| 亚洲人成网站精品片在线观看| 国产欧美一区二区精品性| 欧美日韩免费在线| 欧美精品导航| 欧美寡妇偷汉性猛交| 久久久久久精| 久久不见久久见免费视频1| 亚洲天堂视频在线观看| 亚洲免费播放| 日韩视频二区| 一本久久知道综合久久| 亚洲日韩视频| 亚洲人在线视频| 亚洲精品久久久久| 亚洲精选一区| 亚洲视频免费在线| 亚洲欧美日韩在线不卡| 先锋资源久久| 久久精品国产亚洲精品| 欧美一区二区在线| 久久国产精品色婷婷| 久久国产精品久久久久久| 久久久久国产一区二区三区| 久久亚洲视频| 欧美激情精品久久久久久变态| 欧美紧缚bdsm在线视频| 欧美日韩一区在线观看视频| 欧美日韩一区二区三区视频| 国产精品美女久久久久久免费| 国产乱码精品一区二区三区av| 国产亚洲欧美aaaa| 亚洲第一福利视频| 亚洲激情第一区| 亚洲综合精品四区| 久久久亚洲欧洲日产国码αv| 欧美成人高清视频| 欧美午夜精彩| 黄色精品网站| 宅男精品导航| 久久久久国产精品厨房| 免费观看成人| 欧美久久精品午夜青青大伊人| 国产精品白丝黑袜喷水久久久| 国产日韩在线一区| 亚洲国产乱码最新视频| 亚洲午夜激情网页| 久久综合久久久| 欧美午夜欧美| 在线观看一区| 亚洲男人第一网站| 欧美大片在线看| 国产亚洲人成网站在线观看| 最新成人av在线| 久久av免费一区| 国产精品久久久久久久久果冻传媒| 国产亚洲综合在线| 一本色道久久综合一区| 久久久免费精品视频| 欧美三级精品| 亚洲国产婷婷| 久久久亚洲精品一区二区三区| 国产精品久久久久aaaa樱花| 永久555www成人免费| 亚洲欧美日韩成人| 美女诱惑黄网站一区| 国产精品永久免费| 一区二区三区欧美日韩| 美女网站久久| 国产三级欧美三级| 亚洲婷婷免费| 欧美激情一区二区三区在线视频观看| 国产视频一区三区| 亚洲免费在线观看| 欧美日韩国产精品专区| 亚洲国产精品va| 久久久久久亚洲精品杨幂换脸| 国产精品麻豆欧美日韩ww| 亚洲精品国偷自产在线99热| 久久婷婷丁香| 国产亚洲成精品久久| 午夜一区二区三视频在线观看| 欧美日韩国产欧| 亚洲人成绝费网站色www| 免费看的黄色欧美网站| 禁断一区二区三区在线| 久久天堂国产精品| 激情成人亚洲| 米奇777在线欧美播放| 好吊色欧美一区二区三区四区 | 在线观看亚洲精品| 久久精品国产亚洲aⅴ| 国产欧美在线观看| 午夜欧美理论片| 国产精品一区二区三区乱码| 亚洲欧美日韩国产成人| 国产欧美综合一区二区三区| 香蕉av777xxx色综合一区| 国产伦精品一区二区三区在线观看| 亚洲综合精品一区二区| 国产伦精品一区二区三区| 一本色道久久综合一区| 久久久91精品国产一区二区三区 | 欧美日韩亚洲视频| 精久久久久久| 免费高清在线视频一区·| 亚洲日本激情| 欧美性一二三区| 欧美一区二区三区四区在线观看地址| 狠狠干综合网| 欧美日韩国产色视频| 亚洲欧美日韩国产成人| 国产一区二区三区高清在线观看 | 久久综合网色—综合色88|