Ein Layout definiert die Struktur für eine Benutzeroberfläche in deiner App, wie z.B. in einer Aktivität. Alle Elemente im Layout werden mithilfe einer Hierarchie von View- und ViewGroup-Objekten erstellt. Eine View zeichnet normalerweise etwas, das der Benutzer sehen und mit dem er interagieren kann. Eine ViewGroup ist ein unsichtbarer Container, der die Layoutstruktur für View und andere ViewGroup-Objekte definiert (Abbildung 1).
View-Objekte werden oft als Widgets bezeichnet und können eine von vielen Unterklassen sein, wie z.B. Button oder TextView. ViewGroup-Objekte werden normalerweise als Layouts bezeichnet und können eine von vielen Typen sein, die eine andere Layoutstruktur bereitstellen, wie z.B. LinearLayout oder ConstraintLayout.
Du kannst ein Layout auf zwei Arten deklarieren:
Schreibe das XML
Mit der XML-Vokabular von Android kannst du schnell UI-Layouts entwerfen und die enthaltenen Bildschirmelemente erstellen, ähnlich wie du mit HTML Webseiten erstellst.
Jede Layout-Datei muss genau ein Wurzelelement enthalten, das ein View- oder ViewGroup-Objekt sein muss. Nachdem du das Wurzelelement definiert hast, kannst du weitere Layoutobjekte oder Widgets als untergeordnete Elemente hinzufügen, um allmählich eine View-Hierarchie aufzubauen, die dein Layout definiert. Hier ist ein XML-Layout, das einen vertikalen LinearLayout verwendet, um ein TextView und einen Button zu halten:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hallo, ich bin ein TextView" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hallo, ich bin ein Button" />
</LinearLayout>
Nachdem du dein Layout in XML deklariert hast, speicherst du die Datei mit der .xml-Erweiterung im Verzeichnis res/layout/ deines Android-Projekts, damit es korrekt kompiliert wird.
Erfahre mehr über die Syntax für eine Layout-XML-Datei in der Layout-Ressource.
Lade die XML-Ressource
Wenn du deine App kompilierst, wird jede XML-Layoutdatei in eine View-Ressource kompiliert. Lade die Layout-Ressource in der onCreate()
-Methode der Aktivität deiner App, indem du setContentView()
aufrufst und ihr den Verweis auf deine Layout-Ressource in der Form R.layout.layout_dateiname
übergibst. Wenn zum Beispiel dein XML-Layout als main_layout.xml
gespeichert ist, lädst du es für deine Aktivität wie folgt:
setContentView(R.layout.main_layout);
Das Android-Framework ruft die onCreate()
-Rückrufmethode deiner Aktivität auf, wenn die Aktivität gestartet wird. Für weitere Informationen über Aktivitätslebenszyklen, siehe Einführung in Aktivitäten.
Attribute
Jedes View- und ViewGroup-Objekt unterstützt seine eigene Vielfalt an XML-Attributen. Einige Attribute sind spezifisch für ein View-Objekt. Zum Beispiel unterstützt TextView das Attribut textSize. Diese Attribute werden jedoch auch von allen View-Objekten geerbt, die diese Klasse erweitern. Andere Attribute werden als Layout-Parameter bezeichnet. Sie beschreiben bestimmte Layoutorientierungen des View-Objekts, wie sie von dem übergeordneten ViewGroup-Objekt definiert werden.
ID
Jedes View-Objekt kann eine eindeutige Ganzzahl-ID haben, um die View im Baum eindeutig zu identifizieren. Wenn die App kompiliert wird, wird auf diese ID als Ganzzahl verwiesen. Die ID wird jedoch in der Layout-XML-Datei in Form einer Zeichenfolge im id-Attribut zugewiesen. Dies ist ein XML-Attribut, das allen View-Objekten gemeinsam ist und von der View-Klasse definiert wird. Du verwendest es sehr oft. Die Syntax für eine ID innerhalb eines XML-Tags lautet wie folgt:
android:id="@+id/my_button"
Das @-Symbol (@) am Anfang der Zeichenkette gibt an, dass der XML-Parser die restliche ID-Zeichenfolge analysiert und erweitert und sie als ID-Ressource identifiziert. Das Pluszeichen (+) bedeutet, dass dies ein neuer Ressourcenname ist, der erstellt und der R.java-Datei hinzugefügt werden muss.
Das Android-Framework bietet viele andere ID-Ressourcen an. Wenn du auf eine Android-Ressourcen-ID verweist, benötigst du das Pluszeichen (+) nicht, aber du musst den android-Paketnamensraum wie folgt hinzufügen:
android:id="@android:id/empty"
Der Android-Paketnamensraum gibt an, dass du auf eine ID aus der android.R-Ressourcenklasse verweist, und nicht auf die lokale Ressourcenklasse.
Um Views zu erstellen und auf sie aus deiner App heraus zu verweisen, kannst du das folgende Muster verwenden:
-
Definiere eine Ansicht in der Layout-Datei und weise ihr eine eindeutige ID zu, wie im folgenden Beispiel gezeigt:
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
-
Erstelle eine Instanz des Ansichtsobjekts und nimm es aus dem Layout auf, normalerweise in der
onCreate()
-Methode, wie im folgenden Beispiel gezeigt:
“`java
Button myButton = findViewById(R.id.my_button);
Das Definieren von IDs für Ansichtsobjekte ist wichtig, wenn du ein RelativeLayout erstellst. In einem RelativeLayout können Geschwisteransichten ihre Position relativ zu einer anderen Geschwisteransicht definieren, auf die durch die eindeutige ID verwiesen wird.
Eine ID muss nicht eindeutig im gesamten Baum sein, aber sie muss im Teilbaum, in dem du suchst, eindeutig sein. Das kann oft der gesamte Baum sein, daher ist es am besten, sie eindeutig zu machen, wenn es möglich ist.
Layout-Parameter
XML-Layout-Attribute mit dem Namen layout_etwas
definieren Layout-Parameter für die View, die für die darüber liegende ViewGroup geeignet sind.
Jede ViewGroup-Klasse implementiert eine verschachtelte Klasse, die ViewGroup.LayoutParams erweitert. Diese Unterklasse enthält Eigenschaftstypen, die die Größe und Position jeder untergeordneten Ansicht definieren, wie es für die ViewGroup geeignet ist. Wie in Abbildung 2 gezeigt, definiert die übergeordnete ViewGroup Layout-Parameter für jede untergeordnete Ansicht, einschließlich der untergeordneten ViewGroup.
Jede LayoutParams-Unterklasse hat ihre eigene Syntax zum Festlegen von Werten. Jedes Kindelement muss eine LayoutParams definieren, die für seine Elternelemente geeignet ist, obwohl es auch eine andere LayoutParams für seine eigenen untergeordneten Elemente definieren kann.
Alle ViewGroup enthalten eine Breite und Höhe, die durch layout_width
und layout_height
angegeben werden, und jede Ansicht muss diese definieren. Viele LayoutParams enthalten optionale Seitenabstände und -ränder.
Du kannst Breite und Höhe mit genauen Abmessungen angeben, aber dies solltest du nicht oft tun. Häufiger verwendest du eine dieser Konstanten, um die Breite oder Höhe festzulegen:
wrap_content
: gibt deiner Ansicht die Größe, die für ihren Inhalt erforderlich ist.match_parent
: gibt deiner Ansicht die Größe, die ihr die übergeordnete ViewGroup erlaubt.
Im Allgemeinen empfehlen wir, eine Layoutbreite und -höhe nicht mit absoluten Einheiten wie Pixeln festzulegen. Ein besserer Ansatz ist die Verwendung von relativen Einheiten wie density-independent Pixel Units (dp), wrap_content oder match_parent, da dies dazu beiträgt, dass deine App auf verschiedenen Gerätebildschirmgrößen ordnungsgemäß angezeigt wird. Die akzeptierten Messungstypen sind in der Layout-Ressource definiert.
Layoutposition
Eine Ansicht hat eine rechteckige Geometrie. Sie hat einen Ort, der als Paar von links- und oberen Koordinaten ausgedrückt wird, und zwei Dimensionen, die als Breite und Höhe ausgedrückt werden. Die Einheit für Ort und Dimensionen ist das Pixel.
Du kannst den Ort einer Ansicht abrufen, indem du die Methoden getLeft()
und getTop()
aufrufst. Die erstere gibt die linke (x)-Koordinate des das Rechteck darstellenden Ansicht zurück. Die letztere gibt die obere (y)-Koordinate des das Rechteck darstellenden Ansicht zurück. Diese Methoden geben den Ort der Ansicht relativ zu ihrer übergeordneten Ansicht zurück. Wenn beispielsweise getLeft()
20 zurückgibt, bedeutet dies, dass sich die Ansicht 20 Pixel rechts vom linken Rand ihrer direkten übergeordneten Ansicht befindet.
Zusätzlich gibt es Bequemlichkeitsmethoden, um unnötige Berechnungen zu vermeiden: nämlich getRight()
und getBottom()
. Diese Methoden geben die Koordinaten der rechten und unteren Kante des das Rechteck darstellenden Ansicht zurück. Wenn du beispielsweise getRight()
aufrufst, ist dies ähnlich wie die folgende Berechnung: getLeft() + getWidth()
.
Größe, Padding und Margins
Die Größe einer Ansicht wird durch eine Breite und Höhe ausgedrückt. Eine Ansicht hat zwei Paare von Breiten- und Höhenwerten.
Das erste Paar wird als gemessene Breite und gemessene Höhe bezeichnet. Diese Maße geben an, wie groß eine Ansicht innerhalb ihrer Elternelemente sein möchte. Du kannst die gemessenen Maße abrufen, indem du getMeasuredWidth()
und getMeasuredHeight()
aufrufst.
Das zweite Paar wird als Breite und Höhe oder manchmal als Zeichenbreite und Zeichenhöhe bezeichnet. Diese Maße geben die tatsächliche Größe der Ansicht auf dem Bildschirm zur Zeichnungszeit und nach dem Layout an. Diese Werte können, müssen aber nicht von den gemessenen Breite und Höhe abweichen. Du kannst die Breite und Höhe abrufen, indem du getWidth()
und getHeight()
aufrufst.
Um ihre Abmessungen zu messen, berücksichtigt eine Ansicht ihr Padding. Das Padding wird in Pixel für die linken, oberen, rechten und unteren Teile der Ansicht ausgedrückt. Du kannst Padding verwenden, um den Inhalt der Ansicht um eine bestimmte Anzahl von Pixeln zu verschieben. Wenn z.B. ein linkes Padding von zwei verwendet wird, wird der Inhalt der Ansicht zwei Pixel nach rechts vom linken Rand verschoben. Du kannst Padding mit der Methode setPadding(int, int, int, int)
festlegen und es mit getPaddingLeft()
, getPaddingTop()
, getPaddingRight()
und getPaddingBottom()
abfragen.
Obwohl eine Ansicht ein Padding definieren kann, unterstützt sie keine Margins. ViewGroup unterstützt jedoch Margins. Siehe ViewGroup und ViewGroup.MarginLayoutParams für weitere Informationen.
Für weitere Informationen zu Abmessungen, siehe Dimension.
Neben der Programmierung von Margins und Padding kannst du sie auch in deinen XML-Layouts festlegen, wie im folgenden Beispiel gezeigt:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:padding="8dp"
android:text="Hallo, ich bin ein TextView" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingBottom="4dp"
android:paddingEnd="8dp"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:text="Hallo, ich bin ein Button" />
</LinearLayout>
Im obigen Beispiel werden Margin und Padding angewendet. Der TextView hat einheitliche Margin- und Paddingwerte auf allen Seiten, und der Button zeigt, wie du sie unabhängig voneinander auf verschiedene Kanten anwenden kannst.
Hinweis: Es ist eine gute Praxis, paddingStart
, paddingEnd
, layout_marginStart
und layout_marginEnd
anstelle von paddingLeft
, paddingRight
, layout_marginLeft
und layout_marginRight
zu verwenden, da sie sich sowohl mit Links-nach-rechts- als auch mit Rechts-nach-Links-Skripts besser verhalten.
Häufige Layouts
Jede Unterklasse der ViewGroup-Klasse bietet eine einzigartige Möglichkeit, die Ansichten, die du darin verschachtelst, anzuzeigen. Der flexibelste Layouttyp und derjenige, der die besten Tools zum Halten deiner Layout-Hierarchie flach bietet, ist ConstraintLayout.
Die folgenden sind einige der gängigen Layouttypen, die in die Android-Plattform eingebaut sind.
Hinweis: Obwohl du ein oder mehrere Layouts in ein anderes Layout verschachteln kannst, um dein UI-Design zu erreichen, halte deine Layout-Hierarchie so flach wie möglich. Dein Layout wird schneller gezeichnet, wenn es weniger verschachtelte Layouts hat. Eine breite Baumhierarchie ist besser als eine tiefe Baumhierarchie.
Dynamische Listen erstellen
Wenn der Inhalt für dein Layout dynamisch oder nicht vorbestimmt ist, kannst du RecyclerView oder eine Unterklasse von AdapterView verwenden. RecyclerView ist in der Regel die bessere Option, da es den Speicher effizienter als AdapterView verwendet.
Übliche Layouts, die mit RecyclerView und AdapterView möglich sind, sind:
RecyclerView bietet mehr Möglichkeiten und die Möglichkeit, einen benutzerdefinierten Layout-Manager zu erstellen.
Fülle eine adapteransicht mit Daten
Du kannst eine AdapterView wie ListView oder GridView füllen, indem du die AdapterView-Instanz an einen Adapter bindest, der Daten aus einer externen Quelle abruft und eine View erstellt, die jeden Dateneintrag repräsentiert.
Android stellt mehrere Unterklassen von Adapter bereit, die nützlich sind, um verschiedene Arten von Daten abzurufen und Views für eine AdapterView zu erstellen. Die beiden häufigsten Adapter sind:
-
ArrayAdapter: Verwende diesen Adapter, wenn deine Datenquelle ein Array ist. ArrayAdapter erstellt standardmäßig eine Ansicht für jedes Arrayelement, indem es toString() für jedes Element aufruft und die Inhalte in einem TextView platziert.
Wenn du z.B. ein Array von Zeichenfolgen hast, die du in einer ListView anzeigen möchtest, initialisiere einen neuen ArrayAdapter mit einem Konstruktor, um das Layout für jede Zeichenfolge und das Zeichenfolgenarray anzugeben:
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, R.layout.list_item, stringArray);
listView.setAdapter(arrayAdapter);
Die Argumente für diesen Konstruktor sind:
- Dein App-Kontext
- Das Layout, das einen TextView für jede Zeichenfolge im Array enthält
- Das Zeichenfolgenarray
Dann ruf setAdapter()
auf deiner ListView auf:
listView.setAdapter(arrayAdapter);
Um das Aussehen jedes Elements anzupassen, kannst du die toString()
-Methode für die Objekte in deinem Array überschreiben. Oder um eine Ansicht für jedes Element zu erstellen, die etwas anderes als ein TextView ist – zum Beispiel, wenn du für jedes Arrayelement ein ImageView möchtest – erweitere die ArrayAdapter-Klasse und überschreibe getView()
, um den gewünschten Ansichtstyp für jedes Element zurückzugeben.
- SimpleCursorAdapter: Verwende diesen Adapter, wenn deine Daten aus einem Cursor stammen. Wenn du SimpleCursorAdapter verwendest, gib ein Layout an, das für jede Zeile im Cursor verwendet werden soll, und welche Spalten im Cursor in die Ansichten des gewünschten Layouts eingefügt werden sollen. Wenn du z.B. eine Liste von Namen und Telefonnummern von Personen erstellen möchtest, kannst du eine Abfrage durchführen, die einen Cursor mit einer Zeile für jede Person und Spalten für die Namen und Nummern zurückgibt. Du erstellst dann ein String-Array, das angibt, welche Spalten aus dem Cursor in das Layout für jedes Ergebnis eingefügt werden sollen, und ein Integer-Array, das die entsprechenden Ansichten angibt, in die jede Spalte eingefügt werden muss:
SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(this, R.layout.list_item, cursor, fromColumns, toViews);
listView.setAdapter(simpleCursorAdapter);
Wenn du den SimpleCursorAdapter instanziierst, übergib das Layout für jedes Ergebnis, den Cursor mit den Ergebnissen und diese beiden Arrays:
listView.setAdapter(simpleCursorAdapter);
Der SimpleCursorAdapter erstellt dann eine Ansicht für jede Zeile im Cursor, indem er das angegebene Layout verwendet und jedes fromColumns-Element in die entsprechende toViews-Ansicht einfügt.
Wenn du im Laufe des Lebenszyklus deiner App die zugrunde liegenden Daten, die von deinem Adapter gelesen werden, änderst, rufe notifyDataSetChanged()
auf. Dadurch wird dem angehängten View mitgeteilt, dass die Daten geändert wurden und es sich aktualisiert.
Klickereignisse behandeln
Du kannst auf Klickereignisse auf jedem Element in einer AdapterView reagieren, indem du die AdapterView.OnItemClickListener
-Schnittstelle implementierst. Zum Beispiel:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Führe hier die gewünschte Aktion aus
}
});
Zusätzliche Ressourcen
Sieh dir an, wie Layouts in der Sunflower-Demo-App auf GitHub verwendet werden.