Android10.0 Setting 学习

2,409 阅读3分钟

路径 ./packages/apps/Setting/src/com/android/setting/

根据 AndroidManifest.xml 可以发现首页面为SettingsHomepageActivity,紧接着我们进入到 SettingsHomepageActivity

SettingHomeActivity

路径 ./homepage/SettingHomepageActivity.java

我们直接看它的onCreate方法

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.settings_homepage_container);
        ...
        if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
            // Only allow contextual feature on high ram devices.
            showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
        }
        showFragment(new TopLevelSettings(), R.id.main_content);
        ...
    }

showFragment方法很简单,加入对应的Fragment。由此,我们可以跟进到ContextualCardsFragmentTopLevelSettings

TopLevelSettings

路径 .settings.homepage;

我们可以看到 TopLevelSettings <- DashboardFragment <- SettingsPreferenceFragment <- InstrumentedPreferenceFragment <- ObservablePreferenceFragment <- PreferenceFragmentCompat

TopLevelSettings内可以看到这样一段代码,跟进文件继续看。

getPreferenceScreenResId 继承自 InstrumentedPreferenceFragment

@Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        // 默认为-1,如果大于-1,去加载布局
        final int resId = getPreferenceScreenResId();
        if (resId > 0) {
            addPreferencesFromResource(resId);
        }
    }
    ···
    /**
     * Get the res id for static preference xml for this fragment.
     */
    protected int getPreferenceScreenResId() {
        return -1;
    }
    @Override
    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
        super.addPreferencesFromResource(preferencesResId);
        updateActivityTitleWithScreenTitle(getPreferenceScreen());
    }

PreferenceFragmentCompat

    /**
     * Inflates the given XML resource and adds the preference hierarchy to the current
     * preference hierarchy.
     *
     * @param preferencesResId The XML resource ID to inflate.
     */
    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
        requirePreferenceManager();
        setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext,
                preferencesResId, getPreferenceScreen()));
    }

此时我们在 TopLevelSettings 找到 getPreferenceScreenResId 方法

@Override
    protected int getPreferenceScreenResId() {
        return R.xml.top_level_settings;
    }

去找到对应的XML文件

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="top_level_settings">

    <Preference
        android:key="top_level_network"
        android:title="@string/network_dashboard_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_network"
        android:order="-120"
        android:fragment="com.android.settings.network.NetworkDashboardFragment"
        settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
    
    ···
    
    <Preference
        android:key="top_level_support"
        android:summary="@string/support_summary"
        android:title="@string/page_tab_title_support"
        android:icon="@drawable/ic_homepage_support"
        android:order="100"
        settings:controller="com.android.settings.support.SupportPreferenceController"/>

</PreferenceScreen>

我们发现系统设置内用的是Preference方法构建的界面,到这里我们就很清楚了明白了Settings的一级界面布局。 在这里我们知道了一级菜单的数据,那我们也看下二级菜单的数据吧。我们在一级菜单里面可以看到有framgnet我们跟进去看看NetworkDashboardFragment

NetworkDashboardFragment内,我们同样可以看到 getPreferenceScreenResId 方法,所以明白了,一级菜单和二级菜单构建界面的方法如出一辙。

 @Override
    protected int getPreferenceScreenResId() {
        if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2)) {
            return R.xml.network_and_internet_v2;
        } else {
            return R.xml.network_and_internet;
        }
    }

我们继续跟进network_and_internet_v2去一探究竟。

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:key="network_and_internet_screen"
    android:title="@string/network_dashboard_title"
    settings:initialExpandedChildrenCount="5">

    <PreferenceCategory
        android:key="multi_network_header"
        android:title="@string/summary_placeholder"
        android:layout="@layout/preference_category_no_label"
        settings:allowDividerBelow="true"
        android:order="-40"
        settings:controller="com.android.settings.network.MultiNetworkHeaderController"/>

    <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"
        settings:allowDividerAbove="true">
        <intent
            android:action="android.settings.WIFI_SETTINGS"
            android:targetClass="Settings$WifiSettingsActivity" />
    </com.android.settings.widget.MasterSwitchPreference>

         ···

    <com.android.settings.network.PrivateDnsModeDialogPreference
        android:key="private_dns_settings"
        android:title="@string/select_private_dns_configuration_title"
        android:order="20"
        android:dialogTitle="@string/select_private_dns_configuration_dialog_title"
        android:dialogLayout="@layout/private_dns_mode_dialog"
        android:positiveButtonText="@string/save"
        android:negativeButtonText="@android:string/cancel" />

</PreferenceScreen>

我们可以看到这里是二级菜单,是不是和一级菜单一样呢,所以我们到这里就明白了Settings的菜单数据展示喽~

下一步我们看搜索栏是怎么实现的

在上面的文章中,我们知道了首页面为SettingsHomepageActivity,所以我们直接去看SettingsHomepageActivity就好。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.settings_homepage_container);
        ···
        // 这里有initSearchToolbar,是不是感觉很像
        FeatureFactory.getFactory(this).getSearchFeatureProvider()
                .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
        ···
        ((FrameLayout) findViewById(R.id.main_content))
                .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
    }

我们可以找到 SearchFeatureProvider.initSearchToolbar

    default void initSearchToolbar(Activity activity, Toolbar toolbar, int pageId) {
        if (activity == null || toolbar == null) {
            return;
        }
        if (!Utils.isDeviceProvisioned(activity) ||
                !Utils.isPackageEnabled(activity, getSettingsIntelligencePkgName(activity))) {
            final ViewGroup parent = (ViewGroup) toolbar.getParent();
            if (parent != null) {
                parent.setVisibility(View.GONE);
            }
            return;
        }
        final View navView = toolbar.getNavigationView();
        navView.setClickable(false);
        navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
        navView.setBackground(null);
        // 在这里 我们可以看到点击事件的设置
        toolbar.setOnClickListener(tb -> {
            final Context context = activity.getApplicationContext();
            // 这里有跳转意图 但是我们在本类中没有找到 buildSearchIntent 的实现 
            // 但是我们看到 SearchFeatureProvider 有一个实现类 SearchFeatureProviderImpl
            // 我们进到 SearchFeatureProviderImpl 内部中看看具体实现
            final Intent intent = buildSearchIntent(context, pageId);

            if (activity.getPackageManager().queryIntentActivities(intent,
                    PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
                return;
            }

            FeatureFactory.getFactory(context).getSlicesFeatureProvider()
                    .indexSliceDataAsync(context);
            FeatureFactory.getFactory(context).getMetricsFeatureProvider()
                    .action(context, SettingsEnums.ACTION_SEARCH_RESULTS);
            activity.startActivityForResult(intent, REQUEST_CODE);
        });
    }

SearchFeatureProviderImpl.buildSearchIntent
 @Override
    public Intent buildSearchIntent(Context context, int pageId) {
        return new Intent(Settings.ACTION_APP_SEARCH_SETTINGS)
                .setPackage(getSettingsIntelligencePkgName(context))
                .putExtra(Intent.EXTRA_REFERRER, buildReferrer(context, pageId));
    }

我们此时可以看到,跳转意图为 Settings.ACTION_APP_SEARCH_SETTINGS,跳转到了搜索界面。

搜索界面是Android单独的一个模块 路径android/packages/apps/SettingsIntelligence






未完待续 如有错误欢迎指出 共同学习 共同进步哈~