Как «Разделить иерархию [настроек] на несколько экранов» с помощью NavController в Android?

Итак, я создаю свое первое приложение для Android и уже использую Jetpack Navigation. Я также реализую свои собственные макеты с собственными панелями инструментов. Однако я хотел воспользоваться встроенным экраном настроек Jetpack, то есть библиотекой предпочтений AndroidX (https://developer.android.com/develop/ui/views/comComponents/settings).

Поскольку я не знаю, насколько большими станут мои настройки к концу этого проекта (не судите меня), я хотел иметь возможность разделить свои настройки на несколько экранов, как сказано здесь ( https://developer .android.com/develop/ui/views/comComponents/settings/organize-your-settings).

Но в документации используются фабрики фрагментов и подчеркивается реализация onPreferenceStartFragment() из действия. Тем не менее, мое приложение выводит пользователей на экран настроек через Jetpack Navigation, поэтому оно уже размещено во фрагменте, а не в Activity, а NavControllers/NavigationGraphs до сих пор очень просты и безболезненны в использовании.

Могу ли я реализовать «Разделение вашей иерархии на несколько экранов» с помощью NavController и NavigationGraph?

(Я собираюсь опубликовать ответ, это то, что я пробовал, и пока работает)

1 ответ

Как и было обещано. Вот как я это сделал. Я публикую сообщение, потому что поиск в Интернете не нашел никого, кто бы уже ответил на этот вопрос, и, поскольку я понял это, я подумал, что это может быть полезно другим в будущем.

Кроме того, я думаю, что это, вероятно, очевидно для более опытных разработчиков Android, но я новичок, поэтому не уверен, когда все будет работать.

Итак, я уже использую навигацию из приложения, расположенного в моем отдельном действии.MainActivity.Javaфайл.

И то, что вам нужно ввести, чтобы использовать «Навигацию», намного удобнее, чем использование «Транзакций фрагментов» и «Фабрик фрагментов», поэтому я понял это, чтобы продолжать использовать навигацию и избегать использования методов манипулирования фрагментами.

Теперь, когда пользователи нажимают значок «Настройки», я им предоставляю это в первую очередь.mainNavHostFragmentнаправляет пользователя в класс.

В этомHolderOfSettingsFragment.javaкласс, у меня есть второйNavHostFragmentназванный, который...

  1. инициализируется с использованиемgetChildFragmentManagerвместоgetSupportFragmentManager
  2. и инициализируетсяandroidx.fragment.app.FragmentContainerViewв его макете.

И обычным способом я получил файл .

ЧтоFragmentContainerViewиспользуетnavGraphназванный, который отличается от того, который использует моя MainActivity. Начальный фрагмент - мойroot_preferencesи у него есть [навигационное] действие для моегоSubSettingsExampleFragment.

Согласно документации по адресу https://developer.android.com/develop/ui/views/comComponents/settings , все, что вам нужно сделать, чтобы создать «ссылку» с экрана корневых предпочтений на дополнительный экран, — это указать<Preference/>тег сapp:titleзапись, которая будет соответствовать<PreferenceCategory>на «связанном» экране, а такжеapp:fragmentуказывающий класс фрагмента, к которому нужно перейти.

Сами фрагменты предпочтений на самом деле довольно скучны. Это буквально просто класс, который расширяетPreferenceFragmentCompatкоторый реализуетonCreatePreferencesчьим единственным действием является применениеsetPreferencesFromResourceсоответствующим ресурсам предпочтений, расположенным в вашем каталоге XML.

Согласно той же документации, чтобы реагировать на клики по ссылке вroot_preferncesвам нужно реализовать. В документации сказано сделать это в действии, но я сделал это вHolderOfSettingsFragmentи это работает просто отлично.

Далее, реализацияPreferenceFragmentCompat.OnPreferenceStartFragmentCallbackинтерфейс означает реализациюonPreferenceStartFragmentкоторый называется как сcallerи «предпочтительный». Итак, мы делаем это.

Чтобы позволитьsettingsNavHostFragmentобрабатывать вещи, которые мы просто проверяем, еслиpref.getTitleсоответствует заданному заголовку, и если это так, мы используемsettingsNavControllerчтобы перейти к соответствующему «Фрагменту предпочтений», используя идентификатор действий вsub_navigation_from_settingsграфик.

У меня он работает нормально. Ура!

Вполне возможно, что в дальнейшем это не сработает из-за чего-то, чего я не знаю, вызывающего конфликт, но до тех пор я рад, что это работает.

Надеюсь, это поможет кому-то кроме меня.

Вот весь код, если вы хотите посмотреть на него напрямую.

Вот класс 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>

Вот второй NavGraph:

      <?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>
Другие вопросы по тегам