Android 通过Preference Library实现设置页面

1,881 阅读6分钟

设置页面在每个App中都是必不可少的,用户可以在设置页面中更改App的各种配置,例如开关通知、是否播放背景音等,用户修改的配置信息通常会使用SharedPreferencesDataStore本地保存,以确保用户关闭App后重新进入配置仍然生效。

最近经常会看官方文档,发现官方针对设置页面专门出了一个库Preference Library,该库实现了标准UI以及数据存储功能,可以快速实现设置页面,并且无需额外处理数据存取,下面介绍一下如何使用。

添加依赖库

在项目app module的build.gradle中的dependencies中添加依赖:

dependencies {
    implementation("androidx.preference:preference:1.2.0")
    implementation("androidx.preference:preference-ktx:1.2.0")
}

实现设置页

通过XML配置创建设置页

  1. 在res/xml目录下创建资源文件,根标签为PreferenceScreen,如下:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <PreferenceCategory
        android:key="app_setting"
        android:title="App Setting">

        <SwitchPreferenceCompat
            android:icon="@drawable/notification"
            android:key="notifications_open_status"
            android:title="Enable Notifications"
            app:summary="Turn on notifications to receive application messages" />
    </PreferenceCategory>

    <PreferenceCategory
        android:key="user_categore"
        android:title="Account">

        <SwitchPreferenceCompat
            android:icon="@drawable/login"
            android:key="auto_login_enable"
            android:summary="No need to actively log in"
            android:title="Auto Login" />
    </PreferenceCategory>
</PreferenceScreen>
  1. 自定义SettingFragment继承PreferenceFragmentCompat,重写onCreatePreferences方法, 如下:
class SettingFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        // 使用xml填充页面
        setPreferencesFromResource(R.xml.example_setting, rootKey)
    }
}
  1. Fragment添加到Activity中,如下:
class SettingActivity : AppCompatActivity() {

    private lateinit var binding: LayoutSettingActivityBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.layout_setting_activity)
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.ct_setting_container, SettingFragment())
            .commitAllowingStateLoss()
    }
}

效果如图:

Screenshot_20220923_174629.png

在代码中创建设置页

可以不生成XML文件,直接在代码中生成设置页。

  1. 自定义SettingFragment继承PreferenceFragmentCompat,重写onCreatePreferences方法,并在此方法中通过代码填充页面,如下:
class SettingFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        val context = preferenceManager.context
        val screen = preferenceManager.createPreferenceScreen(context)
        val onlineContactPreference = Preference(context)
        onlineContactPreference.key = "online_customer"
        onlineContactPreference.title = "Online customer"
        onlineContactPreference.summary = "Contact online customer service to solve your problem"
        //设置Preference的点击监听
        onlineContactPreference.setOnPreferenceClickListener {
            requireActivity().runOnUiThread { Toast.makeText(requireContext(), "Click online customer", Toast.LENGTH_SHORT).show() }
            true
        }
        screen.addPreference(onlineContactPreference)
        
        val qaPreferenceCategory = PreferenceCategory(context)
        qaPreferenceCategory.key = "qa"
        qaPreferenceCategory.title = "QA"
        val qaPreference = Preference(context)
        qaPreference.key = "qa1"
        qaPreference.title = "How to open Notification"
        qaPreference.summary = "Open system setting"
        
        //这里要特别注意,如果要对设置进行分组,需要先将PreferenceCategory添加到PreferenceScreen中
        //然后才可以在PreferenceCategory中添加Preference,否做会报错
        screen.addPreference(qaPreferenceCategory)
        qaPreferenceCategory.addPreference(qaPreference)
        
        // 将创建的PreferenceScreen设置到页面中
        preferenceScreen = screen
    }
}
  1. Fragment添加到Activity中,如下:
class SettingActivity : AppCompatActivity() {

    private lateinit var binding: LayoutSettingActivityBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.layout_setting_activity)
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.ct_setting_container, SettingFragment())
            .commitAllowingStateLoss()
    }
}

效果如图:

Screenshot_20220923_174715.png

拆分为多个设置页面

