Wie deklariere ich ein Android-UI-Element mithilfe von XML?
Wie deklariere ich ein Android-UI-Element mithilfe von XML?
Antworten:
Das Android-Entwicklerhandbuch enthält einen Abschnitt namens Erstellen benutzerdefinierter Komponenten . Leider ist die Diskussion der XML-Attribute nur das Deklarieren des Steuerelements in der Layoutdatei und nicht das tatsächliche Behandeln der Werte in der Klasseninitialisierung. Die Schritte sind wie folgt:
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 nicht qualifizierten Namens im declare-styleable
Tag. Nicht standardmäßige Android-Attribute wieextraInformation
müssen ihren Typ deklarieren lassen. In der Oberklasse deklarierte Tags sind in Unterklassen verfügbar, ohne dass sie erneut deklariert werden müssen.
Da es zwei Konstruktoren gibt, die eine AttributeSet
für die Initialisierung verwenden, ist es zweckmäßig, eine separate Initialisierungsmethode zu erstellen, die die Konstruktoren aufrufen können.
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 eine automatisch generierte int[]
Ressource, bei der jedes Element die ID eines Attributs ist. Attribute werden für jede Eigenschaft im XML generiert, indem der Attributname an den Elementnamen angehängt wird. Zum Beispiel R.styleable.MyCustomView_android_text
enthält das android_text
Attribut für MyCustomView
. Attribute können dann TypedArray
mit verschiedenen get
Funktionen aus dem abgerufen werden . Wenn das Attribut nicht im XML definiert ist, null
wird es zurückgegeben. Außer natürlich, wenn der Rückgabetyp ein Grundelement ist. In diesem Fall wird das zweite Argument zurückgegeben.
Wenn Sie nicht alle Attribute abrufen möchten, können Sie dieses Array manuell erstellen. Die ID für Standard-Android-Attribute ist enthalten android.R.attr
, während Attribute für dieses Projekt enthalten sind R.attr
.
int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};
Bitte beachten Sie, dass Sie sollten nicht alles in Verwendung android.R.styleable
, gemäß diesem Thread es in Zukunft ändern kann. Es ist immer noch in der Dokumentation enthalten, dass es nützlich ist, alle diese Konstanten an einem Ort anzuzeigen.
layout\main.xml
Fügen Sie die Namespace-Deklaration xmlns:app="http://schemas.android.com/apk/res-auto"
in das XML-Element der obersten Ebene ein. Namespaces bieten eine Methode, um Konflikte zu vermeiden, die manchmal auftreten, wenn verschiedene Schemas dieselben Elementnamen verwenden ( weitere Informationen finden Sie in diesem Artikel ). Die URL ist einfach eine Möglichkeit , Schemas eindeutig zu identifizieren - unter dieser URL muss eigentlich nichts gehostet werden . Wenn dies anscheinend nichts bewirkt, müssen Sie das Namespace-Präfix nur hinzufügen, wenn Sie einen Konflikt lösen müssen.
<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 mit dem vollständig qualifizierten Namen.
Wenn Sie ein vollständiges Beispiel wünschen, schauen Sie sich das Beispiel für die 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>
<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 in a LinearLayout
mit einem Namespace-Attribut enthalten:xmlns:app="http://schemas.android.com/apk/res-auto"
Tolle Referenz. Vielen Dank! Eine Ergänzung dazu:
Wenn zufällig ein Bibliotheksprojekt enthalten ist, das benutzerdefinierte Attribute für eine benutzerdefinierte Ansicht deklariert hat, müssen Sie Ihren Projektnamespace deklarieren, nicht den der Bibliothek. Z.B:
Vorausgesetzt, die Bibliothek hat das Paket "com.example.library.customview" und das Arbeitsprojekt hat das Paket "com.example.customview", dann:
Funktioniert nicht (zeigt den Fehler "Fehler: Keine Ressourcen-ID für Attribut 'newAttr' im Paket 'com.example.library.customview' gefunden"):
<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" />
xmlns:app="http://schemas.android.com/apk/res-auto"
Siehe Kommentar 57 in code.google.com/p/android/issues/detail?id=9656
Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
res-auto
dass wir Android Studio und Gradle verwenden. Andernfalls (z. B. einige Eclipse-Versionen) würde es normalerweise enden lib/[your package name]
. dhhttp://schemas.android.com/apk/lib/[your package name]
Ergänzung zu den am häufigsten gewählten Antworten.
Ich möchte einige Wörter zur Verwendung von getStyledAttributes () hinzufügen, wenn wir eine benutzerdefinierte Ansicht mit den definierten Attributen android: xxx erstellen. Besonders wenn wir TextAppearance verwenden.
Wie in "2. Erstellen von Konstruktoren" erwähnt, erhält die benutzerdefinierte Ansicht bei ihrer Erstellung AttributeSet. Die Hauptverwendung finden Sie im TextView-Quellcode (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();
Was können wir hier sehen?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Der Attributsatz wird gemäß der Dokumentation thematisch verarbeitet. Attributwerte werden Schritt für Schritt zusammengestellt. Zuerst werden Attribute aus dem Thema gefüllt, dann werden Werte durch Werte aus dem Stil ersetzt, und schließlich ersetzen exakte Werte aus XML für eine spezielle Ansichtsinstanz andere.
Array angeforderter Attribute - com.android.internal.R.styleable.TextView
Dies ist ein gewöhnliches Array von Konstanten. Wenn wir Standardattribute anfordern, können wir dieses Array manuell erstellen.
Was in der Dokumentation nicht erwähnt wird - Reihenfolge der Ergebnisse TypedArray-Elemente.
Wenn die benutzerdefinierte Ansicht in attrs.xml deklariert wird, werden spezielle Konstanten für Attributindizes generiert. Und wir können Werte folgendermaßen extrahieren : a.getString(R.styleable.MyCustomView_android_text)
. Aber für das Handbuchint[]
gibt es jedoch keine Konstanten. Ich nehme an, dass getXXXValue (arrayIndex) gut funktioniert.
Eine andere Frage lautet: "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 möchten, können wir den Code aus TextView folgendermaßen ä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 getStyledAttributes () ist sehr schlecht.
Gleichzeitig können wir einfach die Standard-UI-Komponente unter Verwendung aller deklarierten Attribute erweitern. Dieser Ansatz ist nicht so gut, weil beispielsweise TextView viele Eigenschaften deklariert. Und es wird unmöglich sein, die volle Funktionalität beim Überschreiben von onMeasure () und onDraw () zu implementieren.
Wir können jedoch die theoretisch weite Wiederverwendung von benutzerdefinierten Komponenten opfern. Sagen Sie "Ich weiß genau, welche Funktionen ich verwenden werde" und geben Sie keinen Code an Dritte weiter.
Dann können wir den Konstruktor implementieren CustomComponent(Context, AttributeSet, defStyle)
. Nach dem Aufruf super(...)
werden alle Attribute analysiert und über Getter-Methoden verfügbar sein.
Es scheint, dass Google seine Entwicklerseite aktualisiert und dort verschiedene Schulungen hinzugefügt hat.
Einer von ihnen beschäftigt sich mit der Erstellung von benutzerdefinierten Ansichten und können gefunden werden hier
Vielen Dank für die erste Antwort.
Ich hatte nur ein Problem damit. Beim Aufblasen meiner Ansicht hatte ich einen Fehler: java.lang.NoSuchMethodException: MyView (Kontext, Attribute)
Ich habe es gelöst, indem ich einen neuen Konstruktor erstellt habe:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// some code
}
Hoffe das wird helfen!
Sie können jede Layoutdatei in eine andere Layoutdatei aufnehmen.
<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>
Hier sind die Layoutdateien im Include-Tag andere XML-Layoutdateien im selben res-Ordner.