10

How, in general, do you find what theme attribute to override in order to alter the look of any UI element?

Currently I rely on trawling through framework source files: the theme definitions in values.xml (usually the support library variant), attribute definitions in attrs.xml, and the R.styleable class docs.

But this is totally hit-and-miss. It's not only unduly time-consuming, but sometimes I miss entirely, for instance I've been trying without success to find out how to change the text styles in a DatePickerDialog's OK and Cancel buttons. Feel free to use that as an example, but if you do, please outline your discovery process. The answer I'm looking for is how to discover the applied styles for any UI element,

Or is there just no deterministic way to find out? Do you just have to know?

Cris
  • 1,939
  • 3
  • 23
  • 37
  • I answered this exact question here: [How to explore styling in android](http://stackoverflow.com/questions/28523707/how-to-explore-styling-in-android). Using this info with the following answer should solve your problem: [Change Datepicker dialog color for Android 5.0](http://stackoverflow.com/questions/28738089/change-datepicker-dialog-color-for-android-5-0). – Vikram Sep 06 '15 at 17:29
  • @Vikram, that's incredibly helpful, thanks. Quelle horreur, though ;) – Cris Sep 07 '15 at 05:42
  • Yea, `How to explore styling in android`, although apt, isn't very search-engine friendly I suppose. Glad you found it useful. – Vikram Sep 10 '15 at 04:40

1 Answers1

9

Finding how to change styles for widgets on Android has always been troublesome. For example, DatePickerDialog has different styles for Holo and Material design. So, the style of the dialog may depend on the SDK value or if you are using the AppCompat library. There is also little documentation.

It would be nice if tools like Hierarchy Viewer showed attributes and the current theme of a widget. However, I haven't come across such a tool.

We can get the current theme and attributes after the view is created. Here are a couple methods I wrote and tested on a DatePickerDialog to find the style being used:


Get the current theme from a context:

static String getThemeNameFromContext(Context context) {
    Resources.Theme theme = context.getTheme();
    String themeName;
    try {
        Field field = theme.getClass().getDeclaredField("mThemeResId");
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        int themeResId = field.getInt(theme);
        themeName = context.getResources().getResourceEntryName(themeResId);
    } catch (Exception e) {
        // If we are here then the context is most likely the application context.
        // The theme for an application context is always "Theme.DeviceDefault"
        themeName = "Theme.DeviceDefault";
    }
    return themeName;
}

Get the name/value of an attribute:

static String getResourceName(Context context, int attribute) {
    TypedArray typedArray = context.obtainStyledAttributes(new int[]{attribute});
    try {
        int resourceId = typedArray.getResourceId(0, 0);
        return context.getResources().getResourceEntryName(resourceId);
    } finally {
        typedArray.recycle();
    }
}

In the example below I created a DatePickerDialog and got the theme and attribute values being used by the dialog and the dialog's positive button using the above methods:

// Create the DatePickerDialog
DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(),
        new DatePickerDialog.OnDateSetListener() {

            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {

            }
        }, 2015, Calendar.SEPTEMBER, 9);
// Show the dialog
datePickerDialog.show();

// Get the positive button from the dialog:
Button positiveButton = datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
// Get the theme used by this dialog
String theme = getThemeNameFromContext(datePickerDialog.getContext());
// Get the date picker style used by the dialog
String datePickerStyle = getResourceName(datePickerDialog.getContext(), android.R.attr.datePickerStyle);
// Get the style of the positive button:
String buttonStyle = getResourceName(positiveButton.getContext(), android.R.attr.buttonStyle);

Log.i("LOGTAG", "Theme: " + theme);
Log.i("LOGTAG", "datePickerStyle: " + positiveButton);
Log.i("LOGTAG", "buttonStyle: " + buttonStyle);

In my test project I got these values for the theme, datePickerStyle, and buttonStyle:

Theme: ThemeOverlay.Material.Dialog

datePickerStyle: Widget.Material.Light.DatePicker

buttonStyle: Widget.Material.Light.Button


This is somewhat helpful, but we still haven't changed the positive and negative button style. If we view the source for DatePickerDialog we can see that it extends AlertDialog. This makes things harder as you will need to set a custom style in your theme which would affect all AlertDialog buttons. If you need an example on how to change the buttonStyle please leave a comment.

A better approach would be to style the positive and negative buttons after the dialog is visible. For example, in your DialogFragment you can place the following code in onStart() to style the buttons:

@Override
public void onStart() {
    super.onStart();
    DatePickerDialog dialog = (DatePickerDialog) getDialog();
    Button btnPos = dialog.getButton(DatePickerDialog.BUTTON_POSITIVE);
    Button btnNeg = dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE);

    /* customize the buttons here */
    btnPos.setText("CUSTOM");
    btnPos.setTextAppearance(android.R.style.TextAppearance_Large);
    btnNeg.setTextColor(Color.RED);
}

enter image description here


Conclusion:

You can use the above methods on other views to find the style that applies for that view. It is still a pain in the a** to style widgets on Android. You may need to dig through source code and XML from time to time.

Community
  • 1
  • 1
Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
  • 1
    This is very helpful, and I've learnt several useful things from it. It doesn't *quite* answer my main issue, which is how to find what the attributes are which apply to a given view. I note that by including `android.R.attr.datePickerStyle` in the code you're relying on what you already know, but the issue for me is where that knowledge comes from. Vikram's answer mentioned in his comment actually pretty much covers what I need to know. The bounty has served its purpose for me, so I'll award it even though not accepting your answer. – Cris Sep 07 '15 at 05:41
  • @Cris I'm glad the answer was helpful. I don't think it is possible to find what attributes a view has unless you dig through the source code. If I find a better approach I will update my answer. Thanks. – Jared Rummler Sep 07 '15 at 06:05