Settings 体系架构二 PreferenceGroup和PreferenceScreen

Preference只是设置菜单中的某一项,PreferenceGroup则是Preference的容器,负责多个Preference的显示管理,它也是Preference的子类。

PreferenceGroup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
/**
* Preference列表,里面的元素默认情况下根据Preference的order排序。
* 添加的时候,请使用addPreference,而不要直接使用List#add方法。
*/
private List<Preference> mPreferenceList;
private boolean mOrderingAsAdded = true; // 菜单排序顺序是否和添加的顺序保持一致。
/**
* 添加Preference到List
*/
public boolean addPreference(Preference preference) {
if (mPreferenceList.contains(preference)) {
return true;
}
if (preference.getOrder() == Preference.DEFAULT_ORDER) {
if (mOrderingAsAdded) {
preference.setOrder(mCurrentPreferenceOrder++);
}
}
// ...
synchronized(this) {
int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
if (insertionIndex < 0) {
insertionIndex = insertionIndex * -1 - 1;
}
mPreferenceList.add(insertionIndex, preference); // 根据Order添加到指定位置
}
preference.onAttachedToHierarchy(getPreferenceManager());
// 回调Preference的onAttachedToHierarchy,在该方法中,会去设置Preference的一些默认值。
preference.assignParent(this);
if (mAttachedToActivity) {
preference.onAttachedToActivity();
}
notifyHierarchyChanged();
return true;
}
public boolean removePreference(Preference preference) {
final boolean returnValue = removePreferenceInt(preference);
notifyHierarchyChanged();
return returnValue;
}
private boolean removePreferenceInt(Preference preference) {
synchronized(this) {
preference.onPrepareForRemoval();
if (preference.getParent() == this) {
preference.assignParent(null);
}
return mPreferenceList.remove(preference);
}
}
/**
* Finds a {@link Preference} based on its key.
*/
public Preference findPreference(CharSequence key) {
if (TextUtils.equals(getKey(), key)) {
return this;
}
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
final Preference preference = getPreference(i);
final String curKey = preference.getKey();
if (curKey != null && curKey.equals(key)) {
return preference;
}
if (preference instanceof PreferenceGroup) {
final Preference returnedPreference = ((PreferenceGroup)preference)
.findPreference(key);
if (returnedPreference != null) {
return returnedPreference;
}
}
}
return null;
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
// Mark as attached so if a preference is later added to this group, we
// can tell it we are already attached
mAttachedToActivity = true;
// Dispatch to all contained preferences
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
getPreference(i).onAttachedToActivity();
}
}
}

PreferenceGroup是一个抽象类,它有两个子类PreferenceScreenPreferenceCategory

PreferenceCategory

Used to group {@link Preference} objects and provide a disabled title above the group.

PreferenceScreen

Preference元素根容器,在PreferenceActivity或者PreferenceFragment中,用来显示Preference,使用PreferenceManager#createPreferenceScreen(Context)创建实例对象。

该类有两种使用方法:

  • 使用在PreferenceActivity或者PreferenceFragment中,作为root元素,不会显示该类的一些信息,比如Title,只显示里面的Preference。
  • 嵌套使用在Preference中,它作为另一个PreferenceScreen页面(可能是Dialog,也可以是配置Intent跳转到另一个Activity)的入口,并不直接显示里面的Preference子元素。

下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/network_dashboard_title">
<com.android.settings.widget.MasterSwitchPreference
android:fragment="com.android.settings.wifi.WifiSettings"
android:key="toggle_wifi"
android:title="@string/wifi_settings"
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_settings_wireless"
android:order="-30">
<intent
android:action="android.settings.WIFI_SETTINGS"
android:targetClass="Settings$WifiSettingsActivity"/>
</com.android.settings.widget.MasterSwitchPreference>
<Preference
android:fragment="com.android.settings.ProxySelector"
android:key="proxy_settings"
android:title="@string/proxy_settings_title"/>
</PreferenceScreen>

PreferenceScreen.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener {
private ListAdapter mRootAdapter;
private ListView mListView;
public void onItemClick(AdapterView parent, View view, int position, long id) {
// If the list has headers, subtract them from the index.
if (parent instanceof ListView) {
position -= ((ListView) parent).getHeaderViewsCount();
}
Object item = getRootAdapter().getItem(position);
if (!(item instanceof Preference)) return;
final Preference preference = (Preference) item;
preference.performClick(this); // 分发点击事件
}
/**
* Returns an adapter that can be attached to a {@link PreferenceActivity}
* or {@link PreferenceFragment} to show the preferences contained in this
* {@link PreferenceScreen}.
*/
public ListAdapter getRootAdapter() {
if (mRootAdapter == null) {
mRootAdapter = onCreateRootAdapter();
}
return mRootAdapter;
}
/**
* Creates the root adapter.
*/
protected ListAdapter onCreateRootAdapter() {
return new PreferenceGroupAdapter(this);
}
/** 设置视图Adapter
*/
public void bind(ListView listView) {
listView.setOnItemClickListener(this);
listView.setAdapter(getRootAdapter());
onAttachedToActivity();
}
}

bind方法在PreferenceFragment中,创建好PreferenceScreen后,会主动调用。

PreferenceGroupAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class PreferenceGroupAdapter extends BaseAdapter
implements OnPreferenceChangeInternalListener {
private List<Preference> mPreferenceList;
public View getView(int position, View convertView, ViewGroup parent) {
final Preference preference = this.getItem(position);
// Build a PreferenceLayout to compare with known ones that are cacheable.
mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
// If it's not one of the cached ones, set the convertView to null so that
// the layout gets re-created by the Preference.
if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||
(getItemViewType(position) == getHighlightItemViewType())) {
convertView = null;
}
// 调用Preference#getView获取UI视图
View result = preference.getView(convertView, parent);
if (position == mHighlightedPosition && mHighlightedDrawable != null) {
ViewGroup wrapper = new FrameLayout(parent.getContext());
wrapper.setLayoutParams(sWrapperLayoutParams);
wrapper.setBackgroundDrawable(mHighlightedDrawable);
wrapper.addView(result);
result = wrapper;
}
return result;
}
}