在哪里放置 XML 样式

Where to put XML styles

问题 1

我希望所有 Toolbar 看起来都一样,而不是在所有这些中设置样式, 所以我虽然会为它设置应用程序主题。我希望他们都拥有的东西之一是 colorControlNormal 属性。这些是我的尝试:

代码示例 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>

代码示例 2:

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

代码示例1中,我尝试为Toolbar属性创建自定义样式,并将其设置为主题,但没有成功。

代码示例 2 中,我全局设置了该属性并起作用了。但是,也会影响其他组件,因此无效。


问题 2

我需要更改所有 TabLayouttabTextColortabSelectedTextColor 属性,所以我尝试了这个:

代码示例 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>

但它不起作用,我让它工作的唯一方法是在 XML:

中手动设置它们
<android.support.design.widget.TabLayout
    [...]
    android:background="?android:attr/colorPrimary"
    app:tabTextColor="@color/tabTextColor"
    app:tabSelectedTextColor="@color/tabSelectedTextColor" />

最后,我会对任何对好的风格和主题的引用投赞成票guides/books。我真的需要在这方面改进。

提前致谢。

全局指定Toolbar

中返回箭头的颜色

这并不像它应该的那样简单,但请尝试以下操作:

<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>

android:tint会改变返回箭头的颜色。

一般情况下,默认工具栏样式由属性toolbarStyle设置。在下面的XML中,设置为@style/MyToolbarStyleMyToolbarStyle 定义了将用于应用程序内所有工具栏的三个属性。这是一个的样子:

<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>

如您所见,MyToolbarStyle设置了三个属性的值。不幸的是,这种方法不适用于所有属性,例如 colorControlNormal,如果在主题中指定,将更改后退箭头的颜色。


关于TabLayout的问题:

TabLayout 全局定义属性没有很好的解决方案。您可以执行 this 之类的操作并仅覆盖 Widget.Design.TabLayout,但您必须重新创建在那里定义的所有属性,并且这些属性可能会根据 运行 的版本而改变。这可行,但维护起来可能是个问题。我认为这不是一个好的解决方案。

另一个解决方案是创建您自己的自定义 TabLayout。这将非常简单,只需将 tabTextAppearance 定义为 TabLayout 的全局默认属性。整个自定义布局将如下所示:

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

将对 TabLayout 的所有引用替换为 MyTabLayout 后,您将能够在 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>

这将使选项卡文本颜色为红色和绿色,但它们可以是您选择的颜色。您现在可以在 XML 中声明 TabLayout 并应用默认样式,而无需显式指定 style/theme.

根本问题是 TabLayout 的 CTOR 不使用 defStyleResource,它定义了应用于主题中定义的视图的默认样式,而 tabWidgetStyle 不是它..(参见 this。)自定义布局 MyTabLayout 绕过了这个问题。然而,这确实提出了一个问题,即是否存在未使用 defStyleResource 的原因,也许我们正在深入研究我们最好避免的石楠补丁。

这些方法确实有效,但我还没有完全测试过。您将不得不决定是按照此处描述的方式更好,还是简单地逐个视图地定义样式。

如果有人有一个标准的、Android 批准的全局指定 TabLayout 属性的方法,我将很高兴听到。


问题 1

主题风格之间的区别很微妙,事情比它们应该的更混乱,因为你定义了两者其中使用 <style> 标签。但即使它们都使用 <style> 标签,它们的行为也不同。

视图的 主题 中指定的属性适用于该视图及其所有子视图。在视图的 style 中指定的属性仅适用于该视图。此外,还有一些称为 theme attributes 的属性,只有当它们是主题的一部分时才有效。 colorControlNormal为主题属性;在样式中设置它不会有任何效果。

代码示例 1 不起作用,因为您的 <item name="colorControlNormal"> 元素是在样式而非主题中定义的。

代码示例 2 改变了一切,因为现在 colorControlNormal 在您的 app 的 主题中,所以它适用于你的应用关心它。

不幸的是,我认为没有办法简单地为您的应用程序中的每个 Toolbar 指定一个主题。您将不得不满足以下条件之一:

1) 使用android:theme属性

为此,您需要创建如下样式:

<style name="MyToolbarTheme">
    <item name="colorControlNormal">@color/toolbarColorControlNormal</item>
</style>

然后,在每个 <Toolbar> 标签上,添加

android:theme="@style/MyToolbarTheme"

2) 创建 Toolbar

的自定义子 class

同样,您需要像上面那样的样式。然后你会像这样创建一个 class:

public class MyToolbar extends Toolbar {

    public MyToolbar(Context context, AttributeSet attrs) {
        super(wrapContext(context), attrs);
    }

    private static Context wrapContext(Context context) {
        return new ContextThemeWrapper(context, R.style.MyToolbarTheme);
    }
}

并像这样在布局文件中使用它:

<com.example.Whosebug.MyToolbar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

这是通过获取 Context 工具栏实例并将其主题更改为自定义工具栏主题来实现的。由于此(包装的)上下文仅用于创建您的自定义工具栏,因此不会影响您应用中的其他视图。

对于它的价值,这就是我在我的应用程序中所做的。

问题 2

在这里,为什么更简单一些:android:tabWidgetStyleTabWidget的默认样式,而不是TabLayout。事实上,我不知道 TabLayout.

有任何定义的默认样式

同样,您有两个选择。您可以创建样式并将其应用于每个 <TabLayout> 标签,或者您可以创建自定义子 class 并使用它代替 TabLayout:

public class MyTabLayout extends TabLayout {

    public MyTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        int normalColor = ContextCompat.getColor(context, R.color.tabTextColor);
        int selectedColor = ContextCompat.getColor(context, R.color.tabSelectedTextColor);
        setTabTextColors(normalColor, selectedColor);
    }
}

进一步阅读

这是我在学习这些东西时阅读的一些material:

https://developer.android.com/guide/topics/ui/look-and-feel/themes.html

https://medium.com/google-developers/theming-with-appcompat-1a292b754b35

https://www.androiddesignpatterns.com/2016/08/contextcompat-getcolor-getdrawable.html

右键点击res文件夹,选择新建-->Android资源文件,为新文件设置同名"styles",在Available qualifiers:选择最后一项"Version" 最后设置 "Platform API level" 21.