Android oferuje zaawansowany i zaawansowany model składowy do tworzenia UI 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żesz używać do tworzenia interfejsu użytkownika.
Częściowa lista dostępnych widżetów to Button
,
TextView
,
EditText
,
ListView
,
CheckBox
,
RadioButton
,
Gallery
,
Spinner
oraz bardziej specjalne
AutoCompleteTextView
,
ImageSwitcher
i
TextSwitcher
.
Dostępne układy to 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ć niewielkie zmiany w istniejącym widżecie lub układzie, możesz utworzyć podklasę widżetu lub układu i zastąpić jego metody.
Tworzenie własnych podklas View
daje Ci precyzyjną kontrolę nad wyglądem i funkcją elementu na ekranie. Aby zorientować się, jakie opcje są dostępne w widokach niestandardowych, oto kilka przykładów ich użycia:
-
Możesz utworzyć całkowicie renderowany typ
View
– na przykład pokrętło regulacji głośności renderowane za pomocą grafiki 2D, które przypomina analogowe sterowanie elektronicznym. -
Możesz połączyć grupę komponentów
View
w jeden komponent, na przykład aby utworzyć pole kombi (kombinację wyskakującej listy i pola tekstowego do swobodnego wpisywania), panel z podwójnym panelem (lewy i prawy panel z listą w każdym z nich, w którym możesz zmienić przypisanie elementu, który znajduje się na danej liście) itd. -
Możesz zastąpić sposób renderowania komponentu
EditText
na ekranie. Przykładowa aplikacja NotePad wykorzystuje to rozwiązanie do tworzenia stron notatnika w linii. - Możesz rejestrować inne zdarzenia, np. naciśnięcia klawiszy, i obsługiwać je w niestandardowy sposób, np. w grze.
W sekcjach poniżej wyjaśniamy, jak tworzyć widoki niestandardowe i używać ich w swojej aplikacji. Szczegółowe informacje znajdziesz w klasie View
.
Podstawowe podejście
Oto ogólny przegląd informacji, które pozwolą Ci tworzyć własne komponenty View
:
-
Rozszerz istniejącą klasę lub podklasę
View
o własne zajęcia. -
Zastąp niektóre metody z klasy nadrzędnej. Metody zastępowania klas nadrzędnych zaczynają się od
on
, np.onDraw()
,onMeasure()
ionKeyDown()
. Jest to podobne do zdarzeńon
wActivity
lubListActivity
, które zastępujesz dla cyklu życia i innych punktów zaczepienia funkcjonalności. - Użyj nowej klasy rozszerzenia. Po zakończeniu możesz użyć nowej klasy rozszerzenia zamiast widoku, na podstawie którego zostało utworzone.
W pełni dostosowane komponenty
Możesz tworzyć w pełni dostosowane komponenty graficzne, które będą się wyświetlać w dowolny sposób. Może Ci się przydać graficzny miernik VU, który wygląda jak stary wskaźnik analogowy, albo widok tekstowy do śpiewania, w którym odbijająca się piłka porusza się wzdłuż słów, gdy śpiewasz razem z urządzeniem do karaoke. Możesz potrzebować czegoś, czego nie mogą robić wbudowane komponenty, niezależnie od tego, jak je połączysz.
Na szczęście możesz tworzyć komponenty, które wyglądają i działają tak, jak chcesz, a ograniczenia ogranicza jedynie Twoja wyobraźnia, rozmiar ekranu i dostępna moc obliczeniowa. Mając na uwadze, że Twoja aplikacja będzie musiała uruchomić coś o znacznie niższej mocy niż komputer stacjonarny.
Aby utworzyć w pełni dostosowany komponent, weź pod uwagę te kwestie:
-
Najbardziej ogólnym widokiem, który możesz rozszerzyć, jest
View
, więc zwykle zaczynasz od rozszerzenia go w celu utworzenia nowego superkomponentu. - Możesz dostarczyć konstruktor, który może pobierać atrybuty i parametry z kodu XML oraz korzystać z własnych atrybutów i parametrów, takich jak kolor i zakres miernika VU lub szerokość i tłumienie igły.
- Prawdopodobnie zechcesz utworzyć własne detektory zdarzeń, akcesory właściwości i modyfikatory, a także bardziej zaawansowane zachowanie w klasie komponentu.
-
Prawie na pewno chcesz zastąpić działanie
onMeasure()
, a konieczne jest zastąpienieonDraw()
, jeśli chcesz, by komponent się wyświetlał. Choć w obu przypadkach działa domyślny,onDraw()
nie ma żadnego działania, a domyślnyonMeasure()
zawsze ustawia rozmiar 100 x 100, czego prawdopodobnie nie chcesz. -
W razie potrzeby możesz też zastąpić inne metody
on
.
Rozszerzaj funkcje onDraw() i onMeasure()
Metoda onDraw()
udostępnia obiekt Canvas
, w którym możesz zastosować dowolne elementy: grafikę 2D, inne standardowe lub niestandardowe komponenty, styl tekstu i cokolwiek innego, co przychodzi Ci do głowy.
onMeasure()
bardziej się angażuje. onMeasure()
to kluczowy element umowy renderowania między komponentem a jego kontenerem. Trzeba zastąpić zmienną onMeasure()
, aby skutecznie i dokładnie raportować pomiary jej składowych. Jest to trochę bardziej skomplikowane ze względu na wymagania dotyczące limitów ze strony nadrzędnej, które są przekazywane do metody onMeasure()
, oraz przez wymóg wywołania metody setMeasuredDimension()
z mierzoną szerokością i wysokością po ich obliczeniu. Jeśli nie wywołasz tej metody z zastąpionej metody onMeasure()
, spowoduje to wystąpienie wyjątku w czasie pomiaru.
Ogólnie wdrożenie onMeasure()
wygląda tak:
-
Zastąpiona metoda
onMeasure()
jest wywoływana ze specyfikacją szerokości i wysokości, co jest traktowane jako wymagania dotyczące ograniczeń tworzonych przez Ciebie pomiarów szerokości i wysokości. ParametrywidthMeasureSpec
iheightMeasureSpec
to kody całkowite reprezentujące wymiary. Pełne informacje na temat ograniczeń, jakie mogą być wymagane przez te specyfikacje, znajdziesz w dokumentacji referencyjnej w sekcjiView.onMeasure(int, int)
Ta dokumentacja dotyczy również całego działania pomiaru. -
Metoda
onMeasure()
komponentu oblicza szerokość i wysokość, które są wymagane do renderowania komponentu. Musi ona przestrzegać podanych specyfikacji, ale może przekroczyć podane wymagania. W takim przypadku rodzic może wybrać, co ma zrobić, np. przyciąć, przewinąć, zgłosić wyjątek lub poprosićonMeasure()
o ponowną próbę pomiaru. -
Po obliczeniu szerokości i wysokości wywołaj z obliczonymi pomiarami metodę
setMeasuredDimension(int width, int height)
. Jeśli tego nie zrobisz, zostanie utworzony wyjątek.
Oto podsumowanie innych standardowych metod, które platforma wywołuje w przypadku wyświetleń:
Kategoria | Metody | Opis |
---|---|---|
na podstawie trendów | Konstruktorki | Istnieje forma konstruktora, która jest wywoływana, gdy widok jest tworzony na podstawie kodu, oraz formularz, który jest wywoływany, gdy widok zostanie wyświetlony w pliku układu. Drugi formularz analizuje i stosuje atrybuty zdefiniowane w pliku układu. |
|
Wywoływana po wyświetleniu i wszystkich jego elementach podrzędnych są powiększone z pliku XML. | |
Układ |
|
Wywoływana w celu określenia wymagań dotyczących rozmiaru dla tego widoku i wszystkich jego elementów podrzędnych. |
|
Wywoływana, gdy ten widok musi przypisać rozmiar i pozycję wszystkim jego elementom podrzędnym. | |
|
Wywoływana po zmianie rozmiaru tego widoku. | |
Rysunek |
|
Wywoływane, gdy widok musi wyrenderować swoją treść. |
Przetwarzanie zdarzeń |
|
Wywoływana po wystąpieniu zdarzenia wyłączenia. |
|
Wywoływana po wystąpieniu kluczowego zdarzenia. | |
|
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 traci ostrość. |
|
Wywoływane, gdy okno zawierające widok zyskuje lub traci fokus. | |
Dołączanie |
|
Wywoływane, gdy widok jest dołączony do okna. |
|
Wywoływane, gdy widok zostaje odłączony od jego okna. | |
|
Wywoływana po zmianie widoczności okna zawierającego widok. |
Złożone elementy sterujące
Jeśli nie chcesz tworzyć całkowicie niestandardowego komponentu, ale zamiast tego potrzebujesz wielokrotnego użytku, który składa się z grupy istniejących elementów sterujących, najlepszym rozwiązaniem będzie utworzenie komponentu złożonego (lub elementu sterującego). Podsumowując, gromadzi to wiele bardziej szczegółowych ustawień
lub widoków w logiczną grupę elementów, które można traktować jak jedną rzecz.
Pole kombi może na przykład składać się z jednowierszowego pola EditText
i przyległego przycisku z dołączoną listą. Jeśli użytkownik kliknie przycisk i wybierze pozycję z listy, pole EditText
zostanie wypełnione, ale może też wpisać coś bezpośrednio w polu EditText
.
Na Androidzie łatwo dostępne są 2 inne widoki: Spinner
i AutoCompleteTextView
. Tak czy inaczej, dobrym przykładem jest koncepcja pola kombi.
Aby utworzyć komponent złożony, wykonaj te czynności:
-
Podobnie jak w przypadku
Activity
, użyj metody deklaratywnej (opartej na XML), aby utworzyć zawarte komponenty, lub zagnieźdź je automatycznie z poziomu kodu. Punktem początkowym jest zwykle jakiś elementLayout
, więc utwórz klasę, która będzie rozszerzać tabelęLayout
. W przypadku pola złożonego możesz użyć polaLinearLayout
w orientacji poziomej. Możesz zagnieżdżać w środku inne układy, więc komponent złożony może być dowolnie złożony i uporządkowany. -
W konstruktorze nowej klasy wybierz parametry, których oczekuje superklasa, i przekaż je najpierw do konstruktora klasy nadrzędnej. Następnie możesz skonfigurować pozostałe widoki do użycia w nowym komponencie. W tym miejscu utworzysz pole
EditText
i wyskakujące okienko. Możesz wprowadzić do pliku XML własne atrybuty i parametry, które może pobrać i wykorzystać Twój konstruktor. -
Opcjonalnie możesz utworzyć detektory zdarzeń, które mogą generować zawarte widoki. Przykładem jest metoda odbiornika kliknięć elementu listy, która aktualizuje zawartość pola
EditText
po wybraniu listy. -
Opcjonalnie możesz utworzyć własne właściwości z akcesorami i modyfikatorami. Możesz na przykład ustawić wartość
EditText
na początku w komponencie i w razie potrzeby wysłać zapytanie o jego zawartość. -
Opcjonalnie zastąp
onDraw()
ionMeasure()
. Zwykle nie jest to konieczne przy rozszerzaniu obiektuLayout
, ponieważ układ ma zachowanie domyślne, które prawdopodobnie działa dobrze. -
Opcjonalnie możesz zastąpić inne metody
on
, takie jakonKeyDown()
, aby na przykład wybrać określone wartości domyślne z wyskakującej listy pola złożonego po naciśnięciu określonego klawisza.
Używanie obiektu Layout
jako podstawy niestandardowego elementu sterującego ma wiele zalet, w tym:
- Możesz określić układ za pomocą plików deklaratywnych XML, tak jak w przypadku ekranu aktywności, lub możesz automatycznie 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 stanowiły jeden komponent.
Modyfikowanie dotychczasowego typu widoku
Jeśli znajdziesz komponent, który działa podobnie do Twojego, możesz go rozszerzyć i zastąpić działanie, które chcesz zmienić. Korzystając z w pełni dostosowanego komponentu, możesz wykonywać wszystkie te same czynności, co w przypadku tego komponentu, ale zaczynając od bardziej specjalistycznej klasy w hierarchii View
, możesz bezpłatnie uzyskać działanie, które spełni Twoje oczekiwania.
Na przykład przykładowa aplikacja NotePad pokazuje wiele aspektów korzystania z platformy Androida. Wśród nich jest rozszerzenie widoku EditText
w celu utworzenia notatnika z linią. Nie jest to doskonały przykład, a interfejsy API mogą się zmieniać, ale pokazują zasady.
Zaimportuj przykładowy plik z NotePad do Android Studio lub sprawdź źródło, korzystając z podanego linku. W szczególności zobacz definicję właściwości LinedEditText
w pliku NoteEditor.java
.
Oto kilka rzeczy, które warto wziąć pod uwagę 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, więc można do niej uzyskać dostęp jakoNoteEditor.LinedEditText
spoza klasyNoteEditor
.Poza tym
LinedEditText
tostatic
, co oznacza, że nie generuje tak zwanych „metod syntetycznych”, które mogłyby uzyskać dostęp do danych z klasy nadrzędnej. Oznacza to, że działa ona jako osobna klasa, a nie coś ściśle powiązanego zNoteEditor
. Jest to bardziej przejrzysty sposób tworzenia klas wewnętrznych, jeśli nie potrzebują one dostępu do stanu z klasy zewnętrznej. Utrzymuje wygenerowaną klasę niewielkiego rozmiaru i pozwala na łatwe korzystanie z niej z innych klas.LinedEditText
rozszerza widokEditText
, który w tym przypadku należy dostosować. Gdy skończysz, nowa klasa może zastąpić normalny widokEditText
. -
Inicjowanie klasy
Jak zawsze, pierwszy z nich jest super. Nie jest to konstrukt domyślny, ale z parametrami. Obiekt
EditText
jest tworzony z tymi parametrami, gdy jest powiększony z pliku układu XML. Konstruktor musi je więc pobrać i przekazać do konstruktora klasy nadrzędnej. -
Zastąpione metody
Ten przykład zastępuje 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()
pozwala pomalować niebieskie linie w obszarze roboczym widokuEditText
. Obszar roboczy jest przekazywany do zastąpionej metodyonDraw()
. Metodasuper.onDraw()
jest wywoływana przed zakończeniem. Należy wywołać metodę klasy nadrzędnej. W tym przypadku wywołaj go na końcu po pomalowaniu wierszy, które chcesz uwzględnić. -
Komponent niestandardowy
Masz już komponent niestandardowy, ale jak go wykorzystać? W przykładzie NotePad komponent niestandardowy jest używany bezpośrednio z układu deklaratywnego, więc spójrz na obiekt
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 w pliku XML jako widok ogólny, a klasa określa się za pomocą pełnego pakietu. Do zdefiniowanej przez Ciebie klasy wewnętrznej odwołuje się zapis
NoteEditor$LinedEditText
, który jest standardowym sposobem odwoływania się do wewnętrznych klas w języku programowania Java.Jeśli komponent widoku niestandardowego nie jest zdefiniowany jako klasa wewnętrzna, możesz zadeklarować komponent widoku danych nazwą 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 klas. Jeśli klasa jest umieszczona w klasieNoteEditor
, ta metoda nie działa.Pozostałe atrybuty i parametry w definicji to atrybuty przekazywane do konstruktora komponentu niestandardowego, a potem przekazywane do konstruktora
EditText
, więc są to te same parametry, co w widokuEditText
. Możesz też dodać własne parametry.
Tworzenie komponentów niestandardowych jest tak skomplikowane, jak to konieczne.
Bardziej zaawansowany komponent może zastąpić jeszcze więcej metod on
i wprowadzić własne metody pomocnicze, znacząco dostosowując właściwości i działanie. Jedynym ograniczeniem jest
wyobraźnia i potrzeby komponentu.