Search code examples
androidpreferencespreferenceactivitypreferencefragment

How to detect when an inner PreferenceScreen has been closed


I have a inner PreferenceScreen (call it Users) inside another PreferenceScreen (call it Main).
When i tap Users a new screen opens and I can change my Preferences there (a lot of CheckBoxes).
I want to detect (fire a callback) when this screen is dismissed and when I'm back on the Main PreferenceScreen.
The only way I found is to create a new class inheriting PreferenceScreen and overloading onPrepareForRemoval

I was wondering if there is a simpler way to do that.


Solution

  • Very interesting question! I finally figured out.

    The trick is to set DialogInterface.OnCancelListener for the PreferenceScreen submenu (Users, in our case) and you can do it in onPreferenceTreeClick (as here internal Dialog is already initialized). So void onCancel(DialogInterface dialog) is the callback you've been looking for.

    Here's the xml\preferences.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:key="main">
        <EditTextPreference android:title="Your Name"
            android:key="username"
            android:summary="Please provide your username"/>
        <PreferenceScreen
            android:key="users"
            android:title="Users"
            android:summary="Click here to select users">
            <CheckBoxPreference
                android:title="User 1"
                android:defaultValue="false"
                android:key="user1CheckBox" />
            <CheckBoxPreference
                android:title="User 2"
                android:defaultValue="false"
                android:key="user2CheckBox" />
            <CheckBoxPreference
                android:title="User 3"
                android:defaultValue="false"
                android:key="user3CheckBox" />
        </PreferenceScreen>
    </PreferenceScreen>
    

    Here's the PreferenceActivity and PreferenceFragment I used:

    public class MyPreferencesActivity extends PreferenceActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getFragmentManager().beginTransaction().replace(android.R.id.content, new MainPreferenceFragment()).commit();
        }
    
        public static class MainPreferenceFragment extends PreferenceFragment {
            @Override
            public void onCreate(final Bundle savedInstanceState)
            {
                super.onCreate(savedInstanceState);
                addPreferencesFromResource(R.xml.preferences);
            }
    
            @Override
            public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
                if (preference instanceof PreferenceScreen) {
                    PreferenceScreen submenu = (PreferenceScreen)preference;
                    submenu.getDialog().setOnCancelListener(new DialogInterface.OnCancelListener() {
                        @Override
                        public void onCancel(DialogInterface dialog) {
                            Log.d("MainPreferenceFragment", "Hi! Submenu is closing now!");
                        }
                    });
                }
    
                return super.onPreferenceTreeClick(preferenceScreen, preference);
            }
        }
    }
    

    UPD: Solution for PreferenceFragmentCompat:

    First, we need one more xml: xml\subpreference.xml (duplicate of the submenu from the main preference.xml):

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.preference.PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:key="users"
        android:title="Users"
        android:summary="Click here to select users">
        <CheckBoxPreference
            android:title="User 1"
            android:defaultValue="false"
            android:key="user1CheckBox" />
        <CheckBoxPreference
            android:title="User 2"
            android:defaultValue="false"
            android:key="user2CheckBox" />
        <CheckBoxPreference
            android:title="User 3"
            android:defaultValue="false"
            android:key="user3CheckBox" />
    </android.support.v7.preference.PreferenceScreen>
    

    Then, our hosting activity should implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback. And last step - we need a new subfragment (or pass exact XML you need to inflate as a bundle's parameter):

    public class MainActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback{
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            getSupportFragmentManager().beginTransaction().replace(R.id.container, new MainPreferenceFragment()).commit();
        }
    
        @Override
        public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
                                               PreferenceScreen preferenceScreen) {
            getSupportFragmentManager().beginTransaction().replace(R.id.container, new SubPreferenceFragment()).commit();
            return true;
        }
    
        @Override
        public void onBackPressed() {
            super.onBackPressed();
        }
    
        public static class MainPreferenceFragment extends PreferenceFragmentCompat {
            @Override
            public void onCreatePreferences(Bundle bundle, String s) {
                addPreferencesFromResource(R.xml.preferences);
            }
        }
    
        public static class SubPreferenceFragment extends PreferenceFragmentCompat {
            @Override
            public void onCreatePreferences(Bundle bundle, String s) {
                addPreferencesFromResource(R.xml.sub_preferences);
            }
        }
    }
    

    It this case, you can just listen to normal onBackPressed() of Activity

    I hope, it helps