What's the proper way to setup an Android PreferenceFragment?

Phil picture Phil · Jul 12, 2017 · Viewed 7.6k times · Source

I'm trying to implement a basic settings activity in an Android app and either get a blank white screen or a crash. The documentation and samples I've seen aren't helping because they're either old or inconsistent. For example, depending on where you look, the settings activity should either extend Activity, PreferenceActivity, or AppCompatPreferenceActivity (part of the File>New>Activity>Settings Activity).

developer.android.com says you should implement the following:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
            .replace(android.R.id.content, new SettingsFragment())
            .commit();
    }
}

Yet, the Settings Activity generated in Android Studio uses does not make this call for any of the three fragments it creates. It uses preference headers.

So here are my questions:

  1. If you're using a simple, single preferences.xml file with a single PreferenceFragment and pre-API 19 compatibility is not a requirement, what class should SettingsActivity extend? Activity, PreferenceActivity, or AppCompatPreferenceActivity (for all its support methods and delegation)?
  2. Do you need to call getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit() in SettingsActivity.onCreate()?
  3. With various combinations, I'm either getting a blank white settings screen with no action bar or a crash. What's the right way to setup a single PreferencesFragment within an activity that displays the app action bar?

Answer

RBT picture RBT · Aug 15, 2018

Let's say we want to have a settings screen with one checkbox preference fragment as shown below:

enter image description here

Here is a step by step guide on how to build a settings activity where you can add some preferences to toggle or change the configurations for your Android app:

  1. Add a dependency for support of preference fragment in build.gradle file for app module:

    dependencies {
        compile 'com.android.support:preference-v7:25.1.0'
    }
    
  2. Add xml Android resource directory inside res directory.

  3. Inside xml directory, add a new XML resource file named pref_visualizer.xml as below. We're going to add one check-box preference fragment inside it.

    <?xml version="1.0" encoding="utf-8"?>
    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
        <CheckBoxPreference
            android:defaultValue="true"
            android:key="show_base"
            android:summaryOff="Bass will not be shown currently."
            android:summaryOn="Bass will be shown currently."
            android:title="Show Bass"
            />
    </PreferenceScreen>
    

    PreferenceScreen is the root tag which can hold as many preference fragments as you want. If you want to add more configurations of type list or text box then you need to add it here as a child of PreferenceScreen tag.

  4. Add a new Java class named SettingsFragment which will host PreferenceScreen. It should extend PreferenceFragmentCompat class as shown below:

    import android.content.SharedPreferences;
    import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
    import android.os.Bundle;
    import android.support.v7.preference.CheckBoxPreference;
    import android.support.v7.preference.EditTextPreference;
    import android.support.v7.preference.ListPreference;
    import android.support.v7.preference.Preference;
    import android.support.v7.preference.PreferenceFragmentCompat;
    import android.support.v7.preference.PreferenceScreen;
    import android.widget.Toast;
    
    
    public class SettingsFragment extends PreferenceFragmentCompat {
    
    
    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
            addPreferencesFromResource(R.xml.pref_visualizer);
        }
    }
    
  5. Now comes the final part where we build the association between an activity in the app and SettingsFragment class which hosts PreferenceScreen. Add a new activity named SettingsActivity which inherits from AppCompatActivity class. SettingsActivity class will act as the container for PreferenceScreen.

Java file for SettingsActivity:

import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;

public class SettingsActivity extends AppCompatActivity {

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

}

Layout file for SettingsActivity is shown below (activity_settings.xml). Here android.name property is the crux. It connects this activity to any of the classes present in your entire project which are inheriting from PreferenceFragmentCompat class. I had only one such class named SettingsFragment. You might have more than one class inheriting from PreferenceFragmentCompat class if you app has multiple settings screen.

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_settings"
    android:name="android.example.com.visualizerpreferences.SettingsFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

You're all set!