Android 10.0 Settings源码分析之主界面加载(一)

本篇主要记录AndroidQ Settings源码主界面加载流程,方便后续工作调试其流程。由于篇幅较长,本篇主要记录主界面xml静态加载。

代码路径:

packages/app/Settings/

主界面加载:

从清单文件AndroidManifest.xml中入手:

        <!-- Alias for launcher activity only, as this belongs to each profile. -->
        <activity-alias android:name="Settings"
                android:label="@string/settings_label_launcher"
                android:launchMode="singleTask"
                android:targetActivity=".homepage.SettingsHomepageActivity">
            <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>
            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
        </activity-alias>

Settings的主界面是Settings.java:
在这里插入图片描述从Settings.java来看,除了大量的静态类继承SettingsActivity,就无其他有效信息了。但看其xml定义可以发现targetActivity属性,实质应是SettingsHomepageActivity.java。
先看其xml配置:

        <activity android:name=".homepage.SettingsHomepageActivity"
                  android:label="@string/settings_label_launcher"
                  android:theme="@style/Theme.Settings.Home"
                  android:launchMode="singleTask">
            <intent-filter android:priority="1">
                <action android:name="android.settings.SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                       android:value="true" />
        </activity>

SettingsHomepageActivity.java:
主要从onCreate()方法开始:

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

    setContentView(R.layout.settings_homepage_container);
    final View root = findViewById(R.id.settings_homepage_container);
    root.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    setHomepageContainerPaddingTop();

    final Toolbar toolbar = findViewById(R.id.search_action_bar);
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

    final ImageView avatarView = findViewById(R.id.account_avatar);
    final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView);
    getLifecycle().addObserver(avatarViewMixin);

    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);
    ((FrameLayout) findViewById(R.id.main_content))
            .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}

可以看到主界面的layout为settings_homepage_container.xml:

<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/settings_homepage_container"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.core.widget.NestedScrollView
        android:id="@+id/main_content_scrollable_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">

        <LinearLayout
            android:id="@+id/homepage_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:descendantFocusability="blocksDescendants">

            <FrameLayout
                android:id="@+id/contextual_cards_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/contextual_card_side_margin"
                android:layout_marginEnd="@dimen/contextual_card_side_margin"/>

            <FrameLayout
                android:id="@+id/main_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:background="?android:attr/windowBackground"/>

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <include layout="@layout/search_bar"/>
    </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

主界面布局中主要包含三部分:两个FrameLayout,一个顶部快捷搜索栏。其中Id为main_content的FrameLayout就是用来显示主设置内容的,即Settings的一级菜单项界面。
由于本篇主要记录主界面加载流程,所以主要看main_content。
回到onCreate()方法:

showFragment(new TopLevelSettings(), R.id.main_content);

启动TopLevelSettings的fragment,此fragments主要继承于DashboardFragment.java,先来看TopLevelSettings的构造方法:

    public TopLevelSettings() {
    
    
        final Bundle args = new Bundle();
        // Disable the search icon because this page uses a full search view in actionbar.
        args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);
        setArguments(args);
    }

可以看到构造方法中仅设置了个标志位,再根据framgments生命周期先来看onAttach()方法:

    @Override
    public void onAttach(Context context) {
    
    
        super.onAttach(context);
        use(SupportPreferenceController.class).setActivity(getActivity());
    }

调用父类DashboardFragment.java的onAttach()方法,此方法主要是完成mPreferenceControllers的加载。
onCreate()方法:

    @Override
    public void onCreate(Bundle icicle) {
    
    
        super.onCreate(icicle);
        // Set ComparisonCallback so we get better animation when list changes.
        getPreferenceManager().setPreferenceComparisonCallback(
                new PreferenceManager.SimplePreferenceComparisonCallback());
        if (icicle != null) {
    
    
            // Upon rotation configuration change we need to update preference states before any
            // editing dialog is recreated (that would happen before onResume is called).
            updatePreferenceStates();
        }
    }

第一次进入时,icicle为null,具体应该看引用的父类的onCreate()方法,由于此篇主要说主界面加载,故暂不展开。
根据log定位发现,其后调用DashboardFragment.java的onCreatePreferences()方法:

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    
    
        refreshAllPreferences(getLogTag());
    }

