为什么从Android中的代码设置样式这么复杂

时间:2011-12-03 17:27:18

标签: android themes

如果你想设置你从代码创建的Button的样式,你必须做这样的事情;

Button  btn  = new Button (mActivity, null, R.attr.someattribute);

在attrs.xml中,您设置了一个引用

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

在styles.xml中,您可以定义主题

<resources>
  <style name="Theme.SomeTheme" parent="android:style/Theme.Black">
     <item name="someStyleRef">@style/someStyle</item>
  </style>
</resources>

styles.xml中的lates定义为例如

<style name="someStyle">
        <item name="android:layout_width">2px</item>
        <item name="android:layout_height">fill_parent</item>
        <item name="android:background">@drawable/actionbar_compat_separator</item>
 </style>

这是有效的,根据我的理解,这是从Android中的代码在视图上设置样式的方法。这似乎过于复杂。按钮的第三个构造函数Argument可以很容易地接受样式ID R.style.XXX

有谁能解释为什么需要这种额外的复杂性?

1 个答案:

答案 0 :(得分:65)

它与使用Views的Android中的鼓励模式有关。这不是您想要做的事情的预期方法。首先,我将解释这种机制的用途,然后为您的应用建议一种方法。

获取attr资源的View构造函数的第三个参数通常在实现View子类时使用,如您所示,允许您指定一个theme属性作为View的默认样式的引用。如果你有一个名为AwesomeButton的特殊按钮,你可以实现它的构造函数:

public class AwesomeButton extends Button {
    public AwesomeButton(Context context) {
        this(context, null);
    }

    public AwesomeButton(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.awesomeButtonStyle);
    }

    public AwesomeButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr) {
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.AwesomeButton, defStyleAttr, 0);
        // Read AwesomeButton-specific style attributes from a
        a.recycle();
    }

    // More code
}

当Android的LayoutInflater膨胀视图时,它使用带有参数(Context, AttributeSet)的2参数构造函数。 R.attr常量传递给3参数版本,然后传递到Button调用中super的3参数构造函数。这意味着Button将根据主题中指定的AwesomeButton默认样式读取其封装内容的默认样式信息。 Android中的某些视图与其超类只有在他们使用的默认样式中不同。 (Button实际上就是其中之一。)

您在自己的风格中指定android:layout_widthandroid:layout_height,但这可能会有问题。 LayoutParams(以layout_开头的任何属性)特定于父视图,而不是它们出现的视图。这就是为什么你总是将预期的父视图作为第二个参数传递给LayoutInflater#inflate - 它告诉inflater哪个类应该负责解释LayoutParams。如果你跳过这个,你会经常发现你的LayoutParams没有按照你的预期行事,并且经常被忽略。按照惯例,我们不会将LayoutParams置于样式中,即使在某些特殊情况下它也有效。

看起来你正试图将一种风格用作一种模板。有没有理由不为此使用布局资源并在那里指定样式?

final LayoutInflater inflater = LayoutInflater.from(mActivity);
Button btn = (Button) inflater.inflate(R.layout.styled_button, parentView, false);

RES /布局/ styled_button.xml:

<Button android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/my_button_background"
        [...] />