web-dev-qa-db-ja.com

XMLを使用してカスタムAndroid UI要素を宣言する

XMLを使用してAndroid UI要素を宣言するにはどうすればよいですか?

462
Casebash

Android開発者ガイドには Building Custom Components というセクションがあります。残念ながら、 XML属性の説明 はレイアウトファイル内のコントロールの宣言のみを扱い、実際にはクラスの初期化内の値を処理しません。手順は次のとおりです。

1. values\attrs.xmlで属性を宣言します

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="Android:text"/>
        <attr name="Android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

declare-styleableタグで非修飾名が使用されていることに注意してください。 extraInformationなどの非標準のAndroid属性では、型を宣言する必要があります。スーパークラスで宣言されたタグは、再宣言することなくサブクラスで使用できます。

2.コンストラクターを作成する

初期化にAttributeSetを使用するコンストラクターが2つあるため、コンストラクターが呼び出す別の初期化メソッドを作成すると便利です。

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_Android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_Android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomViewは自動生成されたint[]リソースで、各要素は属性のIDです。属性名を要素名に追加することにより、XMLの各プロパティに対して属性が生成されます。たとえば、R.styleable.MyCustomView_Android_textには、MyCustomViewAndroid_text属性が含まれます。その後、さまざまなTypedArray関数を使用して、getから属性を取得できます。 XMLで定義されている属性が定義されていない場合、nullが返されます。もちろん、戻り値の型がプリミティブの場合を除き、その場合は2番目の引数が返されます。

すべての属性を取得したくない場合は、この配列を手動で作成できます。標準のAndroid属性のIDはAndroid.R.attrに含まれていますが、このプロジェクトの属性はR.attr

int attrsWanted[]=new int[]{Android.R.attr.text, R.attr.textColor};

あなたはnotAndroid.R.styleableで何も使用してはならないことに注意してください このスレッド 。これらの定数をすべて1か所で表示するのが便利であるため、まだドキュメントに記載されています。

3. layout\main.xmlなどのレイアウトファイルで使用します

最上位のxml要素に名前空間宣言xmlns:app="http://schemas.Android.com/apk/res-auto"を含めます。名前空間は、異なるスキーマが同じ要素名を使用しているときに発生する競合を回避する方法を提供します(詳細については、 この記事 を参照してください)。 URLは、単にスキーマを一意に識別する方法です- そのURLで実際にホストする必要はありません 。これが何もしていないように見える場合は、競合を解決する必要がない限り、実際に名前空間プレフィックスを追加する必要がないためです。

<com.mycompany.projectname.MyCustomView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:background="@Android:color/transparent"
    Android:text="Test text"
    Android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

完全修飾名を使用してカスタムビューを参照します。

Android LabelViewサンプル

完全な例が必要な場合は、Androidラベルビューのサンプルをご覧ください。

LabelView.Java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.Android.apis.view.LabelView
    Android:background="@drawable/blue"
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

これは、名前空間属性を持つLinearLayoutに含まれています:xmlns:app="http://schemas.Android.com/apk/res-auto"

リンク集

833
Casebash

素晴らしい参照です。ありがとうございます。それに加えて:

カスタムビューのカスタム属性を宣言したライブラリプロジェクトが含まれている場合は、ライブラリの名前空間ではなく、プロジェクトの名前空間を宣言する必要があります。例えば:

ライブラリにパッケージ "com.example.library.customview"があり、作業プロジェクトにパッケージ "com.example.customview"があるとすると、次のようになります。

機能しません(エラー「エラー:パッケージ 'com.example.library.customview'の属性 'newAttr'にリソースIDが見つかりません」が表示されます)。

<com.library.CustomView
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res/com.example.library.customview"
        Android:id="@+id/myView"
        app:newAttr="value" />

働くでしょう:

<com.library.CustomView
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res/com.example.customview"
        Android:id="@+id/myView"
        app:newAttr="value" />
90
Andy

最も投票された回答への追加。

performStyledAttributes()

