Android bietet ein komplexes und leistungsstarkes komponentenbasiertes Modell zum Erstellen Ihrer UI, das auf den grundlegenden Layoutklassen View
und ViewGroup
basiert. Die Plattform enthält eine Vielzahl vordefinierter View
- und ViewGroup
-Unterklassen (Widgets bzw. Layouts genannt), mit denen Sie Ihre UI erstellen können.
Eine unvollständige Liste der verfügbaren Widgets umfasst Button
, TextView
, EditText
, ListView
, CheckBox
, RadioButton
, Gallery
, Spinner
und die speziellen Widgets AutoCompleteTextView
, ImageSwitcher
und TextSwitcher
.
Zu den verfügbaren Layouts gehören LinearLayout
, FrameLayout
, RelativeLayout
und weitere. Weitere Beispiele finden Sie unter Gängige Layouts.
Wenn keines der vordefinierten Widgets oder Layouts Ihren Anforderungen entspricht, können Sie Ihre eigene abgeleitete Klasse View
erstellen. Wenn Sie nur kleine Anpassungen an einem vorhandenen Widget oder Layout vornehmen müssen, können Sie eine Unterklasse für das Widget oder Layout erstellen und die zugehörigen Methoden überschreiben.
Wenn Sie eigene abgeleitete View
-Klassen erstellen, können Sie die Darstellung und Funktion eines Bildschirmelements präzise steuern. Im Folgenden finden Sie einige Beispiele für die Möglichkeiten, die Sie mit benutzerdefinierten Ansichten haben:
-
Sie können einen vollständig benutzerdefiniert gerenderten
View
-Typ erstellen, zum Beispiel einen in 2D-Grafik gerenderten Lautstärkeregler, der einem analogen elektronischen Steuerelement ähnelt. -
Sie können eine Gruppe von
View
-Komponenten zu einer neuen einzelnen Komponente kombinieren, z. B. zu einem Kombinationsfeld (eine Kombination aus Pop-up-Liste und Textfeld mit kostenlosem Eingabefeld), einem Steuerelement für die Auswahl aus zwei Bereichen (linker und rechter Bereich mit jeweils einer Liste, in der Sie neu zuweisen können, welches Element sich in welcher Liste befindet) usw. -
Sie können die Art und Weise überschreiben, wie eine
EditText
-Komponente auf dem Bildschirm gerendert wird. Die Beispiel-App NotePad nutzt dies, um eine gezeichnete Notizblock-Seite zu erstellen. - Sie können andere Ereignisse wie das Drücken von Tasten erfassen und auf eine benutzerdefinierte Weise verarbeiten, z. B. für ein Spiel.
In den folgenden Abschnitten wird erläutert, wie Sie benutzerdefinierte Ansichten erstellen und in Ihrer Anwendung verwenden. Ausführliche Referenzinformationen finden Sie in der Klasse View
.
Der grundlegende Ansatz
Im Folgenden finden Sie eine allgemeine Übersicht darüber, was Sie wissen müssen, um Ihre eigenen View
-Komponenten zu erstellen:
-
Erweitern Sie eine vorhandene
View
-Klasse oder abgeleitete Klasse mit Ihrer eigenen Klasse. -
Überschreiben Sie einige Methoden aus der Basisklasse. Die zu überschreibenden Basisklassenmethoden beginnen mit
on
, z. B.onDraw()
,onMeasure()
undonKeyDown()
. Dies ähnelt denon
-Ereignissen inActivity
oderListActivity
, die Sie für Lebenszyklus- und andere Funktions-Hooks überschreiben. - Verwenden Sie die neue Erweiterungsklasse. Anschließend können Sie die neue Erweiterungsklasse anstelle der Ansicht verwenden, auf der sie basiert.
Vollständig angepasste Komponenten
Sie können vollständig benutzerdefinierte grafische Komponenten erstellen, die beliebig dargestellt werden. Vielleicht brauchen Sie ein grafisches VU-Messtool, das wie eine alte analoge Anzeige aussieht, oder eine Textansicht zum Mitsingen, in der sich eine hüpfende Kugel über die Wörter bewegt, während Sie mit einer Karaokemaschine singen. Möglicherweise benötigen Sie etwas, das die integrierten Komponenten nicht können, egal wie Sie sie kombinieren.
Glücklicherweise können Sie Komponenten erstellen, die sich ganz nach Ihren Vorstellungen und Funktionen anpassen – allerdings nur durch Vorstellungskraft, Bildschirmgröße und verfügbare Rechenleistung. Beachten Sie dabei, dass Ihre Anwendung unter Umständen mit deutlich weniger Energie als Ihre Desktop-Workstation ausgeführt werden muss.
Berücksichtigen Sie beim Erstellen einer vollständig angepassten Komponente Folgendes:
-
Die allgemeinste Ansicht, die erweitert werden kann, ist
View
. Daher erweitern Sie normalerweise diese Ansicht, um Ihre neue Superkomponente zu erstellen. - Sie können einen Konstruktor angeben, der Attribute und Parameter aus der XML-Datei übernehmen kann. Außerdem können Sie eigene Attribute und Parameter wie die Farbe und den Bereich des VU-Messtools oder die Breite und Dämpfung der Nadel verwenden.
- Sie möchten wahrscheinlich Ihre eigenen Ereignis-Listener, Property-Zugriffsfunktionen und -Modifikatoren erstellen sowie ausgefeiltere Verhaltensweisen in Ihrer Komponentenklasse.
-
Es ist sehr wahrscheinlich, dass Sie
onMeasure()
überschreiben und wahrscheinlich auchonDraw()
überschreiben müssen, wenn die Komponente etwas anzeigen soll. Beide haben das Standardverhalten, aber der Standardwert vononDraw()
hat keine Auswirkungen und der Standardwert vononMeasure()
legt immer eine Größe von 100 × 100 fest, was Sie wahrscheinlich nicht wünschen. -
Sie können bei Bedarf auch andere
on
-Methoden überschreiben.
onDraw() und onMeasure() erweitern
Die Methode onDraw()
liefert eine Canvas
, auf die Sie alles nach Belieben implementieren können: 2D-Grafiken, andere Standard- oder benutzerdefinierte Komponenten, formatierten Text oder alles andere, was Ihnen einfällt.
onMeasure()
ist ein wenig stärker eingebunden. onMeasure()
ist ein wichtiger Bestandteil des Renderingvertrags zwischen Ihrer Komponente und ihrem Container. onMeasure()
muss überschrieben werden, um die Messungen der enthaltenen Teile effizient und genau zu erfassen. Dies wird durch die Grenzwertanforderungen des übergeordneten Elements, die an die Methode onMeasure()
übergeben werden, und durch die Anforderung, nach der Berechnung die Methode setMeasuredDimension()
mit der gemessenen Breite und Höhe aufzurufen, etwas komplexer. Wenn Sie diese Methode nicht über eine überschriebene onMeasure()
-Methode aufrufen, wird zum Messzeitpunkt eine Ausnahme ausgelöst.
Auf übergeordneter Ebene sieht die Implementierung von onMeasure()
in etwa so aus:
-
Die überschriebene
onMeasure()
-Methode wird mit Spezifikationen für Breite und Höhe aufgerufen, die als Anforderungen für die Einschränkungen in Bezug auf die von Ihnen erstellten Breiten- und Höhenmessungen gelten. Die ParameterwidthMeasureSpec
undheightMeasureSpec
sind Ganzzahlcodes, die Dimensionen darstellen. Eine vollständige Referenz zu den Arten von Einschränkungen, für die diese Spezifikationen erforderlich sind, finden Sie in der Referenzdokumentation unterView.onMeasure(int, int)
. Außerdem wird dort der gesamte Messvorgang erläutert. -
Mit der Methode
onMeasure()
der Komponente werden die Breite und Höhe der Messung berechnet, die zum Rendern der Komponente erforderlich sind. Es muss versucht werden, die übergebenen Spezifikationen einzuhalten, auch wenn sie diese überschreiten können. In diesem Fall kann das übergeordnete Element auswählen, was zu tun ist, z. B. Zuschneiden, Scrollen, eine Ausnahme auslösen oderonMeasure()
bitten, es noch einmal zu versuchen – eventuell mit anderen Messspezifikationen. -
Wenn Breite und Höhe berechnet wurden, rufen Sie die Methode
setMeasuredDimension(int width, int height)
mit den berechneten Maßen auf. Andernfalls wird eine Ausnahme ausgelöst.
Nachfolgend finden Sie eine Zusammenfassung weiterer Standardmethoden, die das Framework für Ansichten auffordert:
Kategorie | Methoden | Beschreibung |
---|---|---|
Erstellung | Konstruktoren | Es gibt ein Formular des Konstruktors, der aufgerufen wird, wenn die Ansicht aus Code erstellt wird, und ein Formular, das aufgerufen wird, wenn die Ansicht aus einer Layoutdatei aufgebläht wird. Im zweiten Formular werden die in der Layoutdatei definierten Attribute geparst und angewendet. |
|
Wird aufgerufen, nachdem eine Ansicht und alle untergeordneten Elemente aus XML aufgeblasen wurden. | |
Layout |
|
Wird aufgerufen, um die Größenanforderungen für diese Ansicht und alle untergeordneten Elemente zu ermitteln. |
|
Wird aufgerufen, wenn in dieser Ansicht allen untergeordneten Elementen eine Größe und Position zugewiesen werden muss. | |
|
Wird aufgerufen, wenn die Größe dieser Ansicht geändert wird. | |
Zeichnung |
|
Wird aufgerufen, wenn die Ansicht ihren Inhalt rendern muss. |
Ereignisverarbeitung |
|
Wird aufgerufen, wenn ein Key-down-Ereignis eintritt. |
|
Wird aufgerufen, wenn ein Key-up-Ereignis eintritt. | |
|
Wird aufgerufen, wenn eine Trackball-Bewegung auftritt. | |
|
Wird beim Auftreten einer Touchscreen-Bewegung aufgerufen. | |
Fokus |
|
Wird aufgerufen, wenn die Ansicht gewinnt oder den Fokus verliert |
|
Wird aufgerufen, wenn das Fenster mit der Ansicht gewinnt oder den Fokus verliert. | |
Wird angehängt |
|
Wird aufgerufen, wenn die Ansicht an ein Fenster angehängt ist. |
|
Wird aufgerufen, wenn die Ansicht vom Fenster getrennt ist | |
|
Wird aufgerufen, wenn sich die Sichtbarkeit des Fensters mit der Ansicht ändert. |
Komplexe Steuerelemente
Wenn Sie keine vollständig benutzerdefinierte Komponente erstellen möchten, sondern eine wiederverwendbare Komponente aus einer Gruppe vorhandener Steuerelemente zusammenstellen möchten, ist es wahrscheinlich am besten, eine zusammengesetzte Komponente (oder eine kombinierte Steuerung) zu erstellen. Zusammenfassend lässt sich sagen, dass dadurch eine Reihe von mehr atomaren Steuerelementen oder Ansichten zu einer logischen Gruppe von Elementen zusammengefasst wird, die als ein Element behandelt werden können.
Ein Kombinationsfeld kann beispielsweise eine Kombination aus einem einzeiligen EditText
-Feld und einer angrenzenden Schaltfläche mit einer angehängten Pop-up-Liste sein. Wenn der Nutzer auf die Schaltfläche tippt und etwas aus der Liste auswählt, wird das Feld EditText
ausgefüllt. Er kann aber auch etwas direkt in EditText
eingeben.
In Android stehen dazu zwei weitere Ansichten zur Verfügung: Spinner
und AutoCompleteTextView
. Trotzdem ist dieses Konzept für ein Kombinationsfeld ein gutes Beispiel.
So erstellen Sie eine Verbundkomponente:
-
Verwenden Sie wie bei einem
Activity
entweder den deklarativen (XML-basierten) Ansatz, um die enthaltenen Komponenten zu erstellen, oder verschachteln Sie sie programmatisch aus Ihrem Code. Der übliche Ausgangspunkt ist eineLayout
. Erstellen Sie daher eine Klasse, die eineLayout
erweitert. Bei einem Kombinationsfeld können SieLinearLayout
mit horizontaler Ausrichtung verwenden. Sie können andere Layouts verschachteln, sodass die zusammengesetzte Komponente beliebig komplex und strukturiert sein kann. -
Nehmen Sie im Konstruktor der neuen Klasse alle Parameter, die die übergeordnete Klasse erwartet, und übergeben Sie sie zuerst an den Konstruktor der übergeordneten Klasse. Anschließend können Sie die anderen Ansichten einrichten, die Sie in Ihrer neuen Komponente verwenden möchten. Hier erstellen Sie das Feld
EditText
und die Pop-up-Liste. Sie können eigene Attribute und Parameter in den XML-Code einführen, den Ihr Konstruktor abrufen und verwenden kann. -
Optional können Sie Listener für Ereignisse erstellen, die Ihre enthaltenen Ansichten möglicherweise generieren. Ein Beispiel hierfür ist eine Listener-Methode für den Listener für Listenelemente, mit dem der Inhalt von
EditText
aktualisiert wird, wenn eine Listenauswahl getroffen wird. -
Optional können Sie eigene Attribute mit Zugriffsmethoden und Modifikatoren erstellen. Beispielsweise kann der Wert
EditText
anfangs in der Komponente festgelegt werden und der Inhalt wird bei Bedarf abgefragt. -
Überschreiben Sie optional
onDraw()
undonMeasure()
. Dies ist normalerweise nicht erforderlich, wenn einLayout
erweitert wird, da das Layout ein Standardverhalten hat, das wahrscheinlich gut funktioniert. -
Sie können andere
on
-Methoden wieonKeyDown()
überschreiben, um z. B. bestimmte Standardwerte aus der Pop-up-Liste eines Kombinationsfelds auszuwählen, wenn auf eine bestimmte Taste getippt wird.
Die Verwendung eines Layout
als Grundlage für ein benutzerdefiniertes Steuerelement hat folgende Vorteile:
- Du kannst das Layout wie bei einem Aktivitätsbildschirm mithilfe der deklarativen XML-Dateien angeben oder Ansichten programmatisch erstellen und sie aus deinem Code in das Layout verschachteln.
-
Die Methoden
onDraw()
undonMeasure()
sowie die meisten anderen Methodenon
haben ein geeignetes Verhalten, sodass Sie diese nicht überschreiben müssen. - Beliebig komplexe zusammengesetzte Ansichten lassen sich schnell erstellen und wie eine einzelne Komponente wiederverwenden.
Vorhandenen Ansichtstyp ändern
Wenn es eine Komponente gibt, die Ihrem Wunsch ähnelt, können Sie diese Komponente erweitern und das Verhalten überschreiben, das Sie ändern möchten. Sie können alles tun, was Sie mit einer vollständig angepassten Komponente tun, aber wenn Sie mit einer stärker spezialisierten Klasse in der View
-Hierarchie beginnen, können Sie das Verhalten, das Ihren Vorstellungen entspricht, kostenlos erhalten.
Die Beispiel-App NotePad demonstriert beispielsweise viele Aspekte der Android-Plattform. Dazu gehört unter anderem die Erweiterung einer EditText
-Ansicht, um einen markierten Notizblock zu erstellen. Dies ist kein perfektes Beispiel und die dafür erforderlichen APIs können sich ändern, aber es verdeutlicht die Prinzipien.
Falls noch nicht geschehen, importiere das NotePad-Beispiel in Android Studio oder rufe die Quelle über den angegebenen Link auf. Lesen Sie insbesondere die Definition von LinedEditText
in der Datei NoteEditor.java
.
Beachten Sie Folgendes in dieser Datei:
-
Die Definition
Die Klasse wird mit der folgenden Zeile definiert:
public static class LinedEditText extends EditText
LinedEditText
ist als innere Klasse innerhalb derNoteEditor
-Aktivität definiert. Es ist jedoch öffentlich, sodass von außerhalb derNoteEditor
-Klasse alsNoteEditor.LinedEditText
darauf zugegriffen werden kann.Außerdem ist
LinedEditText
static
. Das bedeutet, dass nicht die sogenannten "synthetischen Methoden" generiert werden, die den Zugriff auf Daten aus der übergeordneten Klasse ermöglichen. Das bedeutet, dass sie sich als separate Klasse verhält und nicht als eng mitNoteEditor
verbunden ist. So lassen sich innere Klassen einfacher erstellen, wenn sie keinen Zugriff auf den Zustand von der äußeren Klasse benötigen. Dadurch bleibt die generierte Klasse klein und kann problemlos von anderen Klassen verwendet werden.LinedEditText
erweitertEditText
. In diesem Fall muss die Ansicht angepasst werden. Wenn Sie fertig sind, kann die neue Klasse die normaleEditText
-Ansicht ersetzen. -
Klasseninitialisierung
Wie immer wird der Supers zuerst genannt. Dies ist kein Standardkonstruktor, sondern ein parametrierter Konstruktor. Der
EditText
wird mit diesen Parametern erstellt, wenn er aus einer XML-Layoutdatei aufgeblasen wird. Daher muss der Konstruktor sie ebenfalls an den Superklassen-Konstruktor übergeben. -
Überschriebene Methoden
In diesem Beispiel wird nur die Methode
onDraw()
überschrieben. Möglicherweise müssen Sie jedoch andere überschreiben, wenn Sie Ihre eigenen benutzerdefinierten Komponenten erstellen.In diesem Beispiel können Sie durch Überschreiben der Methode
onDraw()
die blauen Linien auf dem Canvas derEditText
-Ansicht zeichnen. Der Canvas wird an die überschriebeneonDraw()
-Methode übergeben. Die Methodesuper.onDraw()
wird aufgerufen, bevor die Methode endet. Die übergeordnete Klassenmethode muss aufgerufen werden. Rufen Sie es in diesem Fall am Ende auf, nachdem Sie die Linien gezeichnet haben, die enthalten sein sollen. -
Benutzerdefinierte Komponente
Sie haben jetzt Ihre benutzerdefinierte Komponente, aber wie können Sie sie verwenden? Im NotePad-Beispiel wird die benutzerdefinierte Komponente direkt aus dem deklarativen Layout verwendet. Sehen Sie sich daher
note_editor.xml
im Ordnerres/layout
an:<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
Die benutzerdefinierte Komponente wird als generische Ansicht in der XML-Datei erstellt und die Klasse wird mithilfe des vollständigen Pakets angegeben. Auf die innere Klasse wird mit der
NoteEditor$LinedEditText
-Notation verwiesen. Diese ist eine Standardmethode, um in der Programmiersprache Java auf innere Klassen zu verweisen.Wenn die Komponente der benutzerdefinierten Ansicht nicht als innere Klasse definiert ist, können Sie die Ansichtskomponente mit dem Namen des XML-Elements deklarieren und das Attribut
class
ausschließen. Beispiel:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
Die Klasse
LinedEditText
ist jetzt eine separate Klassendatei. Wenn die Klasse in der KlasseNoteEditor
verschachtelt ist, funktioniert diese Technik nicht.Die anderen Attribute und Parameter in der Definition sind diejenigen, die an den Konstruktor der benutzerdefinierten Komponente übergeben und dann an den
EditText
-Konstruktor übergeben werden. Es handelt sich also um dieselben Parameter, die Sie für eineEditText
-Ansicht verwenden. Sie können auch eigene Parameter hinzufügen.
Das Erstellen benutzerdefinierter Komponenten ist nur so kompliziert wie nötig.
Eine komplexere Komponente kann noch mehr on
-Methoden überschreiben und eigene Hilfsmethoden einführen, um ihre Eigenschaften und ihr Verhalten erheblich anzupassen. Die einzige Grenze ist Ihre Vorstellungskraft
und was die Komponente tun soll.