前述:

本人已工作两年多,但是依然感觉还是Android的门外汉,之前一直从事Android的应用开发,每天就是各种调用SDK方法,各种拷贝网上的源码以及jar包,从来也不管为啥这样用,由于换了一份工作才开始接触到Android的源码,感觉Android的水好深啊。

今天这篇博客也是我的处女作啊,以后也希望通过多多研究源码来写出更多的博客,我觉得写博客主要还是作为一个记录吧,不然感觉有的东西真的很容易丢,尤其是平时不怎么接触的模块。

好啦,接下来开始今天的Setting旅行啦。


Settings简述:

Setting模块大家还是比较熟悉的吧?其实Setting也不是什么高级的东西,它就是一个APP,属于Android的应用层,源码在packages\apps\Settings中,今天分析的源码是基于Android5.1,如下图是5.1Setting模块的界面:



查看一个应用首先是查看这个应用的AndroidManifest.xml文件,以便查看程序的入口,Setting模块的入口是Setting.java这个类,这个类继承SettingActivity,但是没有继承任何的方法,但却定义了一大堆内部类。

/** * Top-level Settings activity */public class Settings extends SettingsActivity {    public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }    public static class StorageSettingsActivity extends SettingsActivity { /* empty */ }    public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }    public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }    public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ }    public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }    public static class InputMethodAndSubtypeEnablerActivity extends SettingsActivity { /* empty */ }    public static class VoiceInputSettingsActivity extends SettingsActivity { /* empty */ }    public static class SpellCheckersSettingsActivity extends SettingsActivity { /* empty */ }    public static class LocalePickerActivity extends SettingsActivity { /* empty */ }    public static class UserDictionarySettingsActivity extends SettingsActivity { /* empty */ }    public static class HomeSettingsActivity extends SettingsActivity { /* empty */ }    public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ }    public static class DeviceInfoSettingsActivity extends SettingsActivity { /* empty */ }    public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }    public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ }}
   
   
   

这些类都是Setting模块的子界面类,是特定功能的类,比如WifiSettingsActivity是WiFi模块相关的类。

所以接下来我们直接分析SettingActivity这个类就可以了。

SettingActivity.java

