Androidでスタイリング可能な属性を宣言する


81

declare-styleableコンポーネントのカスタムスタイルを宣言するためのタグに関する貴重なドキュメントがほとんどありません。タグの属性に有効な値のリストを見つけました。それはそれに関しては素晴らしいことですが、それらの値のいくつかを使用する方法を説明していません。ブラウズattr.xml(標準属性のためのAndroidのソース)を、私はあなたのようなことを行うことができますことを発見しました。formatattr

<!-- The most prominent text color.  -->
<attr name="textColorPrimary" format="reference|color" />

format属性には、明らかに値の組み合わせに設定することができます。おそらく、このformat属性は、パーサーが実際のスタイル値を解釈するのに役立ちます。次に、attr.xmlでこれを発見しました。

<!-- Default text typeface. -->
<attr name="typeface">
    <enum name="normal" value="0" />
    <enum name="sans" value="1" />
    <enum name="serif" value="2" />
    <enum name="monospace" value="3" />
</attr>

<!-- Default text typeface style. -->
<attr name="textStyle">
    <flag name="normal" value="0" />
    <flag name="bold" value="1" />
    <flag name="italic" value="2" />
</attr>

これらは両方とも、示されたスタイルに許可された値のセットを宣言しているようです。

だから私は2つの質問があります:

  1. enum値のセットの1つをとることができるスタイル属性と値のセットをとることができるスタイル属性の違いは何flagですか?
  2. declare-styleable(Androidソースコードのリバースエンジニアリング以外の)動作に関するより良いドキュメントを知っている人はいますか?

回答:


74

ここにこの質問があります:いくつかの情報を使用してカスタム属性定義しますが、それほど多くはありません。

そして、この投稿。フラグと列挙型に関する優れた情報があります。

カスタムXML属性フラグ

フラグは、値の非常に小さなサブセット、つまり属性タグの下に定義されているもののみが許可されるという点で、特別な属性タイプです。フラグは、「name」属性と「value」属性によって指定されます。名前はその属性タイプ内で一意である必要がありますが、値は一意である必要はありません。これが、Androidプラットフォームの進化中に、「fill_parent」と「match_parent」の両方が同じ動作にマッピングされた理由です。それらの値は同一でした。

name属性は、レイアウトXML内の値の場所で使用される名前にマップされ、名前空間プレフィックスを必要としません。したがって、上記の「tilingMode」では、属性値として「center」を選択しました。「伸ばす」または「繰り返す」を簡単に選択することもできましたが、それ以外は何もできませんでした。実際の値に置き換えることさえ許されなかったでしょう。

value属性は整数でなければなりません。16進数または標準の数字表現の選択はあなた次第です。Androidコード内には、両方が使用されている場所がいくつかあり、Androidコンパイラはどちらかを喜んで受け入れます。

カスタムXML属性列挙型

列挙型は、1つのプロビジョニングを持つフラグとほぼ同じ方法で使用され、整数と交換可能に使用できます。内部的には、列挙型と整数は同じデータ型、つまり整数にマップされます。整数で属性定義に表示される場合、列挙型は常に悪い「マジックナンバー」を防ぐのに役立ちます。これが、ディメンション、整数、または名前付き文字列「fill_parent」のいずれかを持つ「android:layout_width」を持つことができる理由です。

これをコンテキストに入れるために、整数または文字列「scroll_to_top」のいずれかを受け入れる「layout_scroll_height」というカスタム属性を作成するとします。これを行うには、「整数」形式の属性を追加し、それに続いて列挙型を追加します。

<attr name="layout_scroll_height" format="integer">  
    <enum name="scroll_to_top" value="-1"/> 
</attr>

この方法で列挙型を使用する場合の1つの条件は、カスタムビューを使用する開発者が、意図的に値「-1」をレイアウトパラメーターに配置できることです。これにより、「scroll_to_top」の特殊なケースのロジックがトリガーされます。このような予期しない(または予期される)動作は、列挙値の選択が不十分な場合、ライブラリを「レガシーコード」の山にすぐに委ねる可能性があります。


私が見ているように、属性に実際に追加できる実際の値は、属性から取得できるものによって制限されます。その他のヒントAttributeSetについては、こちらのクラスリファレンスを確認してください。

あなたは得ることができます:

  • ブール値(getAttributeBooleanValue)、
  • フロート(getAttributeFloatValue)、
  • ints(getAttributeIntValue)、
  • ints(as getAttributeUnsignedIntValue)、
  • および文字列(getAttributeValue

それらのリンクをありがとう。静的に型付けされたブログは特に素晴らしいです。これを解決済みとしてマークするのは、「実際のドキュメント」に十分近いです。
テッドホップ

@テッド確かに、その投稿には素晴らしい情報があります。とにかく、再利用可能なビューのライブラリを作成したり、フレームワークを拡張したりしない限り、これらの列挙型について心配する必要はありません。Bools、float、int、stringsを使用すると、通常のカスタムビューを利用できます。つまり、もちろん、質問が非常に健康的な好奇心を満たすことだけではなかった場合:)
Aleadam

