RKAndroid11-系统设置新增开关选项

场景:在Room 开发中

  • 我们在日常开发的应用里面有一些信息、开关 需要在系统设置里面显示
  • 部分业务逻辑开关需要在系统设置里面可以直接控制


需求

系统设置新增开关选项控制页面,比如 在系统设置里面添加一个 应用自启动开关。

在这里插入图片描述
在这里插入图片描述

一、参考资料

MTKAndroid12-13-开机应用自启功能实现
MTK-删除设置首页菜单项
Android 11 Settings源码入门
Android Preference简单介绍
RK-Android11-系统增加一个属性值

核心知识点:

  • 理解Android 基本架构:代码逻辑、业务、UI层面 架构基本理解
  • Settings App 用到的 Preference 这一套:Preference 、PreferenceScreen 、PreferenceFragment 或 PreferenceActivity 等基本的UI元素理解和其子UI元素及属性了解。

二、实现方案

新增文件:

/packages/apps/Settings/src/com/android/settings/cloud/CloudBootParentPreferenceController.java
/packages/apps/Settings/src/com/android/settings/cloud/CloudBootPreferenceController.java
/packages/apps/Settings/src/com/android/settings/cloud/CloudBootSettings.java
/packages/apps/Settings/res/drawable-hdpi/icon_yun_diannao.png
/packages/apps/Settings/res/xml/cloud_boot_settings.xml

修改文件:

/packages/apps/Settings/AndroidManifest.xml
/packages/apps/Settings/res/values-zh-rCN/strings.xml
/packages/apps/Settings/res/xml/system_dashboard_fragment.xml
/packages/apps/Settings/src/com/android/settings/core/gateway/SettingsGateway.java

三、实现思路

这里还是把思路简要说一下


- 系统界面的布局中添加这个item  这里添加:system_dashboard_fragment.xml 
- 基于Settings 的UI架构,在布局中添加UI元素,总要有Controller->CloudBootParentPreferenceController 和 对应的界面 CloudBootSettings 吧,当然这里有一些配置属性 key 、title、order、icon、keywords  等 
- 配置,可以外部访问,其实就是一个校验、网关的作用,源码架构决定。 SettingsGateway
- 

四、基本UI常识介绍

基本概念

Preference 系统基于以下几个核心类:

  • Preference - 所有设置项的基类
  • PreferenceScreen - 设置界面的容器,相当于一个设置页面
  • PreferenceFragment 或 PreferenceActivity - 用于承载 Preference 界面的组件

Preference 类型

CheckBoxPreference

  • 复选框类型的设置项

  • 保存 boolean 值

<CheckBoxPreference
    android:key="wifi_enabled"
    android:title="启用WiFi"
    android:summary="打开或关闭WiFi连接"
    android:defaultValue="true" />

EditTextPreference

  • 可编辑文本的设置项
  • 保存字符串值
<EditTextPreference
    android:key="user_name"
    android:title="用户名"
    android:summary="请输入您的用户名"
    android:dialogTitle="输入用户名"
    android:defaultValue="默认用户" />

SwitchPreference

  • 开关类型的设置项(Android 4.0+)
  • 保存 boolean 值
<SwitchPreference
    android:key="notifications_enabled"
    android:title="通知"
    android:summary="启用或禁用应用通知"
    android:defaultValue="true" />

PreferenceFragment

现代 Android 应用通常使用 PreferenceFragment 来显示设置界面

public class SettingsFragment extends PreferenceFragmentCompat {
    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences, rootKey);
        
        // 可以在这里添加Preference变更监听器
        Preference preference = findPreference("key");
        preference.setOnPreferenceChangeListener((pref, newValue) -> {
            // 处理变更
            return true; // 返回true保存变更
        });
    }
}

暂且只 简单介绍这些,方便理解, Settings 里面的 UI 子元素蛮多的,用到的时候就自然理解了。

五、源码分析

