489 Stimmen

Deklarieren eines benutzerdefinierten Android UI-Elements mit XML

Wie deklariere ich ein Android UI-Element mit XML?

849voto

Casebash Punkte 106938

Der Android Developer Guide hat einen Abschnitt namens Erstellen von benutzerdefinierten Komponenten . Leider, die Diskussion über XML-Attribute deckt nur die Deklaration des Steuerelements in der Layoutdatei ab, nicht aber die tatsächliche Behandlung der Werte innerhalb der Klasseninitialisierung. Die Schritte sind wie folgt:

1. Deklarieren Sie Attribute in 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>

Beachten Sie die Verwendung eines unqualifizierten Namens in der declare-styleable Tag. Nicht standardisierte Android-Attribute wie extraInformation muss ihr Typ deklariert werden. In der Oberklasse deklarierte Tags sind in Unterklassen verfügbar, ohne dass sie neu deklariert werden müssen.

2. Konstrukteure erstellen

Da es zwei Konstruktoren gibt, die eine AttributeSet für die Initialisierung zu verwenden, ist es zweckmäßig, eine eigene Initialisierungsmethode zu erstellen, die von den Konstruktoren aufgerufen wird.

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 ist ein automatisch generiertes int[] Ressource, wobei jedes Element die ID eines Attributs ist. Attribute werden für jede Eigenschaft in der XML-Datei durch Anhängen des Attributnamens an den Elementnamen erzeugt. Zum Beispiel, R.styleable.MyCustomView_android_text enthält die android_text Attribut für MyCustomView . Die Attribute können dann aus der Datei TypedArray mit verschiedenen get Funktionen. Wenn das Attribut nicht in der XML definiert ist, dann null zurückgegeben wird. Außer natürlich, wenn der Rückgabetyp ein Primitivum ist. In diesem Fall wird das zweite Argument zurückgegeben.

Wenn Sie nicht alle Attribute abrufen möchten, ist es möglich, dieses Array manuell zu erstellen, wobei die ID für Standard-Android-Attribute in android.R.attr , während die Attribute für dieses Projekt in R.attr .

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

Bitte beachten Sie, dass Sie no alles verwenden in android.R.styleable gemäß este hilo das kann sich in Zukunft ändern. Es ist immer noch in der Dokumentation als zu sein, um alle diese Konstanten an einem Ort zu sehen ist nützlich.

3. Verwenden Sie es in einer Layout-Datei wie layout\main.xml

Einfügen der Namespace-Deklaration xmlns:app="http://schemas.android.com/apk/res-auto" im Xml-Element der obersten Ebene. Namensräume bieten eine Methode zur Vermeidung von Konflikten, die manchmal auftreten, wenn verschiedene Schemata die gleichen Elementnamen verwenden (siehe dieser Artikel für weitere Informationen). Die URL ist einfach eine Möglichkeit, Schemata eindeutig zu identifizieren - unter dieser URL muss eigentlich nichts gehostet werden . Wenn dies nichts zu bewirken scheint, liegt das daran, dass Sie das Namespace-Präfix eigentlich nicht hinzufügen müssen, es sei denn, Sie müssen einen Konflikt auflösen.

<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"
/> 

Verweisen Sie auf die benutzerdefinierte Ansicht unter Verwendung des vollqualifizierten Namens.

Android LabelView Beispiel

Wenn Sie ein vollständiges Beispiel wünschen, sehen Sie sich das Beispiel der Android-Etikettenansicht an.

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>

benutzerdefinierte_ansicht_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"/>

Dies ist enthalten in einer LinearLayout mit einem Namespace-Attribut: xmlns:app="http://schemas.android.com/apk/res-auto"

Links

91voto

Andy Punkte 6629

Tolle Referenz. Danke! Eine Ergänzung dazu:

Wenn Sie zufällig ein Bibliotheksprojekt eingebunden haben, das benutzerdefinierte Attribute für eine benutzerdefinierte Ansicht deklariert hat, müssen Sie Ihren Projektnamensraum deklarieren, nicht den der Bibliothek. Beispiel:

Angenommen, die Bibliothek hat das Paket "com.example.library.customview" und das Arbeitsprojekt hat das Paket "com.example.customview", dann:

