如何在Android中使用NavController“将[设置]层次结构拆分为多个屏幕”?

问题描述 投票:0回答:2

所以我正在创建我的第一个 Android 应用程序,并且我已经在使用 Jetpack Navigation。我还使用自己的自定义工具栏来实现我自己的自定义布局。但是,我想利用内置的 Jetpack 设置屏幕,即 AndroidX 首选项库 (https://developer.android.com/develop/ui/views/components/settings)。

因为我不知道在这个项目结束时我的设置会有多大(不要评判我),我希望能够将我的设置拆分到多个屏幕上,正如这里所说的(https:// developer.android.com/develop/ui/views/components/settings/organize-your-settings)。

但是文档利用片段工厂并强调从 Activity 实现 onPreferenceStartFragment()。然而,我的应用程序通过 Jetpack Navigation 将用户带到“设置”屏幕,因此它已经由 Fragment 而不是 Activity 托管,并且 NavControllers/NavigationGraphs 到目前为止使用起来非常简单且轻松。

那么我可以使用 NavController 和 NavigationGraph 实现“将层次结构拆分为多个屏幕”吗?

(我要发布一个答案,这是我尝试过的,到目前为止有效)

android settings preference multiple-screens navcontroller
2个回答
1
投票

正如所承诺的那样。我就是这样做的。我发帖是因为互联网搜索没有发现任何人已经解决了这个问题,而且自从我弄清楚后,我认为这可能会对未来的其他人有所帮助。

另外,我认为这对于经验丰富的 Android 开发者来说可能是显而易见的,但我是新手,所以我不确定什么时候可以正常工作。

所以我已经在使用来自位于我的单个活动的

mainNavHostFragment
文件中的
MainActivity.Java
的导航。

使用“导航”所需输入的内容比使用“片段事务”和“片段工厂”要好得多,所以我想出了这一点,这样我就可以继续使用导航并避免使用 o.g.片段操作技术。

现在,当用户单击我提供的“设置”图标时,首先

mainNavHostFragment
会将用户导航到
HolderOfSettingsFragment.java
类。

在那个

HolderOfSettingsFragment.java
课程中,我有第二个
NavHostFragment
,名为
settingsNavHostFragment
,它是...

  1. 使用
    getChildFragmentManager
    而不是
    getSupportFragmentManager
  2. 进行初始化
  3. 并初始化为其布局中的
    androidx.fragment.app.FragmentContainerView

按照通常的方式,我有一个从

settingsNavController
获得的
settingsNavHostFragment

FragmentContainerView
使用名为
navGraph
sub_navigation_from_settings
,它与我的 MainActivity 使用的不同。
sub_navigation_from_settings
中的起始片段是我的
root_preferences
,它对我的
SubSettingsExampleFragment
有一个 [导航] 操作。

根据https://developer.android.com/develop/ui/views/components/settings的文档,要从根首选项屏幕到附加屏幕建立“链接”,您所需要做的就是列出一个带有

<Preference/>
条目的
app:title
标签,该条目将与“链接”屏幕中的
<PreferenceCategory>
以及指示要跳转到的片段类的
app:fragment
相匹配。

偏好片段本身实际上非常无聊。它们实际上只是一个扩展

PreferenceFragmentCompat
的类,它实现
onCreatePreferences
,其单个操作是将
setPreferencesFromResource
应用于位于 XML 目录中的适当首选项资源。

根据相同的文档,为了响应

root_prefernces
中的“链接”点击,您需要实现
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
。文档说要在 Activity 中执行此操作,但我在
HolderOfSettingsFragment
中执行此操作,并且效果很好。

此外,实现

PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
接口意味着实现
onPreferenceStartFragment
,它是通过
caller
和 'pref` 调用的。所以我们就这么做了。

为了让

settingsNavHostFragment
处理事情,我们只需检查
pref.getTitle
是否匹配给定的标题,如果匹配,我们使用
settingsNavController
使用
sub_navigation_from_settings
中的操作 id 导航到适当的“首选项片段”图表。

它对我来说工作得很好。耶!

这可能会因为我不知道的事情导致冲突而行不通,但在那之前我很高兴它能起作用。

我希望这对我以外的人有帮助。

如果您想直接查看的话,这里是所有代码。

这是持有者java类:

package com.example.app;

import ...

import com.example.app.databinding.FragmentSettingsHolderBinding;


public class HolderOfSettingsFragment extends Fragment implements
        PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {

    public static final String TAG = "HolderOfSettingsFragment-";
    FragmentSettingsHolderBinding bindingOfThis;
    NavController settingsNavController;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        bindingOfThis = FragmentSettingsHolderBinding.inflate(inflater, container, false);
        return bindingOfThis.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        NavHostFragment settingsNavHostFragment = (NavHostFragment) getChildFragmentManager().findFragmentById(R.id.settingsHolderFragContainer);
        settingsNavController = settingsNavHostFragment.getNavController();

        bindingOfThis.imgReturnIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((AppCompatActivity) getActivity()).onBackPressed();
            }
        });

    }
    @Override
    public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller, @NonNull Preference pref) {
        String stringToCompare = (String) pref.getTitle();
        if ( stringToCompare.equalsIgnoreCase("Other Settings") ) {
            settingsNavController.navigate(R.id.action_settingsFragment_to_subSettingsExampleFragment);
        }
        return true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        bindingOfThis = null;
    }
}