找到需要添加UI元素的Fragment 及在哪个界面添加需求的界面 -SystemDashboardFragment

上面有所介绍,这里先找到对应的页面,当我们进入系统页面,会有这些提示:
在这里插入图片描述
找到这个Fragment最终的目的是找到它的 xml 布局,如下:

    protected int getPreferenceScreenResId() {
        return R.xml.system_dashboard_fragment;
    }

布局中新增UI元素- system_dashboard_fragment

 <Preference
        android:key="reset_dashboard"
        android:title="@string/reset_dashboard_title"
        android:summary="@string/reset_dashboard_summary"
        android:icon="@drawable/ic_restore"
        android:order="-50"
        android:fragment="com.android.settings.system.ResetDashboardFragment"
        settings:controller="com.android.settings.system.ResetPreferenceController"/>

<!-- add start -->
    <Preference
        android:key="cloud_boot_summary"
        android:title="@string/cloud_boot_title"
        android:fragment="com.android.settings.cloud.CloudBootSettings"
        android:order="-110"
        android:icon="@drawable/ic_restore"
        settings:controller="com.android.settings.cloud.CloudBootParentPreferenceController"
        settings:keywords="@string/keywords_cloud_boot"/>
<!-- add end -->

    <Preference
            android:key="pointer_speed_summary"
            android:title="@string/pointer_speed"
            android:fragment="com.android.settings.cloud.PointerSpeedSettings"
            android:order="-110"
            settings:controller="com.android.settings.cloud.PointerSpeedParentPreferenceController"
            settings:keywords="@string/keywords_cloud_boot"/>

核心字段 理解:

字段类型必需说明 示例特点
keyString唯一标识符 “wifi_enabled”用于在 SharedPreferences 中存储和检索值;必须唯一;通过 findPreference(key) 方法可以查找特定 Preference
titleString推荐显示的主标题 “Wi-Fi”显示在 Preference 的主要位置;通常简短明了,直接说明设置项功能;可以是字符串资源或直接文本
orderint可选显示顺序 100数值越小,显示位置越靠上 ;如果不指定,将按照 XML 中的声明顺序显示 ;可用于动态调整 Preference 顺序
iconDrawable可选左侧图标 @drawable/ic_wifi
controlleClass系统级 动态控制逻辑"com.android.settings.wifi.WifiPrefere用于系统设置中的高级控制逻辑;必须是 com.android.settingslib.core.AbstractPreferenceController 的子类;通常用于动态控制 Preference 的可用性/可见性
keywordsString可选搜索关键词 “wireless,network”用于系统设置的搜索功能;多个关键词用逗号分隔;应包含相关术语和同义词
fragmentClass可选跳转的Fragment "com.android.settings.wifi.Wif用于实现设置项的深层导航;必须是全限定类名(包含包名)通常用于打开子设置页面
summaryString可选描述文本“Manage wireless networks”
intent Intent可选替代fragment的跳转方式定义Intent动作

自定义控制器 - CloudBootParentPreferenceController

说白了就是当前 在 系统面板里面显示的内容,这里根据属性 显示不同的 字符串、文本内容呀。

package com.android.settings.cloud;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import android.os.SystemProperties;
/**
 * Parent menu summary of media controls settings
 */
public class CloudBootParentPreferenceController extends BasePreferenceController {

    private String clouddesk;
    public CloudBootParentPreferenceController(Context context, String key) {
        super(context, key);
        clouddesk = SystemProperties.get("persist.sys.fise.packagename","com.ctg.itrdc.clouddesk");
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }

    @Override
    public CharSequence getSummary() {
        int summary;
        if (clouddesk.equals(SystemProperties.get("persist.sys.launcher.packagename", ""))) {
            summary = R.string.cloud_boot_start;
        } else {
            summary = R.string.cloud_boot_stop;
        }
        return mContext.getText(summary);
    }
}

自定义跳转的Fragment - CloudBootSettings


package com.android.settings.cloud;
import android.app.settings.SettingsEnums;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;