@ Aleadam-質問は実際のアプリケーションによって動機付けられました。私はカスタム属性を使用していて、新しい属性を追加する必要がありました。これは、新しい属性の適切なフォーマットが列挙型であることが判明していますが、提供されたリンクを読んでまで、私が使用しての違い上の任意の情報を見つけることができなかったenumとしますflag
テッドホップ

@Tedお役に立ててうれしいです。そのブログには他にも面白いと思われる投稿がいくつかありますが、私はそれをざっと読んだだけですが、まだそれらを読む時間がありませんでした。
Aleadam

3
私のブログをリンクしている人には+1。私は実際に、これらのタグのそれぞれが何に使用されているかを解析しようとしました。それは完全ではないかもしれず、おそらく時間が経つにつれて更新されるべきですが、誰かがそれに追加するものがあれば、私はすべての耳です。
ウィーティーズ

70

@Aleadamの答えは非常に役立ちますが、enumとの大きな違いが1つ省略されていflagます。前者は、1つを選択することを目的としており、ビューに対応する属性を割り当てるときに1つの値のみを選択します。ただし、後者の値は、ビット単位のOR演算子を使用して組み合わせることができます。

例、 res/values/attr.xml

<!-- declare myenum attribute -->
<attr name="myenum">
    <enum name="zero" value="0" />
    <enum name="one" value="1" />
    <enum name="two" value="2" />
    <enum name="three" value="3" />
</attr>

<!-- declare myflags attribute -->
<attr name="myflags">
    <flag name="one" value="1" />
    <flag name="two" value="2" />
    <flag name="four" value="4" />
    <flag name="eight" value="8" />
</attr>

<!-- declare our custom widget to be styleable by these attributes -->
<declare-styleable name="com.example.MyWidget">
    <attr name="myenum" />
    <attr name="myflags" />
</declare-styleable>

ではres/layout/mylayout.xml私たちが今できること

<com.example.MyWidget
    myenum="two"
    myflags="one|two"
    ... />

したがって、列挙型は可能な値の1つを選択しますが、フラグは組み合わせることができます。数値はこの違いを反映している必要があります。通常は、シーケンスを0,1,2,3,...列挙型(たとえば、配列インデックスとして使用)に配置し、フラグを移動1,2,4,8,...して、ビット単位のOR|を使用してフラグを組み合わせて個別に追加または削除できるようにします。

2の累乗ではない値を使用して「メタフラグ」を明示的に定義できるため、一般的な組み合わせの一種の省略形を導入できます。たとえば、これをmyflags宣言に含めた場合

<flag name="three" value="3" />

その場合myflags="three"、の代わりにmyflags="one|two"、と完全に同じ結果を書き込むことができます3 == 1|2

個人的に、私は常に含めるのが好きです

<flag name="none" value="0" /> <!-- or "normal, "regular", and so on -->
<flag name="all" value="15" /> <!-- 15 == 1|2|4|8 -->

これにより、すべてのフラグを一度に設定解除または設定できるようになります。

もっと微妙に、あるフラグが別のフラグによって暗示されている場合があります。したがって、この例では、eight設定されているフラグがfourフラグを強制的に設定する必要があると想定します(まだ設定されていない場合)。次にeightfourフラグを事前に含めるように再定義できます。

<flag name="eight" value="12" /> <!-- 12 == 8|4 -->

最後に、ライブラリプロジェクトで属性を宣言しているが、それらを別のプロジェクトのレイアウト(libに依存)に適用する場合は、XMLルート要素でバインドする必要がある名前空間プレフィックスを使用する必要があります。例えば、

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:auto="http://schemas.android.com/apk/res-auto"
    ... >

    <com.example.MyWidget
        auto:myenum="two"
        auto:myflags="one|two"
        ... />

</RelativeLayout>

良い点。プログラミング言語の列挙型に精通している人にとって、フラグと列挙型の違いは第二の性質だと思います。(私はあなたが言っていた点がAleadamの答えから欠落していることにさえ気づいていませんでした。)これは間違いなく有用な追加情報です。+1
Ted Hopp 2014年

この回答は、カスタム属性を定義するときに列挙型とフラグを区別する方法を明確に示しています。フラグを使用するのに明らかに役立ちました:)+ 1
ptitvinou 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.