Lekce 7 - Vlastní Android komponenta - Vytvoření a usazení do layoutu
V předchozím kvízu, Kvíz - Tvorba vlastního Spinneru v Androidu, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
V dnešní lekci započneme vytváření vlastních komponent pro naše Android aplikace v Javě.
Motivace
Android poskytuje sadu obecných komponent, se kterými si, většinou, ve
svých projektech vystačíme. Button, EditText,
TextView, ProgressBar, ... Občas by se nám ale
jistě hodilo, aby nějaká komponenta měla něco navíc - jiný vzhled nebo
jiné chování. Například TextView s vlastním fontem, nebo
nějaké speciální tlačítko. A nebo dokonce budeme potřebovat nějakou
speciální komponentu, která není ve standardní nabídce a ani se jí
žádná standardní komponenta nepodobá. Může to být například ovládací
prvek v přehrávači médií, nějaký graf atd.
Potřebu použít vlastní komponentu lze samozřejmě vyřešit i jinak, než vytvořením nového vlastního objektu, ale bude to pravděpodobně znamenat psaní velkého množství kódu. To je nevhodné hlavně když takovou komponentu budeme chtít v rozsáhlejším projektu použít vícekrát.
Vytvoření vlastní komponenty
Existují dva základní způsoby, jak vytvořit vlastní komponentu:
- Objekt je přímý potomek třídy
View(např.public class MyView extends View {}) - Objekt je potomkem již existující třídy (např.
public class MyView extends TextView {})
Vše si budeme ukazovat na konkrétních příkladech. Postupovat budeme následovně:
- Vytvoříme XML návrh komponenty
- Vytvoříme třídu dědící od třídy
Viewnebo nějakého jejího potomka - Definujeme parametry pro nastavení komponenty
- Vložíme vytvořený XML návrh do layoutu
- Použijeme vytvořené parametry a nastavení komponenty
- S kódem budeme pracovat v
MainActivity.java
Vlastní textové pole
Začneme vytvořením vlastního pole pro zadávání textu. Postupně se dostaneme i ke složitějším příkladům. Na následujícím obrázku je vidět cíl našeho snažení:

