如何使用新的 Android-X API 从当前的 PreferenceFragment 打开一个新的 PreferenceFragment? [英] How to open a new PreferenceFragment from current one, using the new Android-X API?

查看:22
本文介绍了如何使用新的 Android-X API 从当前的 PreferenceFragment 打开一个新的 PreferenceFragment?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

在以前版本的支持库中,我们可以使用标题来拥有一个设置的主菜单屏幕,每个屏幕都会打开一个新的设置屏幕(片段).

问题

现在标题不见了(如

由于有多个子设置屏幕,将所有子设置屏幕的所有首选项都放在主设置屏幕的一个 XML 文件中会非常麻烦.

我的尝试

我唯一成功的是使用 PreferenceScreen 来保存应该显示的子屏幕的首选项.

这是一个工作代码(项目可用这里) 这样的事情:

preferences.xml

<首选项屏幕android:key="screen_preference" android:summary="显示另一个屏幕首选项"android:title="屏幕偏好"><复选框首选项android:key="next_screen_checkbox_preference"android:summary="位于下一个屏幕但具有相同层次结构的首选项"android:title="切换首选项"/></PreferenceScreen></PreferenceScreen>

MainActivity.kt

class MainActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {覆盖 fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)supportActionBar!!.setDisplayHomeAsUpEnabled(true)if (savedInstanceState == null)supportFragmentManager.beginTransaction().replace(android.R.id.content, PrefsFragment()).commit()}覆盖乐趣 onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean {val f = PrefsFragment()val args = 捆绑包(1)args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key)f.arguments = argssupportFragmentManager.beginTransaction().replace(android.R.id.content, f).addToBackStack(null).commit()返回真}类 PrefsFragment : PreferenceFragmentCompat() {覆盖 fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {setPreferencesFromResource(R.xml.preferences, rootKey)}}}

但是,正如我所写的,这不是我想要做的.我想要多个扩展 PreferenceFragmentCompat 的类,每个类都有自己的 XML 文件,这些文件将从主文件中打开.

以下是我尝试过(但失败)的方法:

  1. PreferenceScreen 设置一个android:fragment",指向新的片段类,类似于标题.这根本没有做任何事情.

  2. 使用普通的 Preference 并为其设置单击侦听器,这将按照原始代码所示执行片段事务.这导致了崩溃,上面写着类似带有 key screen_preference 的 Preference object is not a PreferenceScreen" 之类的内容.

  3. 试图避免使用 ARG_PREFERENCE_ROOT ,但与 #2 上的崩溃相同.

  4. 按照此处的建议,我尝试返回this 在函数 getCallbackFragment 中,但这根本没有帮助.

问题

是否可以让主设置片段只让用户导航到其他片段,而没有任何其他属于它们的首选项(在 preferences.xml 内)?

怎么样?

解决方案

您在 1) 中尝试的方法是正确的 - 但您不应为此使用 标签.

您的 XML 资源应如下所示:

<偏好应用程序:键=screen_preference"app:summary="显示另一个偏好屏幕"app:title="屏幕偏好"app:fragment="com.example.user.myapplication.MainActivity$PrefsFragment2"/></PreferenceScreen>

此外,如果您使用的 Preference 版本早于 androidx.preference:preference:1.1.0-alpha01,您将需要实现 onPreferenceStartFragment 来处理片段事务.(在 1.1.0 alpha01 中,此方法有一个默认实现,但仍然鼓励您使用自己的实现来自定义任何动画/过渡)

这应该类似于:

覆盖 fun onPreferenceStartFragment(调用者:PreferenceFragmentCompat,偏好:偏好): 布尔 {//实例化新的片段val args = pref.extrasval 片段 = supportFragmentManager.fragmentFactory.instantiate(类加载器,预制片段,参数).申请 {参数 = argssetTargetFragment(调用者,0)}//用新的 Fragment 替换现有的 FragmentsupportFragmentManager.beginTransaction().replace(R.id.settings, 片段).addToBackStack(null).犯罪()返回真}

有关更多信息,您可以查看设置 指南和 AndroidX 首选项示例

<小时>

第一个解决方案的示例,更新后可用此处.

这是它的工作原理(样本可用此处):

MainActivity.kt

class MainActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {覆盖乐趣 onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {//注意:使用新版本的片段依赖(1.1.0及以上)时,不需要整个功能val fragment = Fragment.instantiate(this, pref.fragment, pref.extras)fragment.setTargetFragment(caller, 0)supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment).addToBackStack(null).commit()返回真}覆盖 fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)supportActionBar!!.setDisplayHomeAsUpEnabled(true)if (savedInstanceState == null)supportFragmentManager.beginTransaction().replace(android.R.id.content, PrefsFragment()).commit()}类 PrefsFragment : PreferenceFragmentCompat() {覆盖 fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {setPreferencesFromResource(R.xml.preferences, rootKey)}}类 PrefsFragment2 : PreferenceFragmentCompat() {覆盖 fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {setPreferencesFromResource(R.xml.preferences2, null)}}}

preferences.xml

 <偏好app:fragment="com.example.user.myapplication.MainActivity$PrefsFragment2" app:key="screen_preference" app:summary="显示另一个屏幕首选项"app:title="屏幕偏好"/></PreferenceScreen>

preferences2.xml

<PreferenceCategory android:title="Category"><复选框首选项android:key="next_screen_checkbox_preference" android:summary="AAAA" android:title="切换首选项"/></PreferenceCategory></PreferenceScreen>

gradle 依赖:

实现'androidx.appcompat:appcompat:1.0.2'实现 'androidx.constraintlayout:constraintlayout:1.1.3'实现 'androidx.preference:preference:1.0.0'

Background

On previous versions of support library, we could use headers in order to have a main-menu screen of settings, that each would open a new settings screen (fragment) .

The problem

Now headers are gone (as written here) for some time, and I think it became worse on android-x :

One thing you’ll note isn’t in here is preference headers and you’d be totally right. However, that doesn’t mean a single list of preferences need to span a 10" tablet screen. Instead, your Activity can implement OnPreferenceStartFragmentCallback (link) to handle preferences with an app:fragment attribute or OnPreferenceStartScreenCallback (link) to handle PreferenceScreen preferences. This allows you to construct a ‘header’ style PreferenceFragmentCompat in one pane and use those callbacks to replace a second pane without working in two separate types of XML files.

Thing is, I fail to use these on the new android-x API.

Each fragment has its own preferences XML tree (using setPreferencesFromResource within onCreatePreferences) , but each solution I've come up with has either done nothing, or crashed.

To put it in a visual way, this is what I'm trying to achieve :

Since there are multiple sub settings screens, it would be very messy to have all of the preferences of all of them be put in one XML file of the main settings screen.

What I've tried

Only thing I've succeeded, is to use the PreferenceScreen to hold the preferences of the sub-screen that's supposed to be shown.

Here's a working code (project available here) of such a thing :

preferences.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="Demo">

    <PreferenceScreen
        android:key="screen_preference" android:summary="Shows another screen of preferences"
        android:title="Screen preferenc">

        <CheckBoxPreference
            android:key="next_screen_checkbox_preference"
            android:summary="Preference that is on the next screen but same hierarchy"
            android:title="Toggle preference"/>

    </PreferenceScreen>

</PreferenceScreen>

MainActivity.kt

class MainActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
        if (savedInstanceState == null)
            supportFragmentManager.beginTransaction().replace(android.R.id.content, PrefsFragment()).commit()
    }

    override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean {
        val f = PrefsFragment()
        val args = Bundle(1)
        args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key)
        f.arguments = args
        supportFragmentManager.beginTransaction().replace(android.R.id.content, f).addToBackStack(null).commit()
        return true
    }

    class PrefsFragment : PreferenceFragmentCompat() {
        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            setPreferencesFromResource(R.xml.preferences, rootKey)
        }
    }
}