调用refreshAllPreferences():

    /**
     * Refresh all preference items, including both static prefs from xml, and dynamic items from
     * DashboardCategory.
     */
    private void refreshAllPreferences(final String TAG) {
    
    
        final PreferenceScreen screen = getPreferenceScreen();
        // First remove old preferences.
        if (screen != null) {
    
    
            // Intentionally do not cache PreferenceScreen because it will be recreated later.
            screen.removeAll();
        }

        // Add resource based tiles.
        displayResourceTiles();

        refreshDashboardTiles(TAG);

        final Activity activity = getActivity();
        if (activity != null) {
    
    
            Log.d(TAG, "All preferences added, reporting fully drawn");
            activity.reportFullyDrawn();
        }

        updatePreferenceVisibility(mPreferenceControllers);
    }

可以看到此方法主要是用来加载显示的preference items,主要分为两部分,一个是静态xml定义的prefs(调用displayResourceTiles()方法),另一部分是从DashboardCategory动态加载(调用refreshDashboardTiles(TAG)方法,其中TAG为 “TopLevelSettings”)。

displayResourceTiles()

此方法主要是从xml资源文件中加载显示prefs:

    /**
     * Displays resource based tiles.
     */
    private void displayResourceTiles() {
    
    
        final int resId = getPreferenceScreenResId();
        if (resId <= 0) {
    
    
            return;
        }
        addPreferencesFromResource(resId);
        final PreferenceScreen screen = getPreferenceScreen();
        screen.setOnExpandButtonClickListener(this);
        mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach(
                controller -> controller.displayPreference(screen));
    }

首先调用getPreferenceScreenResId()方法获取所要加载的xml的ID:

    @Override
    protected abstract int getPreferenceScreenResId();

调用子类TopLevelSettings.java的getPreferenceScreenResId()方法:

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

可以看到Settings主界面加载的xml文件是top_level_settings,其内主要配置的是一些Preference菜单项如网络和互联网、已连接的设备、应用和通知、电池等等。以网络和互联网菜单项为例,xml配置如下:

    <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"/>

1、key定义此preference的唯一性ID;
2、title定义标题,此字串显示网络和互联网;
3、summary,此显示WLAN、移动网络、流量使用和热点;
4、icon,定义图标;
5、order,加载显示优先级,order为负时,绝对值越高,界面显示越靠前;order为正时,值越高,显示越靠后;
6、fragment,定义点击此preference所跳转的fragment界面;
7、controller,控制管理类。
相关属性配置后,在机器设备上显示的实际效果如下:
在这里插入图片描述
再回到displayResourceTiles()中,继续来看:

addPreferencesFromResource(resId);

此主要是调用androidX Preference的addPreferencesFromResource()方法,由于androidX无源码不详细展开。此方法主要是将preferenceScreen下所有Preference添加到ArrayList中,然后再根据此集合构建生成PreferenceGroupAdapter,最后将此adapter设置到listview中,完成数据绑定,从而完成界面加载。
继续看displayResourceTiles()余下逻辑:

        final PreferenceScreen screen = getPreferenceScreen();
        screen.setOnExpandButtonClickListener(this);
        mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach(
                controller -> controller.displayPreference(screen));

先来看mPreferenceControllers是什么:

    private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers =
            new ArrayMap<>();
    protected void addPreferenceController(AbstractPreferenceController controller) {
    
    
        if (mPreferenceControllers.get(controller.getClass()) == null) {
    
    
            mPreferenceControllers.put(controller.getClass(), new ArrayList<>());
        }
        mPreferenceControllers.get(controller.getClass()).add(controller);
    }

可以看到主要是在addPreferenceController()方法里面去完成赋值的。而调用此方法主要是在onAttach()中:

        final List<AbstractPreferenceController> controllers = new ArrayList<>();
        // Load preference controllers from code
        final List<AbstractPreferenceController> controllersFromCode =
                createPreferenceControllers(context);
        // Load preference controllers from xml definition
        final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
                .getPreferenceControllersFromXml(context, getPreferenceScreenResId());
        // Filter xml-based controllers in case a similar controller is created from code already.
        final List<BasePreferenceController> uniqueControllerFromXml =
                PreferenceControllerListHelper.filterControllers(
                        controllersFromXml, controllersFromCode);

        // Add unique controllers to list.
        if (controllersFromCode != null) {
    
    
            controllers.addAll(controllersFromCode);
        }
        controllers.addAll(uniqueControllerFromXml);

        // And wire up with lifecycle.
        final Lifecycle lifecycle = getSettingsLifecycle();
        uniqueControllerFromXml
                .stream()
                .filter(controller -> controller instanceof LifecycleObserver)
                .forEach(
                        controller -> lifecycle.addObserver((LifecycleObserver) controller));

        mPlaceholderPreferenceController =
                new DashboardTilePlaceholderPreferenceController(context);
        controllers.add(mPlaceholderPreferenceController);
        for (AbstractPreferenceController controller : controllers) {
    
    
            addPreferenceController(controller);
        }