它具有以下布局:

请注意,Android Studio 编辑器不会渲染此布局,我相信这是因为设置资源是存储在 XML 资源目录中的纯 XML。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".HolderOfSettingsFragment">

    <ImageView
        android:id="@+id/imgReturnIcon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="12dp"
        android:src="?attr/actionModeCloseDrawable"
        app:layout_constraintEnd_toStartOf="@+id/txtSettingsTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txtSettingsTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="12dp"
        android:text="Settings"
        android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionMode.Title"
        app:layout_constraintBottom_toBottomOf="@+id/imgReturnIcon"
        app:layout_constraintStart_toEndOf="@+id/imgReturnIcon"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/settingsHolderFragContainer"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toBottomOf="@id/imgReturnIcon"
        android:layout_marginTop="40dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/sub_navigation_from_settings"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

这是第二个导航图:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/sub_navigation_from_settings"
    app:startDestination="@id/settingsFragment">

    <fragment
        android:id="@+id/settingsFragment"
        android:name="com.example.app.SettingsFragment"
        android:label="SettingsFragment" >
        <action
            android:id="@+id/action_settingsFragment_to_subSettingsExampleFragment"
            app:destination="@id/subSettingsExampleFragment" />
    </fragment>
    <fragment
        android:id="@+id/subSettingsExampleFragment"
        android:name="com.example.app.SubSettingsExampleFragment"
        android:label="SubSettingsExampleFragment" />
</navigation>

这是根设置 XML:

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

    <PreferenceCategory app:title="Current User">

        <SwitchPreferenceCompat
            app:key="current_user_hide_tb_labels"
            app:title="Hide labels underneath Toolbar Icons?"
            app:defaultValue="false"/>

    </PreferenceCategory>

    <Preference
        app:title="Other Settings"
        app:summary="Other Settings Example"
        app:fragment="com.example.app.SubSettingsExampleFragment" /> 

最后是单独的示例设置屏幕的 xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
    <PreferenceCategory app:title="Other Settings">

        <SwitchPreferenceCompat
            app:key="do_you_like"
            app:title="Do you like me?"
            app:defaultValue="false"/>

    </PreferenceCategory>
</PreferenceScreen>

0
投票

打开 nav_graph XML 并为您想要的每个额外屏幕添加额外的设置片段。

然后在根设置 XML 中添加标题...

<Preference
    app:key="display_settings"
    app:icon="@drawable/display_settings_icon"
    app:title="@string/display_settings" />

<Preference
    app:key="notification_settings"
    app:icon="@drawable/notification_settings_icon"
    app:title="@string/notification_settings" />

现在在根设置片段中添加首选项标题键的侦听器...

val displayPreferences: Preference? = findPreference("display_settings")

    displayPreferences!!.onPreferenceClickListener =
        Preference.OnPreferenceClickListener {
            // must match name given in nav_graph XML
            navController.navigate(R.id.nav_display_settings)
            true
        }

val notificationPreferences: Preference? = findPreference("notification_settings")

    notificationPreferences!!.onPreferenceClickListener =
        Preference.OnPreferenceClickListener {
            // must match name given in nav_graph XML
            navController.navigate(R.id.nav_notification_settings)
            true
        }

然后将所有相关代码从根设置片段和 XML 移动到您创建的新片段和 XML 文件。

当您单击根设置屏幕中的标题时,它们现在将打开相关片段。

希望对某人有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.