Funktioniert nicht (zeigt den Fehler " error: Kein Ressourcenbezeichner für das Attribut 'newAttr' im Paket gefunden 'com.example.library.customview'" ):

<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" />

Wird funktionieren:

<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" />

27voto

yuriy.weiss Punkte 291

Zusatz zur meistgewählten Antwort.

obtainStyledAttributes()

Ich möchte einige Worte über die Verwendung von obtainStyledAttributes() hinzufügen, wenn wir eine benutzerdefinierte Ansicht mit Android:xxx prdefined attributes erstellen. Vor allem, wenn wir TextAppearance verwenden.
Wie bereits in "2. Erstellen von Konstruktoren" erwähnt, erhält eine benutzerdefinierte Ansicht bei ihrer Erstellung ein AttributeSet. Die Hauptverwendung können wir im TextView-Quellcode (API 16) sehen.

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();

Was können wir hier sehen?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Der Attributsatz wird vom Thema gemäß der Dokumentation verarbeitet. Die Attributwerte werden Schritt für Schritt zusammengestellt. Zuerst werden die Attribute aus dem Thema gefüllt, dann werden die Werte durch die Werte aus dem Stil ersetzt, und schließlich werden die exakten Werte aus dem XML für die spezielle Ansichtsinstanz durch andere ersetzt.
Array der angeforderten Attribute - com.android.internal.R.styleable.TextView
Es ist ein gewöhnliches Array von Konstanten. Wenn wir Standardattribute abfragen, können wir dieses Array manuell erstellen.

Was in der Dokumentation nicht erwähnt wird - die Reihenfolge der TypedArray-Elemente des Ergebnisses.
Wenn eine benutzerdefinierte Ansicht in attrs.xml deklariert wird, werden spezielle Konstanten für Attributindizes erzeugt. Und wir können auf diese Weise Werte extrahieren: a.getString(R.styleable.MyCustomView_android_text) . Aber für manuelle int[] es gibt keine Konstanten. Ich nehme an, dass getXXXValue(arrayIndex) gut funktionieren wird.

Und eine andere Frage ist: "Wie können wir interne Konstanten ersetzen und Standardattribute anfordern?" Wir können Android.R.attr.* Werte verwenden.

Wenn wir also das Standardattribut TextAppearance in der benutzerdefinierten Ansicht verwenden und seine Werte im Konstruktor lesen wollen, können wir den Code von TextView auf diese Weise ändern:

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();
}

Wo CustomLabel definiert ist:

<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>

Vielleicht irre ich mich irgendwie, aber die Android-Dokumentation zu obtainStyledAttributes() ist sehr dürftig.

Erweitern der Standard-UI-Komponente

Gleichzeitig können wir die Standard-UI-Komponente einfach erweitern, indem wir alle ihre deklarierten Attribute verwenden. Dieser Ansatz ist nicht so gut, weil TextView zum Beispiel eine Menge von Eigenschaften deklariert. Und es wird unmöglich sein volle Funktionalität in den überschriebenen onMeasure() und onDraw() zu implementieren.

Aber wir können auf eine theoretisch breite Wiederverwendung von benutzerdefinierten Komponenten verzichten. Sagen Sie "Ich weiß genau, welche Funktionen ich verwenden werde", und teile den Code nicht mit anderen.

Dann können wir den Konstruktor CustomComponent(Context, AttributeSet, defStyle) . Nach dem Aufruf super(...) werden alle Attribute geparst und über Getter-Methoden verfügbar sein.

13voto

mitch000001 Punkte 161

Es scheint, dass Google seine Entwicklerseite aktualisiert und dort verschiedene Schulungen hinzugefügt hat.

Eine davon befasst sich mit der Erstellung von benutzerdefinierten Ansichten und ist zu finden unter aquí

5voto

user2346922 Punkte 157

Vielen Dank für die erste Antwort.

Was mich betrifft, so hatte ich nur ein Problem damit. Beim Aufblasen meiner Ansicht, hatte ich einen Fehler: java.lang.NoSuchMethodException : MyView(Context, Attribute)

Ich habe das Problem gelöst, indem ich einen neuen Konstruktor erstellt habe:

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

Ich hoffe, das hilft!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X