API 级别 21 之前的默认样式资源

Posted

技术标签:

【中文标题】API 级别 21 之前的默认样式资源【英文标题】:Default Style Resource pre API Level 21 【发布时间】:2017-07-31 01:32:07 【问题描述】:

我希望创建一个自定义 ViewGroup 以在库中使用;其中包含一些 ImageButton 对象。我希望能够为每个ImageButton 应用一个样式;但我无法弄清楚如何以编程方式应用样式,除非将属性资源应用于defStyleAttr 参数;像这样:

mImageButton = new ImageButton(
        getContext(),                    // context
        null,                            // attrs
        R.attr.customImageButtonStyle);  // defStyleAttr

问题在于,更改每个ImageButton 样式的唯一方法是将样式应用于父主题中的此属性。但我希望能够设置默认样式,而不必为使用此库的每个项目手动设置此属性。

有一个参数完全符合我的要求; defStyleRes,可以这样使用:

mImageButton = new ImageButton(
        getContext(),                    // context
        null,                            // attrs
        R.attr.customImageButtonStyle,   // defStyleAttr
        R.style.customImageButtonStyle); // defStyleRes

此参数仅适用于 API 级别 21 及以上,但我的项目针对 API 级别 16 及以上。那么如何设置defStyleRes,或者应用默认样式,而不访问这个参数呢?


按照@EugenPechanec 的建议,我使用ContextThemeWrapper 应用了我的样式,这似乎效果很好,但是每个ImageButton 现在都有默认的ImageButton 背景,即使我的样式应用了<item name="android:background">@null</item>

这是我使用的样式:

<style name="Widget.Custom.Icon" parent="android:Widget">
    <item name="android:background">@null</item>
    <item name="android:minWidth">56dp</item>
    <item name="android:minHeight">48dp</item>
    <item name="android:tint">@color/selector_light</item>
</style>

这就是我的应用方式:

ContextThemeWrapper wrapper = new ContextThemeWrapper(getContext(), R.style.Widget_Custom_Icon);
mImageButton = new AppCompatImageButton(wrapper);

左边是我得到的,右边是我想要的样子:

【问题讨论】:

【参考方案1】:

defStyleAttr 用于从主题属性解析默认小部件样式。

示例:AppCompatCheckBox 请求 R.attr.checkBoxStyle。您的主题定义了&lt;item name="checkBoxStyle"&gt;@style/Widget.AppCompat.CheckBox&lt;/item&gt;

如果您的主题中未定义该属性,小部件将获取其defStyleRes,例如R.style.Widget_AppCompat_CheckBox.

请注意,这些不是小部件使用的实际值。

我没有看到在框架之外使用defStyleRes构造函数参数。但是,在向TypedArray 询问资源时,会使用所有这些参数(加上默认值)。

如何真正解决您的问题

所以四参数构造函数并不是在所有平台上都可用。您需要找到一种方法来输入您的默认样式。考虑一下您要应用的样式:

<style name="MyImageButtonStyle" parent=""> ... </style>

您需要一种将其转换为defStyleAttr 参数的方法。在主题叠加层上定义默认样式:

<style name="MyImageButtonThemeOverlay" parent="">
    <!-- AppCompat widgets don't use the android: prefix. -->
    <item name="imageButtonStyle">@style/MyImageButtonStyle</item>
</style>

现在您可以使用此主题叠加创建您的ImageButton

// When creating manually you have to include the AppCompat prefix.
mImageButton = new AppCompatImageButton(
    new ContextThemeWrapper(getContext(), R.style.MyImageButtonThemeOverlay)
);

您不需要指定任何其他参数,因为AppCompatImageButton 默认会拾取R.attr.imageButtonStyle


如果这看起来很麻烦,您可以随时从您指定 style="@style/MyImageButtonStyle" 属性的 XML 中扩充您的自定义视图层次结构或单个小部件。

【讨论】:

ContextThemeWrapper 似乎是正确的方法。我以前遇到过它,但它完全不在我的脑海里。不幸的是,它引起了另一个问题;出于某种原因,它在每个ImageButton 上添加了一个背景资源。我可以通过设置mImageButton.setBackgroundResource(0) 在事后删除背景,但我不能在我的样式资源中使用&lt;item name="android:background"&gt;@null&lt;/item&gt; 执行此操作,即使我可以更改其他属性。知道是什么原因造成的吗? @Bryan 好的,请在您的问题末尾发布更改内容,我会调查一下。 @Bryan 你混合了风格和主题(主题覆盖)。 不要跳过主题覆盖。 不要将样式资源 ID 与 ContextThemeWrapper 一起使用。不要在主题中定义样式属性,反之亦然。这可能会帮助你chris.banes.me/2014/11/12/theme-vs-style 啊,我明白了,谢谢。不过,我不能动态应用样式,所以如果 ContextThemeWrappertheme 应用于 ImageButton;除了在主题覆盖中这样做之外,还有一种方法可以在创建时将 style 应用于ImageButton @Bryan 在这种情况下,这种方法(一组相应的主题覆盖和样式)可能不是最佳选择。您可能希望在 Java 中的构造函数之后设置小部件的样式。

以上是关于API 级别 21 之前的默认样式资源的主要内容,如果未能解决你的问题,请参考以下文章

如何为 google map api v2 设置默认位置和缩放级别?

API 级别低于 21 的状态栏颜色不变

WPF-06 样式(Style)

camera2(API 级别 21)是不是暴露了所有旧摄像头?

Visual Studio:Android SDK 设置(API 级别 19 和 21、22、23)

Unity要求Android SDK为最低API级别21