Android - Lollipop's new View constructor (Default style)

     Lollipop added a new overloaded constructor for View which takes an additional parameter, defStyleRes. As per the documentation, this is a style resource providing default values for View's attributes and can't be changed by a theme unlike the reference attribute defStyleAttr. The documentation also claims that this is more useful for sub classes and is meant for developers who would like to fall back to a definitive default values for custom attributes etc. 

   But what does this mean for Application developers who most likely would continue to inflate these views via XML? Lets take a look at a custom TextView,

public class MyTextView extends TextView
{
    public MyTextView(Context context) {
        this(context, null);
    }

    public MyTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, R.style.DefaultStyle);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}


The widget prefers a default red color specified by DefaultStyle,

    <style name="TextView" />
    <style name="DefaultStyle" parent="TextView">
        <item name="android:textColor">#FFFF4444</item> <!-- Red color -->
    </style>

This has a typical usage in the Application via layout xml and the TextView displays the text in white color as expected,

<com.sample.MyTextView
        android:text="@string/hello_world"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

Next, the color is specified via style and the text is displayed in dark blue color as expected,

<com.sample.MyTextView
        android:text="@string/hello_world"
        style="@style/XmlAttrStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <style name="XmlAttrStyle" parent="TextView">
        <item name="android:textColor">#FF0099CC</item> <!-- Dark blue color -->
    </style>

Finally, the color is specified via Theme when BaseTheme is applied to the activity hosting the TextView.

<com.sample.MyTextView
        android:text="@string/hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <style name="BaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="android:textColor">#FFFF8800</item> <!-- Dark orange -->
    </style>

    <activity
          android:name=".MyActivity"
          android:theme="@style/BaseTheme"
          android:label="@string/app_name" />

    Application developers might expect the color specified in the theme to be applied and in this case for the text to be displayed in dark orange color. However, the color specified in the Default style (R.style.DefaultStyle) gets a higher priority and the text is displayed in Red color. This is well documented in the overridden constructor but could easily be missed by developers using 3rd party widgets, especially when inflating views from xml. Widget developers should perhaps document the fall back behavior when using default styles and the fact that it would always override the values specified in current theme.


 

No comments: