web-dev-qa-db-ja.com

カスタム属性の定義

com.Android.R.attrのように自分の属性を実装する必要があります。

公式ドキュメントには何も見つからなかったので、これらの属性を定義する方法と私のコードからそれらをどのように使用するかについての情報が必要です。

437

現在最良のドキュメントがソースです。あなたはそれを見ることができる ここで(attrs.xml)

属性は先頭の<resources>要素内または<declare-styleable>要素内に定義できます。 attrを複数の場所で使うつもりなら、それをルート要素に入れます。すべての属性は同じグローバル名前空間を共有します。つまり、<declare-styleable>要素の内側に新しい属性を作成したとしても、その属性をその外側で使用することはできますが、同じ名前で異なるタイプの別の属性を作成することはできません。

<attr>要素には、2つのxml属性nameformatがあります。 nameはあなたがそれを何かと呼ぶことを可能にします、そしてこれはあなたがコードでそれを参照することになってしまう方法です、例えばR.attr.my_attributeformat属性は、必要な属性の 'type'に応じて異なる値をとることができます。

  • reference - 他のリソースIDを参照する場合(例: "@ color/my_color"、 "@ layout/my_layout")
  • ブール値
  • 寸法
  • float
  • 整数
  • ひも
  • 分数
  • enum - 通常は暗黙的に定義されています
  • flag - 通常は暗黙的に定義されています

|を使用して、フォーマットを複数のタイプに設定できます(例:format="reference|color")。

enum属性は次のように定義できます。

<attr name="my_enum_attr">
  <enum name="value1" value="1" />
  <enum name="value2" value="2" />
</attr>

flag属性は似たようなものですが、値を定義する必要がある点が異なります。

<attr name="my_flag_attr">
  <flag name="fuzzy" value="0x01" />
  <flag name="cold" value="0x02" />
</attr>

属性に加えて、<declare-styleable>要素があります。これにより、カスタムビューで使用できる属性を定義できます。 <attr>要素を指定してこれを行います。以前に定義されている場合はformatを指定しません。 Android attr、たとえばAndroid:gravityを再利用したい場合は、以下のようにnameでそれを実行できます。

カスタムビュー<declare-styleable>の例:

<declare-styleable name="MyCustomView">
  <attr name="my_custom_attribute" />
  <attr name="Android:gravity" />
</declare-styleable>

カスタムビューでカスタム属性をXMLで定義するときには、いくつかの作業が必要です。まずあなたの属性を見つけるために名前空間を宣言しなければなりません。これはルートレイアウト要素で行います。通常はxmlns:Android="http://schemas.Android.com/apk/res/Android"しかありません。 xmlns:whatever="http://schemas.Android.com/apk/res-auto"も追加する必要があります。

例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:Android="http://schemas.Android.com/apk/res/Android"
  xmlns:whatever="http://schemas.Android.com/apk/res-auto"
  Android:orientation="vertical"
  Android:layout_width="fill_parent"
  Android:layout_height="fill_parent">

    <org.example.mypackage.MyCustomView
      Android:layout_width="fill_parent"
      Android:layout_height="wrap_content"
      Android:gravity="center"
      whatever:my_custom_attribute="Hello, world!" />
</LinearLayout>

最後に、そのカスタム属性にアクセスするには、通常、カスタムビューのコンストラクタで次のようにしてアクセスします。

public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);

  TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);

  String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);

  //do something with str

  a.recycle();
}

終わり。 :)

914
Rich Schuler

Qberticusの答えは良いですが、有用な詳細が1つ欠けています。これらをライブラリに実装している場合は、以下を置き換えます。

xmlns:whatever="http://schemas.Android.com/apk/res/org.example.mypackage"

と:

xmlns:whatever="http://schemas.Android.com/apk/res-auto"

それ以外の場合は、ライブラリを使用するアプリケーションにランタイムエラーが発生します。

84
Neil Miller

上記の答えは、いくつかのことを除いて、すべてを詳細に網羅しています。

まず、スタイルがない場合は、(Context context, AttributeSet attrs)メソッドシグネチャを使用して設定をインスタンス化します。この場合はcontext.obtainStyledAttributes(attrs, R.styleable.MyCustomView)を使ってTypedArrayを取得してください。