1、定义集合controllers;
2、从代码中加载preference controllers,调用createPreferenceControllers()方法:

    /**
     * Get a list of {@link AbstractPreferenceController} for this fragment.
     */
    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
    
    
        return null;
    }

抽象方法,具体实现是在其子类中,上面分析可知应是子类TopLevelSettings.java实现,由于TopLevelSettings未实现此方法,故此返回null。
3、从xml定义中加载preference controllers,调用:

        final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
                .getPreferenceControllersFromXml(context, getPreferenceScreenResId());

此时getPreferenceScreenResId()根据上面分析,加载的xml应是top_level_settings.xml,调用getPreferenceControllersFromXml()方法:

    /**
     * Instantiates a list of controller based on xml definition.
     */
    @NonNull
    public static List<BasePreferenceController> getPreferenceControllersFromXml(Context context,
            @XmlRes int xmlResId) {
    
    
        final List<BasePreferenceController> controllers = new ArrayList<>();
        List<Bundle> preferenceMetadata;
        try {
    
    
            preferenceMetadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId,
                    MetadataFlag.FLAG_NEED_KEY | MetadataFlag.FLAG_NEED_PREF_CONTROLLER
                            | MetadataFlag.FLAG_INCLUDE_PREF_SCREEN);
        } catch (IOException | XmlPullParserException e) {
    
    
            Log.e(TAG, "Failed to parse preference xml for getting controllers", e);
            return controllers;
        }

        for (Bundle metadata : preferenceMetadata) {
    
    
            final String controllerName = metadata.getString(METADATA_CONTROLLER);
            if (TextUtils.isEmpty(controllerName)) {
    
    
                continue;
            }
            BasePreferenceController controller;
            try {
    
    
                controller = BasePreferenceController.createInstance(context, controllerName);
            } catch (IllegalStateException e) {
    
    
                Log.d(TAG, "Could not find Context-only controller for pref: " + controllerName);
                final String key = metadata.getString(METADATA_KEY);
                if (TextUtils.isEmpty(key)) {
    
    
                    Log.w(TAG, "Controller requires key but it's not defined in xml: "
                            + controllerName);
                    continue;
                }
                try {
    
    
                    controller = BasePreferenceController.createInstance(context, controllerName,
                            key);
                } catch (IllegalStateException e2) {
    
    
                    Log.w(TAG, "Cannot instantiate controller from reflection: " + controllerName);
                    continue;
                }
            }
            controllers.add(controller);
        }
        return controllers;
    }
  1. 主要读取xml中配置的每个preference的METADATA_CONTROLLER即(“settings:controller”)属性,以上述网络和互联网菜单项为例,读取的即为"com.android.settings.network.TopLevelNetworkEntryPreferenceController";
  2. 首先根据此去调用BasePreferenceController.java的createInstance方法,即调用TopLevelNetworkEntryPreferenceController.java的带一个参数的构造方法:
    /**
     * Instantiate a controller as specified controller type.
     * <p/>
     * This is done through reflection. Do not use this method unless you know what you are doing.
     */
    public static BasePreferenceController createInstance(Context context, String controllerName) {
    
    
        try {
    
    
            final Class<?> clazz = Class.forName(controllerName);
            final Constructor<?> preferenceConstructor = clazz.getConstructor(Context.class);
            final Object[] params = new Object[]{
    
    context};
            return (BasePreferenceController) preferenceConstructor.newInstance(params);
        } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
    
    
            throw new IllegalStateException(
                    "Invalid preference controller: " + controllerName, e);
        }
    }

而TopLevelNetworkEntryPreferenceController.java中只包含了一个带两个参数的构造函数,故执行此方法应会抛出异常。

  1. 从而执行异常内语句,首先会再去读取xml中配置的每个preference的METADATA_KEY即(android:key)属性,同样的再据此去调用TopLevelNetworkEntryPreferenceController.java的构造函数:
    public TopLevelNetworkEntryPreferenceController(Context context, String preferenceKey) {
    
    
        super(context, preferenceKey);
        mMobileNetworkPreferenceController = new MobileNetworkPreferenceController(mContext);
        mTetherPreferenceController = new TetherPreferenceController(
                mContext, null /* lifecycle */);
        mWifiPreferenceController = new WifiMasterSwitchPreferenceController(
                mContext, null /* metrics */);
    }