Hlavní součástí budoucí komponenty bude EditText, jehož
obsah bude aplikace při psaní textu průběžně kontrolovat a zjišťovat,
zda zadaný text odpovídá nastaveným kritériím. Můžeme kontrolovat
například počet zadaných znaků nebo přítomnost předem definovaného
textového řetězce. Pokud zadaný text nebude splňovat stanovené požadavky,
bude v pravém dolním rohu komponenty zobrazen červený varovný text. Obsah
tohoto textu bude též možné nastavit, stejně tak i text v hlavičce
komponenty.
Jdeme tedy na to a v Android Studiu založíme nový projekt, který
pojmenujeme CustomInput. Jako první vytvoříme XML návrh
komponenty.
XML návrh komponenty
V následující ukázce kódu je XML rozvržení budoucího objektu
vlastního zadávacího pole. Ve složce projektu res/layout/
vytvoříme nový XML soubor pojmenovaný my_input.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/my_input_background" android:orientation="vertical" android:paddingStart="10dp" android:paddingLeft="10dp" android:paddingTop="10dp" android:paddingEnd="10dp" android:paddingRight="10dp" android:paddingBottom="3dp"> <TextView android:id="@+id/labelTitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Text v záhlaví komponenty" > </TextView> <EditText android:id="@+id/etInput" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" /> <TextView android:id="@+id/labelErrText" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="right" android:text="Chybová zpráva" android:textColor="#C50000" android:textSize="12sp" android:visibility="invisible" /> </LinearLayout>
Hlavní layout komponenty má nastaveno pozadí pomocí odkazu na soubor XML
android:background="@drawable/my_input_background". Ten vytvoříme
ve složce projektu res/drawable/. Soubor s pozadím pojmenujeme
my_input_background.xml a vložíme do něj tento kód:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <solid android:color="#ECECEC" /> <stroke android:width="1dp" android:color="#000000"/> <corners android:radius="2dp"/> </shape>
Třída dědící od třídy
View
Základem každé vlastní komponenty je třída, která je odvozena, ať už
přímo nebo nepřímo, od třídy View. Naše ukázkové
zadávací pole bude dědit od třídy LinearLayout, což znamená,
že nepřímo dědíme od třídy View. Celá hierarchie dědění
vypadá takto: Object -> View ->
ViewGroup -> LinearLayout. Třídu nové komponenty
pojmenujeme CustomInput a vytvoříme jí v hlavní složce
projektu java/com.example.custominput/. Základem každé třídy,
která je potomkem třídy View, jsou čtyři přetížení
konstruktoru:
public class CustomInput extends LinearLayout { public CustomInput(Context context) { super(context); } public CustomInput(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CustomInput(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CustomInput(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } }
První konstruktor je používán k vytvoření instance objektu pomocí kódu. Ostatní konstruktory jsou zde pro objekt konfigurovaný v XML rozvržení.
Budete-li pracovat na projektu, který bude určen pro velký
rozsah verzí systému Android, bude pravděpodobně nutné ke čtvrtému
konstruktoru přidat anotaci např.
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP). Tato anotace
označuje konstruktor použitelný pouze na dané úrovni API nebo vyšší.
Konstruktory určené pro konfiguraci v XML obsahují parametr
AttributeSet attrs. Zjednodušeně řečeno se jedná o kolekci
atributů, které byly nalezeny u objektu v XML rozvržení a jsou v parametru
attrs předávány do třídy objektu, kde s nimi můžeme dále
pracovat.
V okamžiku, kdy při psaní vlastní třídy v hlavičce třídy doplníme
extends LinearLayout, nás Android Studio upozorní na nutnost
přidání konstruktorů a samo nám nabídne jejich vygenerování.
Parametry pro nastavení komponenty
Budeme-li chtít, aby bylo možné našemu objektu nastavovat nějaké
vlastnosti, musíme tyto vlastnosti reprezentovat nějakými atributy a
definovat je. Nyní si ukážeme, jak na to. Nejprve je nutné v
resources projektu vytvořit soubor attrs.xml, jehož
umístění je patrné z obrázku:

A zde je příklad obsahu souboru attrs.xml pro zmíněné
zadávací pole:
<resources> <declare-styleable name="CustomInput"> <attr name="ciText" format="string" /> <attr name="ciTitleText" format="string" /> <attr name="ciErrText" format="string" /> <attr name="ciRequiredText" format="string" /> <attr name="ciRequiredNumberOfCharacters" format="integer" /> </declare-styleable> </resources>
V řádku <declare-styleable name="CustomInput"> je
důležitý atribut name. Ten totiž hraje roli v
"pojmenovávání" parametrů ve třídě naší komponenty při jejich
získávání z XML - za chvíli si to ukážeme.
Pojďme se seznámit s právě definovanými atributy v
attrs.xml:
ciText- nastavení výchozího textu pole pro zadávání textuciTitleText- nastavení textu v hlavičce komponentyciErrText- nastavení textu červené chybové zprávyciRequiredText- znak nebo textový řetězec, který musí zadaný text obsahovatciRequiredNumberOfCharacters- minimální požadovaný počet znaků v zadaném textu
První čtyři parametry jsou typu string, poslední je typu
integer. Samozřejmě existuje více datových typů, které je
možné v jiných případech použít. Zde je stručný přehled použitelných
typů parametrů:
| Parametr | Datový typ |
|---|---|
| boolean | boolean |
| color | @ColorInt int |
| dimension | @LayoutDimension int |
| dimension | @Px int |
| enum | int |
| flag | int |
| float | float |
| fraction | @Fraction(base = ?, pbase = ?) float |
| integer | int |
| reference | @AnyRes* int |
| reference | CharSequence[] |
| reference | ColorStateList |
| reference | Drawable |
| reference | Typeface |
| string | CharSequence |
| string | String |
| string | Typeface |
*lze použít libovolnou anotaci @...Res
Definované atributy později (po "rebuildu" projektu) naleznete mezi defaultními parametry při konfiguraci zadávacího pole v XML v Android Studiu, viz. následující obrázek:

Naše nové atributy začínají na ci. A to jen kvůli
přehlednosti - abychom je mezi ostatními defaultními parametry nalezli
všechny na jednom místě. V Android Studiu jsou totiž atributy řazeny podle
abecedy.
Vložení vytvořeného XML návrhu do layoutu
Vlastní atributy použijeme v souborech XML rozvržení stejně jako ty
defaultní. Jediným rozdílem je, že naše atributy patří do jiné skupiny.
Namísto toho, aby patřily do
http://schemas.android.com/apk/res/android, patří do
http://schemas.android.com/apk/res/[náš_název_balíčku].
V Android Studiu to znamená to, že do nadřazeného (nebo do hlavního)
layoutu v XML vložíme řádek
xmlns:app="http://schemas.android.com/apk/res-auto". Pokud na to
zapomenete, Android Studio vás na to upozorní.
Náš vlastní atribut potom nebude vložen stylem android: ...
ale app: .... Všimněte si i názvu naší komponenty v XML -
není to nic moc hezkého, ale má to logiku
Následuje XML kód layoutu
aplikace, ve kterém je vložena nová komponenta:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.custominput.CustomInput android:id="@+id/myCustomInput1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" app:ciErrText="Text neobsahuje slovo PES nebo je kratší než 10 znaků!" app:ciRequiredNumberOfCharacters="10" app:ciRequiredText="pes" app:ciTitleText="Zadej text obsahjící slovo PES. Text musí obsahovat minimálně 10 znaků"> </com.example.custominput.CustomInput> </LinearLayout>
Takto použité zadávací pole tedy bude požadovat zadání minimálně deseti znaků a zároveň, aby vložený text obsahoval řetězec "pes". Dokud nebudou obě tyto podmínky splněny, bude zobrazena červená hláška v pravé spodní části komponenty. Text této chybové hlášky je také nastaven v XML. Jen poznamenám, že chybová hláška nebude zobrazena v případě, že textové pole nebude obsahovat žádný text.
Proměnné pro parametry
Na závěr této části si ve vytvořené třídě CustomInput
deklarujeme proměnné a metody, kterými později komponentu nastavíme
programově v kódu:
// Proměnné pro reference na jednotlivé komponenty našeho View v XML TextView labelTitleText; // Text nad textovým polem TextView labelErrText; // Chybová hláška pod textovým polem EditText etInput; // Pole pro zadání textu public void setTitleText(String titleText) { this.titleText = titleText; this.labelTitleText.setText(titleText); } public void setErrText(String errText) { this.errText = errText; this.labelErrText.setText(errText); } public void setRequiredText(String requiredText) { this.requiredText = requiredText; }
Příště, v lekci Vlastní Android komponenta - Dokončení textového pole, si ukážeme, jak vytvořené atributy použít



