Search code examples
androidandroid-support-libraryandroid-appcompatpreference-v7

Inner PreferenceScreen does not open with PreferenceFragmentCompat


My inner PreferenceScreen of PreferenceFragmentCompat is not showing, or seems to ignore tapping events.

I created MyPreferenceFragment that extends PreferenceFragmentCompat

public class MyPreferenceFragment extends PreferenceFragmentCompat {
 @Override
  public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    addPreferencesFromResource(R.xml.preferences);
  }
}

then I changed my theme at styles.xml like

<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
  <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>

And finally create my preferences.xml file like

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference android:title="Check Me"/>
    <PreferenceScreen android:title="My Screen"> <!-- This is not opening -->
        <EditTextPreference android:title="Edit text" />
    </PreferenceScreen>
</PreferenceScreen>

At the build.gradle I have added both:

compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:preference-v7:23.0.1'

code of the Activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

activity_main.xml

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment"
    android:name="com.mando.preferenceapp.MyPreferenceFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Testing the above code I cannot open / get into the preference screen. Am I missing something? Why this isn't working?


Solution

  • After spending many many hours with tries, searching and thankfully with some assistance from the creators of the support library. I've managed to make it work.

    Step 1. Activity

    public class MyActivity extends AppCompatActivity implements
            PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            if (savedInstanceState == null) {
                // Create the fragment only when the activity is created for the first time.
                // ie. not after orientation changes
                Fragment fragment = getSupportFragmentManager().findFragmentByTag(MyPreferenceFragment.FRAGMENT_TAG);
                if (fragment == null) {
                    fragment = new MyPreferenceFragment();
                }
    
                FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                ft.replace(R.id.fragment_container, fragment, MyPreferenceFragment.FRAGMENT_TAG);
                ft.commit();
            }
        }
    
        @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.replace(R.id.fragment_container, fragment, preferenceScreen.getKey());
            ft.addToBackStack(preferenceScreen.getKey());
            ft.commit();
            return true;
        }
    }
    

    Tips.

    • Do not add the fragment by xml you will have crashes on orientation changes.
    • Handle the recreations of activity / fragment add in onCreate so as to avoid losing your fragment when inside a preference screen.
    • The host activity of the fragment should implement the PreferenceFragmentCompat.OnPreferenceStartScreenCallback and recreate fragments of the same instance.

    Step 2. PreferenceFragment

    public class MyPreferenceFragment extends PreferenceFragmentCompat {
    
        public static final String FRAGMENT_TAG = "my_preference_fragment";
    
        public MyPreferenceFragment() {
        }
    
        @Override
        public void onCreatePreferences(Bundle bundle, String rootKey) {
            setPreferencesFromResource(R.xml.preferences, rootKey);
        }
    
    }
    

    Tips.

    • Use the method setPreferencesFromResource and take advantage of the rootKey of each screen. This way your code will be reused properly.
    • Keep in mind that if you have code like findPreference in your fragment it should have null checks as when you were in inner screens this will give you nothing.

    The thing that is missing now is the implementation of the back arrow in the actionbar (home action) but this never works by itself ;-)

    I' also created a demo app wrapping all this code you can find it on github.