Settings 体系架构四 DashboardFragment

Settings中的DashboardFragment,是大多数页面的父类,该类里面实现了页面加载管理,数据交互等核心工作。

页面加载控制流程

1
2
3
4
5
6
7
8
9
10
onCreate()
onCreatePreferences() { // Preference列表创建完成
refreshAllPreferences() { // 刷新所有的Preference
displayResourceTiles() { // 显示Preference及加载处理相应的UI
addPreferencesFromResource() // 加载PreferehceScreen -> setPreferenceScreen
controller.displayPreference(screen) // 将Screen对象传递到Controller,由Controller管理UI
}
refreshDashboardTiles() // 更新需要由DashboardCategory管理的Preference
}
}

页面更新

1
2
onCategoriesChanged()
refreshDashboardTiles()
1
2
3
onResume()
updatePreferenceStates()
controller.updateState(preference)

其它

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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/**
* Base fragment for dashboard style UI containing a list of static and dynamic setting items.
*/
public abstract class DashboardFragment extends SettingsPreferenceFragment
implements SettingsDrawerActivity.CategoryListener, Indexable,
SummaryLoader.SummaryConsumer {
private static final String TAG = "DashboardFragment";
// PreferenceController键值对集合,用于处理页面中的Preference点击类事件。
private final Map<Class, PreferenceController> mPreferenceControllers =
new ArrayMap<>();
private final Set<String> mDashboardTilePrefKeys = new ArraySet<>();
protected ProgressiveDisclosureMixin mProgressiveDisclosureMixin;
protected DashboardFeatureProvider mDashboardFeatureProvider;
private DashboardTilePlaceholderPreferenceController mPlaceholderPreferenceController;
private boolean mListeningToCategoryChange;
private SummaryLoader mSummaryLoader;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mDashboardFeatureProvider =
FeatureFactory.getFactory(context).getDashboardFeatureProvider(context);
mProgressiveDisclosureMixin = mDashboardFeatureProvider
.getProgressiveDisclosureMixin(context, this, getArguments());
getLifecycle().addObserver(mProgressiveDisclosureMixin);
// 获取PreferenceControllers,由子类实现返回
List<PreferenceController> controllers = getPreferenceControllers(context);
if (controllers == null) {
controllers = new ArrayList<>();
}
mPlaceholderPreferenceController =
new DashboardTilePlaceholderPreferenceController(context);
controllers.add(mPlaceholderPreferenceController);
// 重新以 key-values 形式保存 Controller
for (PreferenceController controller : controllers) {
addPreferenceController(controller); // 以 key-value 形式保存Controller。
}
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Set ComparisonCallback so we get better animation when list changes.
// 设置比较监听器,优化动画。
getPreferenceManager().setPreferenceComparisonCallback(
new PreferenceManager.SimplePreferenceComparisonCallback());
}
@Override
public void onCategoriesChanged() { // Category发生改变
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
return;
}
refreshDashboardTiles(getLogTag());
}
@Override // 创建Preference
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
super.onCreatePreferences(savedInstanceState, rootKey);
refreshAllPreferences(getLogTag()); // 更新Preference
}
/**
* 更新所有的Preference项, 包含从xml中加载的固定选项和从DashboardCategory中动态加载的选项。
*/
private void refreshAllPreferences(final String TAG) {
// First remove old preferences.
if (getPreferenceScreen() != null) {
getPreferenceScreen().removeAll();
}
// 显示ResourceTile,从ScreenResId中加载PreferenceScreen
displayResourceTiles();
mProgressiveDisclosureMixin.collapse(getPreferenceScreen());
// 更新DashboardTile
refreshDashboardTiles(TAG);
}
@Override
public void onStart() {
super.onStart();
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
return;
}
if (mSummaryLoader != null) {
// SummaryLoader can be null when there is no dynamic tiles.
mSummaryLoader.setListening(true);
}
final Activity activity = getActivity();
if (activity instanceof SettingsDrawerActivity) {
mListeningToCategoryChange = true;
((SettingsDrawerActivity) activity).addCategoryListener(this);
}
}
@Override // 通知Summary发生改变
public void notifySummaryChanged(Tile tile) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
final Preference pref = mProgressiveDisclosureMixin.findPreference(
getPreferenceScreen(), key);
pref.setSummary(tile.summary);
}
@Override
public void onResume() {
super.onResume();
updatePreferenceStates(); // 更新Preference的状态。
}
/**
* Update state of each preference managed by PreferenceController.
*/
protected void updatePreferenceStates() {
Collection<PreferenceController> controllers = mPreferenceControllers.values();
final PreferenceScreen screen = getPreferenceScreen();
for (PreferenceController controller : controllers) {
if (!controller.isAvailable()) {
continue;
}
final String key = controller.getPreferenceKey();
final Preference preference = mProgressiveDisclosureMixin.findPreference(screen, key);
controller.updateState(preference);
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
Collection<PreferenceController> controllers = mPreferenceControllers.values();
// If preference contains intent, log it before handling.
mMetricsFeatureProvider.logDashboardStartIntent(
getContext(), preference.getIntent(), getMetricsCategory());
// Give all controllers a chance to handle click.
// 如果有Controller处理了点击事件并返回true,则不再向下执行。
for (PreferenceController controller : controllers) {
if (controller.handlePreferenceTreeClick(preference)) {
return true;
}
}
// 否则,交由父类处理,即默认的处理逻辑。
return super.onPreferenceTreeClick(preference);
}
@Override
public void onStop() {
super.onStop();
if (mSummaryLoader != null) {
// SummaryLoader can be null when there is no dynamic tiles.
mSummaryLoader.setListening(false);
}
if (mListeningToCategoryChange) {
final Activity activity = getActivity();
if (activity instanceof SettingsDrawerActivity) {
((SettingsDrawerActivity) activity).remCategoryListener(this);
}
mListeningToCategoryChange = false;
}
}
// 通过Class对象从Map中获取PreferenceController。
protected <T extends PreferenceController> T getPreferenceController(Class<T> clazz) {
PreferenceController controller = mPreferenceControllers.get(clazz);
return (T) controller;
}
// 添加键值对到Map
protected void addPreferenceController(PreferenceController controller) {
mPreferenceControllers.put(controller.getClass(), controller);
}
/**
* 获取PreferenceScreen的资源Id,由子类实现
*/
protected abstract int getPreferenceScreenResId();
/**
* 获取PreferenceController列表,由子类实现
*/
protected abstract List<PreferenceController> getPreferenceControllers(Context context);
/**
* 是否显示Tile,默认为true
*/
protected boolean displayTile(Tile tile) {
return true;
}
/**
* 显示ResourceTile,从ScreenResId中加载PreferenceScreen
*/
private void displayResourceTiles() {
final int resId = getPreferenceScreenResId();
if (resId <= 0) {
return;
}
// 从资源文件中添加Preferences。
addPreferencesFromResource(resId);
final PreferenceScreen screen = getPreferenceScreen();
Collection<PreferenceController> controllers = mPreferenceControllers.values();
// 交由PreferenceController去管理PreferenceScreen中相应的UI。
for (PreferenceController controller : controllers) {
controller.displayPreference(screen);
}
}
/**
* 更新由DashboardCategory管理支撑的Preference
*/
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
void refreshDashboardTiles(final String TAG) {
final PreferenceScreen screen = getPreferenceScreen();
// 获取DashboardCategory
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
Log.d(TAG, "NO dashboard tiles for " + TAG);
return;
}
List<Tile> tiles = category.tiles;
if (tiles == null) {
return;
}
// Create a list to track which tiles are to be removed.
final List<String> remove = new ArrayList<>(mDashboardTilePrefKeys);
// There are dashboard tiles, so we need to install SummaryLoader.
if (mSummaryLoader != null) {
mSummaryLoader.release();
}
final Context context = getContext();
mSummaryLoader = new SummaryLoader(getActivity(), getCategoryKey());
mSummaryLoader.setSummaryConsumer(this);
final TypedArray a = context.obtainStyledAttributes(new int[]{
android.R.attr.colorControlNormal});
final int tintColor = a.getColor(0, context.getColor(android.R.color.white));
a.recycle();
final String pkgName = context.getPackageName();
// Install dashboard tiles.
for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) {
continue;
}
if (!displayTile(tile)) {
continue;
}
if (pkgName != null && tile.intent != null
&& !pkgName.equals(tile.intent.getComponent().getPackageName())) {
// If this drawable is coming from outside Settings, tint it to match the color.
tile.icon.setTint(tintColor);
}
if (mDashboardTilePrefKeys.contains(key)) {
// 更新UI视图
final Preference preference = mProgressiveDisclosureMixin.findPreference(
screen, key);
mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), getMetricsCategory(),
preference, tile, key, mPlaceholderPreferenceController.getOrder());
} else {
// Don't have this key, add it.
final Preference pref = new Preference(getPrefContext());
mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), getMetricsCategory(),
pref, tile, key, mPlaceholderPreferenceController.getOrder());
mProgressiveDisclosureMixin.addPreference(screen, pref);
mDashboardTilePrefKeys.add(key);
}
remove.remove(key);
}
// Finally remove tiles that are gone.
for (String key : remove) {
mDashboardTilePrefKeys.remove(key);
mProgressiveDisclosureMixin.removePreference(screen, key);
}
mSummaryLoader.setListening(true);
}
}