如果设置项很多,需要拆分为不同子页面,整体实现方式与前两部分差不多,区别如下:

  1. xml配置调整如下:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
        <!--设置此Preference跳转的Fragment-->
        <Preference
            android:fragment="xxx.xxx.SubpageFragment"
            ... />
</PreferenceScreen>
  1. 代码中创建调整如下:
class SettingFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        val context = preferenceManager.context
        val screen = preferenceManager.createPreferenceScreen(context)
        val subpagePreference = Preference(context)
        subpagePreference.fragment = SubpageFragment::class.java.canonicalName
        screen.addPreference(onlineContactPreference)
        preferenceScreen = screen
    }
}
  1. Activity 实现PreferenceFragmentCompat.OnPreferenceStartFragmentCallback接口,如下:
class SettingActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback  {
    
    ...
    
    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
        pref.fragment?.let {
            val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, it)
            supportFragmentManager.beginTransaction()
                .replace(R.id.ct_setting_container, fragment)
                .addToBackStack(null)
                .commitAllowingStateLoss()
        }
        return true
    }
}

此部分效果可以在最后的示例中看到。

更改样式

系统默认实现的样式可能不满足我们的需求,可以通过重写style文件来修改系统样式。

这边用PreferenceCategory标题的文字样式来做示范,代码如下:

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="PreferenceCategoryTitleTextStyle" tools:override="true">
        <item name="android:textAppearance">?attr/preferenceCategoryTitleTextAppearance</item>
        <item name="android:textSize">16sp</item>
        <item name="android:fontFamily">@font/noto_serif_bold</item>
        <item name="android:textColor">@color/color_blue_0083ff</item>
    </style>
</resources>

这边与通过XML配置创建设置页中的效果图作对比,可以看见PreferenceCategory标题文字的颜色、大小、字体都改变了:

Screenshot_20220923_175159.png

如果对控件的UI不满意,例如需要调整间距、大小等,可以通过自定义同名的layout文件来修改库中控件的UI。

企业微信截图_a76134a8-c35e-4d42-80ee-3d7e8d7bb0bf.png

注意,要确保自定义layout的命名,以及layout中的控件id均与库中的相同

更改PreferenceDataStore

Preference Library默认通过SharedPreferences来实现数据存储,可以替换为自己想要的方式。需要注意的是,如果更改了PreferenceDataStore,那么打开设置页面时,所有类型的Preference组件都不会被自动赋值。

自定义DataStore继承PreferenceDataStore,重写存取方法,如下:

object ExampleDataStore : PreferenceDataStore() {

    override fun putInt(key: String?, value: Int) {
        ...
    }

    override fun getInt(key: String?, defValue: Int): Int {
        ...
    }

    override fun putLong(key: String?, value: Long) {
        ...
    }

    override fun getLong(key: String?, defValue: Long): Long {
        ...
    }

    override fun putFloat(key: String?, value: Float) {
        ...
    }

    override fun getFloat(key: String?, defValue: Float): Float {
        ...
    }

    override fun putBoolean(key: String?, value: Boolean) {
        ...
    }

    override fun getBoolean(key: String?, defValue: Boolean): Boolean {
        ...
    }

    override fun putString(key: String?, value: String?) {
        ...
    }

    override fun getString(key: String?, defValue: String?): String? {
        ...
    }

    override fun putStringSet(key: String?, values: MutableSet<String>?) {
        ...
    }

    override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String> {
        ...
    }   
}

然后通过PreferenceManagersetPreferenceDataStore方法,设置自定义的DataStore,如下:

class SettingFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.example_setting, rootKey)
        preferenceManager.preferenceDataStore = ExampleDataStore
    }
}

示例

整合之后做了个示例Demo,并演示了一些Preference组件的用法,示例代码如下:

