Customizing Android Preference Classes

Allowing the user to set preferences is fairly straightforward with Android via the PreferenceActivity and the associated Preference classes. As part of writing my first app for Android, I ended up tweaking them a little.  These changes are discussed below, and a sample Android project incorporating them is available on github here.  This project has a customized checkbox, color picker (based on the one from the Android graphics demo api), seekbar (based on the one by Matthew Wiggins), and text preference.



Adding a "Reset" Button to Change Preference Back to Its Default

If I have changed a preference value, then I want it to be easy to set the value back to the default, and be able to see that the preference is different than the default.  So, I added a "Reset" button to each preference that shows up only if the value is different than the default, as shown below.
"Speech Speed" Preference, showing "Reset" Button that Appears only if Value is Different than Default
This requires subclassing the relevant preference class, and then adding some code to the onCreateView method.  The "Reset" button is placed to the left of the widget that actually changes the preference value. Otherwise, the default widget must slide to the left to make room for the "Reset" button, which is distracting.  The "Reset" button also serves as an effective visual cue for which preferences have actually been changed from their default value.

Adding this extra button to the standard preference classes is accomplished by overriding the onCreateView method of the Preference class, and then:
  • grab a handle to the ViewGroup that contains the standard widget (which has id android.R.id.widget_frame)
  • make sure that the layout has a horizontal orientation (the stock one from the API has vertical orientation)
  • create the new "Reset" button and adding this to the ViewGroup at index 0, so that it appears to the left of the existing widget
It is recognized that there is a chance that this introduces an unwanted dependency on the internals of the android system; in particular, the value of the id for the relevant ViewGroup containing the preference widget (android.R.id.widget_frame).  However, this value is the same for all API levels up to and including HoneyComb.  And you can always tailor the id sought based on the API level (if it changes in Ice Cream Sandwich or later API's).

Gradient in the Background of the Preference Screen

Several of the preferences in my first app involved setting a color, and a small button of the chosen color is included in the list of preferences in these cases.  Because these colors can be arbitrary, there is the chance that the button is hard to discern, no matter what background color is chosen.  This situation is partially addressed by including a general slight gradient in the preference activity background itself, as shown below:
Left-to-Right Gradient in Preferences to Help User Discern Color Buttons for all Colors
This is done by setting the background of each preference in the list to a rectangular GradientDrawable that goes from transparent to a light white (left to right), and with dithering set to true.  This is what is implemented for the function setGradientForPreferenceView referred to above:

public static void setGradientForPreferenceView(View v) {		
  int[] colorsForGradient = new int[2];
  colorsForGradient[0]=Color.argb(0, 0,0,0);
  colorsForGradient[1]=Color.argb(64, 255,255,255);
  GradientDrawable d = new GradientDrawable(
                                 GradientDrawable.Orientation.LEFT_RIGHT ,
                                 colorsForGradient);
  d.setGradientType(GradientDrawable.LINEAR_GRADIENT);
  d.setShape(GradientDrawable.RECTANGLE);
  d.setDither(true);
  v.setBackgroundDrawable(d);	
}


I noticed that this gradient can consistently look banded, so following the information detailed by Eric Burke here, the format of the window  is set to PixelFormat.RGBA_8888 in the (overridden) onAttachedToWindow method of the PreferenceActivity.

Note that setting the format of the window is apparently not supported for Android prior to level 7 (2.1), so if you must support those versions as well, you'll of course need to implement one of the design patterns to gracefully handle multiple API levels.


Color Picker

In all cases where the user needs to pick a color, a modified version of the color picker in the Android API demo package is used.
As with the original Android demo color picker on which this one is based, the user changes the possible color to pick by sliding their finger on the outer circle. I find this to be a very satisfying and intuitive way to pick the color.  For the color picker in the Android demo, the user picks the color by clicking the middle circle. This functionality was retained, but because it was thought that this action might not be obvious to the user, an extra "Set Color" button is included.

Another addition was the insertion of a white-to-black gradient on the color wheel, so that the sweep gradient uses these colors:

mColors = new int[] {
                      0xFF000000, 0xFFFFFFFF,
                      0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
                      0xFFFFFF00, 0xFFFF0000,
                      0xFF000000
                   };

An additional modification was the line emanating from the circle to the corresponding location on the outer circle. This helps the user to see exactly where on the wheel the color is. And for good measure, a slight animation effect is added by having little pulsing "beads" be "emitted" from the wheel, draining towards the middle, whenever the user has their finger on the color wheel. 

And finally, the same transparent-to-light-white gradient for the preference itself is used as a background for the color picker, so as to help the middle circle to be more discernible no matter what the (arbitrary) color chosen is.



So there ya go - some simple tweaks that definitely filled some gaps for me when I needed to implement user preferences. Hope you find them useful, if for nothing else than as a starting place for your own customizations.

Popular Posts