代码之家  ›  专栏  ›  技术社区  ›  android developer

如何使用新的Android-xapi从当前的PreferenceFragment打开一个新的PreferenceFragment?

  •  2
  • android developer  · 技术社区  · 6 年前

    背景

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

    问题

    现在标题不见了(如前所述) here )一段时间以来,我认为android-x的情况变得更糟:

    有一件事你会注意到不在这里是首选项标题,你会 完全正确。然而,这并不意味着一个单一的偏好列表 需要跨越10个平板电脑屏幕。相反,您的活动可以实现 OnPreferenceStartFragmentCallback( link OnPreferenceStartScreenCallback( link )处理 首选项屏幕首选项。这允许您构造标头 替换第二个窗格而不使用两种不同类型的XML

    问题是,我没有在新的android-xapi上使用这些。

    每个片段都有自己的首选项XML树(使用 setPreferencesFromResource 在内部 onCreatePreferences ),但我提出的每个解决方案要么毫无效果,要么崩溃。

    从视觉上来说,这就是我想要达到的目标:

    enter image description here

    由于有多个子设置屏幕,所以将所有子设置屏幕的所有首选项放在主设置屏幕的一个XML文件中会非常混乱。

    我试过的

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

    here )关于这样一件事:

    首选项.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>
    

    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)
            }
        }
    }
    

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

    1. 设置一个“android:fragment“为了 PreferenceScreen

    2. 使用一个普通的首选项并让click listener为其执行片段事务,如原始代码所示。这导致了一个类似于“带有键屏幕的首选项对象不是首选项屏幕”的崩溃。

    3. 如建议的那样 here this 在功能上 getCallbackFragment ,但这毫无帮助。

    有没有可能让主设置片段只允许用户导航到其他片段,而没有属于它们的任何其他首选项(内部) preferences.xml

    怎么用?

    2 回复  |  直到 6 年前
        1
  •  3
  •   android developer    6 年前

    你在1)中尝试的方法是正确的,但你不应该使用 <PreferenceScreen>

    您的XML资源应该如下所示:

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

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

    这应该看起来像:

    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
    }
    

    Settings 指导 以及 AndroidX Preference Sample


    here .

    here ) :

    主要活动.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)
            }
        }
    }
    

    首选项.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>
    

    首选项2.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>
    

    渐变相关性:

    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.preference:preference:1.0.0'
    
        2
  •  3
  •   android developer    6 年前

    好吧,我找到了两个可能的,但很奇怪的解决方案。

    解决方案1

    在主设置首选项XML文件中,为每个子 PreferenceScreen Preference 标签。

    首选项.xml

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

    我将的第二个参数传递null setPreferencesFromResource

    以下是代码(项目可用) here ) :

    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 {
            supportFragmentManager.beginTransaction().replace(android.R.id.content, PrefsFragment2()).addToBackStack(null).commit()
            return true
        }
    
        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)
            }
        }
    }
    

    当然,这需要修改,以便您知道要创建和添加哪个片段。。。

    解决方案2

    我用普通的 而不是每个 ,对于每一个,我选择在单击时添加片段(项目可用) here

    首选项.xml

    <Preference
        android:key="screen_preference" android:summary="Shows another screen of preferences"
        android:title="Screen preference"/>
    

    主要活动.kt

    class MainActivity : AppCompatActivity() {
        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)
                setPreferenceToOpenFragmentAsNewPage(findPreference("screen_preference"), PrefsFragment2::class.java)
            }
    
            private fun setPreferenceToOpenFragmentAsNewPage(pref: Preference, java: Class<out PreferenceFragmentCompat>) {
                pref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
                    val fragment = java.newInstance()
                    val args = Bundle(1)
                    args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key)
                    fragment.arguments = args
                    activity!!.supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment).addToBackStack(null).commit()
                    true
                }
            }
        }
    
        class PrefsFragment2 : PreferenceFragmentCompat() {
            override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
                setPreferencesFromResource(R.xml.preferences2, null)
            }
        }
    }
    

    编辑:对第二个解决方案稍加修改就可以让它变得更好:

    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="Demo">
    
        <Preference
            android:fragment="com.example.user.myapplication.MainActivity$PrefsFragment2" android:key="screen_preference"
            android:summary="Shows another screen of preferences" android:title="Screen preference"/>
    
    </PreferenceScreen>
    

    主要活动.kt

    class MainActivity : AppCompatActivity() {
        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)
                setPreferenceToOpenFragmentAsNewPage(findPreference("screen_preference"))
            }
    
            private fun setPreferenceToOpenFragmentAsNewPage(pref: Preference) {
                pref.onPreferenceClickListener = Preference.OnPreferenceClickListener {
                    val clazz = Class.forName(pref.fragment)
                    val fragment: PreferenceFragmentCompat = clazz.newInstance() as PreferenceFragmentCompat
                    val args = Bundle(1)
                    args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key)
                    fragment.arguments = args
                    activity!!.supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment).addToBackStack(null).commit()
                    true
                }
            }
        }
    
        class PrefsFragment2 : PreferenceFragmentCompat() {
            override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
                setPreferencesFromResource(R.xml.preferences2, null)
            }
        }
    
    }
    

    请注意,您需要将此添加到Proguard规则:

    -keepnames public class * extends androidx.preference.PreferenceFragmentCompat
    

    class PrefsFragment : BasePreferenceFragment() {
        override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
            setPreferencesFromResource(R.xml.preferences_headers, rootKey)
            val preferenceScreen = preferenceScreen
            val preferenceCount = preferenceScreen.preferenceCount
            for (i in 0 until preferenceCount) {
                val pref = preferenceScreen.getPreference(i)
                val fragmentClassName = pref.fragment
                if (fragmentClassName.isNullOrEmpty())
                    continue
                pref.setOnPreferenceClickListener {
                    showPreferenceFragment(activity!!, fragmentClassName)
                    true
                }
            }
        }
    }
    
    companion object {
        @JvmStatic
        private fun showPreferenceFragment(activity: FragmentActivity, fragmentClassName: String) {
            val clazz = Class.forName(fragmentClassName)
            val fragment: PreferenceFragmentCompat = clazz.newInstance() as PreferenceFragmentCompat
            val fragmentsCount = activity.supportFragmentManager.fragments.size
            val transaction = activity.supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment)
            if (fragmentsCount > 0)
                transaction.addToBackStack(null)
            transaction.commit()
        }
    }
    

    编辑:似乎第一个解决方案是正确的,但需要改变。核对答案 here . 提供完整样品 here .

        3
  •  3
  •   TwiXter    5 年前

    仅供参考,如果您使用的是导航抽屉+androidx.appcompat,则可以:

    1) 将每个PreferenceScreen子级拆分为preference.xml文件: ie“Pref\u general.xml”将是主要首选项,“Pref\u ServerSettings.xml”包含服务器设置的首选项屏幕子项。 2) 为每个preference.xml创建PreferenceFragmentCompat:

    “预碎片常规”

    在PrefFragmentGeneral.xml文件中,为任何子xml添加首选项,而不是像下面这样的首选项屏幕:

    <Preference
        android:key="pref_serverPref"
        android:summary="@string/settings_serverPrefSum"
        android:title="@string/settings_serverPrefTitle"
        />
    

    “预碎片服务器”

    public class PrefFragmentGeneral extends PreferenceFragmentCompat {
        @Override
        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
            setPreferencesFromResource(R.xml.Pref_general, rootKey);
            //find your preference(s) using the same key
            Preference serverPref=findPreference("pref_serverPref");
            if(serverPref!=null){
                //Assign the click listener to navigate to the fragment using the navigation controller
                serverPref.setOnPreferenceClickListener(preference -> {
                    NavController navController = Navigation.findNavController(getActivity(), R.id.nav_host_fragment);
                    navController.navigate(R.id.nav_PrefFragmentServer);
                    return true;
                });
            }
        }
    //and the PrefFragmentServer 
    public class PrefFragmentServer extends PreferenceFragmentCompat {
        @Override
        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
            setPreferencesFromResource(R.xml.pref_ServerSettings,rootKey);
         }
    }
    

    3) 在导航抽屉中注册所有片段:

    Navigation drawer

    好好享受吧!

    优点:当你导航回来,你回到“一般”偏好,好像你回到一个偏好活动的孩子! 你不会得到一个异常告诉你片段不是FragmentManager的一部分。

    推荐文章