//主Settin页面XML
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <PreferenceCategory
        android:key="app_setting"
        android:title="App Setting">

        <SwitchPreferenceCompat
            android:icon="@drawable/notification"
            android:key="notifications_open_status"
            android:title="Enable Notifications"
            app:summary="Turn on notifications to receive application messages" />

        <Preference
            android:fragment="com.chenyihong.exampledemo.setting.HelperFragment"
            android:key="help"
            android:summary="View FAQ or contact customer service"
            app:title="Help Center" />

        <Preference
            android:key="system_system"
            android:summary="Modify system settings"
            app:title="System Setting" />
    </PreferenceCategory>

    <PreferenceCategory
        android:key="user_categore"
        android:title="Account">

        <Preference
            android:fragment="com.chenyihong.exampledemo.setting.UserInfoFragment"
            android:key="user_info"
            android:summary="Modify and view user information"
            app:title="User Info" />

        <SwitchPreferenceCompat
            android:icon="@drawable/login"
            android:key="auto_login_enable"
            android:summary="No need to actively log in"
            android:title="Auto Login" />

        <SwitchPreferenceCompat
            android:icon="@drawable/google"
            android:key="google_account_bind_status"
            android:summaryOff="Bind Google account for login"
            android:title="Bind Google Account" />

        <SwitchPreferenceCompat
            android:icon="@drawable/facebook"
            android:key="facebook_account_bind_status"
            android:summaryOff="Bind Facebook account for login"
            android:title="Bind Facebook Account" />

        <!--配置Intent用于跳转页面-->
        <Preference
            android:key="goto_user_profile"
            android:summary="View User Agreement page"
            app:title="Agreement">
            <intent
                android:targetClass="com.chenyihong.exampledemo.web.WebViewActivity"
                android:targetPackage="com.chenyihong.exampledemo" />
        </Preference>
    </PreferenceCategory>
</PreferenceScreen>

//用户信息页XML
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <PreferenceCategory
        android:key="user_info"
        android:title="User Info">

        <EditTextPreference
            android:key="user_info_nick_name"
            app:title="Nickname" />

        <EditTextPreference
            android:key="user_info_real_name"
            app:title="Real name" />

        <EditTextPreference
            android:key="user_info_age"
            app:title="Age" />

        <ListPreference
            android:entries="@array/gender"
            android:entryValues="@array/gender"
            android:key="user_info_gender"
            app:title="Gender"
            app:useSimpleSummaryProvider="true" />
    </PreferenceCategory>
</PreferenceScreen>

