XMLを使用したカスタムAndroid UI要素の宣言


回答:


840

Androidデベロッパーガイドには、カスタムコンポーネントの構築というセクションがあります。残念ながら、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タグで非修飾名を使用していることに注意してください。のような非標準のandroid属性extraInformationは、型を宣言する必要があります。スーパークラスで宣言されたタグは、再宣言しなくてもサブクラスで使用できます。

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

AttributeSetfor初期化に使用する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.MyCustomViewint[]各要素が属性のIDである自動生成リソースです。要素名に属性名を追加することにより、XMLの各プロパティの属性が生成されます。たとえばR.styleable.MyCustomView_android_text、のandroid_text属性が含まれていますMyCustomView。次に、TypedArrayさまざまなget関数を使用して属性を取得できます。XMLで定義されている属性が定義されていない場合は、nullが返されます。もちろん、戻り値の型がプリミティブの場合を除いて、その場合、2番目の引数が返されます。

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

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

このスレッドでは将来的に変更される可能性があるため、では何も使用しないでください。これらすべての定数を1か所に表示することは有用であるため、ドキュメントにはまだ記載されています。android.R.styleable

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

xmlns:app="http://schemas.android.com/apk/res-auto"最上位のxml要素に名前空間宣言を含めます。名前空間は、異なるスキーマが同じ要素名を使用する場合に発生する可能性がある競合を回避する方法を提供します(詳細については、この記事参照してください)。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、namespace属性を持つにいます。xmlns:app="http://schemas.android.com/apk/res-auto"

リンク集


14
ルート要素にカスタム名前空間が必要な場合は、標準のAndroid名前空間と独自のカスタム名前空間の両方を追加する必要があります。追加しない場合、ビルドエラーが発生する可能性があります。
追跡

11
この答えは、私が見つけたカスタムXMLパラメータに関するインターネットで最も明確なリソースです。Casebash、ありがとうございます。
Artem Russakovskii、2011年

2
何らかの理由で、ビジュアルエディターはandroid:textに書き込まれたテキスト値の使用を拒否しますが、デバイスはそれを問題なく使用します。どうして ?
Android開発者、

2
@androiddeveloper Eclipseエディターがすべてのandroid:属性の値の使用を拒否しているようです。機能かバグかを知りたい
deej

4
xmlns:app名前空間とres-autoの目的は何ですか?
IgorGanapolsky 2014年

91

素晴らしい参照。ありがとう!それへの追加:

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

ライブラリにパッケージ「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" />

47
これはADT 17プレビューで修正されました。ライブラリのアプリの名前空間を使用するにはdeclare code.google.com/p/android/issues/detail?id=9656のxmlns:app="http://schemas.android.com/apk/res-auto"コメント57を参照してください
nmr

2
カスタム名前空間を含めるとエラーが返されるようになりましたSuspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
Ben Wilkinson

Android StudioとGradleを使用しているため、カスタム名前空間はres-autoで終わります。それ以外の場合(たとえば、一部のEclipseバージョン)、通常はlib / [パッケージ名]で終わります
ユニバース

res-autoAndroid StudioとGradleを使用しているため、カスタム名前空間は終了します。それ以外の場合(たとえば、一部のEclipseバージョン)、通常はで終わりlib/[your package name]ます。iehttp://schemas.android.com/apk/lib/[your package name]
2016

27

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

obtainStyledAttributes()

android:xxx prdefined属性を使用してカスタムビューを作成するときに、obtainStyledAttributes()の使用方法について説明します。特に、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>

たぶん、私は何らかの間違いをしているのですが、obtainStyledAttributes()に関するAndroidのドキュメントは非常に貧弱です。

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

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

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

次に、コンストラクタを実装できますCustomComponent(Context, AttributeSet, defStyle)。呼び出し後、super(...)すべての属性が解析され、ゲッターメソッドを通じて利用できるようになります。


android:xxxの定義済み属性はEclipse GUIデザイナーで機能しますか?
deej 2013年

このような属性は、プロパティエディターのEclipse ADTプラグインによって認識されます。一部の値が定義されていない場合、自分のスタイルのデフォルトを確認できます。そして、クラスに@RemoteViewアノテーションを追加することを忘れないでください。
yuriy.weiss 2013年

それを機能させることはできません。EclipseはgetTextにnullをロードし続け、getResourceIdにandroid.content.res.Resources $ NotFoundExceptionをスローし続けますが、アプリはデバイスで正常に実行されます。
deej 2013年

申し訳ありません、私はあなたを助けることはできません。私は可能性をテストするためのデモプロジェクトのみを作成しましたが、そのようなエラーは満たしていません。
yuriy.weiss

これは、カスタムビューのカスタム属性を、その中に含まれている組み込みビューの組み込み属性にマッピングするよりもはるかに優れています。
samis 2014

13

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

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


5

最初の答えをありがとう。

私に関しては、問題が1つだけありました。ビューを拡張すると、バグが発生しました: java.lang.NoSuchMethodException:MyView(Context、Attributes)

新しいコンストラクタを作成して解決しました:

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

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


0

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

             <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レイアウトファイルです。


私はこれを試しましたが、それに含まれている問題は、含まれているレイアウトを「適合」させることができず、ジェネリックを作成できないことです。たとえば、同様の方法でボタンを含める場合、テキストをxmlに設定しようとしても機能します。
cfl 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.