android M(6.0) Settigs模块源码分析

博客参考链接:Android5.0 Settings各个子模块跳转和布局实现  

android Settings 添加新菜单项参考链接:Settings源码6.0 7.0添加新菜单选项

  Settings继承自SettingsActivity,该类除了实现的空内部类外,就是一个判断Fragment是否有效的方法。实现的空内部类作用:在加载类设置项时不适用SubSettings,当创建快捷方式时调用queryIntentActivities()方法查询到的类为这些类的空实现的内部类。SettingsActivity的onCreate()方法如下:

private static final String META_DATA_KEY_FRAGMENT_CLASS = "com.android.settings.FRAGMENT_CLASS";

@Override
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        // Should happen before any call to getIntent()
       //1.获得meta-data中key为""com.android.settings.FRAGMENT_CLASS"的值,并赋值给MFragmentClass
        getMetaData();
       //2.
        final Intent intent = getIntent();
        if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
            getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
        }
       //3获取preference
        mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
                Context.MODE_PRIVATE);
        //获取Fragment的类名
        final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
        //intent是否为快捷方式的intent
        mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
       //获得intent中的组件名
        final ComponentName cn = intent.getComponent();
       //根据组件名获得类名
        final String className = cn.getClassName();
      //4 由于是从Settings这个类跳过来的,因此mIsShowingDashboard为true
        mIsShowingDashboard = className.equals(Settings.class.getName());
       //是否为SubSettings的判断逻辑;此时为false
        final boolean isSubSettings = className.equals(SubSettings.class.getName()) ||
                intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
        // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
        if (isSubSettings) {
            // Check also that we are not a Theme Dialog as we don't want to override them
            final int themeResId = getThemeResId();
            if (themeResId != R.style.Theme_DialogWhenLarge &&
                    themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
                setTheme(R.style.Theme_SubSettings);
            }
        }
   //此时,由于mIsShowingDashboard 为true,因而加载的是settings_main_dashboard布局
        setContentView(mIsShowingDashboard ?
                R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
        mContent = (ViewGroup) findViewById(R.id.main_content);
        getFragmentManager().addOnBackStackChangedListener(this);
        if (mIsShowingDashboard) {
            Index.getInstance(getApplicationContext()).update();
        }
        if (savedState != null) {
            // We are restarting from a previous saved state; used that to initialize, instead
            // of starting fresh.
            mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
            mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
            setTitleFromIntent(intent);
            ArrayList<DashboardCategory> categories =
                    savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
            if (categories != null) {
                mCategories.clear();
                mCategories.addAll(categories);
                setTitleFromBackStack();
            }

            mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
            mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
            mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
                    1 /* one home activity by default */);
        } else {
            if (!mIsShowingDashboard) {
                // Search is shown we are launched thru a Settings "shortcut". UP will be shown
                // only if it is a sub settings
                if (mIsShortcut) {
                    mDisplayHomeAsUpEnabled = isSubSettings;
                    mDisplaySearch = false;
                } else if (isSubSettings) {
                    mDisplayHomeAsUpEnabled = true;
                    mDisplaySearch = true;
                } else {
                    mDisplayHomeAsUpEnabled = false;
                    mDisplaySearch = false;
                }
                if (isActivityShouldBeAvoided(this.getClass().getSimpleName())) {
                    mDisplaySearch = false;
                }
                setTitleFromIntent(intent);

                Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
                switchToFragment(initialFragmentName, initialArguments, true, false,
                        mInitialTitleResId, mInitialTitle, false);
            } else {//首次加载时会进入该分支
                // No UP affordance if we are displaying the main Dashboard
                mDisplayHomeAsUpEnabled = false;
                // Show Search affordance
                mDisplaySearch = true;
                mInitialTitleResId = R.string.dashboard_title;
              //5 主要是切换到DashboardSummary这个Fragment
                switchToFragment(DashboardSummary.class.getName(), null, false, false,
                        mInitialTitleResId, mInitialTitle, false);
            }
        }

        mActionBar = getActionBar();
        if (mActionBar != null) {
            mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
            mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
        }
        mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
        // see if we should show Back/Next buttons
        if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {

            View buttonBar = findViewById(R.id.button_bar);
            if (buttonBar != null) {
                buttonBar.setVisibility(View.VISIBLE);

                Button backButton = (Button)findViewById(R.id.back_button);
                backButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_CANCELED, getResultIntentData());
                        finish();
                    }
                });
                Button skipButton = (Button)findViewById(R.id.skip_button);
                skipButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_OK, getResultIntentData());
                        finish();
                    }
                });
                mNextButton = (Button)findViewById(R.id.next_button);
                mNextButton.setOnClickListener(new OnClickListener() {
                    public void onClick(View v) {
                        setResult(RESULT_OK, getResultIntentData());
                        finish();
                    }
                });
                // set our various button parameters
                if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
                    String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
                    if (TextUtils.isEmpty(buttonText)) {
                        mNextButton.setVisibility(View.GONE);
                    }
                    else {
                        mNextButton.setText(buttonText);
                    }
                }
                if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
                    String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
                    if (TextUtils.isEmpty(buttonText)) {
                        backButton.setVisibility(View.GONE);
                    }
                    else {
                        backButton.setText(buttonText);
                    }
                }
                if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
                    skipButton.setVisibility(View.VISIBLE);
                }
            }
        }
        mHomeActivitiesCount = getHomeActivitiesCount();
    }
