Add a Toolbar to a PreferenceActivity
3 min read

Add a Toolbar to a PreferenceActivity

Add a Toolbar to a PreferenceActivity

When trying to move a playground to Material Design, I stumbled upon the new Toolbar widget, which is supposed to replace the ActionBar (well, that's my understanding from all the posts I've read). Thing is, the Toolbar is designed to be added to an Activity. To do that, the Activity class has a new method public void setActionBar (Toolbar toolbar) since API 21.

The problem

Adding the Toolbar to an activity is easy and documented in all sources implementing material design on github. However, Adding an action bar to a PreferenceActivity, is nowhere to be found. You can add a Toolbar alright, but PreferenceActivity has no setActionBar() method available.

My solution

My approach was to replicate the functionality of the ActionBar, particularly the menu interactions. What I needed was a way to display a menu (an About menu in my case) and trigger the relevant action on click.

I'll split the implementation in two parts; the layout components and the Java implementation.

Layout

The layout has four parts:

  1. The menu: This is quite simple as I only require an About component:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        tools:context=".SettingsActivity">
    
        <item
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/action_about"
            android:orderInCategory="100"
            app:showAsAction="never"
            android:title="@string/action_about" />
    </menu>
    

    You may want to extend it to suit your needs.

  2. The toolbar: It has the empty toolbar:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- based on:
    http://www.android4devs.com/2014/12/how-to-make-material-design-app.html
    -->
    <android.support.v7.widget.Toolbar
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        android:elevation="4dp"
        >
    
    </android.support.v7.widget.Toolbar>
    
  3. The preferences layout: It includes the toolbar and a ListView for the preferences:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/windowBackgroundColor"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        tools:context=".SettingsActivity">
    
        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar" />
    
        <ListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/toolbar"
            android:background="@color/windowBackgroundColor" />
    </RelativeLayout>
    
  4. The Preferences components: In my case, I have a headers with multiple fragments:

    <preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
        <header
            android:fragment="com.example.ui.EffectsPreferenceFragment"
            android:title="@string/pref_header_effects" />
    
        <header
            android:fragment="com.example.ui.TimePreferenceFragment"
            android:title="@string/pref_header_time" />
    
    </preference-headers>
    

The java implementation

As you can figure out from the above XML files, I intend to use a programmatic approach to enabling this functionality. It will involve a listener to the menu actions. To make my life easier, I'll make my class implement the relevant listener:

public class SettingsActivity
    extends PreferenceActivity
    implements android.support.v7.widget.Toolbar.OnMenuItemClickListener {

Using butterknife, I'll have a reference to the toolbar

    @InjectView(R.id.toolbar)
    Toolbar mToolbar;

All the UI is done in onPostCreate() (parts of it are from Jake Wharton's U+2020). First, I'm setting up the preferences and enable ButterKnife:

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

        setupSimplePreferencesScreen();
        setContentView(R.layout.layout_preferences);
        ButterKnife.inject(this);

Then I'm inflating the menu in the toolbar and register the listened to the toolbar itself:

        mToolbar.inflateMenu(R.menu.menu_main);
        mToolbar.setOnMenuItemClickListener(this);
    }

Then, I'm implementing the listener method:

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        if (item.getItemId() == R.id.action_about) {

            Colors c = new Colors(R.color.colorPrimary, R.color.colorPrimaryDark);
            new Libs.Builder()
                    .withFields(R.string.class.getFields())
                    .withVersionShown(true)
                    .withLicenseShown(true)
                    .withAnimations(false)
                    .withActivityTitle(this.getResources().getString(R.string.action_about))
                    .withActivityTheme(R.style.AppThemeBar)
                    .withActivityColor(c)
                    .start(this);
        }
        //return true;
        return false;
    }

This way, when the About menu is clicked, the application will display a screen with about information, using Mike Penz's awesome AboutLibraries.

This is it. You can customise the menu and its listener. You can probably include the toolbar layout in the preferences layout too but I haven't tried it.

Alternatives

Depending on the complexity of your preferences, you may choose to bypass the PreferenceActivity class altogether and use Activity instead (like here, here, or here). This approach is definitely cleaner and easier, but it doesn't offer the perks of PreferenceActivity with multiple fragments.

Another, hack-ier solution would be to implement your own PreferenceActivity similar to this implementation. I tried this variant but stopped after a couple of hours' work.