以编程方式为TextView设置样式

263

我正在尝试使用带有样式的 TextView 构造函数,就像这样:

TextView myText = new TextView(MyActivity.this, null, R.style.my_style);

不过,当我这样做时,文本视图似乎没有采用样式(我通过在静态对象上设置样式来验证了这一点)。

我还尝试使用myText.setTextAppearance(MyActivity.this, R.style.my_style),但它也无效。

13个回答

331

我认为你无法通过编程方式设置样式。为了解决这个问题,您可以创建一个模板布局xml文件,并分配样式。例如,在res/layout中创建tvtemplate.xml,其中包含以下内容:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="This is a template"
        style="@style/my_style" />

然后通过inflate方法实例化您的新TextView:

TextView myText = (TextView)getLayoutInflater().inflate(R.layout.tvtemplate, null);

1
这个方案胜过Dandre Allison的答案之处在于它不需要API v11。 - Martin Capodici
2
可能它能够工作,但这没有意义。如果不是通过编程方式,那么它怎么可能被设置为xml文件呢?有一些不使用xml文件的原因,其中之一是我习惯于编写代码来构建UI,所以我不想学习新的方法,因为我的记忆有限,我想留出一些空间做其他事情。 - Iharob Al Asimi
@IharobAlAsimi 没错,所有的事情都可以通过编程实现,而LayoutInflater则了解如何将XML转换为对象。不幸的是,许多这样的事情都隐藏在私有API下,创建某些对象的唯一方法是向构造函数提供AttributeSet。 - Miha_x64
与其他所有答案不同,这个可以在旋转器中正常工作,所以你获得了金星。 - Rowan Berry
只有这个解决方案适用于我样式中定义的所有属性,不像使用4个参数构造函数的解决方案(例如,textAppearance定义为样式)。 - Myroslav
唯一有用的答案! - Zhou Hongbo

131

你可以创建一个通用的样式并在多个文本视图上重复使用,就像下面这个例子:

textView.setTextAppearance(this, R.style.MyTextStyle);

编辑: this 指的是 Context 对象。


55
只要你只是调整文本的“样式”,你的答案就有效。但如果你想对 TextView 做其他事情,比如添加填充或边距,则无效。所以,我不是说你错了,但你的答案有限。运行时膨胀视图是应用全面样式的唯一方法。 - Bill Mote
1
使用充气模板和使用setTextAppearance两种解决方案都是合理的。为了解决第二种解决方案中的填充/边距问题,您可以调用textView.setLayoutParams(layoutParams)。 - AlanKley
该方法已在API 23中被弃用,请使用setTextAppearance(int)代替。 - Sergii
请确保版本至少为API 23(M)。 - Mouhamed Ndiaye
5
请使用支持库中的方法TextViewCompat.setTextAppearance - Rubin Yoo

104
你可以像这样将 ContextThemeWrapper 传递给构造函数:
TextView myText = new TextView(new ContextThemeWrapper(MyActivity.this, R.style.my_style));

1
太棒了!这是我在支持API 8的同时能够以编程方式设置样式的唯一方法。 - tbraun
1
由于某种原因,它不会影响我的按钮样式。我已经定义了我的样式如下:<style name="TabButton"><item name="android:background">@drawable/tabbar_button_bkgnd</item>...</style>。然后我使用以下构造函数调用它:new Button(new ContextThemeWrapper(context, R.style.TabButton));。但是按钮完全忽略了我的设置。我在这里做错了什么吗? - Aleks N.
@AlaksiejN. 你应该像这样继承 Widget.Button 样式:parent="@android:style/Widget.Button" - maxcanna
@maxcanna 由于某些原因,添加 parent 没有改变任何东西。所以我选择了使用 inflate 解决方案。 - Aleks N.
11
你需要使用三个参数的构造函数,例如 new Button(new ContextThemeWrapper(context, R.style.TabButton), null, 0)。否则,将应用默认的按钮样式,这个样式会覆盖你通过ContextThemeWrapper合并到主题中的按钮样式。 - Newtonx
显示剩余2条评论

18

您可以在构造函数中设置样式(但样式无法动态更改/设置)。

View(Context, AttributeSet, int)int 是当前主题中包含引用样式的属性)

Romain Guy的回答