第二に、それはplauralsリソース(数量文字列)を扱う方法をカバーしていません。これらはTypedArrayを使って処理することはできません。これは私のSeekBarPreferenceからのコードスニペットで、設定の値に従って設定された設定値の概要を設定します。設定のxmlがAndroid:summaryをテキスト文字列または文字列リソースに設定している場合、設定の値は文字列にフォーマットされます(値を取得するには%dが必要です)。 Android:summaryがplauralsリソースに設定されている場合は、それが結果のフォーマットに使用されます。

// Use your own name space if not using an Android resource.
final static private String Android_NS = 
    "http://schemas.Android.com/apk/res/Android";
private int pluralResource;
private Resources resources;
private String summary;

public SeekBarPreference(Context context, AttributeSet attrs) {
    // ...
    TypedArray attributes = context.obtainStyledAttributes(
        attrs, R.styleable.SeekBarPreference);
    pluralResource =  attrs.getAttributeResourceValue(Android_NS, "summary", 0);
    if (pluralResource !=  0) {
        if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
            pluralResource = 0;
        }
    }
    if (pluralResource ==  0) {
        summary = attributes.getString(
            R.styleable.SeekBarPreference_Android_summary);
    }
    attributes.recycle();
}

@Override
public CharSequence getSummary() {
    int value = getPersistedInt(defaultValue);
    if (pluralResource != 0) {
        return resources.getQuantityString(pluralResource, value, value);
    }
    return (summary == null) ? null : String.format(summary, value);
}

  • これはほんの一例ですが、設定画面でサマリーを設定したい場合は、設定のonDialogClosedメソッドでnotifyChanged()を呼び出す必要があります。
15
Steve Waring

従来のアプローチは、定型コードと不器用なリソース処理でいっぱいです。だから私は Spyglassフレームワーク を作ったのです。これがどのように機能するかを説明するために、Stringのタイトルを表示するカスタムビューを作成する方法を示す例を示します。

手順1:カスタムビュークラスを作成します。

public class CustomView extends FrameLayout {
    private TextView titleView;

    public CustomView(Context context) {
        super(context);
        init(null, 0, 0);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0, 0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr, 0);
    }

    @RequiresApi(21)
    public CustomView(
            Context context, 
            AttributeSet attrs,
            int defStyleAttr,
            int defStyleRes) {

        super(context, attrs, defStyleAttr, defStyleRes);
        init(attrs, defStyleAttr, defStyleRes);
    }

    public void setTitle(String title) {
        titleView.setText(title);
    }

    private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        inflate(getContext(), R.layout.custom_view, this);

        titleView = findViewById(R.id.title_view);
    }
}

ステップ2:values/attrs.xmlリソースファイルに文字列属性を定義します。

<resources>
    <declare-styleable name="CustomView">
        <attr name="title" format="string"/>
    </declare-styleable>
</resources>

ステップ3:@StringHandlerアノテーションをsetTitleメソッドに適用して、ビューが膨らんだときに属性値をこのメソッドにルーティングするようにSpyglassフレームワークに指示します。

@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
    titleView.setText(title);
}

クラスにSpyglassアノテーションが追加されたので、Spyglassフレームワークはコンパイル時にそれを検出し、自動的にCustomView_SpyglassCompanionクラスを生成します。

手順4:カスタムビューのinitメソッドで生成されたクラスを使用する

private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    inflate(getContext(), R.layout.custom_view, this);

    titleView = findViewById(R.id.title_view);

    CustomView_SpyglassCompanion
            .builder()
            .withTarget(this)
            .withContext(getContext())
            .withAttributeSet(attrs)
            .withDefaultStyleAttribute(defStyleAttr)
            .withDefaultStyleResource(defStyleRes)
            .build()
            .callTargetMethodsNow();
}

それでおしまい。 XMLからクラスをインスタンス化すると、Spyglassコンパニオンは属性を解釈して必要なメソッド呼び出しを行います。たとえば、次のレイアウトを展開すると、引数として"Hello, World!"を付けてsetTitleが呼び出されます。

<FrameLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:width="match_parent"
    Android:height="match_parent">

    <com.example.CustomView
        Android:width="match_parent"
        Android:height="match_parent"
        app:title="Hello, World!"/>
</FrameLayout>

フレームワークは文字列リソースに限定されず、他のリソースタイプを処理するためのさまざまなアノテーションがあります。メソッドに複数のパラメータがある場合は、デフォルト値を定義し、プレースホルダ値を渡すための注釈もあります。

より多くの情報と例についてはGithubレポジトリを見てください。

4
Helios