Layouts in Ansichten
Ein Layout definiert die Struktur einer Benutzeroberfläche in Ihrer App, z. B. in einer Aktivität. Alle Elemente im Layout werden mit einer Hierarchie aus View
- und ViewGroup
-Objekten erstellt. Eine View
zeichnet in der Regel etwas, das der Nutzer sehen und mit dem er interagieren kann. Ein ViewGroup
ist ein unsichtbarer Container, der die Layoutstruktur für View
und andere ViewGroup
-Objekte definiert (siehe Abbildung 1).
View
-Objekte werden häufig als Widgets bezeichnet und können eine von vielen abgeleiteten Klassen sein, z. B. Button
oder TextView
. ViewGroup
-Objekte werden in der Regel als Layouts bezeichnet und können einer von vielen Typen sein, die eine andere Layoutstruktur haben, z. B. LinearLayout
oder ConstraintLayout
.
Sie können ein Layout auf zwei Arten deklarieren:
- UI-Elemente in XML deklarieren Android bietet ein einfaches XML-Vokabular, das den
View
-Klassen und abgeleiteten Klassen entspricht, z. B. denen für Widgets und Layouts. Du kannst auch den Layout-Editor von Android Studio verwenden, um dein XML-Layout über eine Drag-and-drop-Oberfläche zu erstellen. - Instanziieren Sie Layoutelemente zur Laufzeit. Ihre Anwendung kann
View
- undViewGroup
-Objekte erstellen und ihre Eigenschaften programmatisch bearbeiten.
Wenn Sie die UI in XML deklarieren, können Sie die Präsentation Ihrer Anwendung vom Code trennen, der ihr Verhalten steuert. Mit XML-Dateien ist es auch einfacher, unterschiedliche Layouts für unterschiedliche Bildschirmgrößen und -ausrichtungen bereitzustellen. Dies wird unter Unterstützung verschiedener Bildschirmgrößen näher erläutert.
Das Android-Framework bietet Ihnen die Flexibilität, eine oder beide dieser Methoden zum Erstellen der Benutzeroberfläche Ihrer App zu verwenden. Sie können die Standardlayouts Ihrer Anwendung beispielsweise in XML deklarieren und dann das Layout zur Laufzeit ändern.
XML schreiben
Mit dem XML-Vokabular von Android können Sie schnell UI-Layouts und die darin enthaltenen Bildschirmelemente entwerfen, so wie Sie Webseiten in HTML mit einer Reihe verschachtelter Elemente erstellen.
Jede Layoutdatei muss genau ein Stammelement enthalten, bei dem es sich um ein View
- oder ViewGroup
-Objekt handeln muss. Nachdem Sie das Stammelement definiert haben, können Sie weitere Layoutobjekte oder Widgets als untergeordnete Elemente hinzufügen, um nach und nach eine View
-Hierarchie zu erstellen, die Ihr Layout definiert. Hier siehst du beispielsweise ein XML-Layout, das ein vertikales LinearLayout
für TextView
und Button
verwendet:
<?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="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a Button" /> </LinearLayout>
Nachdem du dein Layout im XML-Format deklariert hast, speichere die Datei mit der Erweiterung .xml
im Verzeichnis res/layout/
deines Android-Projekts, damit sie korrekt kompiliert wird.
Weitere Informationen zur Syntax einer Layout-XML-Datei finden Sie unter Layoutressource.
XML-Ressource laden
Beim Kompilieren Ihrer Anwendung wird jede XML-Layoutdatei in einer View
-Ressource kompiliert. Laden Sie die Layoutressource in der Callback-Implementierung Activity.onCreate()
Ihrer App. Rufen Sie dazu setContentView()
auf und übergeben Sie den Verweis auf Ihre Layoutressource in folgendem Format: R.layout.layout_file_name
. Wenn Ihr XML-Layout beispielsweise als main_layout.xml
gespeichert ist, laden Sie es so für Activity
:
Kotlin
fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.main_layout) }
Java
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); }
Das Android-Framework ruft die Callback-Methode onCreate()
in der Activity
auf, wenn das Activity
gestartet wird. Weitere Informationen zu Aktivitätslebenszyklen finden Sie unter Einführung in Aktivitäten.
Merkmale
Jedes View
- und ViewGroup
-Objekt unterstützt seine eigenen XML-Attribute. Einige Attribute gelten nur für ein View
-Objekt. Beispielsweise unterstützt TextView
das Attribut textSize
. Diese Attribute werden jedoch auch von allen View
-Objekten übernommen, die diese Klasse erweitern. Einige sind allen View
-Objekten gleich, da sie von der Stammklasse View
wie dem Attribut id
übernommen werden. Andere Attribute werden als Layoutparameter bezeichnet. Dabei handelt es sich um Attribute, die bestimmte Layoutausrichtungen des View
-Objekts beschreiben, die durch das übergeordnete ViewGroup
-Objekt dieses Objekts definiert sind.
ID
Jedem View
-Objekt kann eine Ganzzahl-ID zugeordnet werden, um das View
-Objekt im Baum eindeutig zu identifizieren. Beim Kompilieren der Anwendung wird auf diese ID als Ganzzahl verwiesen. Die ID wird jedoch in der Regel in der Layout-XML-Datei als String im Attribut id
zugewiesen. Dies ist ein für alle View
-Objekte gemeinsames XML-Attribut, das von der Klasse View
definiert wird. Sie verwenden es sehr oft. Die Syntax für eine ID in einem XML-Tag lautet so:
android:id="@+id/my_button"
Das at-Symbol (@) am Anfang des Strings zeigt an, dass der XML-Parser den Rest des ID-Strings parst und erweitert und ihn als ID-Ressource identifiziert. Das Plussymbol (+) bedeutet, dass dies ein neuer Ressourcenname ist, der erstellt und den Ressourcen in der Datei R.java
hinzugefügt werden muss.
Das Android-Framework bietet viele weitere ID-Ressourcen. Wenn Sie auf eine Android-Ressourcen-ID verweisen, benötigen Sie das Plussymbol nicht. Sie müssen aber den Namespace des android
-Pakets so hinzufügen:
android:id="@android:id/empty"
Der Namespace des Pakets android
gibt an, dass Sie auf eine ID aus der Ressourcenklasse android.R
und nicht auf die lokale Ressourcenklasse verweisen.
Um Ansichten zu erstellen und in Ihrer App darauf zu verweisen, können Sie ein gängiges Muster verwenden:
- Definieren Sie eine Ansicht in der Layoutdatei und weisen Sie 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"/>
- Erstellen Sie eine Instanz des Ansichtsobjekts und erfassen Sie sie aus dem Layout, normalerweise in der Methode
onCreate()
, wie im folgenden Beispiel gezeigt:Kotlin
val myButton: Button = findViewById(R.id.my_button)
Java
Button myButton = (Button) findViewById(R.id.my_button);
Beim Erstellen einer RelativeLayout
ist es wichtig, IDs für Ansichtsobjekte zu definieren.
In einem relativen Layout können gleichgeordnete Ansichten ihr Layout relativ zu einer anderen gleichgeordneten Ansicht definieren, auf die durch die eindeutige ID verwiesen wird.
Eine ID muss nicht innerhalb der gesamten Baumstruktur eindeutig sein, aber innerhalb des Teils des gesuchten Baums eindeutig. Oft handelt es sich dabei um den gesamten Baum. Deshalb sollte er möglichst einzigartig sein.
Layoutparameter
XML-Layoutattribute mit dem Namen layout_something
definieren Layoutparameter für das View
, die für die ViewGroup
geeignet sind, in der es sich befindet.
Jede ViewGroup
-Klasse implementiert eine verschachtelte Klasse, die ViewGroup.LayoutParams
erweitert.
Diese abgeleitete Klasse enthält Property-Typen, die die Größe und Position jeder untergeordneten Ansicht entsprechend der Ansichtsgruppe definieren. Wie in Abbildung 2 dargestellt, definiert die übergeordnete Ansichtsgruppe Layoutparameter für jede untergeordnete Ansicht, einschließlich der untergeordneten Ansichtsgruppe.
Jede abgeleitete LayoutParams
-Klasse hat eine eigene Syntax zum Festlegen von Werten. Jedes untergeordnete Element muss eine LayoutParams
definieren, die für das übergeordnete Element geeignet ist. Es kann aber auch eine andere LayoutParams
für seine eigenen untergeordneten Elemente definieren.
Alle Ansichtsgruppen enthalten eine Breite und Höhe mithilfe von layout_width
und layout_height
, die jeweils für jede Ansicht definiert werden müssen. Viele LayoutParams
haben optionale Ränder und Rahmen.
Sie können Breite und Höhe mit genauen Maßen angeben, dies ist jedoch nicht immer sinnvoll. Häufiger verwenden Sie eine dieser Konstanten, um die Breite oder Höhe festzulegen:
wrap_content
: Die Ansicht wird automatisch an die für ihren Inhalt erforderlichen Abmessungen angepasst.match_parent
: Damit wird die Ansicht so groß, wie die übergeordnete Ansichtsgruppe zulässt.
Im Allgemeinen raten wir davon ab, die Breite und Höhe des Layouts in absoluten Einheiten wie Pixeln anzugeben. Ein besserer Ansatz ist die Verwendung relativer Messwerte wie dichteunabhängige Pixeleinheiten (dp), wrap_content
oder match_parent
, da Ihre App so auf einer Vielzahl von Bildschirmgrößen korrekt angezeigt wird. Die zulässigen Messtypen sind in der Layoutressource definiert.
Layout position
Eine Ansicht weist eine rechteckige Geometrie auf. Sie besteht aus einem Standort, der durch left- und top-Koordinaten ausgedrückt wird, und zwei Dimensionen, die als Breite und Höhe ausgedrückt werden. Die Einheit für Standort und Abmessungen ist das Pixel.
Sie können den Speicherort einer Ansicht abrufen, indem Sie die Methoden getLeft()
und getTop()
aufrufen.
Ersteres gibt die linke Koordinate (x) des Rechtecks zurück, das die Ansicht darstellt. Letzteres gibt die oberste Koordinate (y) des Rechtecks zurück, das die Ansicht darstellt. Diese Methoden geben den Speicherort der Ansicht in Bezug auf die übergeordnete Ansicht zurück. Wenn getLeft()
beispielsweise 20 zurückgibt, befindet sich die Ansicht 20 Pixel rechts vom linken Rand des direkten übergeordneten Elements.
Darüber hinaus gibt es praktische Methoden, um unnötige Berechnungen zu vermeiden: getRight()
und getBottom()
.
Diese Methoden geben die Koordinaten des rechten und unteren Rands des Rechtecks zurück, das die Ansicht darstellt. Das Aufrufen von getRight()
erfolgt beispielsweise ähnlich wie die folgende Berechnung: getLeft() + getWidth()
.
Größe, Abstand und Ränder
Die Größe einer Ansicht wird mit einer Breite und Höhe ausgedrückt. Eine Ansicht hat zwei Wertepaare für Breite und Höhe.
Das erste Paar wird als gemessene Breite und gemessene Höhe bezeichnet. Diese Dimensionen bestimmen, wie groß eine Ansicht innerhalb
der übergeordneten Ansicht sein soll. Sie können die gemessenen Dimensionen abrufen, indem Sie getMeasuredWidth()
und getMeasuredHeight()
aufrufen.
Das zweite Paar wird als Breite und Höhe oder manchmal als Zeichenbreite und Zeichenhöhe bezeichnet. Diese Abmessungen definieren die tatsächliche Größe der Ansicht auf dem Bildschirm zum Zeitpunkt der Zeichnung und nach dem Layout. Diese Werte können von der gemessenen Breite und Höhe abweichen. Das ist aber nicht zwingend erforderlich. Breite und Höhe erhalten Sie durch Aufrufen von getWidth()
und getHeight()
.
Zur Messung der Abmessungen wird bei einer Ansicht der Abstand berücksichtigt. Der Abstand wird für den linken, oberen, rechten und unteren Teil der Ansicht in Pixeln angegeben.
Mithilfe von Innenabständen können Sie den Inhalt der Ansicht um eine bestimmte Anzahl von Pixeln verschieben. Beispielsweise wird bei einem linken Abstand von zwei Pixeln der Inhalt der Ansicht um zwei Pixel nach rechts vom linken Rand verschoben. Sie können mit der Methode setPadding(int, int, int, int)
einen Abstand festlegen und ihn durch Aufrufen von getPaddingLeft()
, getPaddingTop()
, getPaddingRight()
und getPaddingBottom()
abfragen.
Obwohl für eine Ansicht ein Abstand definiert werden kann, werden in dieser Ansicht keine Ränder unterstützt. Ränder werden jedoch von Ansichtsgruppen unterstützt. Weitere Informationen finden Sie unter ViewGroup
und ViewGroup.MarginLayoutParams
.
Weitere Informationen zu Dimensionen finden Sie unter Dimension.
Neben dem programmatischen Festlegen von Rändern und Innenabständen können Sie sie auch in Ihren 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="Hello, I am a 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="Hello, I am a Button" /> </LinearLayout>
Das vorherige Beispiel zeigt, wie Rand und Padding angewendet werden. Bei TextView
werden überall einheitliche Ränder und Abstände angewendet. Button
zeigt, wie Sie diese unabhängig auf verschiedene Kanten anwenden können.
Gängige Layouts
Jede abgeleitete Klasse der ViewGroup
-Klasse bietet eine einzigartige Möglichkeit, die darin verschachtelten Ansichten anzuzeigen. ConstraintLayout
ist der flexibelste Layouttyp und derjenige, der die besten Tools für eine flache Layouthierarchie bietet.
Im Folgenden findest du einige der gängigen Layouttypen, die in die Android-Plattform integriert sind.
Ordnet die untergeordneten Elemente in einer einzelnen horizontalen oder vertikalen Zeile an und erstellt eine Bildlaufleiste, wenn die Länge des Fensters die Länge des Bildschirms überschreitet.
Dynamische Listen erstellen
Wenn der Inhalt für Ihr Layout dynamisch oder nicht vordefiniert ist, können Sie RecyclerView
oder eine abgeleitete Klasse von AdapterView
verwenden.
RecyclerView
ist in der Regel die bessere Option, da sie Arbeitsspeicher effizienter nutzt als AdapterView
.
Zu den gängigen Layouts, die mit RecyclerView
und AdapterView
möglich sind, gehören:
RecyclerView
bietet mehr Möglichkeiten und die Möglichkeit, einen benutzerdefinierten Layout-Manager zu erstellen.
Adapteransicht mit Daten füllen
Sie können ein AdapterView
wie ListView
oder GridView
füllen, indem Sie die AdapterView
-Instanz an eine Adapter
binden, die Daten aus einer externen Quelle abruft und einen View
erstellt, der jeden Dateneintrag darstellt.
Android bietet mehrere abgeleitete Klassen von Adapter
, die zum Abrufen verschiedener Arten von Daten und zum Erstellen von Ansichten für eine AdapterView
nützlich sind. Die beiden gängigsten Adapter sind:
ArrayAdapter
- Verwenden Sie diesen Adapter, wenn Ihre Datenquelle ein Array ist. Standardmäßig erstellt
ArrayAdapter
eine Ansicht für jedes Array-Element. Dazu wirdtoString()
für jedes Element aufgerufen und der Inhalt in eineTextView
platziert.Wenn Sie beispielsweise ein Array mit Strings haben, das Sie in einem
ListView
darstellen möchten, initialisieren Sie ein neuesArrayAdapter
-Objekt. Verwenden Sie dazu einen Konstruktor, um das Layout für jeden String und das String-Array anzugeben:Kotlin
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
Java
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
Die Argumente für diesen Konstruktor sind folgende:
- Deine App
Context
- Das Layout, das ein
TextView
für jeden String im Array enthält - String-Array
Rufen Sie dann
setAdapter()
auf IhremListView
auf:Kotlin
val listView: ListView = findViewById(R.id.listview) listView.adapter = adapter
Java
ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(adapter);
Wenn Sie die Darstellung der einzelnen Elemente anpassen möchten, können Sie die Methode
toString()
für die Objekte in Ihrem Array überschreiben. Um für jedes Element, das nichtTextView
ist, eine Ansicht zu erstellen, beispielsweise einImageView
für jedes Arrayelement, erweitern Sie dieArrayAdapter
-Klasse und überschreiben SiegetView()
, um für jedes Element den gewünschten Ansichtstyp zurückzugeben. - Deine App
SimpleCursorAdapter
- Verwenden Sie diesen Adapter, wenn Ihre Daten aus einem
Cursor
stammen. Wenn duSimpleCursorAdapter
verwendest, musst du ein Layout angeben, das für jede Zeile inCursor
verwendet werden soll und welche Spalten inCursor
in die Ansichten des gewünschten Layouts eingefügt werden sollen. Wenn Sie beispielsweise eine Liste mit den Namen und Telefonnummern von Personen erstellen möchten, können Sie eine Abfrage ausführen, die einCursor
zurückgibt, das eine Zeile für jede Person und Spalten für die Namen und Nummern enthält. Anschließend erstellen Sie ein String-Array, das angibt, welche Spalten ausCursor
im Layout für die einzelnen Ergebnisse verwendet werden sollen, und ein Ganzzahl-Array, das die entsprechenden Ansichten für die Platzierung jeder Spalte angibt:Kotlin
val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER) val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
Java
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews = {R.id.display_name, R.id.phone_number};
Wenn Sie das
SimpleCursorAdapter
instanziieren, übergeben Sie das für jedes Ergebnis zu verwendende Layout, dasCursor
mit den Ergebnissen und diese beiden Arrays:Kotlin
val adapter = SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0) val listView = getListView() listView.adapter = adapter
Java
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); ListView listView = getListView(); listView.setAdapter(adapter);
Das
SimpleCursorAdapter
erstellt dann eine Ansicht für jede Zeile imCursor
unter Verwendung des angegebenen Layouts, indem jedesfromColumns
-Element in die entsprechendetoViews
-Ansicht eingefügt wird.
Wenn Sie im Laufe der Lebensdauer Ihrer App die zugrunde liegenden Daten ändern, die von Ihrem Adapter gelesen werden, rufen Sie notifyDataSetChanged()
auf.
Dadurch wird die angehängte Ansicht darüber informiert, dass die Daten geändert wurden, und die Ansicht wird aktualisiert.
Klickereignisse verarbeiten
Mithilfe der AdapterView.OnItemClickListener
-Schnittstelle können Sie auf Klickereignisse für jedes Element in einem AdapterView
reagieren. Beispiele:
Kotlin
listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> // Do something in response to the click. }
Java
// Create a message handling object as an anonymous class. private OnItemClickListener messageClickedHandler = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // Do something in response to the click. } }; listView.setOnItemClickListener(messageClickedHandler);
Weitere Informationen
Informationen zur Verwendung von Layouts findest du in der Sunflower-Demo-App auf GitHub.