此时preferenceKey即为xml中配置的android:key属性的值,为"top_level_network"。

  1. 调用父类BasePreferenceController.java的构造方法,初始化其他变量完成构造:
    public BasePreferenceController(Context context, String preferenceKey) {
    
    
        super(context);
        mPreferenceKey = preferenceKey;
        if (TextUtils.isEmpty(mPreferenceKey)) {
    
    
            throw new IllegalArgumentException("Preference key must be set");
        }
    }

赋值mPreferenceKey;
controller构造方法的相关堆栈调用如下:
在这里插入图片描述

综上所述,故getPreferenceControllersFromXml()方法主要是获取xml中每个preference定义的“settings:controller”属性配置的controller name,通过此name去构造相应的controller类,将其添加到集合中并返回。

4、过滤重复定义的controller等,赋值填充mPreferenceControllers。

故mPreferenceControllers主要是各种控制管理类的集合,包含xml中配置的每个preference的“settings:controller”属性和代码中通过createPreferenceControllers()方法构建的。

再回到displayResourceTiles()方法:

mPreferenceControllers.values().stream().flatMap(Collection::stream).forEach(
                controller -> controller.displayPreference(screen));

此语句主要就是调用各个controller的displayPreference()方法。
依旧以网络和互联网菜单项为例,xml中配置的controller为"com.android.settings.network.TopLevelNetworkEntryPreferenceController",查看TopLevelNetworkEntryPreferenceController.java发现,其内并未实现displayPreference()方法,查看继承关系:

public class TopLevelNetworkEntryPreferenceController extends BasePreferenceController

查看BasePreferenceController.java的displayPreference()方法:

    /**
     * Displays preference in this controller.
     */
    @Override
    public void displayPreference(PreferenceScreen screen) {
    
    
        super.displayPreference(screen);
        if (getAvailabilityStatus() == DISABLED_DEPENDENT_SETTING) {
    
    
            // Disable preference if it depends on another setting.
            final Preference preference = screen.findPreference(getPreferenceKey());
            if (preference != null) {
    
    
                preference.setEnabled(false);
            }
        }
    }

1、继续先调用父类的displayPreference()方法,而继承关系如下:

public abstract class BasePreferenceController extends AbstractPreferenceController implements
        Sliceable 

AbstractPreferenceController.java的displayPreference()方法:

    /**
     * Displays preference in this controller.
     */
    public void displayPreference(PreferenceScreen screen) {
    
    
        final String prefKey = getPreferenceKey();
        if (TextUtils.isEmpty(prefKey)) {
    
    
            Log.w(TAG, "Skipping displayPreference because key is empty:" + getClass().getName());
            return;
        }
        if (isAvailable()) {
    
    
            setVisible(screen, prefKey, true /* visible */);
            if (this instanceof Preference.OnPreferenceChangeListener) {
    
    
                final Preference preference = screen.findPreference(prefKey);
                preference.setOnPreferenceChangeListener(
                        (Preference.OnPreferenceChangeListener) this);
            }
        } else {
    
    
            setVisible(screen, prefKey, false /* visible */);
        }
    }
  1. getPreferenceKey()获取preference的key:
    /**
     * Returns the key for this preference.
     */
    public abstract String getPreferenceKey();

BasePreferenceController.java的getPreferenceKey()方法:

    @Override
    public String getPreferenceKey() {
    
    
        return mPreferenceKey;
    }

而据上面分析到mPreferenceKey实质上即为xml中每个preference配置的android:key属性的值,即此处应为"top_level_network"。

  1. isAvailable();判断此preference是否可用即是否应该被显示。如果返回true,则被显示出来,反之则不被显示:
    /**
     * Returns true if preference is available (should be displayed)
     */
    public abstract boolean isAvailable();

