Wie deklariere ich ein Android UI-Element mit XML?
Antworten
Zu viele Anzeigen?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.
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);
<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
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" />
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.
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í
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!
- See previous answers
- Weitere Antworten anzeigen