Android bietet ein ausgefeiltes und leistungsstarkes komponentenbasiertes Modell zum Erstellen von Benutzeroberflächen, das auf
den grundlegenden Layoutklassen
View und
ViewGroup basiert. Die Plattform umfasst eine
Vielzahl von vorgefertigten View und ViewGroup Unterklassen, die als Widgets bzw.
Layouts bezeichnet werden und mit denen Sie Ihre Benutzeroberfläche erstellen können.
Eine unvollständige Liste der verfügbaren Widgets umfasst Button,
TextView,
EditText,
ListView,
CheckBox,
RadioButton,
Gallery,
Spinner, und die spezielleren Widgets
AutoCompleteTextView,
ImageSwitcher, und
TextSwitcher.
Zu den verfügbaren Layouts gehören LinearLayout, FrameLayout und RelativeLayout. Weitere Beispiele finden Sie unter
Häufig verwendete Layouts.
Wenn keines der vorgefertigten Widgets oder Layouts Ihren Anforderungen entspricht, können Sie Ihre eigene View-Unterklasse erstellen. Wenn Sie nur kleine Anpassungen an einem vorhandenen Widget oder Layout vornehmen müssen, können Sie eine Unterklasse des Widgets oder Layouts erstellen und die zugehörigen Methoden überschreiben.
Wenn Sie Ihre eigenen View-Unterklassen erstellen, haben Sie die volle Kontrolle über das Aussehen und die Funktion eines Bildschirmelements. Um Ihnen eine Vorstellung davon zu geben, wie viel Kontrolle Sie mit benutzerdefinierten Ansichten haben, finden Sie hier einige Beispiele für die Möglichkeiten:
-
Sie können einen vollständig benutzerdefinierten
ViewTyp erstellen, z. B. einen "volume control"-Regler, der mit 2D-Grafiken gerendert wird und einem analogen elektronischen Regler ähnelt. -
Sie können eine Gruppe von
ViewKomponenten zu einer neuen einzelnen Komponente kombinieren, vielleicht um etwas wie ein Kombinationsfeld (eine Kombination aus einer Pop-up-Liste und einem Textfeld für die kostenlose Eingabe) oder ein Steuerelement mit zwei Bereichen (ein linker und ein rechter Bereich mit einer Liste in jedem Bereich, in der Sie neu zuweisen können, welches Element sich in welcher Liste befindet) zu erstellen. -
Sie können die Art und Weise überschreiben, wie eine
EditText-Komponente auf dem Bildschirm gerendert wird. Die Beispiel-App NotePad nutzt dies effektiv, um eine linierte Notizblockseite zu erstellen. - Sie können andere Ereignisse wie Tastendrücke erfassen und auf 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. Detaillierte Referenzinformationen finden Sie in der View-Klasse.
Grundlegender Ansatz
Hier finden Sie eine allgemeine Übersicht über die Schritte, die Sie ausführen müssen, um Ihre eigenen View-Komponenten zu erstellen:
-
Erstellen Sie eine Unterklasse einer vorhandenen
View-Klasse oder -Unterklasse. -
Überschreiben Sie einige der Methoden aus der Basisklasse. Die Methoden der Basisklasse, die überschrieben werden müssen, beginnen mit
on, z. B.onDraw(),onMeasure(), undonKeyDown(). Dies ähnelt denonEreignissen inActivityoderListActivitydie Sie für Lebenszyklus- und andere Funktions-Hooks überschreiben. - Verwenden Sie Ihre neue Erweiterungsklasse. Nachdem Sie die Erweiterungsklasse erstellt haben, können Sie sie anstelle von der Ansicht verwenden, auf der sie basiert.
Vollständig benutzerdefinierte Komponenten
Sie können vollständig benutzerdefinierte grafische Komponenten erstellen, die so aussehen, wie Sie es möchten. Vielleicht möchten Sie ein grafisches VU-Meter, das wie ein altes analoges Messgerät aussieht, oder eine Textansicht zum Mitsingen, in der sich ein springender Ball entlang der Wörter bewegt, während Sie mit einer Karaoke-Maschine mitsingen. Möglicherweise möchten Sie etwas, das die integrierten Komponenten nicht leisten können, unabhängig davon, wie Sie sie kombinieren.
Glücklicherweise können Sie Komponenten erstellen, die so aussehen und sich so verhalten, wie Sie es möchten. Die einzigen Einschränkungen sind Ihre Fantasie, die Größe des Bildschirms und die verfügbare Rechenleistung. Beachten Sie dabei, dass Ihre Anwendung möglicherweise auf einem Gerät mit deutlich weniger Leistung als Ihre Desktop-Workstation ausgeführt werden muss.
Wenn Sie eine vollständig benutzerdefinierte Komponente erstellen möchten, beachten Sie Folgendes:
-
Die allgemeinste Ansicht, die Sie erweitern können, ist
View. Daher beginnen Sie in der Regel damit, diese zu erweitern, um Ihre neue Superkomponente zu erstellen. - Sie können einen Konstruktor angeben, der Attribute und Parameter aus dem XML-Code übernehmen kann. Außerdem können Sie Ihre eigenen Attribute und Parameter verwenden, z. B. die Farbe und den Bereich des VU-Meters oder die Breite und Dämpfung der Nadel.
- Sie sollten auch eigene Ereignis-Listener, Property-Accessors und -Modifizierer sowie ein komplexeres Verhalten in Ihrer Komponentenklasse erstellen.
-
Sie müssen fast immer
onMeasure()überschreiben und wahrscheinlich auchonDraw()überschreiben, wenn die Komponente etwas anzeigen soll. Beide haben zwar ein Standardverhalten, aber die StandardmethodeonDraw()tut nichts und die StandardmethodeonMeasure()legt immer eine Größe von 100 × 100 fest, was Sie wahrscheinlich nicht möchten. -
Sie können bei Bedarf auch andere
on-Methoden überschreiben.
onDraw() und onMeasure() erweitern
Die Methode onDraw() liefert ein Canvas, auf dem Sie alles implementieren können, was Sie möchten: 2D-Grafiken, andere Standard- oder benutzerdefinierte Komponenten, formatierten Text oder alles andere, was Ihnen einfällt.
onMeasure() ist etwas komplizierter. onMeasure() ist ein wichtiger Bestandteil des Rendering-Vertrags zwischen Ihrer Komponente und ihrem Container. onMeasure() muss überschrieben werden, um die Maße der enthaltenen Teile effizient und genau zu melden. Das wird durch die Anforderungen des übergeordneten Elements, die an die Methode onMeasure() übergeben werden, und durch die Anforderung, die Methode setMeasuredDimension() mit der gemessenen Breite und Höhe aufzurufen, sobald diese berechnet wurden, etwas komplexer. Wenn Sie diese Methode nicht über eine überschriebene onMeasure()-Methode aufrufen, führt das zur Messzeit zu einer Ausnahme.
Im Allgemeinen sieht die Implementierung von onMeasure() so aus:
-
Die überschriebene
onMeasure()Methode wird mit Breiten- und Höhen Spezifikationen aufgerufen, die als Anforderungen für die Einschränkungen der von Ihnen erstellten Breiten- und Höhen Messungen behandelt werden.widthMeasureSpecundheightMeasureSpecParameter sind beides Ganzzahlcodes, die Dimensionen darstellen. Eine vollständige Referenz zu den Arten von Einschränkungen, die diese Spezifikationen erfordern können, finden Sie in der Referenzdokumentation unterView.onMeasure(int, int)In dieser Referenzdokumentation wird auch der gesamte Messvorgang erläutert. -
Die Methode
onMeasure()Ihrer Komponente berechnet eine Messbreite und -höhe, die zum Rendern der Komponente erforderlich sind. Sie muss versuchen, die übergebenen Spezifikationen einzuhalten kann sie aber auch überschreiten. In diesem Fall kann das übergeordnete Element entscheiden, was zu tun ist, z. B. Clipping, Scrollen, eine Ausnahme auslösen oderonMeasure()auffordern, es noch einmal zu versuchen, möglicherweise mit anderen Messspezifikationen. -
Wenn die Breite und Höhe berechnet wurden, rufen Sie die
setMeasuredDimension(int width, int height)Methode mit den berechneten Maßen auf. Andernfalls wird eine Ausnahme ausgelöst.
Hier finden Sie eine Zusammenfassung anderer Standardmethoden, die das Framework für Ansichten aufruft:
| Kategorie | Methoden | Beschreibung |
|---|---|---|
| Erstellung | Konstruktoren | Es gibt eine Form des Konstruktors, die aufgerufen wird, wenn die Ansicht aus Code erstellt wird und eine Form, die aufgerufen wird, wenn die Ansicht aus einer Layoutdatei aufgeblasen wird. Die zweite Form analysiert und wendet Attribute an, die in der Layoutdatei definiert sind. |
|
Wird aufgerufen, nachdem eine Ansicht und alle ihre untergeordneten Elemente aus XML aufgeblasen wurden. | |
| Layout | |
Wird aufgerufen, um die Größenanforderungen für diese Ansicht und alle ihrer untergeordneten Elemente zu bestimmen. |
|
Wird aufgerufen, wenn diese Ansicht allen ihren untergeordneten Elementen eine Größe und Position zuweisen 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 Tastendruckereignis auftritt. |
|
Wird aufgerufen, wenn ein Tastendruckereignis auftritt. | |
|
Wird aufgerufen, wenn ein Trackball-Motion-Event auftritt. | |
|
Wird aufgerufen, wenn ein Touchscreen-Motion-Event auftritt. | |
| Fokus | |
Wird aufgerufen, wenn die Ansicht den Fokus erhält oder verliert. |
|
Wird aufgerufen, wenn das Fenster, das die Ansicht enthält, den Fokus erhält oder verliert. | |
| Anhängen | |
Wird aufgerufen, wenn die Ansicht an ein Fenster angehängt wird. |
|
Wird aufgerufen, wenn die Ansicht von ihrem Fenster getrennt wird. | |
|
Wird aufgerufen, wenn die Sichtbarkeit des Fensters, das die Ansicht enthält, geändert wird. |
Zusammengesetzte 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 möglicherweise am besten, eine zusammengesetzte Komponente (oder ein zusammengesetztes Steuerelement) zu erstellen. Zusammenfassend werden dabei mehrere atomare Steuerelemente oder Ansichten zu einer logischen Gruppe von Elementen zusammengefasst, die als eine Einheit behandelt werden können.
Eine 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 EditText-Feld ausgefüllt. Er kann aber auch direkt etwas in das EditText-Feld eingeben.
In Android stehen zwei weitere Ansichten zur Verfügung, um dies zu tun: Spinner und AutoCompleteTextView. Unabhängig davon ist dieses Konzept für ein Kombinationsfeld ein gutes Beispiel.
So erstellen Sie eine zusammengesetzte Komponente:
-
Verwenden Sie wie bei einem
Activityentweder den deklarativen (XML-basierten) Ansatz zum Erstellen der enthaltenen Komponenten oder verschachteln Sie sie programmatisch aus Ihrem Code. Der übliche Ausgangspunkt ist einLayout. Erstellen Sie also eine Klasse, die einLayouterweitert. Im Fall eines Kombinationsfelds können Sie einLinearLayoutmit horizontaler Ausrichtung verwenden. Sie können andere Layouts darin verschachteln, sodass die zusammengesetzte Komponente beliebig komplex und strukturiert sein kann. -
Übernehmen Sie im Konstruktor für die neue Klasse alle Parameter, die die Basisklasse erwartet, und übergeben Sie sie
zuerst an den Konstruktor der Basisklasse. Anschließend können Sie die anderen Ansichten für die Verwendung
in Ihrer neuen Komponente einrichten. Hier erstellen Sie das
EditTextFeld und die Pop-up-Liste. Sie können eigene Attribute und Parameter in das XML-Dokument einfügen, die Ihr Konstruktor abrufen und verwenden kann. -
Optional können Sie Listener für Ereignisse erstellen, die von den enthaltenen Ansichten generiert werden können. Ein Beispiel ist eine Listener-Methode für den Click Listener für Listenelemente, um den Inhalt von
EditTextzu aktualisieren, wenn eine Listenauswahl getroffen wird. -
Optional können Sie eigene Properties mit Accessors und Modifizierern erstellen. Lassen Sie beispielsweise den
EditTextWert anfänglich in der Komponente festlegen und fragen Sie bei Bedarf nach dem Inhalt. -
Optional können Sie
onDraw()undonMeasure()überschreiben. Das ist in der Regel nicht erforderlich, wenn Sie einLayouterweitern , da das Layout ein Standardverhalten hat, das wahrscheinlich gut funktioniert. -
Optional können Sie andere
on-Methoden überschreiben, z. B.onKeyDown(), um bestimmte Standardwerte aus der Pop-up-Liste eines Kombinationsfelds auszuwählen, wenn eine bestimmte Taste gedrückt wird.
Die Verwendung eines Layout als Grundlage für ein benutzerdefiniertes Steuerelement bietet mehrere Vorteile:
- Sie können das Layout mit den deklarativen XML-Dateien angeben, genau wie bei einem Aktivitätsbildschirm, oder Sie können Ansichten programmatisch erstellen und sie aus Ihrem Code in das Layout verschachteln.
-
Die Methoden
onDraw()undonMeasure()sowie die meisten anderenonMethoden haben ein geeignetes Verhalten, sodass Sie sie nicht überschreiben müssen. - Sie können beliebig komplexe zusammengesetzte Ansichten schnell erstellen und sie so wiederverwenden, als wären sie eine einzelne Komponente.
Vorhandenen Ansichtstyp ändern
Wenn es eine Komponente gibt, die dem ähnelt, was Sie möchten, 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 benutzerdefinierten Komponente tun können. Wenn Sie jedoch mit einer spezialisierteren Klasse in der View-Hierarchie beginnen, erhalten Sie einige Verhaltensweisen, die Ihre Anforderungen erfüllen.
Die Beispiel-App
NotePad
veranschaulicht viele Aspekte der Verwendung der Android-Plattform. Dazu gehört auch das Erweitern einer EditText-Ansicht, um einen linierten Notizblock zu erstellen. Das ist kein perfektes Beispiel und die APIs dafür können sich ändern, aber es veranschaulicht die Prinzipien.
Wenn Sie das noch nicht getan haben, importieren Sie das NotePad-Beispiel in Android Studio oder sehen Sie sich den Quellcode über den Link an. Sehen Sie sich insbesondere die Definition von LinedEditText
in der
NoteEditor.java
Datei an.
Hier sind einige Punkte, die Sie in dieser Datei beachten sollten:
-
Die Definition
Die Klasse wird mit der folgenden Zeile definiert:
public static class LinedEditText extends EditTextLinedEditTextwird als innere Klasse in derNoteEditorAktivität definiert, ist aber öffentlich, sodass von außerhalb derNoteEditorKlasse alsNoteEditor.LinedEditTextdarauf zugegriffen werden kann.Außerdem ist
LinedEditTextstatic. Das bedeutet, dass keine sogenannten "synthetischen Methoden" generiert werden, mit denen auf Daten aus der Basisklasse zugegriffen werden kann. Daher verhält sie sich eher wie eine separate Klasse als wie etwas, das eng mitNoteEditorverbunden ist. Das ist eine sauberere Möglichkeit, innere Klassen zu erstellen, wenn sie keinen Zugriff auf den Status der äußeren Klasse benötigen. Dadurch bleibt die generierte Klasse klein und kann problemlos von anderen Klassen verwendet werden.LinedEditTexterweitertEditText, die Ansicht, die in diesem Fall angepasst werden soll. Wenn Sie fertig sind, kann die neue Klasse eine normaleEditTextAnsicht ersetzen. -
Klasseninitialisierung
Wie immer wird zuerst die Basisklasse aufgerufen. Das ist kein Standardkonstruktor, sondern ein parametrisierter Konstruktor. Das
EditTextwird mit diesen Parametern erstellt, wenn es aus einer XML-Layoutdatei aufgeblasen wird. Daher muss der Konstruktor sie übernehmen und auch an den Konstruktor der Basisklasse ü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
onDraw()Methode die blauen Linien auf dasEditTextAnsichts-Canvas zeichnen. Das Canvas wird an die überschriebeneonDraw()Methode übergeben. Diesuper.onDraw()Methode wird aufgerufen, bevor die Methode endet. Die Methode der Basisklasse muss aufgerufen werden. In diesem Fall rufen Sie sie am Ende auf, nachdem Sie die gewünschten Linien gezeichnet haben. -
Benutzerdefinierte Komponente
Sie haben jetzt Ihre benutzerdefinierte Komponente. Wie können Sie sie verwenden? Im NotePad-Beispiel wird die benutzerdefinierte Komponente direkt aus dem deklarativen Layout verwendet. Sehen Sie sich also
note_editor.xmlimres/layoutOrdner 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 im XML-Code erstellt und die Klasse wird angegeben mit dem vollständigen Paket. Auf die von Ihnen definierte innere Klasse wird mit der
NoteEditor$LinedEditTextNotation verwiesen. Das ist eine Standardmethode, um in der Programmiersprache Java auf innere Klassen zu verweisen.Wenn Ihre benutzerdefinierte Ansichtskomponente nicht als innere Klasse definiert ist, können Sie die Ansicht Komponente mit dem XML-Elementnamen deklarieren und das
classAttribut ausschließen. Beispiel:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
Beachten Sie, dass die Klasse
LinedEditTextjetzt eine separate Klassendatei ist. Wenn die Klasse in derNoteEditorKlasse verschachtelt ist, funktioniert diese Technik nicht.Die anderen Attribute und Parameter in der Definition werden an den Konstruktor der benutzerdefinierten Komponente übergeben und dann an den
EditTextKonstruktor weitergeleitet. Es sind also dieselben Parameter, die Sie für eineEditTextAnsicht verwenden. Sie können auch eigene Parameter hinzufügen.
Das Erstellen benutzerdefinierter Komponenten ist nur so kompliziert, wie Sie es benötigen.
Eine komplexere Komponente kann noch mehr on-Methoden überschreiben und eigene Hilfsmethoden einführen, wodurch ihre Eigenschaften und ihr Verhalten erheblich angepasst werden. Die einzigen Einschränkungen sind Ihre Fantasie und die Anforderungen an die Komponente.