// 自定义DataStore
object ExampleDataStore : PreferenceDataStore() {

    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "ExamplePreferencesDataStore")

    var lifecycleScope: LifecycleCoroutineScope? = null

    override fun putInt(key: String?, value: Int) {
        lifecycleScope?.launch {
            putIntImpl(key, value)
        }
    }

    override fun getInt(key: String?, defValue: Int): Int {
        var getValue: Int
        runBlocking {
            getValue = getIntImpl(key, defValue)
        }
        return getValue
    }

    override fun putLong(key: String?, value: Long) {
        lifecycleScope?.launch {
            putLongImpl(key, value)
        }
    }

    override fun getLong(key: String?, defValue: Long): Long {
        var getValue: Long
        runBlocking {
            getValue = getLongImpl(key, defValue)
        }
        return getValue
    }

    override fun putFloat(key: String?, value: Float) {
        lifecycleScope?.launch {
            putFloatImpl(key, value)
        }
    }

    override fun getFloat(key: String?, defValue: Float): Float {
        var getValue: Float
        runBlocking {
            getValue = getFloatImpl(key, defValue)
        }
        return getValue
    }

    override fun putBoolean(key: String?, value: Boolean) {
        lifecycleScope?.launch {
            putBooleanImpl(key, value)
        }
    }

    override fun getBoolean(key: String?, defValue: Boolean): Boolean {
        var getValue: Boolean
        runBlocking {
            getValue = getBooleanImpl(key, defValue)
        }
        return getValue
    }

    override fun putString(key: String?, value: String?) {
        lifecycleScope?.launch {
            putStringImpl(key, value)
        }
    }

    override fun getString(key: String?, defValue: String?): String? {
        var getValue: String?
        runBlocking {
            getValue = getStringImpl(key, defValue)
        }
        return getValue
    }

    override fun putStringSet(key: String?, values: MutableSet<String>?) {
        lifecycleScope?.launch {
            putStringSetImpl(key, values)
        }
    }

    override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String> {
        val getValue = mutableSetOf<String>()
        runBlocking {
            getValue.addAll(getStringSetImpl(key, defValues))
        }
        return getValue
    }

    private suspend fun putIntImpl(key: String?, value: Int?) {
        if (key?.isNotEmpty() == true && value != null) {
            val preferencesKey = intPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getIntImpl(key: String?, defaultValue: Int?): Int {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = intPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: 0)
            }?.first() ?: 0
        } else {
            0
        }
    }

    private suspend fun putLongImpl(key: String?, value: Long?) {
        if (key?.isNotEmpty() == true && value != null) {
            val preferencesKey = longPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getLongImpl(key: String?, defaultValue: Long?): Long {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = longPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: 0L)
            }?.first() ?: 0L
        } else {
            0L
        }
    }

    private suspend fun putFloatImpl(key: String?, value: Float?) {
        if (key?.isNotEmpty() == true && value != null) {
            val preferencesKey = floatPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getFloatImpl(key: String?, defaultValue: Float?): Float {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = floatPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: 0f)
            }?.first() ?: 0f
        } else {
            0f
        }
    }

    private suspend fun putBooleanImpl(key: String?, value: Boolean?) {
        if (key?.isNotEmpty() == true && value != null) {
            val preferencesKey = booleanPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getBooleanImpl(key: String?, defaultValue: Boolean?): Boolean {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = booleanPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: false)
            }?.first() ?: false
        } else {
            false
        }
    }

    private suspend fun putStringImpl(key: String?, value: String?) {
        if (key?.isNotEmpty() == true && value?.isNotEmpty() == true) {
            val preferencesKey = stringPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getStringImpl(key: String?, defaultValue: String?): String? {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = stringPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: "")
            }?.first()
        } else {
            ""
        }
    }

    private suspend fun putStringSetImpl(key: String?, value: Set<String>?) {
        if (key?.isNotEmpty() == true && value?.isNotEmpty() == true) {
            val preferencesKey = stringSetPreferencesKey(key)
            ExampleApplication.exampleContext?.run {
                dataStore.edit {
                    it[preferencesKey] = value
                }
            }
        }
    }

    private suspend fun getStringSetImpl(key: String?, defaultValue: Set<String>?): Set<String> {
        return if (key?.isNotEmpty() == true) {
            val preferencesKey = stringSetPreferencesKey(key)
            ExampleApplication.exampleContext?.dataStore?.data?.map {
                it[preferencesKey] ?: (defaultValue ?: setOf())
            }?.first() ?: setOf()
        } else {
            setOf()
        }
    }
}

//SettingActivity
class SettingActivity : BaseGestureDetectorActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {

    private lateinit var binding: LayoutSettingActivityBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.layout_setting_activity)
        ExampleDataStore.lifecycleScope = lifecycleScope
        supportFragmentManager
            .beginTransaction()
            .replace(R.id.ct_setting_container, SettingFragment())
            .commitAllowingStateLoss()
    }

    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
        pref.fragment?.let {
            val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, it)
            supportFragmentManager.beginTransaction()
                .replace(R.id.ct_setting_container, fragment)
                .addToBackStack(null)
                .commitAllowingStateLoss()
        }
        return true
    }
}

//SettingFragment(XML配置)
class SettingFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.example_setting, rootKey)
        preferenceManager.preferenceDataStore = ExampleDataStore
        val notificationOpenStatus = findPreference<SwitchPreferenceCompat>("notifications_open_status")
        val autoLoginStatus = findPreference<SwitchPreferenceCompat>("auto_login_enable")
        val googleAccountStatus = findPreference<SwitchPreferenceCompat>("google_account_bind_status")
        val facebookAccountStatus = findPreference<SwitchPreferenceCompat>("facebook_account_bind_status")
        findPreference<Preference>("system_system")?.setOnPreferenceClickListener {
            try {
                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                val uri: Uri = Uri.fromParts("package", requireActivity().packageName, null)
                intent.data = uri
                startActivity(intent)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            true
        }

        val notificationOpenStatusValue = ExampleDataStore.getBoolean("notifications_open_status", false)
        val autoLoginStatusValue = ExampleDataStore.getBoolean("auto_login_enable", false)
        val googleAccountStatusValue = ExampleDataStore.getBoolean("google_account_bind_status", false)
        val facebookAccountStatusValue = ExampleDataStore.getBoolean("facebook_account_bind_status", false)

        // 修改了DataStore,初始值需要自己处理
        notificationOpenStatus?.isChecked = notificationOpenStatusValue
        autoLoginStatus?.isChecked = autoLoginStatusValue
        googleAccountStatus?.run {
            isChecked = googleAccountStatusValue
            // 设置状态变化监听,返回true,则控件的状态会立刻改变,返回false,则维持原样。
            // 可以在此处理异步事件,先返回false,根据事件处理结果再修改控件的状态。
            onPreferenceChangeListener = OnPreferenceChangeListener { preference, newValue ->
                googleAccountStatus.summaryOn = "xxxxx@gmail.com"
                true
            }
        }
        facebookAccountStatus?.run {
            isChecked = facebookAccountStatusValue
            onPreferenceChangeListener = OnPreferenceChangeListener { preference, newValue ->
                facebookAccountStatus.summaryOn = "xxxxxxfbaccount"
                true
            }
        }
    }
}

