web-dev-qa-db-ja.com

Androidのコードからスタイルを設定するのがとても複雑な理由

コードから作成したボタンのスタイルを設定したい場合は、次のようにする必要があります。

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の後半では次のように定義されています。

<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のコードからビューにスタイルを設定する方法です。これは複雑すぎるようです。ボタンの3番目のコンストラクターArgumentは、スタイルID R.style.XXXを簡単に受け入れることができます。

なぜこの余分な複雑さが必要なのか誰でも説明できますか?

35
Glenn Bech

これは、ビューの使用に関するAndroid内の推奨されるパターンに関係しています。これは、あなたがしようとしているように見えるものに対して意図されたアプローチではありません。最初に、このメカニズムについて説明しますアプリのアプローチを提案し、提案します。

Attrリソースを使用するViewコンストラクターの3番目の引数は、一般に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は実際にはこれらの1つです)。

スタイルにはAndroid:layout_widthAndroid:layout_heightを指定しますが、これは問題になる可能性があります。 LayoutParamslayout_で始まるすべての属性)は、それらが表示されるビューではなく、親ビューに固有です。これが、目的の親ビューを常に2番目のパラメータとしてLayoutInflater#inflateに渡す理由です。これにより、インフレータにLayoutParamsの解釈を担当するクラスが通知されます。これをスキップすると、LayoutParamsが期待どおりに動作せず、完全に無視されることがよくあります。慣例により、LayoutParamsをスタイルで使用することはありません。ただし、一部の特殊なケースでは機能します。

スタイルを一種のテンプレートとして使用しようとしているようです。これにレイアウトリソースを使用せず、そこでスタイルを指定する理由はありますか?

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

res/layout/styled_button.xml:

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