Как «Разделить иерархию [настроек] на несколько экранов» с помощью 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
названный, который...
- инициализируется с использованием
getChildFragmentManager
вместоgetSupportFragmentManager
- и инициализируется
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>