Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preference sub-screen not opening when using support.v7.preference

I am trying to implement preferences with sub-screens using AppCompatActivity and support.v7.preference

According to the docs, every PreferenceScreen within another PreferenceScreen functions as a sub-screen, and the framework will handle displaying it when clicked. http://developer.android.com/guide/topics/ui/settings.html#Subscreens

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">     <!-- opens a subscreen of settings -->     <PreferenceScreen         android:key="button_voicemail_category_key"         android:title="@string/voicemail"         android:persistent="false">         <ListPreference             android:key="button_voicemail_provider_key"             android:title="@string/voicemail_provider" ... />         <!-- opens another nested subscreen -->         <PreferenceScreen             android:key="button_voicemail_setting_key"             android:title="@string/voicemail_settings"             android:persistent="false">             ...         </PreferenceScreen>         <RingtonePreference             android:key="button_voicemail_ringtone_key"             android:title="@string/voicemail_ringtone_title"             android:ringtoneType="notification" ... />         ...     </PreferenceScreen>     ... </PreferenceScreen> 

This works fine using native Activity, PreferenceFragment... but using AppCompatActivity and PreferenceFragmentCompat, clicking the Preference element just highlights it, but doesn't open the sub-screen.

I couldn't find anything on this reading the docs and the code... do I need to implement any additional callbacks?


EDIT: just for completeness...

This works and opens the sub-screen:

public class MainActivity extends Activity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          if (savedInstanceState == null) {             getFragmentManager().beginTransaction()                     .replace(android.R.id.content, new DemoPreferenceFragment())                     .commit();         }     }      static public class DemoPreferenceFragment extends PreferenceFragment {          @Override         public void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);             addPreferencesFromResource(R.xml.preferences);         }     } } 

This doesn't work/open the sub-screen:

public class MainActivity extends AppCompatActivity {      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);          if (savedInstanceState == null) {             getSupportFragmentManager().beginTransaction()                     .replace(android.R.id.content, new DemoPreferenceFragment())                     .commit();         }     }      static public class DemoPreferenceFragment extends PreferenceFragmentCompat {          @Override         public void onCreatePreferences(Bundle bundle, String s) {             addPreferencesFromResource(R.xml.preferences);         }     } } 

Edit: 25/01/2016

After fiddling with support.v7.preference for a few days, I've summed up my findings here, hoping it may help others: HowTo use support.v7.preference with AppCompat and potential drawbacks

like image 955
maxdownunder Avatar asked Jan 10 '16 03:01

maxdownunder


People also ask

Where is the preference option in Android Studio?

The preference option doesn't exist anymore. You will need to right click the res -> new -> Android resource file and choose the resource type as xml in the dropdown. Then you will manually need to add the layout for preference xml.

What is preference screen?

Use the AndroidX Preference Library for consistent behavior across all devices. For more information on using the AndroidX Preference Library see Settings. Represents a top-level Preference that is the root of a Preference hierarchy. A PreferenceActivity points to an instance of this class to show the preferences.


2 Answers

It looks like a bug in PreferenceFragmentCompat or insufficiency of docs. It has method onNavigateToScreen which is called when you click on PreferenceScreen item.

But method getCallbackFragment() returns null by default, so you need override it in your fragment to return this. Also you need to implement PreferenceFragmentCompat.OnPreferenceStartScreenCallback.

public class SettingsFragment extends PreferenceFragmentCompat implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {      public static SettingsFragment newInstance() {         return new SettingsFragment();     }      @Override     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {         addPreferencesFromResource(R.xml.news_settings);     }      @Override     public Fragment getCallbackFragment() {         return this;     }      @Override     public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {         preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);         return true;     } } 

But it leads to another problem when you can't get back to your initial PreferenceScreen,

Another way is to replace fragment which is described here How to move back from Preferences subscreen to main screen in PreferenceFragmentCompat?

like image 157
LackOfKnowledge Avatar answered Sep 23 '22 08:09

LackOfKnowledge


This is complete working example, I hope this will be helpful to someone.It covers opening the preference subscreen and moving back to main Settings screen.

I followed this issue in Android open source issue tracker --here

The official documentation is missing the documentation for loading preference subscreen—Refer here for official documentation--

The main advanced settings screen has 2 checkboxes and a disabled subscreen title(custom Pattern Settings):-

subscreen title disabled

Once we check the Custom checkbox, the subscreen title is enabled. subscreen title enabled

On click of Custom pattern settings, the subscreen opens in new screen

enter image description here

Here is the example code with documentation:--

In res/xml/preferences.xml file:--

    <?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"     android:summary="Trying intro text">     <PreferenceCategory android:title="Settings">         <CheckBoxPreference             android:defaultValue="true"             android:key="defaultPress"             android:title="Default settings" />         <CheckBoxPreference             android:defaultValue="false"             android:key="customKey"             android:title="Custom" />         <PreferenceScreen             android:key="customPrefKey"             android:title="Custom Pattern Settings">             <PreferenceCategory                 android:key="customSettingsKey"                 android:title="Custom Settings">                 <ListPreference                     android:defaultValue="4"                     android:entries="@array/initialClickArray"                     android:entryValues="@array/initialClickValues"                     android:key="initialClicks"                     android:summary="initialClicksSummary"                     android:title="No. Of Clicks" />                 <ListPreference                     android:defaultValue="5"                     android:entries="@array/initialTimeArray"                     android:entryValues="@array/initialTimeValues"                     android:key="initialTimeKey"                     android:summary="Time to complete clicks"                     android:title="Time to complete" />             </PreferenceCategory>         </PreferenceScreen>     </PreferenceCategory> </PreferenceScreen> 

MainActivity.java should implement interface PreferenceFragmentCompat.OnPreferenceStartScreenCallback and then override the method-- onPreferenceStartScreen

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;     } 

For the main Settings screen(fragment):-

public class AdvancedSettingsFragment extends PreferenceFragmentCompat {     private static final String TAG = AdvancedSettingsFragment.class.getName();     public static final String PAGE_ID = "page_id";      public static AdvancedSettingsFragment newInstance(String pageId) {         AdvancedSettingsFragment f = new AdvancedSettingsFragment();         Bundle args = new Bundle();         args.putString(PAGE_ID, pageId);         f.setArguments(args);         return (f);     }      @Override     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {         // Load the preferences from an XML resource         addPreferencesFromResource(R.xml.preferences);         final CheckBoxPreference customPreference = (CheckBoxPreference) findPreference("customKey");         final Preference customSettings = (Preference) findPreference("customPrefKey");         // First time loading the preference screen, we check the saved settings and enable/disable the custom settings, based on the custom check box         //get the customSettings value from shared preferences         if (getCustomSettings(getActivity())) {             customPreference.setChecked(true);             customSettings.setEnabled(true);         } else {             customPreference.setChecked(false);             customSettings.setEnabled(false);         }         customPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {             @Override             public boolean onPreferenceChange(Preference preference, Object selectedValue) {                 Log.d(TAG, "Inside on preference change of custom checkbox selection " + selectedValue.getClass());                 if ((Boolean) selectedValue) {                     customSettings.setEnabled(true);                 }else{                     customSettings.setEnabled(false);                 }                 return true;             }         });     }     private boolean getCustomSettings(Context context) {         return PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("customKey", false);     } } 

and finally for the loading of subscreen:

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);     } } 
like image 43
Nicks Avatar answered Sep 24 '22 08:09

Nicks