But, as I wrote, this is not what I'm trying to do. I want to have multiple classes that extend PreferenceFragmentCompat, each with its own XML file, which will be opened from the main one.

Here are the things I've tried (and failed) :

  1. Set a "android:fragment" for the PreferenceScreen, to point to the new fragments classes, similar to headers. This didn't do anything at all.

  2. Use a normal Preference and have click listener for it, that will do the fragment transaction as shown on the original code. This caused a crash that says something like "Preference object with key screen_preference is not a PreferenceScreen" .

  3. Tried to avoid using ARG_PREFERENCE_ROOT , but had same crash as on #2 .

  4. As suggested here, I tried to return this in function getCallbackFragment, but this didn't help at all.

The question

Is it possible to have the main settings fragment just let the user to navigate to the other fragments, while not having any other preferences that belong to them (inside preferences.xml) ?

How?

解决方案

What you tried in 1) was the correct approach - but you should not use <PreferenceScreen> tags for this.

Your XML resource should look like this instead:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <Preference
        app:key="screen_preference" 
        app:summary="Shows another screen of preferences"
        app:title="Screen preference"
        app:fragment="com.example.user.myapplication.MainActivity$PrefsFragment2"/>

</PreferenceScreen>

Also, if you are using a version of Preference older than androidx.preference:preference:1.1.0-alpha01, you will need to implement onPreferenceStartFragment to handle the fragment transaction. (in 1.1.0 alpha01 this method has a default implementation, but you are still encouraged to use your own implementation to customize any animations / transitions)

This should look something like:

override fun onPreferenceStartFragment(
        caller: PreferenceFragmentCompat,
        pref: Preference
): Boolean {
    // Instantiate the new Fragment
    val args = pref.extras
    val fragment = supportFragmentManager.fragmentFactory.instantiate(
            classLoader,
            pref.fragment,
            args
    ).apply {
        arguments = args
        setTargetFragment(caller, 0)
    }
    // Replace the existing Fragment with the new Fragment
    supportFragmentManager.beginTransaction()
            .replace(R.id.settings, fragment)
            .addToBackStack(null)
            .commit()
    return true
}

For more information you can check out the Settings guide and the AndroidX Preference Sample


EDIT: a sample of the first solution, after updating, available here.

Here's how it can work (sample available here) :

MainActivity.kt

class MainActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, pref: Preference): Boolean {
        //Note: this whole function won't be needed when using new version of fragment dependency (1.1.0 and above)
        val fragment = Fragment.instantiate(this, pref.fragment, pref.extras)
        fragment.setTargetFragment(caller, 0)
        supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment).addToBackStack(null).commit()
        return true
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
        if (savedInstanceState == null)
            supportFragmentManager.beginTransaction().replace(android.R.id.content, PrefsFragment()).commit()
    }

    class PrefsFragment : PreferenceFragmentCompat() {
        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            setPreferencesFromResource(R.xml.preferences, rootKey)
        }
    }

    class PrefsFragment2 : PreferenceFragmentCompat() {
        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            setPreferencesFromResource(R.xml.preferences2, null)
        }
    }
}

preferences.xml

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

    <Preference
      app:fragment="com.example.user.myapplication.MainActivity$PrefsFragment2" app:key="screen_preference" app:summary="Shows another screen of preferences"
      app:title="Screen preference"/>

  </PreferenceScreen>

preferences2.xml

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="Demo">

  <PreferenceCategory android:title="Category">
    <CheckBoxPreference
      android:key="next_screen_checkbox_preference" android:summary="AAAA" android:title="Toggle preference"/>
  </PreferenceCategory>

</PreferenceScreen>

gradle dependencies:

implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.0.0'

这篇关于如何使用新的 Android-X API 从当前的 PreferenceFragment 打开一个新的 PreferenceFragment?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