Search code examples
androidandroid-toolbarandroid-themeandroid-styles

Where to put XML styles


Question 1

I want all Toolbar to look the same, without setting styles in all of them, so I though I would set the app theme for it. One of the things I wanted all them to have is the colorControlNormal atribute. These were my attempts:

Code sample 1:

<resources>
    <style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="toolbarStyle">@style/MyTheme.ToolbarStyle</item>
    </style>

    <style name="MyTheme.ToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
        <item name="colorControlNormal">@color/toolbarColorControlNormal</item>
    </style>
</resources>

Code sample 2:

<resources>
    <style name="MyTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorControlNormal">@color/toolbarColorControlNormal</item>
    </style>
</resources>

In Code sample 1, I tried to create a custom style for the Toolbar atributes, and set it to the theme, but it didn't work.

In Code sample 2, I set the attribute globally and worked. However, affects other components too, so it's not valid.

  • Why isn't Code sample 1 working ?

Question 2

I need to change the tabTextColor and tabSelectedTextColor attributes of all TabLayout, so I tried this:

Code sample 3:

<resources>
  <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="android:tabWidgetStyle">@style/TabsStyle</item>
  </style>

  <style name="TabsStyle" parent="Widget.Design.TabLayout">
    <item name="tabTextColor">@color/tabTextColor</item>
    <item name="tabSelectedTextColor">@color/tabSelectedTextColor</item>
  </style>
</resources>

But it's not working, the only way I got it working is manually setting them in XML:

<android.support.design.widget.TabLayout
    [...]
    android:background="?android:attr/colorPrimary"
    app:tabTextColor="@color/tabTextColor"
    app:tabSelectedTextColor="@color/tabSelectedTextColor" />
  • Why isn't Code sample 3 working ?

Last, I will upvote any reference to good style & theme guides/books. I really need to improve at this.

Thanks in advance.


Solution

  • Globally specify a color for the back arrow in the Toolbar

    This is not as straightforward as it probably should be, but try the following:

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- style the back arrow -->
        <item name="toolbarNavigationButtonStyle">@style/NavButtonStyle</item>
    </style>
    
    <style name="NavButtonStyle" parent="@style/Widget.AppCompat.Toolbar.Button.Navigation">
        <item name="android:tint">#FF0000</item>
    </style>
    

    It is android:tint that will change the color of the back arrow.

    In general, the default toolbar style is set by the attribute toolbarStyle. In the XML below, it is set to @style/MyToolbarStyle. MyToolbarStyle defines three attributes that will be used for all toolbars within the app. Here is what one looks like:

    <resources>
        <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="toolbarStyle">@style/MyToolbarStyle</item>
            <item name="titleTextColor">@android:color/holo_red_light</item>
        </style>
    
        <style name="MyToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
            <item name="titleTextColor">@android:color/white</item>
            <item name="subtitleTextColor">@android:color/holo_green_light</item>
            <item name="android:background">@color/vermillion</item>
        </style>
    </resources>
    

    enter image description here

    As you can see, MyToolbarStyle sets the value of the three attributes. Unfortunately, this approach does not work for all attributes such as colorControlNormal which, if specified in the theme, will change the color of the back arrow.


    Regarding the question about TabLayout:

    There is no pretty solution to globally define attributes for TabLayout. You can do something like this and just overlay Widget.Design.TabLayout but you will have to recreate all the attributes defined there and these attributes may change depending upon the version that is running. This works, but may be a problem to maintain. I don't think that it is a good solution.

    Another solution is to create your own custom TabLayout. It will be very simple and will just define tabTextAppearance as a global default attribute for TabLayout. The entire custom layout will look like this:

    public class MyTabLayout extends TabLayout {
        public MyTabLayout(Context context, AttributeSet attrs) {
            super(context, attrs, R.attr.tabTextAppearance);
        }
    }
    

    Once you replace all references to TabLayout to MyTabLayout, you will be able to do the following in styles.xml:

    <style name="AppTheme" parent="BaseAppTheme">
        <item name="tabTextAppearance">@style/MyTabStyle</item>
    </style>
    
    <style name="MyTabStyle" parent="Widget.Design.TabLayout">
        <item name="tabTextColor">#FF0000</item>
        <item name="tabSelectedTextColor">#00FF00</item>
    </style>
    

    This will make the tab text colors red and green but they can be colors that you choose. You can now declare a TabLayout in XML and have the default style apply to it without having to explicitly specify a style/theme.

    The underlying issue is that the CTORs for TabLayout do not use defStyleResource which defines the default style to apply to the view that is defined in the theme and tabWidgetStyle is not it.. (See this.) The custom layout MyTabLayout circumvents the problem. However, this does raise the question about whether there is a reason the defStyleResource is not used and maybe we are diving into a briar patch that we may best avoid.

    These methods do work, but I have not fully tested them. You will have to make the determination about whether it is better to do what is described here or to simply define the style on a view-by-view basis.

    If someone has a standard, Android-approved way to specify TabLayout attributes globally, I will be glad to hear about it.