最近觸及修改源生的Settings。用到了PreferenceActivity、以及preference控件。簡單講一下PreferenceActivity的自定義。
1.分左右屏。
PreferenceActivity繼承ListActivity。在在frameworks中找到其佈局文件
<?xml version="1.0" encoding="UTF-8"?>
-<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
-<LinearLayout android:layout_width="match_parent" android:layout_height="0px" android:orientation="horizontal" android:layout_weight="1">
-<LinearLayout android:layout_width="0px" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="@android:integer/preferences_left_pane_weight" android:layout_marginStart="@android:dimen/preference_screen_side_margin" android:layout_marginEnd="@android:dimen/preference_screen_side_margin_negative" android:id="@android:id/headers">
<ListView android:layout_width="match_parent" android:layout_height="0px" android:layout_weight="1" android:id="@android:id/list" android:scrollbarAlwaysDrawVerticalTrack="true" android:listPreferredItemHeight="48dp" android:cacheColorHint="@android:color/transparent" android:drawSelectorOnTop="false" android:scrollbarStyle="@android:integer/preference_screen_header_scrollbarStyle" android:clipToPadding="false" android:paddingBottom="@android:dimen/preference_screen_header_vertical_padding" android:paddingTop="@android:dimen/preference_screen_header_vertical_padding" android:paddingEnd="@android:dimen/preference_screen_header_padding_side" android:paddingStart="@android:dimen/preference_screen_header_padding_side"/>
<FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0" android:id="@android:id/list_footer"/>
</LinearLayout>
-<LinearLayout android:layout_width="0px" android:layout_height="match_parent" android:orientation="vertical" android:layout_weight="@android:integer/preferences_right_pane_weight" android:id="@android:id/prefs_frame" android:visibility="gone" style="@android:attr/preferencePanelStyle">
<!-- 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="@android:layout/breadcrumbs_in_fragment"/>
<android.preference.PreferenceFrameLayout android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:id="@android:id/prefs"/>
</LinearLayout>
</LinearLayout>
-<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0" android:id="@android:id/button_bar" android:visibility="gone">
<Button android:layout_width="150dip" android:layout_height="wrap_content" android:id="@android:id/back_button" android:text="@android:string/back_button_label" android:layout_alignParentStart="true" android:layout_margin="5dip"/>
-<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentEnd="true">
<Button android:layout_width="150dip" android:layout_height="wrap_content" android:id="@android:id/skip_button" android:visibility="gone" android:text="@android:string/skip_button_label" android:layout_margin="5dip"/>
<Button android:layout_width="150dip" android:layout_height="wrap_content" android:id="@android:id/next_button" android:text="@android:string/next_button_label" android:layout_margin="5dip"/>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
可以看出主要是一個ListView 和 android.preference.PreferenceFrameLayout這兩個部分。
在手機中,Settings是由多級界面組成的,通過代碼的控制,一級界面即這個ListView。二級界面、三級界面等進行具體設置的頁面由這個PreferenceFrameLayout控制,這個PreferenceFrameLayout顯然是Fragment的容器。
在平板中往往都是左邊顯示標題,右邊顯示具體設置。
看一下PreferenceActivity的onCreate()的部分源碼。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.android.internal.R.layout.preference_list_content);
mListFooter = (FrameLayout)findViewById(com.android.internal.R.id.list_footer);
mPrefsContainer = (ViewGroup) findViewById(com.android.internal.R.id.prefs_frame);
boolean hidingHeaders = onIsHidingHeaders();
mSinglePane = hidingHeaders || !onIsMultiPane();
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) {
// We are restarting from a previous saved state; used that to
// initialize, instead of starting fresh.
ArrayList<Header> headers = savedInstanceState.getParcelableArrayList(HEADERS_TAG);
if (headers != null) {
mHeaders.addAll(headers);
int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
(int) HEADER_ID_UNDEFINED);
if (curHeader >= 0 && curHeader < mHeaders.size()) {
setSelectedHeader(mHeaders.get(curHeader));
}
}
} else {
if (initialFragment != null && mSinglePane) {
// 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) {
if (!mSinglePane) {
if (initialFragment == null) {
Header h = onGetInitialHeader();
switchToHeader(h);
} else {
switchToHeader(initialFragment, initialArguments);
}
}
}
}
}
…………………………………………
}
可以看到boolean mSinglePane控制了這兩個組件的顯示與隱藏。
如果需要手動控制分屏。可以覆寫onIsMultiPane()這個方法,設定返回值。false爲多級界面、true爲一個界面分成兩塊。
public boolean onIsMultiPane() {
boolean preferMultiPane = getResources().getBoolean(
com.android.internal.R.bool.preferences_prefer_dual_pane);
return preferMultiPane;
}
2.獲取標題
通常採取覆寫onBindHeader這個方法
@Override
public void onBuildHeaders(List<Header> target) {
// Should be overloaded by subclasses
loadHeadersFromResource(R.xml.headers,target)
}
loadHeadersFromResource這個方法實現了對headers.xml文件的解析。並將數據存放在一個List<Header>對象中。
源碼存放中有一個HeaderAdapter類,用於將解析完的標題適配進先前提到的ListVIew中。
如果希望對這個ListView界面進行修改,可以用自定義的Adapter進行替換。
在我們的代碼中可以先找到這個ListView。並獲取其Adapter及Adapter的數據。
最用用自定義Adapter將數據重新放入ListView。
ListView lv =(ListView) findViewById(android.R.id.list); //找到ListView
List<Header> mHeader = new ArrayList<Header>();
while(lv.getAdapter.getItem != null){
mHeader.add(lv.getAdapter.getItem);
}
MyAdapter adapter = new MyAdapter(this,mHeader); //自定義Adapter
lv.setAdapter(adapter);
注意,這段代碼一定要在super.onCreate();之後執行。