Can we use Android Jetpack Navigation Component with Settings preferences?
Asked Answered
G

2

12

Is it possible to use Android Jetpack Navigation Component to navigate the fragments of a Settings Preferences? I have a Settings screens with multiple nested Preference Fragments. I was wondering if it is feasible to use Android Jetpack Navigation Component to facilitate my task?

Guthrun answered 10/7, 2019 at 12:10 Comment(2)
possible duplicate of #53030321Nano
Na, i want to navigate to nested fragments WITHIN my settings activity using Navigation component.Guthrun
L
6

The google documentation states that hierarchical nesting of preferences are now deprecated in favour of inter-fragment navigation; that is the structure <PreferencesScreen> ... <PreferencesScreen ...> ... </PreferenceScreen> ... </PreferenceScreen> is now out.

The documents propose that one replace the nested <PreferenceScreen/> with a preference and set the app:fragment attribute, hence

<xml ...>
<PreferencesScreen ...>
    <Preference
        android:title="Title"
        app:fragment="TLD.DOMAIN.PACKAGE.preferences.SUBPreferenceFragment"
        app:destination="@+id/SUBPreferenceFragment" # Not supported by Schema
        android:summary="Summary"/> 
</PreferenceScreen>

Note : It'd be mighty handy if they allowed app:destination="@+id/SUBPreferenceFragment" to identify the navigation destination; I simply duplicate app:destination from the navigation graph's XML file.

From the documentation it is then possible to navigate between the PREFERENCEFramgents by referencing this preference.fragment as follows

class Activity : AppCompatActivity(),
                  // Preferences Interface
                  PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
{
    ...
    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, preference: Preference): Boolean 
    {
        // Instantiate the new Fragment
        val args = preference.extras
        val fragment = supportFragmentManager.fragmentFactory.instantiate(
            classLoader,
            preference.fragment)
        fragment.arguments = args
        fragment.setTargetFragment(caller, 0)
        // Replace the existing Fragment with the new Fragment
        supportFragmentManager.beginTransaction()
            .replace(R.id.FRAGMENT, fragment)
            .addToBackStack(null)
            .commit()
       return true
    }
    ...
}

There is however a small Snafoo ! R.id.FRAGMENT in a normal activity represents the placeholder within the activities' view where one would swap the main fragments in an out of existence. When using Navigation R.id.FRAGMENT maps to R.id.NAVIGATION and the code above swaps out the entire navigation fragment with the preference. For an activity using Navigation a reformulation is necessary.

class Activity : AppCompatActivity(),
                  // Preferences Interface
                  PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
{
    ...
    override fun onPreferenceStartFragment(caller: PreferenceFragmentCompat, preference: Preference): Boolean {
        // Retrieve the navigation controller
        val navctrl = findNavController(R.id.navigation)
        // Identify the Navigation Destination
        val navDestination = navctrl.graph.find { target -> preference.fragment.endsWith(target.label?:"") } 
        // Navigate to the desired destination
        navDestination?.let { target -> navctrl.navigate(target.id) }
    }
    ...
}

While this works I am a bit dubious about the line that determines the navDestination. If you have a better incantation for val navDestination = navctrl.graph.find { target -> preference.fragment.endsWith(target.label?:"") } then please edit the question or leave a comment accordingly so I can update the answer.

Langdon answered 19/8, 2019 at 22:49 Comment(0)
P
1

Yes, you can use Navigation components inside Preference fragments.

I have also a main preference fragment with multiple nested fragments. For me the best way was to navigate just inside preference fragments without any activity. So I created onPreferenceClickListeners and navigate just like you would from a normal fragment.

findPreference(getString(R.string.PREF_GN_BUTTON_MAIN)).setOnPreferenceClickListener(preference5 -> {
            Navigation.findNavController(requireView()).navigate(R.id.another_settings_fragment);
            return true;
        });

The advantage of this way is that you can use gradle plugin Safe args to navigate between destinations. So it is a little bit safer than to use it only with ids just like Carel from Activity.

I tried to override onPreferenceStartFragment in my activity but I could not see any advantages of it. I use single Activity architecture.

Photoconduction answered 6/8, 2020 at 5:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.