How to move back from Preferences subscreen to main screen in PreferenceFragmentCompat?
Asked Answered
C

2

11

I am trying to implement a Settings screen using PreferenceFragmentCompat. My preference xml has a preference subscreen like this:

preferences.xml

    <CheckBoxPreference
        android:defaultValue="false"
        android:key="@string/pref_sound_key"
        android:summary="@string/pref_sound_summary"
        android:title="@string/pref_sound_title" />

    <PreferenceScreen android:title="Inner Screen">
        <CheckBoxPreference
            android:defaultValue="true"
            android:key="@string/key_1"
            android:title="@string/title_1" />

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="@string/key_1"
            android:title="@string/title_1" />

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="@string/key_2"
            android:title="@string/title_2" />

        <CheckBoxPreference
            android:defaultValue="true"
            android:key="@string/key_3"
            android:title="@string/title_3" />
    </PreferenceScreen>

</PreferenceScreen>

Preference Main Screen

Now, in the app, the subscreen does not open until I implement PreferenceFragmentCompat.OnPreferenceStartScreenCallback interface in parent activity, as specified in PreferenceFragmentCompat doc.

MainActivity.java

public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
 PreferenceScreen preferenceScreen) {    
    preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
    return true;
}

Here's where the problem arises. On implementing the interface, the subscreen opens, but then there is no way I can find to move back to first screen.

Preference Subscreen

Pressing back key closes the app.

Is there any way I can put a back arrow on app bar so that pressing it will bring the main screen back?

Chaddie answered 10/9, 2015 at 6:21 Comment(0)
C
18

By using setPreferenceScreen you are setting the root preference screen to the sub preference screen which is preventing you from having a hierarchy of preference screens to navigate back through.

I suggest that you treat each PreferenceScreen as a Fragment and add a new Fragment when you navigate into a sub screen.

@Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
                                       PreferenceScreen preferenceScreen) {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    MyPreferenceFragment fragment = new MyPreferenceFragment();
    Bundle args = new Bundle();
    args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
    fragment.setArguments(args);
    ft.add(R.id.fragment_container, fragment, preferenceScreen.getKey());
    ft.addToBackStack(preferenceScreen.getKey());
    ft.commit();
    return true;
}

MyPreferenceFragment

public class MyPreferenceFragment extends AppPreferenceFragment {

   public static final String FRAGMENT_TAG = "my_preference_fragment";

   public MyPreferenceFragment() {
   }

   @Override
   public void onCreatePreferences(Bundle bundle, String rootKey) {
       setPreferencesFromResource(R.xml.preferences, rootKey);
   }
}

AppPreferenceFragment

public abstract class AppPreferenceFragment extends PreferenceFragmentCompat {

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

    // Set the default white background in the view so as to avoid transparency
      view.setBackgroundColor(
              ContextCompat.getColor(getContext(), R.color.background_material_light));

  }
}

That way when you press the back button each Fragment will be popped from the stack.

For more information see this GitHub project

Cabrera answered 22/9, 2015 at 9:29 Comment(5)
Can you add on to what do you mean by treating each PreferenceScreen has a Fragment. Should I define two Preference Fragments, one for the main settings, and one for inner settings? How would I achieve that?Chaddie
@ArpitJaiswal Yes you need one Fragment per PreferenceScreen but you can re-use MyPreferenceFragment by creating a new instance for each PreferenceScreen and passing in the PreferenceScreen key. This key is used in MyPreferenceFragment to inflate the XML of your Preferences. I've just added a GitHub link to my answer which has more information.Cabrera
This doesn't handle using the home up button (the back arrow in the action/tool bar, it simply closes the entire preference activity.Diarrhea
What is the relationship of MyPreferenceFragment and AppPreferenceFragment ?Roanna
@StealthRabbi PreferenceFragments have transparent backgrounds by default, AppPreferenceFragment is a simple base class that applies a white background to each Fragment that extends it. In hindsight there are probably better ways of achieving the same result.Cabrera
S
2

I have added a complete working example with screenshots and code snippets here in this post, I hope this will be helpful to someone.

It covers following scenarios:-- 1) A main setting screen with two checkboxes and a subscreen title. 2) On click of subscreen title, the new preference subscreen opens. 3) On back pressed, the control goes to main Settings screen. So the back press is handled properly.

The MainActivity looks like this(overridden onPreferenceStartScreen method handles the opening of new subscreen in a new window):--

  public class MainActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {

    private static final String TAG = MainActivity.class.getName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = null;
        if (savedInstanceState == null) {
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragment = new AdvancedSettingsFragment().newInstance("Advanced Setting");
            fragmentTransaction.add(R.id.fragment_container, fragment);
            fragmentTransaction.commit();
        }
    }

    @Override
        public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
                                           PreferenceScreen preferenceScreen) {
        Log.d(TAG, "callback called to attach the preference sub screen");
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        AdvancedSettingsSubScreenFragment fragment = AdvancedSettingsSubScreenFragment.newInstance("Advanced Settings Subscreen");
        Bundle args = new Bundle();
        //Defining the sub screen as new root for the  subscreen
        args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
        fragment.setArguments(args);
        ft.replace(R.id.fragment_container, fragment, preferenceScreen.getKey());
        ft.addToBackStack(null);
        ft.commit();
        return true;
    }

and finally the in subscreen fragment setPreferencesFromResource(R.xml.preferences, rootKey); handles the attachment of subscreen to the rootkey.

public class AdvancedSettingsSubScreenFragment extends PreferenceFragmentCompat {
    private static final String TAG = AdvancedSettingsSubScreenFragment.class.getName();
    public static final String PAGE_ID = "page_id";

    public static AdvancedSettingsSubScreenFragment newInstance(String pageId) {
        AdvancedSettingsSubScreenFragment f = new AdvancedSettingsSubScreenFragment();
        Bundle args = new Bundle();
        args.putString(PAGE_ID, pageId);
        f.setArguments(args);
        return (f);
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        // rootKey is the name of preference sub screen key name , here--customPrefKey
        setPreferencesFromResource(R.xml.preferences, rootKey);
        Log.d(TAG, "onCreatePreferences of the sub screen " + rootKey);
    }
}
Straight answered 20/3, 2016 at 23:26 Comment(1)
Is it possible to avoid a single global settings XML file, and use one per each sub-settings fragment ? I asked about this here: https://mcmap.net/q/536809/-how-to-open-a-new-preferencefragment-from-current-one-using-the-new-android-x-api/878126Endosperm

© 2022 - 2024 — McMap. All rights reserved.