//HelperFragmetn(代码创建)
class HelperFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        val context = preferenceManager.context
        val screen = preferenceManager.createPreferenceScreen(context)
        val qaPreferenceCategory = PreferenceCategory(context)
        qaPreferenceCategory.key = "qa"
        qaPreferenceCategory.title = "QA"
        val qa1Preference = Preference(context)
        qa1Preference.key = "qa1"
        qa1Preference.title = "How to open Notification"
        qa1Preference.summary = "Open system setting"
        val qa2Preference = Preference(context)
        qa2Preference.key = "qa2"
        qa2Preference.title = "How to bind google account"
        qa2Preference.summary = "oOpen settings, bind Google login"
        screen.addPreference(qaPreferenceCategory)
        qaPreferenceCategory.addPreference(qa1Preference)
        qaPreferenceCategory.addPreference(qa2Preference)
        val contactPreferenceCategory = PreferenceCategory(context)
        contactPreferenceCategory.key = "contact"
        contactPreferenceCategory.title = "Contact"
        val onlineContactPreference = Preference(context)
        onlineContactPreference.key = "online_customer"
        onlineContactPreference.title = "Online customer"
        onlineContactPreference.summary = "Contact online customer service to solve your problem"
        onlineContactPreference.setOnPreferenceClickListener {
            requireActivity().runOnUiThread { Toast.makeText(requireContext(), "Click online customer", Toast.LENGTH_SHORT).show() }
            true
        }
        val telephoneContactPreference = Preference(context)
        telephoneContactPreference.key = "telephone_customer"
        telephoneContactPreference.title = "Telephone customer"
        telephoneContactPreference.summary = "Contact telephone_contact customer service to solve your problem"
        telephoneContactPreference.setOnPreferenceClickListener {
            requireActivity().runOnUiThread { Toast.makeText(requireContext(), "Click Telephone customer", Toast.LENGTH_SHORT).show() }
            true
        }
        screen.addPreference(contactPreferenceCategory)
        contactPreferenceCategory.addPreference(onlineContactPreference)
        contactPreferenceCategory.addPreference(telephoneContactPreference)
        preferenceScreen = screen
    }
}

//UserInfoFragment(XML创建)
class UserInfoFragment : PreferenceFragmentCompat() {

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        setPreferencesFromResource(R.xml.example_user_info, rootKey)
        // 自定义摘要提供者,可以更改默认值的文案。
        findPreference<EditTextPreference>("user_info_nick_name")?.summaryProvider = Preference.SummaryProvider<EditTextPreference> { preference ->
            preference.text ?: "Please enter a nickname"
        }
        findPreference<EditTextPreference>("user_info_real_name")?.summaryProvider = Preference.SummaryProvider<EditTextPreference> { preference ->
            preference.text ?: "Please enter real name"
        }
        findPreference<EditTextPreference>("user_info_age")?.run {
            summaryProvider = Preference.SummaryProvider<EditTextPreference> { preference ->
                preference.text ?: "Please enter your age"
            }
            // 调整EditTextPreference的输入类型
            setOnBindEditTextListener { it.inputType = InputType.TYPE_CLASS_NUMBER }
        }
    }
}

ExampleDemo github

ExampleDemo gitee

效果如图:

video_2_.gif