1.  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());
        }
    }
getMetaData()函数用来获得Activity的额外数据mFragmentClass,如果可以获得这个数据,下面就会去现实mFragmentClass对应的Activity;而直接启动Settings模块是不会获得该数据的。那么,Activity时如何加载Fragment的呢?
2       @Override
    public Intent getIntent() {
        Intent superIntent = super.getIntent();
        // 处理个别特殊情况,本例就是将mFragmentClass赋值给了startingFragment
        String startingFragment = getStartingFragmentClass(superIntent);
        // This is called from super.onCreate, isMultiPane() is not yet reliable
        // Do not use onIsHidingHeaders either, which relies itself on this method
        if (startingFragment != null) {
            Intent modIntent = new Intent(superIntent);
            // 将startingFragment放入intent的以EXTRA_SHOW_FRAGMENT(":settings:show_fragment")为key的键值对中。
            modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
            Bundle args = superIntent.getExtras();
            if (args != null) {
                args = new Bundle(args);
            } else {
                args = new Bundle();
            }
            args.putParcelable("intent", superIntent);
            modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
            return modIntent;
        }
        return superIntent;
    }
 private String getStartingFragmentClass(Intent intent) {
        if (mFragmentClass != null) return mFragmentClass;

        String intentClass = intent.getComponent().getClassName();
        if (intentClass.equals(getClass().getName())) return null;

        if ("com.android.settings.ManageApplications".equals(intentClass)
                || "com.android.settings.RunningServices".equals(intentClass)
                || "com.android.settings.applications.StorageUse".equals(intentClass)) {
            // Old names of manage apps.
            intentClass = com.android.settings.applications.ManageApplications.class.getName();
        }
        return intentClass;
    }
geIntent()函数的作用就是构造Intent,并且为它增加一个特殊的键值对,该键值对的key为"settings:show_fragment",value为mFragmentClass指定的Fragment类名。从这就可以看出,onCreate()方法要求必须先执行getMetaDate()方法,再去执行getIntent(),就是因为mFragmentClass是在getMetaData()方法中获得的。
5 DashboardSummary:是用来显示Settings所有项的,即点击Settings图标所展示的;
@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        mLayoutInflater = inflater;
        mLte4GEnabler = new Lte4GEnabler(getActivity(), new Switch(getActivity()));
        //加载了dashboard这个布局
        final View rootView = inflater.inflate(R.layout.dashboard, container, false);
        mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
        return rootView;
    }