抽象方法,继续看子类BasePreferenceController.java的实现:

    /**
     * @return {@code true} when the controller can be changed on the device.
     *
     * <p>
     * Will return true for {@link #AVAILABLE} and {@link #DISABLED_DEPENDENT_SETTING}.
     * <p>
     * When the availability status returned by {@link #getAvailabilityStatus()} is
     * {@link #DISABLED_DEPENDENT_SETTING}, then the setting will be disabled by default in the
     * DashboardFragment, and it is up to the {@link BasePreferenceController} to enable the
     * preference at the right time.
     *
     * TODO (mfritze) Build a dependency mechanism to allow a controller to easily define the
     * dependent setting.
     */
    @Override
    public final boolean isAvailable() {
    
    
        final int availabilityStatus = getAvailabilityStatus();
        return (availabilityStatus == AVAILABLE
                || availabilityStatus == AVAILABLE_UNSEARCHABLE
                || availabilityStatus == DISABLED_DEPENDENT_SETTING);
    }

调用getAvailabilityStatus()方法:

    /**
     * @return {@AvailabilityStatus} for the Setting. This status is used to determine if the
     * Setting should be shown or disabled in Settings. Further, it can be used to produce
     * appropriate error / warning Slice in the case of unavailability.
     * </p>
     * The status is used for the convenience methods: {@link #isAvailable()},
     * {@link #isSupported()}
     */
    @AvailabilityStatus
    public abstract int getAvailabilityStatus();

抽象方法,按照上述举例,继续查看子类TopLevelNetworkEntryPreferenceController.java的getAvailabilityStatus()方法:

    @Override
    public int getAvailabilityStatus() {
    
    
        return Utils.isDemoUser(mContext) ? UNSUPPORTED_ON_DEVICE : AVAILABLE_UNSEARCHABLE;
    }
  1. 调用setVisible()方法设置是否可被显示:
setVisible(screen, prefKey, true /* visible */);
    /**
     * Show/hide a preference.
     */
    protected final void setVisible(PreferenceGroup group, String key, boolean isVisible) {
    
    
        final Preference pref = group.findPreference(key);
        if (pref != null) {
    
    
            pref.setVisible(isVisible);
        }
    }
  1. 判断controller是否实现了Preference.OnPreferenceChangeListener接口,是,则设置监听:
            if (this instanceof Preference.OnPreferenceChangeListener) {
    
    
                final Preference preference = screen.findPreference(prefKey);
                preference.setOnPreferenceChangeListener(
                        (Preference.OnPreferenceChangeListener) this);
            }

综上,如果希望preference不被显示在界面上,可以通过实现相关preference的controller的getAvailabilityStatus()方法,使此方法的返回值不为AVAILABLE、AVAILABLE_UNSEARCHABLE、DISABLED_DEPENDENT_SETTING即可。

2、继续看查看BasePreferenceController.java的displayPreference()方法的剩余语句:

        if (getAvailabilityStatus() == DISABLED_DEPENDENT_SETTING) {
    
    
            // Disable preference if it depends on another setting.
            final Preference preference = screen.findPreference(getPreferenceKey());
            if (preference != null) {
    
    
                preference.setEnabled(false);
            }
        }

根据子类controller实现的getAvailabilityStatus()方法的返回值判断是否需要将此preference置为不可点击。

至此,DashboardFragment.java中displayResourceTiles()方法分析完成。

总结:

1、Settings的主Activity实质实现是在SettingsHomepageActivity.java内;
2、Settings的主界面设置item的显示是在fragment上,fragment为TopLevelSettings.java,加载显示的布局为top_level_settings.xml;
3、Settings主界面设置项item的加载显示主要分为两部分,一部分是xml定义的静态加载,xml为top_level_settings.xml;一部分是DashboardCategory来获取动态加载,此部分下篇分析;
4、每个设置项item均为一个preference,通过xml定义加载时,必须要有一个controller,可以是在xml中定义"settings:controller"属性声明,名称必须与类的包名路径相同;也可直接在相关fragment中实现createPreferenceControllers()方法去调用构造相关controller。此二者存其一即可。
5、xml中配置preference时,必须定义”android:key“属性;
6、需要隐藏不显示某个设置项时,一是可以直接在xml中注释其定义;二是可以在相关设置项preference的controller类中实现getAvailabilityStatus()方法,使此方法的返回值不为AVAILABLE、AVAILABLE_UNSEARCHABLE、DISABLED_DEPENDENT_SETTING即可;
7、如果需要某个设置项不可点击,一是可以直接调用setEnabled():

            final Preference preference = screen.findPreference(getPreferenceKey());
            if (preference != null) {
    
    
                preference.setEnabled(false);
            }

二是可以在相关设置项preference的controller类中实现getAvailabilityStatus()方法,使此方法的返回值为DISABLED_DEPENDENT_SETTING即可。

猜你喜欢

转载自blog.csdn.net/Otaku_627/article/details/108618647