一个视图中的多个样式值

40

我在styles.xml中定义了2种样式,现在想将它们应用到一个TextView上。如何使用style="@style/"实现?

6个回答

45

无法直接实现,您需要创建一个将两种样式结合在一起的样式。(或者创建一个继承自其中一种样式的样式,并添加第二个样式的额外数据)。


1
任何代码都会有帮助 - Mohd Qasim

19

你可以创建一个继承其他样式的样式。

例如:

<style name="Side_Menu_Button" parent="android:attr/buttonStyleSmall">
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">match_parent</item>
</style>

侧边菜单按钮继承了buttonStyleSmall的所有属性。


7
如果您想使用自己的样式,请使用"@styles/mystyle"而不是android:attr。 - Ron
抱歉,buttonStyleSmall是默认的Android样式之一。 - hook38
1
我个人认为这是最好的答案。我有一个“StandardViewSize”样式,被“StandardButton”继承,进而被“LoginButton”继承。本质上是一整个样式树,尽可能通用地开始,并将它们组合成更具体的组件样式,以创建统一的外观和感觉。 - G_V
我使用了parent="@android:style/Widget.Button.Small",而不是这里显示的android:attr... - ggorlen

3
作为某些情况下可行的解决方法,您可以使用LinearLayout将目标视图包装起来,并为布局分配一个样式,为视图分配另一个样式。
<LinearLayout
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  style="@style/padding">

  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Bold text with padding"
    style="@style/text_bold" />

</LinearLayout>

2
不起作用,将样式应用于包含布局不会影响内部布局。 - tbkn23
1
@tbkn23 这不必影响内部布局。例如,您想更改背景或添加边框,可以在父布局上完成。 - peceps
@tbkn23 这取决于你想做什么。比如样式A设置背景颜色和尺寸,而样式B设置文本颜色和大小。那么你可以在布局上使用样式A,在TextView上使用样式B。这就是为什么示例被命名为它们的原因。话虽如此,这似乎是非常糟糕的做法,可以通过其他提出的答案更好地实现。作为一般规则,仅为了设置一个可以直接应用的附加样式而创建整个视图实例可能是一个坏主意。 - Benjamin Basmaci

3
这是我成功运用的一个技巧:

这是一个我成功运用的黑客技巧:

<style name="TextAppearance.Title.App" parent="TextAppearance.AppCompat.Subhead">
    <item name="android:textColor">@color/primary_text_default_material_light</item>
</style>

<style name="Custom.TV" parent="TextView.App">
    <item name="android:textAppearance">@style/TextAppearance.Other.App</item>
</style>

1
针对 Button 以及其他支持 textAttribute 属性的 View,你可以将这两种样式分为特定于 Button 的样式,该样式将被指定到 attribute:style 属性中;以及特定于文本的样式,该样式将被指定到 attribute:textAppearance 属性中。请注意,定义在 attribute:style 中的属性将覆盖在 attribute:textAppearance 中定义的值。

0

我知道我已经晚了10年,但我自己遇到了这个问题,并找到了一个解决方法,尽管它是一个相当麻烦的解决方法。

要开始,您需要声明样式属性,以便稍后分配给您的视图。

<declare-styleable name="TextView">
    <attr name="style1" format="reference" />
    <attr name="style2" format="reference" />
    <attr name="style3" format="reference" />
    <attr name="style4" format="reference" />
    <attr name="style5" format="reference" />
</declare-styleable>

您可以在布局中的视图中添加这些样式属性,例如:

<TextView
    android:id="@+id/button_1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/nice_cta"
    app:style1="@style/Background_Blue"
    app:style2="@style/CallToAction.Primary"
    app:style3="@style/Button_Layout" />

为了使其工作,您需要实现一个自定义的ViewInflater,并将其分配给应用程序主题下的viewInflaterClass。在这个ViewInflater内部,您需要收集styleable属性,并将它们合并到theme中,如下所示:
class MultiStyleViewInflater : MaterialComponentsViewInflater() {
    // override the creators of any view you want to have multiple styles
    override fun createTextView(context: Context, attrs: AttributeSet?): AppCompatTextView {
        // create context if needed and set the attributes as usual
        return super.createTextView(createContextIfMultiStyle(context, attrs), attrs)
    }
    
    // override fun anyOtherView as needed ...

    private fun createContextIfMultiStyle(context: Context, attrs: AttributeSet?): Context {
        // get our handy custom attributes
        val styleAttributes = context.obtainStyledAttributes(attrs, R.styleable.TextView)
        
        // collect the styles added to the view
        val styles = extractStyles(styleAttributes)
        
        // create the custom ContextThemeWrapper only if the view has a custom multi style attribute
        val createdContext = if (styles.any { it != 0 }) {     
            // create a theme, add styles and create the wrapper using the theme
            val theme = context.resources.newTheme()
            
            theme.applyValidStyles(styles)
            ContextThemeWrapper(context, theme)  
        } else {
            // or just return the original context
            context
        }
        
        // don't forget to call this!
        styleAttributes.recycle()
        
        return createdContext
    }

    private fun extractStyles(styleAttributes: TypedArray) = listOf(
        // the zero values help us determine if we have a custom style added at all
        styleAttributes.getResourceId(R.styleable.TextView_style1, 0),
        styleAttributes.getResourceId(R.styleable.TextView_style2, 0),
        styleAttributes.getResourceId(R.styleable.TextView_style3, 0),
        styleAttributes.getResourceId(R.styleable.TextView_style4, 0),
        styleAttributes.getResourceId(R.styleable.TextView_style5, 0)
    )

    private fun Resources.Theme.applyValidStyles(styles: List<Int>) {
        // adding styles that actually exist. note we force update duplicate attributes
        styles.filterNot { it == 0 }.forEach { this.applyStyle(it, true) }
    }
}

将以下行添加到您的应用程序主题的ViewInflater以使其生效:
<item name="viewInflaterClass">com.agostonr.multistyleapp.utils.MultiStyleViewInflater</item>

如果你构建应用程序,这样做后,样式将显示在编辑器中以及在设备上运行的应用程序中。

有关更详细的说明,请参阅我在Medium上撰写的文章。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接