安卓:以编程方式设置视图样式

286

以下是XML:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/LightStyle"
    android:layout_width="fill_parent"
    android:layout_height="55dip"
    android:clickable="true"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />

</RelativeLayout>

如何以编程方式设置style属性?


看这里'response',这对我有用。 - Artificioo
哎呀,我上一个问题可能是一个重复的问题。 - SebasSBM
16个回答

271

从技术上讲,您可以以编程方式应用样式,无论是使用自定义视图还是其他方式:

private MyRelativeLayout extends RelativeLayout {
  public MyRelativeLayout(Context context) {
     super(context, null, R.style.LightStyle);
  }
}

当您以编程方式实例化视图时,可以使用单参数构造函数。

因此,请将此构造函数链接到带有样式参数的超级构造函数上。

RelativeLayout someLayout = new MyRelativeLayout(new ContextThemeWrapper(this,R.style.RadioButton));

或者像 @Dori 指出的那样简单:

RelativeLayout someLayout = new RelativeLayout(new ContextThemeWrapper(activity,R.style.LightStyle));

现在在 Kotlin 中:

class MyRelativeLayout @JvmOverloads constructor(
    context: Context, 
    attributeSet: AttributeSet? = null, 
    defStyleAttr: Int = R.style.LightStyle,
) : RelativeLayout(context, attributeSet, defStyleAttr)
或者
 val rl = RelativeLayout(ContextThemeWrapper(activity, R.style.LightStyle))

39
您可以直接以编程的方式完成此操作,而无需扩展,因为三参数构造函数本身是公共的,具体请参见http://developer.android.com/reference/android/widget/RelativeLayout.html#RelativeLayout(android.content.Context, android.util.AttributeSet, int)。 - Dori
2
@Dori 你会为 AttributeSet 传递什么? - Blundell
11
如果它是一个TextView,你需要使用setTextAppearance(R.style.small_text)来设置那些影响文本(大小、颜色等)的属性。 - Maragues
3
不知道为什么第三参数方法不起作用,我尝试应用于“按钮”。@Benjamin Piette的方法有效。 - Youngjae
1
对我没用。我注意到还有另一个构造函数:public View (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes),而我的样式在styles.xml中,所以也许那个会起作用。但是API级别对我来说太高了(添加于API 21)。 - Rock Lee
显示剩余8条评论

152

对我有效的方法:

Button b = new Button(new ContextThemeWrapper(this, R.style.ButtonText), null, 0);
  • 使用ContextThemeWrapper

AND

  • 使用三个参数的构造函数(没有这个就无法工作)

使用您的方法可以解决问题,但我遇到了W/ResourceType: Too many attribute references, stopped at: 0x01010034错误。有什么解决方法吗? - HaloMediaz
我从未见过这个问题。那可能是设备特定的问题,或者与最近的Android版本有关? - Benjamin Piette
14
重要!这种方法确实可行,但如果使用 ContextThemeWrapper 创建新布局,则会将样式设置为每个子视图。 - aProperFox
1
@aProperFox,你能解释得更清楚一些吗?对我来说,只需将其添加到按钮即可正常工作。你是指布局吗? - Gilian
2
@Gilian 没错,如果你在一个包含一个或多个子视图的复合视图上使用它,那么它们将会获得与你为父视图设置样式相同的样式。这可能会导致内部视图中过多的填充或边距,如果你不注意的话,可能会让你发疯 :) - aProperFox
显示剩余3条评论

93

更新:回答这个问题的时候(2012年中期,API级别14-15),尽管有一些非常不容易的解决方法,但是在程序中设定视图还不是可行的选项,而在最近的API版本中已经变得可行了。详细信息请参见@Blundell的答案。

旧答案:

目前无法通过编程方式设置视图的样式,但您可能会发现这个线程有用。


16
不正确。请查看@Blundell的回答。 - a.bertucci
35
在某些情况下(例如使用TextView),您可以使用textView.setTextAppearance(context, R.style.mystyle);。根据文档,“从指定的TextAppearance资源设置文本颜色、大小、样式、提示颜色和突出显示颜色。”http://developer.android.com/reference/android/widget/TextView.html#setTextAppearance(android.content.Context, int) - Rogel Garcia
在 API 23 中,setTextAppearance 已被弃用。 - Arpit Patel
6
api > 23; textView.setTextAppearance(R.style.mystyle); API 大于 23;textView.setTextAppearance(R.style.mystyle); - alican akyol
1
我在4年前回答了@HaydenKai的问题。自那时以来,API已经发生了很大变化,使得可以通过编程方式应用样式。 - Korhan Ozturk
3
同意,但在像这样有活跃度的问题上,在Stack Overflow上应该重新开放问题并接受新答案。 - HaydenKai

24

对于一个新的按钮/Button 或 文本视图/TextView:

Button mMyButton = new Button(new ContextThemeWrapper(this, R.style.button_disabled), null, 0);

对于已存在的实例:

mMyButton.setTextAppearance(this, R.style.button_enabled);

对于图片或布局:

Image mMyImage = new ImageView(new ContextThemeWrapper(context, R.style.article_image), null, 0);

15

这是一个比较旧的问题,但对我现在有效的解决方法是,在视图上使用构造函数的第四个参数defStyleRes(如果可用)来设置样式。

以下是适用于我目的的代码(kotlin):

val textView = TextView(context, null, 0, R.style.Headline1)

3
请注意,此构造函数是在API 21中添加的。因此,如果您的 minSdkVersion<21,则会出现错误“调用要求API级别21(当前最小值为19)”。 - Albert Vila Calvo

11

如果您想继续使用XML(而被接受的答案不允许您这样做)并在视图创建后设置样式,则可以尝试使用Paris库,该库支持所有可用属性的子集。

