Fabrizio Russo AppCompatPreferenceActivity - Preference vecchio stile


2017/01/14   AppCompatPreferenceActivity - Preference vecchio stile

Le nuove specifiche per l'implementazione delle activity per i settings consigliano di utilizzare gli header in modo da avere, sopratutto su un table, una ben chiara distinzione tra le varie sezioni delle configurazioni.

Nel caso di settings per uno smartphone pero' queste "separazioni" sono piuttosto scomode ed anche l'activity proposta da Android Studio non e' di facile customizzazione.

Ecco perche' questa classe che implementa, sempre sftuttando le nuove classi messe a disposizione dalle ultime versioni di android, una pagina di setting "vecchio stile", cioe' tutta in verticale ed eventualmente divisia in categorie (Categories).

Prima di tutto e' necessario un file XML con all'interno le preferences che si intende mostrare

il file preference.xml da inserire nella cartella res/xml e' il seguente:


<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory android:title="General" >

    <Preference
            android:key="aboutPreference"
            android:title="About" android:summary="About this application" />
    </PreferenceCategory>

</PreferenceScreen>

dove, a titolo di esempio, viene mostrata una singola prefereze all'interno di una getegoria. Da notare che le categorie assumono gia' il colore definito come 'accent' come da specifiche del Marieal Design

Siccome attualmente Android non mette a disposizione una PreferenceActivity che estenda a AppCompatActivity, ho creato una classe che estenda da AppCompat ed utilizza un fragment per disegnare i settings.

Il Fragment e' imlementato come inner class in modo da mantenere l'unica di compilazione il piu' compatta (e portabile possibile).

La classe e' la seguente:


public class AppCompatPreferenceActivity extends AppCompatActivity {

    private AppCompatDelegate mDelegate;
    private MyPreferenceFragment myFragmentReference = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        getDelegate().installViewFactory();
        getDelegate().onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);
        myFragmentReference = new MyPreferenceFragment();
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, myFragmentReference).commit();
    }


    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        getDelegate().onPostCreate(savedInstanceState);
    }

    public ActionBar getSupportActionBar() {
        return getDelegate().getSupportActionBar();
    }

    public void setSupportActionBar(@Nullable Toolbar toolbar) {
        getDelegate().setSupportActionBar(toolbar);
    }

    @Override
    public MenuInflater getMenuInflater() {
        return getDelegate().getMenuInflater();
    }
    public void setContentView(@LayoutRes int layoutResID) {  getDelegate().setContentView(layoutResID); }
    public void setContentView(View view) {
        getDelegate().setContentView(view);
    }
    public void setContentView(View view, ViewGroup.LayoutParams params) {  getDelegate().setContentView(view, params); }
    public void addContentView(View view, ViewGroup.LayoutParams params) { getDelegate().addContentView(view, params); }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        getDelegate().onPostResume();
    }

    @Override
    protected void onTitleChanged(CharSequence title, int color) {
        super.onTitleChanged(title, color);
        getDelegate().setTitle(title);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getDelegate().onConfigurationChanged(newConfig);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getDelegate().onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getDelegate().onDestroy();
    }

    public void invalidateOptionsMenu() {
        getDelegate().invalidateOptionsMenu();
    }

    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, null);
        }
        return mDelegate;
    }


    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
        super.onSaveInstanceState(outState);
    }


    // ---------------------------------------------------------------------------------

	/**
	 * PreferenceFragment per l'implementazione dei settings
	 */
    public static class MyPreferenceFragment extends PreferenceFragment {

        // Add here the rferences to your preferences in R.xml.preference.xml
        Preference aboutPreference = null;

        @Override
        public void onSaveInstanceState(Bundle outState) {
            Logger.debug("Calling onSaveInstanceState in Fragment");
            // removed to fix the IllegalStateException
            // super.onSaveInstanceState(outState);
        }

        public AppCompatPreferenceActivity getSettingsActivity() {
            return (AppCompatPreferenceActivity)getActivity();
        }




        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);
            setHasOptionsMenu(true);

            final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());

            // Add here reference to your preferences.
            aboutPreference= (Preference) findPreference("aboutPreference");


            if (aboutPreference != null) aboutPreference.setOnPreferenceClickListener(
                    new Preference.OnPreferenceClickListener() {
                        @Override
                        public boolean onPreferenceClick(Preference preference) {
                            return doAboutPrefence();
                        }
                    }
            );
        }

        private boolean doAboutPrefence() {
            Toast.makeText(getActivity(), "Application " + getString(R.string.app_name), Toast.LENGTH_SHORT)
                    .show();
            return true;
        }

    }  // End of fragment

}  // End of class

Ho dovuto implementare alcuni work-around per risolvere qualche bug interno ed in particolare NON invocare il metodo super all'interno del metodo onSaveInstanceState def fragment e passare almeno un parametro nel meotodo onSaveInstanceState dell'activity contemitore.

Il risultato, volutamente semplice ma da usare come base di implementazione, e' il seguente.

La fase vera e propria di implementazione e' nel metodo onCreate del fragment.