参考文献


你的回答有误导性 - 你是否尝试阅读完整个帖子? - andr
4
为什么会误导人呢?因为Romain Guy明确表示你可以通过编程来“设置”样式,但实际上并没有提供动态更改的功能。然而,这个讨论似乎并不认同这一点。 - Dandre Allison
1
请注意,此调用需要API级别11。 - Martin Capodici
1
@PaulVerest 这不是一句引语。 - Dandre Allison
6
最后一个整数不是样式资源,而是“当前主题中包含对一个样式资源的引用的属性,该样式资源为视图提供默认值”。 - rve
显示剩余4条评论

15

参数 int defStyleAttr 没有指定样式。来自 Android 文档:

defStyleAttr - 当前主题中包含对样式资源的引用,该样式资源提供视图默认值的属性。可以为 0,以不查找默认值。

在 View 构造函数中设置样式,我们有两种可能的解决方案:

  1. 使用 ContextThemeWrapper:

    ContextThemeWrapper wrappedContext = new ContextThemeWrapper(yourContext, R.style.your_style);
    TextView textView = new TextView(wrappedContext, null, 0);
    
  2. 使用四个参数的构造函数(从LOLLIPOP版本开始提供):

    TextView textView = new TextView(yourContext, null, 0, R.style.your_style);
    

两种解决方案的关键 - defStyleAttr 参数应该设为0,才能将我们的样式应用于视图。


1
谢谢你的答复。我可以确认这对我有效。特别感谢你在答复末尾的注释。 - Danish Ansari
样式设置在 margin 属性上不起作用,为什么? - deadfish

5
动态更改样式目前不受支持。您必须通过XML在视图创建之前设置样式。

22
实际上,这恰巧是同一件事情。 - njzk2
@njzk2 谢谢...显然在发布之前没有考虑他的评论。在构建UI之前,无法以编程方式设置样式,因此相同的代码将用于以编程方式更改或设置样式。 - Chris Cashwell

3

我也遇到了这个问题,并找到了通过程序设置样式的方法。也许大家都需要它,因此我在此更新。

View构造函数的第三个参数接受你的主题中attr的类型作为源代码如下:

public TextView(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.textViewStyle);
}

所以你必须使用 R.attr.** 而不是 R.style.** 这种类型。

在我的代码中,我执行了以下步骤:

首先,在 attr.xml 中自定义一个用于主题的定制化属性。

<attr name="radio_button_style" format="reference" />

其次,在 style.xml 文件中指定你使用的主题样式。

 <style name="AppTheme" parent="android:Theme.Translucent">
    <!-- All customizations that are NOT specific to a particular API-level can go here. -->
    <item name="radio_button_style">@style/radioButtonStyle</item>
</style>
<style name="radioButtonStyle" parent="@android:style/Widget.CompoundButton.RadioButton">
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">64dp</item>
    <item name="android:background">#000</item>
    <item name="android:button">@null</item>
    <item name="android:gravity">center</item>
    <item name="android:saveEnabled">false</item>
    <item name="android:textColor">@drawable/option_text_color</item>
    <item name="android:textSize">9sp</item>
</style>

最后,使用它!
            RadioButton radioButton = new RadioButton(mContext, null, R.attr.radio_button_style);

动态创建的视图将使用您主题中指定的样式。 您可以尝试一下,希望它能完美地为您工作。

3

当使用可能使用样式继承(甚至可定制的属性)的自定义视图时,您需要修改第二个构造函数以避免丢失样式。这对我有用,无需使用setTextAppearence()

public CustomView(Context context, AttributeSet attrs) {
    this(context, attrs, attrs.getStyleAttribute());
}

3
我们可以使用TextViewCompact.setTextAppearance(textView, R.style.xyz)。可参考Android文档

3
接受的答案对我来说是一个很好的解决方案。唯一需要补充的是关于inflate()方法。
在接受的答案中,所有的android:layout_*参数都不会被应用。
原因是没有办法进行调整,因为将null作为ViewGroup parent传递了。
可以像这样使用它: View view = inflater.inflate(R.layout.view, parent, false);parentViewGroup,从哪里想要调整android:layout_*
在这种情况下,所有相关属性都将被设置。
希望对某人有用。

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