由于您正在从XML中填充视图,因此需要在布局中指定一个ID:

<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_styleable_relative_layout"
    style="@style/LightStyle"
    ...

然后当您需要在填充布局之后以编程方式更改样式时:

// Any way to get the view instance will do
RelativeLayout myView = findViewById(R.id.my_styleable_relative_layout);

// This will apply all the supported attribute values of the style
Paris.style(myView).apply(R.style.LightStyle);

更多详细信息请查看:支持的视图类型和属性列表(包括背景、内边距、外边距等,可轻松扩展)以及安装说明和其他文档

免责声明:本人为该库的原始作者。


这种方法在2019年仍然推荐使用吗? - IgorGanapolsky
1
据我所知,是的!我不知道有更好的。 - Nathanael
感谢@Nathanael提供这个解决方案。还要感谢他作为维护者,更新了line_height属性。继续保持好工作!非常有用。 - Guillem Roca
是的,但请注意目前不支持使用材料组件属性。(如果我说错了,请纠正我) 我是 Android 开发新手,目前最好的解决方案是使用不同样式隐藏/显示相同的视图? - I.Step
有趣……你的这个库是否适用于Android SDK 31? - SebasSBM

6

这是我的一个简单示例,关键在于使用ContextThemeWrapper包装器,没有它,我的样式就无法生效,并且使用View的三个参数构造函数。

ContextThemeWrapper themeContext = new ContextThemeWrapper(this, R.style.DefaultLabelStyle);
TextView tv = new TextView(themeContext, null, 0);
tv.setText("blah blah ...");
layout.addView(tv);

6
提供的答案都不正确。
你可以通过编程设置样式。
简短的回答是看一下这个链接:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/content/Context.java#435 长的回答是,以下是将自定义定义的样式以编程方式设置到视图中的代码片段:
1)在styles.xml文件中创建一个样式。
 <style name="MyStyle">
    <item name="customTextColor">#39445B</item>
    <item name="customDividerColor">#8D5AA8</item>
</style>

请不要忘记在attrs.xml文件中定义您的自定义属性。
我的attrsl.xml文件:
<declare-styleable name="CustomWidget">
    <attr name="customTextColor" format="color" />
    <attr name="customDividerColor" format="color" />
</declare-styleable>

请注意,您可以为自己的可样式化(my CustomWidget)使用任何名称。

现在让我们通过编程方式将样式设置为小部件。这是我的简单小部件:

public class StyleableWidget extends LinearLayout {

private final StyleLoader styleLoader = new StyleLoader();

private TextView textView;
private View divider;

public StyleableWidget(Context context) {
    super(context);
    init();
}

private void init() {
    inflate(getContext(), R.layout.widget_styleable, this);
    textView = (TextView) findViewById(R.id.text_view);
    divider = findViewById(R.id.divider);
    setOrientation(VERTICAL);
}

protected void apply(StyleLoader.StyleAttrs styleAttrs) {
    textView.setTextColor(styleAttrs.textColor);
    divider.setBackgroundColor(styleAttrs.dividerColor);
}

public void setStyle(@StyleRes int style) {
    apply(styleLoader.load(getContext(), style));
}
}

布局:

<TextView
    android:id="@+id/text_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="22sp"
    android:layout_gravity="center"
    android:text="@string/styleble_title" />

<View
    android:id="@+id/divider"
    android:layout_width="match_parent"
    android:layout_height="1dp"/>

</merge>

最后,实现了StyleLoader类。
public class StyleLoader {

public StyleLoader() {

}

public static class StyleAttrs {
    public int textColor;
    public int dividerColor;
}

public StyleAttrs load(Context context, @StyleRes int styleResId) {
    final TypedArray styledAttributes = context.obtainStyledAttributes(styleResId, R.styleable.CustomWidget);
    return load(styledAttributes);
}

@NonNull
private StyleAttrs load(TypedArray styledAttributes) {
    StyleAttrs styleAttrs = new StyleAttrs();
    try {
        styleAttrs.textColor = styledAttributes.getColor(R.styleable.CustomWidget_customTextColor, 0);
        styleAttrs.dividerColor = styledAttributes.getColor(R.styleable.CustomWidget_customDividerColor, 0);
    } finally {
        styledAttributes.recycle();
    }
    return styleAttrs;
}
}

您可以在https://github.com/Defuera/SetStylableProgramatically找到完全可工作的示例。

18
这不是真正的样式,只是通过在视图上应用/设置保存的设置(实际上是您的XML文件中保存的某种设置)来模仿样式(这里只有两个固定的视图 - 并且 您肯定需要预先知道它们)。所有的魔法都在“apply”方法中,它并没有做任何有趣的事情。真正意义上的样式是会自动地为所有未来动态添加的视图应用某些视觉样式。当然,这段代码无法做到这一点,这里没有任何动态性,我们需要知道哪些视图和哪些属性/字段需要设置。 - King King
2
我不介意指定要应用样式的视图,但这种解决方案将样式元素硬编码(例如textColor和dividerColor)。它将无法动态查找在样式中定义的所有元素并将它们应用于视图。 - nasch
你发的链接打不开。能否请你重新发一下? - IgorGanapolsky

6

您可以通过以下方式为您的活动应用样式:

super.setTheme( R.style.MyAppTheme );

或者 Android 默认:

super.setTheme( android.R.style.Theme );

在您的活动中,在setContentView()之前。

5
@siemian,请允许FOM自由表达,不需要进行编辑! - Mika

3

简单的方法是通过构造函数传递参数

RadioButton radioButton = new RadioButton(this,null,R.style.radiobutton_material_quiz);

更详细地说明如何使用这个解决方案会更有帮助。 - IgorGanapolsky

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