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
View
nebo 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