私たちがAndroid:xxxの定義済み属性を使ってカスタムビューを作成するときに、performStyledAttributes()の使用法についていくつかの単語を追加します。特にTextAppearanceを使うとき。
「2.コンストラクタの作成」で説明したように、カスタムビューは作成時にAttributeSetを取得します。 TextViewのソースコード(API 16)で見られる主な使い方。

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.Android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

ここで何が見えますか?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
属性セットはドキュメントに従ってテーマによって処理されます。属性値は段階的にコンパイルされます。最初の属性はテーマから埋められ、次に値はスタイルの値に置き換えられ、最後に特別なビューインスタンスのXMLの正確な値は他のものに置き換えられます。
要求された属性の配列 - com.Android.internal.R.styleable.TextView
これは通常の定数の配列です。標準属性を要求している場合は、この配列を手動で構築できます。

ドキュメントに記載されていないもの - 結果のTypedArray要素の順序。
カスタムビューがattrs.xmlで宣言されると、属性インデックス用の特別な定数が生成されます。そしてa.getString(R.styleable.MyCustomView_Android_text)のように値を抽出することができます。しかし手動のint[]には定数はありません。 getXXXValue(arrayIndex)は問題なく動作すると思います。

そして他の質問は、「どうやって内部定数を置き換えて、標準属性を要求することができるのか」ということです。 Android.R.attr。*値を使用できます。

そのため、カスタムビューで標準のTextAppearance属性を使用し、コンストラクタでその値を読み取る場合は、TextViewから次のようにコードを変更できます。

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_Android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { Android.R.attr.textColor, Android.R.attr.textSize, 
            Android.R.attr.typeface, Android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

CustomLabelが定義されている場所:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="Android:text" />
    <!-- Label text color. -->
    <attr name="Android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="Android:textAppearance" />
</declare-styleable>

たぶん、私はいくつかの方法で間違っていますが、performStyledAttributes()に関するAndroidのドキュメントは非常に貧弱です。

標準UIコンポーネントの拡張

同時に、宣言されたすべての属性を使用して、標準のUIコンポーネントを拡張することもできます。たとえばTextViewは多くのプロパティを宣言しているため、この方法はそれほど良くありません。そして、オーバーライドされたonMeasure()とonDraw()に完全な機能を実装することは不可能でしょう。

しかし、カスタムコンポーネントの理論的な再利用を犠牲にすることはできます。 「私が使用する機能を正確に知っている」と言って、誰ともコードを共有しないでください。

それからコンストラクタCustomComponent(Context, AttributeSet, defStyle)を実装することができます。 super(...)を呼び出した後、すべての属性を解析してgetterメソッドで利用できるようにします。

26
yuriy.weiss

Googleが開発者ページを更新し、そこにさまざまなトレーニングを追加したようです。

そのうちの一つはカスタムビューの作成を扱い、ここで見つけることができます

11
mitch000001

最初の回答をどうもありがとう。

私に関しては、私はそれに関してただ一つの問題を抱えていました。私の見解を膨らませるとき、私はバグがありました: Java.lang.NoSuchMethodException:MyView(コンテキスト、属性)

私は新しいコンストラクタを作成することでそれを解決しました:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

これが役立つことを願っています!

5
user2346922

他のレイアウトファイルに任意のレイアウトファイルを含めることができます。

             <RelativeLayout
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"
                Android:layout_marginLeft="10dp"
                Android:layout_marginRight="30dp" >

                <include
                    Android:id="@+id/frnd_img_file"
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    Android:id="@+id/frnd_video_file"
                    Android:layout_width="wrap_content"
                    Android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    Android:id="@+id/downloadbtn"
                    Android:layout_width="30dp"
                    Android:layout_height="30dp"
                    Android:layout_centerInParent="true"
                    Android:src="@drawable/plus"/>
            </RelativeLayout>

ここでは、includeタグ内のレイアウトファイルは、同じresフォルダ内の他の.xmlレイアウトファイルです。

0
Akshay Paliwal