Android Settings详细解析
一.Setting.java
在Settings/AndroidMainfest.xml 找到Settings代码入口
<activity-alias android:name="Settings"
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:targetActivity="Settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
由此可知代码的入口是Settings.java
public class Settings extends SettingsActivity {
/*
* Settings subclasses for launching independently.
*/
public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }
public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
public static class TetherSettingsActivity extends SettingsActivity { /* empty */ }
public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
.....此处省略
Setting中都是一些静态类他继承了SettingsActivity类,所以直接进入其父类SettingsActivity当中
1)public class SettingsActivity extends Activity
implements PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback,
ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
SearchView.OnQueryTextListener, SearchView.OnCloseListener,MenuItem.OnActionExpandListener
2)进入Oncreate()方法详解:
getMetaData(); //获取元数据 每个activity在manifest中都可以定义自己的元数据,
一下是settings自己配置的元数据
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" android:value="true" />
3)final Intent intent = getIntent(); 获取Intent的对象,从里面获取到传递的信息,包括是否要打开fragment啊,一些配置参数等等。
4) mIsShowingDashboard = className.equals(Settings.class.getName()); 获取当前是否是我们的主界面也就是仪表盘界面
先假设我们现在就是在主界面,等分析分析完了就知道如果不是会怎么样,非常简单。那么
4)setContentView(mIsShowingDashboard ?R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
先加载一个xml,点进去看这个xml发现是一个容器,也就拿给fragment使用,继续往下看。
5)switchToFragment(DashboardSummary.class.getName(), null, false, false,mInitialTitleResId, mInitialTitle, false);
这个函数的作用说通俗就是打开dashboardSummary这个fragment.下面来到这个fragment
三.DashboardSummary.java
既然这个类是一个Fragment那么直接先找到onCreateView方法:
1)public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mLayoutInflater = inflater;
final View rootView = inflater.inflate(R.layout.dashboard, container, false);
mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
return rootView;
}
发现这个fragment填充了布局dashboard.xml,点进去一看,这个xml布局其实就是一个空布局,只是作为一个容器使用,
并且容器变量为mDashboard,那么继续看,发现这个fragment注册了一个广播接受者。
2)广播的注册在onResume方法:
@Override
public void onResume() {
super.onResume();
sendRebuildUI();
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
getActivity().registerReceiver(mHomePackageReceiver, filter);
}
可以看见这只是说当系统有应用的安装卸载等等之类才会执行。但是再往前看发现一个方法sendRebuildUI();
private void sendRebuildUI() {
if (!mHandler.hasMessages(MSG_REBUILD_UI)) {
mHandler.sendEmptyMessage(MSG_REBUILD_UI);
}
这个函数发送了一个消息给handler.看看如何处理的:
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REBUILD_UI: {
final Context context = getActivity();
rebuildUI(context);
} break;
}
发现里面执行了rebuildUI(context);
3)rebuildUI(context):
mDashboard.removeAllViews();先清除容器
List<DashboardCategory> categories =
((SettingsActivity) context).getDashboardCategories(true);
看见后面执行的函数getDashboardCategories,一步一步点进去看,最后发现重要的一句
loadCategoriesFromResource(R.xml.dashboard_categories, categories); 这个xml打开后一看,大家就发现非常熟悉了
R.xml.dashboard_categories:
<!-- WIRELESS and NETWORKS -->
<dashboard-category
android:id="@+id/wireless_section"
android:title="@string/header_category_wireless_networks" >
<!-- Wifi -->
<dashboard-tile
android:id="@+id/wifi_settings"
android:title="@string/wifi_settings_title"
android:fragment="com.android.settings.wifi.WifiSettings"
android:icon="@drawable/ic_settings_wireless"
/>
........
注意里面的一个参数android:fragment:可以看见每个item都对应一个fragment,但在这里还没发挥作用。
加载完这个xml,获取到里面的DashboardCategory。然后再把这些item加到容器中去
for (int n = 0; n < count; n++) {
DashboardCategory category = categories.get(n);
View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
false);
TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
categoryLabel.setText(category.getTitle(res));
ViewGroup categoryContent =
(ViewGroup) categoryView.findViewById(R.id.category_content);
final int tilesCount = category.getTilesCount();
for (int i = 0; i < tilesCount; i++) {
DashboardTile tile = category.getTile(i);
DashboardTileView tileView = new DashboardTileView(context);
updateTileView(context, res, tile, tileView.getImageView(),
tileView.getTitleTextView(), tileView.getStatusTextView());
tileView.setTile(tile);
categoryContent.addView(tileView);
}
// Add the category
mDashboard.addView(categoryView);
}
添加好了View后,Settings的大致布局就差不多了,接下来肯定就是重要的点击每个item的监听事件。找半天,没找到,后来就想不会自己在View里面实现了吧,点进去一看果然。
四.DashboardTileView.java
1)不多看了,直接进入里面的onClick方法
@Override
public void onClick(View v) {
if (mTile.fragment != null) {
Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0,
mTile.titleRes, mTile.getTitle(getResources()));
} else if (mTile.intent != null) {
getContext().startActivity(mTile.intent);
}
}
还是点进去看吧,一层一层最后看见了:
intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
titleResId, title, isShortcut);
context.startActivity(intent);
前面这个返回intent的函数是干嘛的呢?
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(context, SubSettings.class);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
titleResPackageName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
原来是设置intent信息的,而且看到跳转class是subSettings.class,把点击的这个item的fragmentName的信息也传过去了。
返回了intent后就执行startActivity了。然后再看SubSettings
五.SubSettings.class
1)public class SubSettings extends SettingsActivity{},一看也是一个SettingsActivity的子类。现在又回到了SettingsActivity的onCreate方法了。
2)还记得前面的mIsShowingDashboard吗,mIsShowingDashboard = className.equals(Settings.class.getName());
现在这个变量为false了执行的语句也走向了另外一个岔路口。
3) setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
现在加载的是R.layout.settings_main_prefs这个布局。
4)final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
获取到刚才给intent的fragmentName信息。
5)switchToFragment(initialFragmentName, initialArguments, true, false,mInitialTitleResId, mInitialTitle, false);
又来到了这个函数,开启fragment,又开始一个轮回了。
一.Setting.java
在Settings/AndroidMainfest.xml 找到Settings代码入口
<activity-alias android:name="Settings"
android:taskAffinity="com.android.settings"
android:label="@string/settings_label_launcher"
android:launchMode="singleTask"
android:targetActivity="Settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
由此可知代码的入口是Settings.java
public class Settings extends SettingsActivity {
/*
* Settings subclasses for launching independently.
*/
public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
public static class WirelessSettingsActivity extends SettingsActivity { /* empty */ }
public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
public static class TetherSettingsActivity extends SettingsActivity { /* empty */ }
public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
.....此处省略
Setting中都是一些静态类他继承了SettingsActivity类,所以直接进入其父类SettingsActivity当中
1)public class SettingsActivity extends Activity
implements PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback,
ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
SearchView.OnQueryTextListener, SearchView.OnCloseListener,MenuItem.OnActionExpandListener
2)进入Oncreate()方法详解:
getMetaData(); //获取元数据 每个activity在manifest中都可以定义自己的元数据,
一下是settings自己配置的元数据
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" android:value="true" />
3)final Intent intent = getIntent(); 获取Intent的对象,从里面获取到传递的信息,包括是否要打开fragment啊,一些配置参数等等。
4) mIsShowingDashboard = className.equals(Settings.class.getName()); 获取当前是否是我们的主界面也就是仪表盘界面
先假设我们现在就是在主界面,等分析分析完了就知道如果不是会怎么样,非常简单。那么
4)setContentView(mIsShowingDashboard ?R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
先加载一个xml,点进去看这个xml发现是一个容器,也就拿给fragment使用,继续往下看。
5)switchToFragment(DashboardSummary.class.getName(), null, false, false,mInitialTitleResId, mInitialTitle, false);
这个函数的作用说通俗就是打开dashboardSummary这个fragment.下面来到这个fragment
三.DashboardSummary.java
既然这个类是一个Fragment那么直接先找到onCreateView方法:
1)public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mLayoutInflater = inflater;
final View rootView = inflater.inflate(R.layout.dashboard, container, false);
mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
return rootView;
}
发现这个fragment填充了布局dashboard.xml,点进去一看,这个xml布局其实就是一个空布局,只是作为一个容器使用,
并且容器变量为mDashboard,那么继续看,发现这个fragment注册了一个广播接受者。
2)广播的注册在onResume方法:
@Override
public void onResume() {
super.onResume();
sendRebuildUI();
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
getActivity().registerReceiver(mHomePackageReceiver, filter);
}
可以看见这只是说当系统有应用的安装卸载等等之类才会执行。但是再往前看发现一个方法sendRebuildUI();
private void sendRebuildUI() {
if (!mHandler.hasMessages(MSG_REBUILD_UI)) {
mHandler.sendEmptyMessage(MSG_REBUILD_UI);
}
这个函数发送了一个消息给handler.看看如何处理的:
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REBUILD_UI: {
final Context context = getActivity();
rebuildUI(context);
} break;
}
发现里面执行了rebuildUI(context);
3)rebuildUI(context):
mDashboard.removeAllViews();先清除容器
List<DashboardCategory> categories =
((SettingsActivity) context).getDashboardCategories(true);
看见后面执行的函数getDashboardCategories,一步一步点进去看,最后发现重要的一句
loadCategoriesFromResource(R.xml.dashboard_categories, categories); 这个xml打开后一看,大家就发现非常熟悉了
R.xml.dashboard_categories:
<!-- WIRELESS and NETWORKS -->
<dashboard-category
android:id="@+id/wireless_section"
android:title="@string/header_category_wireless_networks" >
<!-- Wifi -->
<dashboard-tile
android:id="@+id/wifi_settings"
android:title="@string/wifi_settings_title"
android:fragment="com.android.settings.wifi.WifiSettings"
android:icon="@drawable/ic_settings_wireless"
/>
........
注意里面的一个参数android:fragment:可以看见每个item都对应一个fragment,但在这里还没发挥作用。
加载完这个xml,获取到里面的DashboardCategory。然后再把这些item加到容器中去
for (int n = 0; n < count; n++) {
DashboardCategory category = categories.get(n);
View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
false);
TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
categoryLabel.setText(category.getTitle(res));
ViewGroup categoryContent =
(ViewGroup) categoryView.findViewById(R.id.category_content);
final int tilesCount = category.getTilesCount();
for (int i = 0; i < tilesCount; i++) {
DashboardTile tile = category.getTile(i);
DashboardTileView tileView = new DashboardTileView(context);
updateTileView(context, res, tile, tileView.getImageView(),
tileView.getTitleTextView(), tileView.getStatusTextView());
tileView.setTile(tile);
categoryContent.addView(tileView);
}
// Add the category
mDashboard.addView(categoryView);
}
添加好了View后,Settings的大致布局就差不多了,接下来肯定就是重要的点击每个item的监听事件。找半天,没找到,后来就想不会自己在View里面实现了吧,点进去一看果然。
四.DashboardTileView.java
1)不多看了,直接进入里面的onClick方法
@Override
public void onClick(View v) {
if (mTile.fragment != null) {
Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0,
mTile.titleRes, mTile.getTitle(getResources()));
} else if (mTile.intent != null) {
getContext().startActivity(mTile.intent);
}
}
还是点进去看吧,一层一层最后看见了:
intent intent = onBuildStartFragmentIntent(context, fragmentName, args, titleResPackageName,
titleResId, title, isShortcut);
context.startActivity(intent);
前面这个返回intent的函数是干嘛的呢?
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(context, SubSettings.class);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
titleResPackageName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleResId);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
原来是设置intent信息的,而且看到跳转class是subSettings.class,把点击的这个item的fragmentName的信息也传过去了。
返回了intent后就执行startActivity了。然后再看SubSettings
五.SubSettings.class
1)public class SubSettings extends SettingsActivity{},一看也是一个SettingsActivity的子类。现在又回到了SettingsActivity的onCreate方法了。
2)还记得前面的mIsShowingDashboard吗,mIsShowingDashboard = className.equals(Settings.class.getName());
现在这个变量为false了执行的语句也走向了另外一个岔路口。
3) setContentView(mIsShowingDashboard ? R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
现在加载的是R.layout.settings_main_prefs这个布局。
4)final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
获取到刚才给intent的fragmentName信息。
5)switchToFragment(initialFragmentName, initialArguments, true, false,mInitialTitleResId, mInitialTitle, false);
又来到了这个函数,开启fragment,又开始一个轮回了。