从dashboard布局中可知,settings的选项视图是显示在dashboard_container上
         @Override
    public void onResume() {
        super.onResume();
        mLte4GEnabler.resume();
        sendRebuildUI();//建立UI,就是发送一个MSG_REBUILD_UI消息,进而调用到RebuildUI方法
        //包的安装、卸载等监听
        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);
        //飞行、SIM卡状态的广播
        // Register for intent broadcasts
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
        getActivity().registerReceiver(mReceiver, intentFilter);
    }
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();
    //先移除settings所有的视图
        mDashboard.removeAllViews();
    //获取Settings的所有categories,加载settings的所有内容
        List<DashboardCategory> categories = ((SettingsActivity) context).getDashboardCategories(true);
     //获取categories的总数
        final int count = categories.size();
        for (int n = 0; n < count; n++) {
          //获取categories中的dashboardcategory
            DashboardCategory category = categories.get(n);
            //加载dashboard_category布局,mDashboard是整个界面的总View
            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);
          //获取dashboard所具有的dashboardTiles
            final int tilesCount = category.getTilesCount();
            for (int i = 0; i < tilesCount; i++) {
                DashboardTile tile = category.getTile(i);
                DashboardTileView tileView;
                if (tile.getTitle(res).equals(res.getString(R.string.lte_4g_settings_title))) {
                    tileView = new DashboardTileView(context,true);
                    mLte4GEnabler.setSwitch(tileView.getSwitch());
                    int simState = TelephonyManager.getDefault().getSimState(PhoneConstants.SUB1);
                    boolean enabled = (Settings.System.getInt(context.getContentResolver(),
                            Settings.System.AIRPLANE_MODE_ON, 0) == 0)
                            && (simState == TelephonyManager.SIM_STATE_READY);
                    tileView.setEnabled(enabled);
                    tileView.getTitleTextView().setEnabled(enabled);
                    // update icons
                    if (enabled) {
                        tile.iconRes = R.drawable.ic_settings_4g;
                    } else {
                        tile.iconRes = R.drawable.ic_settings_4g_dis;
                    updateTileView(context, res, tile, tileView.getImageView(),
                            tileView.getTitleTextView(), tileView.getSwitch());
                } else {
                    tileView = new DashboardTileView(context,false);
                    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");
    }
上面函数的作用:遍历categories这个列表来获取DashboardCategory对象,将所有的DashboardCategory对象和DashboardCAtegory对象中的DashboardTitle对象转换成视图对象并添加到主视图对象mDashboard中。
DashboardTitleViewz这个类是每一条目数据的类,通过onClcik()方法来启动不同的功能:如点击wifi或蓝牙等启动额界面。
明白了上述加载选项的方式,我们再次回到SettingsActivity中看如下两个函数就很清晰了:
    public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
        if (forceRefresh || mCategories.size() == 0) {
            buildDashboardCategories(mCategories);
        }
        return mCategories;
    }
    /**
     * Called when the activity needs its list of categories/tiles built.
     *
     * @param categories The list in which to place the tiles categories.
     */
    private void buildDashboardCategories(List<DashboardCategory> categories) {
        categories.clear();
      //加载了dashboard_categories布局
        loadCategoriesFromResource(R.xml.dashboard_categories, categories);
        updateTilesList(categories);
    }
dashboard_categories布局就是加载所有的categories,这里的categories为ArrayList的mCategories;通过查看该布局,可知dashboard布局就是Settings模块的首界面的一个抽象,dashboard_categories则是设置分类集合的抽象。代码中的List对应的是dashboard_categories,DAshboardCategory对应的是dashboard_category,而dashboard_title对应的是DashboardTitlle。当加载完这些对象后SettingsActivity会得到mCategories返回给DashboardSummary来初始化Settings的各个设置选项。

介绍了Settings模块的加载,在此再简单介绍下点击时是如何跳转到各个功能界面的,这里以Storage选项简单举例:
从前面的分析可知:配置文件中的dashboard_tile对应的是DashboardTile, DashboardTile又对应的是DashboardTileView视图,一个DashboardTileView对象就是设置模块中的一个设置选项;当我们点击Storage选项时,就会调用DashboardTileView的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);
        }
    }
mTile.fragment变量就代表的是dashboard_categories.xml文件中的
<!-- Storage -->
        <dashboard-tile
                android:id="@+id/storage_settings"
                android:title="@string/storage_settings"
                android:fragment="com.android.settings.deviceinfo.StorageSettings"
                android:icon="@drawable/ic_settings_storage"
         />
Storage节点的 android:fragment="com.android.settings.deviceinfo.StorageSettings" 属性,因此就实现了跳转到各个功能选项的Fragment中。
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值