Android oferuje zaawansowany i zaawansowany model z komponentami do tworzenia interfejsu użytkownika oparty na podstawowych klasach układu View
i ViewGroup
. Platforma zawiera różne gotowe podklasy View
i ViewGroup
, nazywane odpowiednio widżetami i układami, których można używać do tworzenia interfejsu użytkownika.
Częściowa lista dostępnych widżetów to Button
,
TextView
,
EditText
,
ListView
,
CheckBox
,
RadioButton
,
Gallery
,
Spinner
i bardziej specjalne
AutoCompleteTextView
,
ImageSwitcher
i TextSwitcher
.
Dostępnych jest m.in. układy LinearLayout
, FrameLayout
, RelativeLayout
i inne. Więcej przykładów znajdziesz w artykule Typowe układy.
Jeśli żaden z gotowych widżetów ani układów nie spełnia Twoich wymagań, możesz utworzyć własną podklasę View
. Jeśli chcesz wprowadzić tylko niewielkie zmiany w istniejącym widżecie lub układzie, możesz podklasyfikować widżet lub układ i zastąpić jego metody.
Utworzenie własnych podklas View
daje Ci precyzyjną kontrolę nad wyglądem i działaniem elementu ekranu. Aby dać wyobrażenie o możliwościach, jakie dają widoki niestandardowe, podajemy kilka przykładów:
-
Możesz utworzyć całkowicie renderowany typ
View
, np. pokrętło „regulacji głośności” wyrenderowane za pomocą grafiki 2D, który przypomina analogowe sterowanie elektroniczne. -
Grupę komponentów
View
możesz połączyć w jeden element. Może to być na przykład pole kombi (wyskakujące okienko z polem tekstowym w polu tekstowym), element sterujący podwójnym selektorem (lewy i prawy panel z listą w każdym miejscu, w którym możesz ponownie przypisać element na liście) itd. -
Możesz zastąpić sposób renderowania komponentu
EditText
na ekranie. Przykładowa aplikacja Notatnik wykorzystuje to z dobrym wynikiem do utworzenia strony notatnika z liniami. - Możesz rejestrować inne zdarzenia, takie jak naciśnięcia klawiszy, i obsługiwać je w niestandardowy sposób, np. podczas gry.
Poniżej dowiesz się, jak tworzyć widoki niestandardowe i używać ich w aplikacji. Szczegółowe informacje znajdziesz w opisie klasy View
.
Podstawowe podejście
Oto ogólny przegląd niezbędnych informacji do tworzenia własnych komponentów View
:
-
Rozszerz istniejące zajęcia
View
lub podklasy o własne zajęcia. -
Zastąp niektóre metody z klasy nadrzędnej. Metody nadrzędne do zastąpienia zaczynają się od
on
, na przykładonDraw()
,onMeasure()
ionKeyDown()
. Jest to podobne do zdarzeńon
wActivity
lubListActivity
, które zastępujesz na potrzeby punktów zaczepienia cyklu życia i innych funkcji. - Użyj nowej klasy rozszerzenia. Gdy to zrobisz, możesz użyć nowej klasy rozszerzenia zamiast widoku, na podstawie którego została utworzona.
Komponenty w pełni dostosowane
Możesz tworzyć w pełni dostosowane komponenty graficzne i wyświetlać je w dowolny sposób. Może to być np. graficzny wskaźnik VU, który wygląda jak stary miernik analogowy, lub widok tekstu do śpiewania, w którym odbijająca się kula porusza się, gdy śpiewasz przy urządzeniu do karaoke. Być może potrzebujesz funkcji, której nie da się zrobić we wszystkich komponentach.
Na szczęście możesz tworzyć komponenty, które wyglądają i działają w dowolny sposób, ograniczając się tylko Twoją wyobraźnią, rozmiarem ekranu i dostępną mocą obliczeniową. Pamiętaj, że Twoja aplikacja może działać na znacznie mniej wydajnym urządzeniu niż komputerowa stacja robocza.
Aby utworzyć w pełni dostosowany komponent, weź pod uwagę te kwestie:
-
Najbardziej ogólny widok, jaki możesz rozszerzyć, to
View
, więc zwykle na początek tworzysz nowy superkomponent. - Możesz dostarczyć konstruktor, który może pobierać atrybuty i parametry z pliku XML oraz korzystać z własnych atrybutów i parametrów, takich jak kolor i zasięg wskaźnika VU lub szerokość i tłumienie igły.
- Prawdopodobnie chcesz tworzyć własne detektory zdarzeń, metody dostępu właściwości i modyfikatory, a także bardziej zaawansowane działania w klasie komponentu.
-
Prawie na pewno chcesz zastąpić atrybut
onMeasure()
. Prawdopodobnie będzie też potrzebny zastąpienieonDraw()
, jeśli chcesz, by komponent wyświetlał coś. Oba mają działanie domyślne, ale domyślnyonDraw()
nic nie robi, a domyślny rozmiaronMeasure()
zawsze ustawia rozmiar 100 x 100, którego prawdopodobnie nie chcesz używać. -
W razie potrzeby możesz też zastąpić inne metody
on
.
Rozszerz onDraw() i onMeasure()
Metoda onDraw()
dostarcza element Canvas
, do którego możesz zaimplementować cokolwiek, co chcesz: grafikę 2D, inne komponenty standardowe lub niestandardowe, stylizowany tekst lub wszystko, co tylko wymyślisz.
onMeasure()
ma większe znaczenie. onMeasure()
to kluczowy element umowy renderowania między komponentem a jego kontenerem. Element onMeasure()
musi być zastąpiony, aby efektywnie i dokładnie raportować pomiary jego części. Jeszcze bardziej komplikuje to wymagania dotyczące limitów od zasobu nadrzędnego – które są przekazywane do metody onMeasure()
– oraz wymaganie wywoływania metody setMeasuredDimension()
z mierzoną szerokością i wysokością po ich obliczeniu. Jeśli nie wywołasz tej metody za pomocą zastąpionej metody onMeasure()
, w czasie pomiaru stanie się to wyjątek.
Ogólnie implementacja onMeasure()
wygląda tak:
-
Zastąpiona metoda
onMeasure()
jest wywoływana ze specyfikacjami szerokości i wysokości, które są traktowane jako wymagania dotyczące ograniczeń szerokości i wysokości pomiarów. ParametrywidthMeasureSpec
iheightMeasureSpec
są liczbami całkowitymi reprezentującymi wymiary. Pełne informacje o ograniczeniach, których mogą wymagać te specyfikacje, znajdziesz w dokumentacji referencyjnejView.onMeasure(int, int)
Ta dokumentacja referencyjna objaśnia również całą operację pomiaru. -
Metoda
onMeasure()
komponentu oblicza szerokość i wysokość pomiaru, które są wymagane do wyrenderowania komponentu. Muszą być zgodne z przekazanymi specyfikacjami, ale mogą też przekraczać te wymagania. W tym przypadku rodzic może wybrać, co zrobić – na przykład przyciąć plik, przewinąć, zgłosić wyjątek lub poprosićonMeasure()
o spróbowanie jeszcze raz, np. z innymi specyfikacjami pomiaru. -
Podczas obliczania szerokości i wysokości wywołaj metodę
setMeasuredDimension(int width, int height)
z obliczonymi pomiarami. Jeśli tego nie zrobisz, zostanie uznany za wyjątek.
Oto podsumowanie innych standardowych metod, z których korzystają widoki danych w ramach platformy:
Kategoria | Metody | Opis |
---|---|---|
na podstawie trendów | Konstruktorzy | Istnieje forma konstruktora, która jest wywoływana, gdy widok jest tworzony na podstawie kodu, i formularz, który jest wywoływany, gdy widok zostanie przekroczony z pliku układu. W drugim formularzu analizuje i stosuje atrybuty zdefiniowane w pliku układu. |
|
Wywoływane po wyświetleniu widoku i wszystkich jego elementach podrzędnych z pliku XML. | |
Układ |
|
Wywoływana w celu określenia wymagań dotyczących rozmiaru tego widoku i wszystkich jego elementów podrzędnych. |
|
Wywoływane, gdy ten widok musi przypisać rozmiar i pozycję do wszystkich swoich elementów podrzędnych. | |
|
Wywoływane po zmianie rozmiaru tego widoku. | |
Rysunek |
|
Wywoływana, gdy widok musi wyrenderować swoją zawartość. |
Przetwarzanie zdarzeń |
|
Wywoływane, gdy wystąpi zdarzenie wyłączenia klucza. |
|
Wywoływane po wystąpieniu zdarzenia kluczowego. | |
|
Wywoływane po wystąpieniu zdarzenia ruchu kulki. | |
|
Wywoływane po wystąpieniu zdarzenia ruchu na ekranie dotykowym. | |
główny temat filmu, |
|
Wywoływane, gdy widok zyskuje lub przestaje być aktywny. |
|
Wywoływane, gdy okno zawierające widok zyskuje lub traci zaznaczenie. | |
Dołączanie |
|
Wywoływana, gdy widok jest dołączony do okna. |
|
Wywoływana po odłączeniu widoku od okna. | |
|
Wywoływane po zmianie widoczności okna zawierającego widok. |
Elementy sterujące komponentem
Jeśli nie chcesz tworzyć w pełni niestandardowego komponentu, ale wolisz połączyć
komponent wielokrotnego użytku z grupą istniejących elementów sterujących, najlepszym rozwiązaniem będzie utworzenie komponentu złożonego (czyli elementu sterującego złożonego). Podsumowując, takie działanie łączy wiele bardziej
atomowych elementów sterujących lub widoków w logiczną grupę elementów, które można traktować jak jedną rzecz.
Na przykład pole kombi może się składać z pojedynczego pola EditText
w pojedynczym wierszu i przyległego przycisku z dołączoną listą wyskakujących okienek. Jeśli użytkownik kliknie przycisk i wybierze coś z listy, pole EditText
zostanie wypełnione. Jeśli wolisz, może też wpisać coś bezpośrednio w polu EditText
.
Na urządzeniu z Androidem możesz to zrobić za pomocą 2 innych widoków: Spinner
i AutoCompleteTextView
. Dobrym przykładem jest jednak to pole kombi.
Aby utworzyć komponent złożony:
-
Podobnie jak w przypadku obiektu
Activity
, utwórz zawarte w nim komponenty za pomocą deklaratywnej (opartej na formacie XML) lub zagnieźdź je programowo na podstawie kodu. Zazwyczaj punktem początkowym jestLayout
jakiś rodzaj, utwórz więc klasę, która rozszerzaLayout
. W przypadku pola kombi możesz użyć polaLinearLayout
w orientacji poziomej. W środku możesz zagnieździć inne układy, aby komponent złożony może być dowolnie złożony i mieć strukturę. -
W konstruktorze nowej klasy pobierz parametry oczekiwane przez klasę nadrzędną i najpierw przekaż je do konstruktora klasy nadrzędnej. Następnie możesz skonfigurować inne widoki do użycia w nowym komponencie. Tutaj utworzysz pole
EditText
i listę w wyskakującym okienku. Możesz wprowadzić do kodu XML własne atrybuty i parametry, które będzie mógł pobierać i wykorzystywać Twój konstruktor. -
Opcjonalnie utwórz detektory zdarzeń, które mogą generować zawarte przez Ciebie widoki. Przykładem jest odbiornik, który po wybraniu elementu listy aktualizuje zawartość elementu
EditText
w celu aktualizowania jego zawartości. -
Opcjonalnie utwórz własne właściwości z akcesoriami i modyfikatorami. Na przykład ustaw wartość
EditText
w komponencie na początku, a w razie potrzeby wysyłaj zapytanie o jej zawartość. -
Opcjonalnie zastąp
onDraw()
ionMeasure()
. Zazwyczaj nie jest to konieczne przy rozszerzaniu właściwościLayout
, ponieważ układ działa domyślnie i prawdopodobnie zadziała. -
Opcjonalnie możesz zastąpić inne metody
on
, takie jakonKeyDown()
, np. aby wybrać określone wartości domyślne z wyskakującej listy pola kombi po kliknięciu określonego klawisza.
Użycie elementu Layout
jako podstawy elementu sterującego niestandardowego ma swoje zalety:
- Układ możesz określić za pomocą deklaratywnego pliku XML (tak jak w przypadku ekranu aktywności) lub możesz programowo tworzyć widoki i zagnieżdżać je w układzie z poziomu kodu.
-
Metody
onDraw()
ionMeasure()
oraz większość pozostałych metodon
działają prawidłowo, więc nie musisz ich zastępować. - Możesz szybko tworzyć dowolnie złożone widoki złożone i używać ich ponownie tak, jakby były jednym elementem.
Modyfikowanie istniejącego typu widoku
Jeśli występuje komponent podobny do Twojego, możesz go rozszerzyć i zastąpić działanie, które chcesz zmienić. Korzystając z w pełni niestandardowego komponentu, możesz wykonywać wszystkie te czynności, ale zaczynając od bardziej specjalistycznej klasy w hierarchii View
, możesz uzyskać pewne zachowanie, które pozwoli Ci to robić bezpłatnie.
Na przykład przykładowa aplikacja NotePad pokazuje wiele aspektów korzystania z platformy Androida. Między innymi rozszerzyliśmy widok EditText
, aby utworzyć notatnik z liniami. To nie jest doskonały przykład, a interfejsy API służące do tego celu mogą się zmienić, ale obrazuje zasady.
Zaimportuj przykładowy notatnik do Android Studio lub sprawdź źródło, korzystając z podanego linku. Sprawdź definicję właściwości LinedEditText
w pliku NoteEditor.java
.
Oto kilka informacji, które warto uwzględnić w tym pliku:
-
Definicja
Klasa jest zdefiniowana w tym wierszu:
public static class LinedEditText extends EditText
LinedEditText
jest zdefiniowaną jako klasa wewnętrzna w aktywnościNoteEditor
, ale jest publiczna, aby można było uzyskać do niej dostęp jakoNoteEditor.LinedEditText
spoza klasyNoteEditor
.Poza tym
LinedEditText
ma wartośćstatic
, co oznacza, że nie generuje tzw. „metod syntetycznych”, które umożliwiają mu dostęp do danych z klasy nadrzędnej. Oznacza to, że zachowuje się ona jako osobna klasa, a nie jako ściśle powiązana zNoteEditor
. Jest to prostszy sposób tworzenia klas wewnętrznych, jeśli nie potrzebują one dostępu do stanu z klasy zewnętrznej. Wygenerowana klasa jest mała i pozwala łatwo używać jej z innych klas.LinedEditText
rozszerza zakresEditText
, który w tym przypadku należy dostosować. Gdy skończysz, nowa klasa może zastąpić zwykły widokEditText
. -
Inicjowanie klas
Pierwszeństwo jest zawsze nazywane super. Nie jest to konstruktor domyślny, ale z parametrami. Element
EditText
jest tworzony z tymi parametrami, gdy jest powiększany z pliku układu XML. Dlatego konstruktor musi je przyjąć i przekazać do konstruktora klasy nadrzędnej. -
Zastąpione metody
W tym przykładzie zastępujesz tylko metodę
onDraw()
, ale podczas tworzenia własnych komponentów niestandardowych może być konieczne zastąpienie innych.W tym przykładzie zastąpienie metody
onDraw()
umożliwia malowanie niebieskich linii w obszarze roboczym widokuEditText
. Obszar roboczy jest przekazywany do zastąpionej metodyonDraw()
. Metodasuper.onDraw()
jest wywoływana przed zakończeniem działania. Należy wywołać metodę nadklasy. W tym przypadku wywołaj go na końcu po pomalowaniu wierszy, które chcesz uwzględnić. -
Komponent niestandardowy
Masz już komponent niestandardowy. Jak mogę go wykorzystać? W przykładzie Notatnika komponent niestandardowy jest używany bezpośrednio z układu deklaratywnego, więc sprawdź
note_editor.xml
w folderzeres/layout
:<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" />
Komponent niestandardowy jest tworzony jako ogólny widok w pliku XML, a klasa jest określana w pełnym pakiecie. Do zdefiniowanej klasy wewnętrznej odwołuje się zapis
NoteEditor$LinedEditText
, który jest standardowym sposobem odwoływania się do klas wewnętrznych w języku programowania Java.Jeśli komponent widoku niestandardowego nie jest zdefiniowany jako klasa wewnętrzna, możesz zadeklarować komponent widoku za pomocą nazwy elementu XML i wykluczyć atrybut
class
. Na przykład:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
Zwróć uwagę, że klasa
LinedEditText
jest teraz oddzielnym plikiem zajęć. Jeśli klasa jest zagnieżdżona w klasieNoteEditor
, ta metoda nie działa.Pozostałe atrybuty i parametry w definicji to atrybuty przekazywane do konstruktora komponentu niestandardowego, a następnie przekazywane do konstruktora
EditText
, więc są to te same parametry, których używasz w widokuEditText
. Możesz też dodawać własne parametry.
Tworzenie komponentów niestandardowych jest skomplikowane tylko wtedy, gdy jest to potrzebne.
Bardziej zaawansowany komponent może zastąpić jeszcze więcej metod on
i wprowadzić własne metody pomocnicze, znacząco dostosowując jego właściwości i działanie. Jedynym ograniczeniem jest Twoja wyobraźnia
i to, do czego służy komponent.