As promised. This is how I did it. I'm posting because an internet search dindn't find anyone already address the question and since I figured it out I thought it might be helpful to others in the future.
Also, I think its probably obvious to more seasoned Android Developers but I'm new so I'm unsure when things work.
So I'm already using Navigation from a mainNavHostFragment
located in my single activity's MainActivity.Java
file.
And what you have to type to use "Navigation" is so much nicer than using "Fragment Transactions" and "Fragment Factories" so I figured this out so that I could keep using Navigation and avoid using the o.g. Fragment manipulation techniques.
Now when users click the "Settings" icon I provide them, that first mainNavHostFragment
navigates the user to a HolderOfSettingsFragment.java
class.
In that HolderOfSettingsFragment.java
class I have a second NavHostFragment
named settingsNavHostFragment
which is ...
- initialized using
getChildFragmentManager
instead of getSupportFragmentManager
- and initialized to the
androidx.fragment.app.FragmentContainerView
in its layout.
And in the usual fashion I have a settingsNavController
gotten from the settingsNavHostFragment
.
That FragmentContainerView
uses a navGraph
named sub_navigation_from_settings
which is different than the one my MainActivity uses. The starting fragment in sub_navigation_from_settings
is my root_preferences
and its got a [navigation] action to my SubSettingsExampleFragment
.
According to the documentation at https://developer.android.com/develop/ui/views/components/settings all you have to do to make a "link" from the root preference screen to an additional screen is list a <Preference/>
tag with an app:title
entry that will match the <PreferenceCategory>
in the "linked" screen as well as app:fragment
indicating the fragment class to jump to.
The Preference Fragments themselves are actually pretty boring. They are literally just a class that extends PreferenceFragmentCompat
which implements onCreatePreferences
whose single action is to apply setPreferencesFromResource
to appropriate preference resources located in your XML directory.
According to the same documentation in order to respond to "link" clicks in the root_prefernces
you need to implement PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
. The documentation says to do this in the Activity but I did it in HolderOfSettingsFragment
and it works just fine.
Further, implementing the PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
interface means implementing the onPreferenceStartFragment
which is called with both a caller
and 'pref`. So we do that.
To let the settingsNavHostFragment
handle things we just check if pref.getTitle
matches a given title and if it does we use the settingsNavController
to navigate to the appropriate "Preference Fragment" using the id of the actions in the sub_navigation_from_settings
graph.
Its working just fine for me. Yay!
Its possible that down the line this won't work because of something I don't know causing a conflict but until then I'm glad it works.
I hope this helps someone other than me.
Here is all the code if you want to look at that directly.
Here is the holder java class:
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;
}
}
It has the following layout:
Note that the Android Studio Editor will not render this layout and I believe that's because the Settings resources are pure-er XML stored in the XML resource directory.
<?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>
Here is the second 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>
Here is the root settings 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" />
And finally the separate example settings screen's 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>