先看该类的OnCreate方法

 @Override    protected void onCreate(Bundle savedState) {        super.onCreate(savedState);        // Should happen before any call to getIntent()        getMetaData();        final Intent intent = getIntent();

先调用getMetaData()方法,用于加载一些元数据,进入getMetaData()方法

private void getMetaData() {        try {            ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),                    PackageManager.GET_META_DATA);            if (ai == null || ai.metaData == null) return;            mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);        } catch (NameNotFoundException nnfe) {            // No recovery            Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());        }}
   

主要作用就是通过META_DATA_KEY_FRAGMENT_CLASS这个属性获得额外的mFragmentClass,如果可以获得将启动对应的mFragmentClass的Activity,但是直接启动Setting不会获得该数据。

继续往下看代码

final ComponentName cn = intent.getComponent();final String className = cn.getClassName();mIsShowingDashboard = className.equals(Settings.class.getName());// This is a "Sub Settings" when:// - this is a real SubSettings// - or :settings:show_fragment_as_subsetting is passed to the Intentfinal boolean isSubSettings = className.equals(SubSettings.class.getName()) ||       intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
   
   


由于我们是从Setting启动的,所以mIsShowingDashboard的值为true,而isSubSettings的值是false。

setContentView(mIsShowingDashboard ?R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
   

由于mIsShowingDashboard的值为true,所以使用的是R.layout.settings_main_dashboard

同时继续往下走,会看到这段代码块:

if (savedState != null) {   ...         } else {    if (!mIsShowingDashboard) {      ...    } else {// No UP affordance if we are displaying the main DashboardmDisplayHomeAsUpEnabled = false;        // Show Search affordance        mDisplaySearch = true;        mInitialTitleResId = R.string.dashboard_title;        switchToFragment(DashboardSummary.class.getName(), null, false, false,        mInitialTitleResId, mInitialTitle, false);    }}
   

这里由于是第一次启动,所以savedState 为null,同时mIsShowingDashboard的值为true,看到进入了switchToFragment这个方法,这里准备切换到DashboardSummary这个Fragment。

DashboardSummary.java

DashboardSummary的onCreateView方法加载了R.layout.dashboard,代码如下:

        


接下来将是重点,开始真正加载Setting的界面了,在OnResume方法中最终会调用rebuildUI()方法,该方法源码:


private void rebuildUI(Context context) {        if (!isAdded()) {            Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");            return;        }        long start = System.currentTimeMillis();        final Resources res = getResources();//mDashboard这个View就是整个界面的总View        mDashboard.removeAllViews();(1)这里调用SettingActivity的getDashboardCategories,也就是加载整个Setting的内容        List categories =                ((SettingsActivity) context).getDashboardCategories(true);        final int count = categories.size();        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);//(2)创建DashboardTileView,也就是每个Setting的内容                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);        }        long delta = System.currentTimeMillis() - start;        Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");}


接下来将对上面代码标注的序号处进行说明:

(1)处最终会调用SettingActivity的buildDashboardCategories方法,


private void buildDashboardCategories(List categories) {        categories.clear();        loadCategoriesFromResource(R.xml.dashboard_categories, categories);        updateTilesList(categories);}

该方法将加载一个xml文档并使用Android默认的xml解析器XmlPullParser对文档进行解析,最终将解析结果存入到一个List中,然后在上面代码的rebuildUI方法中for循环遍历读取。


以下为Setting页面的xml文档:


<?xml version="1.0" encoding="utf-8"?>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    

根据这个文件可看出来,dashboard-categories这个标签对应着Java代码中的List集合,dashboard-category这个标签对应着DashboardCategory类,dashboard-tile这个标签对应着DashboardTile这个类。


(2)处将通过for循环遍历而来的数据通过创建DashboardTileView最终全部存入到mDashboard这个布局中,至此整个Setting模块的界面布局已经完成了。


DashboardTileView.java

这个类是Setting中每个条目数据的类,通过onClick方法启动不同的功能,比如WiFi,Bluetooth等

public class DashboardTileView extends FrameLayout implements View.OnClickListener {    @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);        }    }}
   
最终启动不同的Setting子模块, 至此整个Setting模块的整体框架就已分析结束了。


总结一下:

1、整个Setting模块是在SettingActivity中加载DashboardSummary这个Fragment,然后从dashboard_categories.xml中读取预先配置好的文件来初始化Settings的首界面视图。

2、Setting的子界面基本上都是一个Fragment,并且基本上都是通过SettingActivity的OnCreate方法去加载的,同时大部分SubSetting在加载界面时,用的都是PreferenceFragment技术(在以后的博客中会讲述)。

3、Android5.1的Setting使用的是DashboardCategory和DashboardTile类来存储整个xml数据结构。

更多相关文章

  1. android google 分屏 多窗口 按home键界面错乱故障分析(一)分屏的
  2. 客觀評 Android、iOS、WP7
  3. 移动语音引擎相关开发笔记
  4. 改变Android按钮背景颜色的高效方法
  5. 作为Android开发者 你真的知道Android按下开机键到启动发生什么
  6. Android核心分析28篇,强烈推荐android初学者,android进阶者看看这
  7. Android(安卓)横向ScrollView照片浏览器实现
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 如何通过mysql / php中的最佳匹配对搜索
  2. ubuntu16.04配置Lamp环境(搭建linux+apach
  3. MySql高级查询(1)
  4. MySQL视图-(视图创建,修改,删除,查看,更新数
  5. 2、MySQL 8.0参考手册 连接到服务器并断
  6. ERROR 2002 (HY000): Can't connect to l
  7. 如何在mysql数据库中找到类似的二进制字
  8. 初始化mysql数据库 /usr/bin/mysql_insta
  9. mysql数据库集群方案(内部资料)
  10. MySQL:ERROR 1227(42000):访问被拒绝 - 无法