/**
 * Media control settings located in the sound menu
 */
@SearchIndexable
public class CloudBootSettings extends DashboardFragment {

    private static final String TAG = "CloudBootSettings";

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

    @Override
    protected String getLogTag() {
        return TAG;
    }

    @Override
    public int getMetricsCategory() {
        return 6;
    }

    public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
            new BaseSearchIndexProvider(R.xml.cloud_boot_settings);
}

这里核心内容还是 xml 相关布局内容 cloud_boot_settings

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

跳转的Fragment 的布局 - cloud_boot_settings

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:title="@string/cloud_boot_title">

    <SwitchPreference
        android:key="cloud_boot_resume_switch"
        android:title="@string/cloud_boot_summary"
        android:summary="@string/cloud_boot_resume_description"
        app:keywords="@string/keywords_cloud_boot"
        app:controller="com.android.settings.cloud.CloudBootPreferenceController"
        app:allowDividerAbove="true" />

</PreferenceScreen>

这里面的 UI子字段属性暂不协助理解,自己可以查询下,见名知意的了,看多了其实很熟悉了的。

控制子界面控制器-CloudBootPreferenceController

 

package com.android.settings.cloud;

import static android.provider.Settings.Secure.MEDIA_CONTROLS_RESUME;

import android.content.Context;
import android.provider.Settings;

import com.android.settings.core.TogglePreferenceController;
import android.os.SystemProperties;
/**
 * Toggle for media controls settings
 */
public class CloudBootPreferenceController extends TogglePreferenceController {

    private String clouddesk;
    public CloudBootPreferenceController(Context context, String key) {
        super(context, key);
        clouddesk = SystemProperties.get("persist.sys.fise.packagename","com.ctg.itrdc.clouddesk");
    }

    @Override
    public boolean isChecked() {
        return clouddesk.equals(SystemProperties.get("persist.sys.launcher.packagename", ""));
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        if (isChecked)
            SystemProperties.set("persist.sys.launcher.packagename", clouddesk);
        else
            SystemProperties.set("persist.sys.launcher.packagename", "");
        return true;
    }

    @Override
    public int getAvailabilityStatus() {
        return AVAILABLE;
    }
}

其实就是一个设置属性 、 默认 判断 CheckBox 状态的 控制器。

相关string字段-strings.xml

 <string name="cloud_boot_title">云电脑自启动</string>
	<string name="cloud_boot_summary">云电脑自启动开关</string>
	<string name="cloud_boot_resume_description">选择云电脑是否开机自启动</string>
	<string name="cloud_boot_stop">开机不自启动</string>
	<string name="cloud_boot_start">开机自启动</string>
	<string name="keywords_cloud_boot">云电脑</string>

路由 -SettingsGateway

/**
     * A list of fragment that can be hosted by SettingsActivity. SettingsActivity will throw a
     * security exception if the fragment it needs to display is not in this list.
     */
    public static final String[] ENTRY_FRAGMENTS = {
            AdvancedConnectedDeviceDashboardFragment.class.getName(),
            CreateShortcut.class.getName(),
            WifiSettings.class.getName(),
           
		    // add start 
            CloudBootSettings.class.getName()
			//add end 
    };

如同 方法注释,一个校验功能,如果不在路由里面配置,这个控制界面Fragment是无法展示出来的,非法。
在这里插入图片描述
在这里插入图片描述

总结

  • 这里就是在Settings App 里面添加一个开关功能 。 这个扩展性很大,比如:自启应用、自启服务 等所有跟开关相关的业务都可以借助这个来实现。
  • 了解一下开关在设置里面如何添加,进而了解一下Settigns 架构、逻辑、业务
  • 结合属性来存储开关基本值的一个业务逻辑
  • 这个开关逻辑的业务实现了,你会发现 列表、展示 相关的业务,在Settigns 里面的需求也就一样了,没多少难度。 Settings 本身没那么复杂,接触多了发现不难。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值