Lekce 8 - Vlastní Android komponenta - Dokončení textového pole
V minulé lekci, Vlastní Android komponenta - Vytvoření a usazení do layoutu, jsme vytvořenou komponentu usadili do layoutu.
V této lekci navážeme tím, že si ukážeme použití atributů vlastní textové komponenty v praxi.
Dokončení vlastního textového pole
Budeme pokračovat v našem postupu, čekají nás parametry komponenty.
Použití vytvořených parametrů a nastavení komponenty
Prvně pojďme dostat parametry z XML rozvržení do třídy reprezentující
vlastní View
.
Načtení parametrů
Ve třídě CustomInput
jsme si zatím ukázali pouze
konstruktory. Jak již bylo zmíněno, tyto parametry získáme v konstruktoru z
parametru attrs
. Nyní si napíšeme metodu, která se postará o
zpracování našich vlastních atributů převzatých z XML:
String text = null; // Text zadávacího pole String titleText = null; // Text nad zadávacím polem String errText = null; // Text chybové hlášky String requiredText = null; // Textový řetězec, který zadaný text musí obsahovat int requiredNumberOfCharacters = 0; // Minimální počet zadaných znaků private void applyAttributeSet(AttributeSet attrs) { if (attrs == null) return; TypedArray ta = getContext().getTheme().obtainStyledAttributes( attrs, R.styleable.CustomInput, 0, 0); if (ta == null) return; try { text = ta.getString(R.styleable.CustomInput_ciText); titleText = ta.getString(R.styleable.CustomInput_ciTitleText); errText = ta.getString(R.styleable.CustomInput_ciErrText); requiredText = ta.getString(R.styleable.CustomInput_ciRequiredText); requiredNumberOfCharacters = ta.getInteger( R.styleable.CustomInput_ciRequiredNumberOfCharacters, 0); } finally { ta.recycle(); } }
Na začátku metody vytváříme instanci TypedArray
, ze které
následně získáváme samotné atributy a ukládáme je do připravených
proměnných. Všimněte si, jakým způsobem získáváme jednotlivé parametry
– jedná se o název vzniklý složeninou dvou "slov" oddělených
podtržítkem (např. CustomInput_ciText
). Tento název tedy
obsahuje:
- Atribut
name
v hlavičce v souboruattrs.xml
(<declare-styleable name="CustomInput">
) - Název parametru (deklarovaný v
attrs.xml
)
Zavolání metody
Volání vytvořené metody
applyAttributeSet(AttributeSet attrs)
přidáme do všech
konstruktorů, které jsou používány při konfiguraci objektu v XML
rozvržení, tedy do všech konstruktorů kromě toho prvního a to takto:
applyAttributeSet(attrs);
Zpracování parametrů
Parametry máme z XML načtené ve třídě v proměnných, nyní je potřeba
pomocí získaných atributů inicializovat naši komponentu. K tomuto účelu
si napíšeme metodu init()
.
V této metodě nastavíme všem prvkům požadované parametry. Nastavíme
tak text v hlavičce, text chybové hlášky, případně i výchozí text
komponenty EditText
. Následuje nastavení Listener
pro EditText
. Listener
bude hlídat změny obsahu
EditText
a za běhu bude kontrolovat splnění zadaných podmínek.
Pokud Listener
usoudí, že zadaný text neodpovídá nastaveným
požadavkům, zobrazí červený text s chybovou hláškou.
Kód metody je následující:
View rootView; TextView labelTitleText; TextView labelErrText; EditText etInput; private void init() { // Získání XML rozvržení vlastního View rootView = inflate(getContext(), R.layout.my_input, this); // Proměnné pro objekty vlastního View, se kterému chceme pracovat labelTitleText = rootView.findViewById(R.id.labelTitleText); labelErrText = rootView.findViewById(R.id.labelErrText); etInput = rootView.findViewById(R.id.etInput); if (titleText != null) { labelTitleText.setText(titleText); } else { labelTitleText.setText(""); } if (errText != null) { labelErrText.setText(errText); } else { labelErrText.setText(""); } // "Odposlouchávač" změn textu etInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void afterTextChanged(Editable editable) { String inputText = editable.toString().toLowerCase(); boolean showError = false; if (inputText.equals("")) { hideErrText(); return; } if (requiredText != null) { if (!requiredText.equals("")) { if (!inputText.contains(requiredText)) { showError = true; } } } if (requiredNumberOfCharacters > 0) { if (inputText.length() < requiredNumberOfCharacters) { showError = true; } } if (showError) { showErrText(); } else { hideErrText(); } } }); }
Zavolání metody
Volání metody init()
rovněž vložíme do konstruktorů
naší komponenty. Na rozdíl od předchozí metody metoda init()
bude volána ve všech konstruktorech. Pojďme si pro úplnost raději ukázat,
jako konstruktory vypadají:
public CustomInput(Context context) { super(context); init(); } public CustomInput(Context context, @Nullable AttributeSet attrs) { super(context, attrs); applyAttributeSet(attrs); init(); } public CustomInput(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); applyAttributeSet(attrs); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CustomInput(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); applyAttributeSet(attrs); init(); }
Práce s komponentou
Nyní se podíváme na to, jak vypadá práce s vlastní komponentou, která byla vložena do XML. Půjde o práci z Java kódu aplikace:
// Získání reference na objekt v XML customInput1 = findViewById(R.id.myCustomInput1); // Programové nastavení atributů. customInput1.setTitleText("Title"); customInput1.setErrText("Error text"); customInput1.setRequiredText("required text");
Vytvoření komponenty programově
Ještě si ukážeme, jak náš objekt vytvořit programově, tedy bez vkládání do XML aplikace:
newCustomInput = new CustomInput(context); newCustomInput.setTitleText("Ahoj! Napiš alespoň dva znaky!"); newCustomInput.setErrText("Toto je málo..."); newCustomInput.setRequiredNumberOfCharacters(2); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(dipToPx(10), dipToPx(10), dipToPx(10), dipToPx(10)); newCustomInput.setLayoutParams(params); layout.addView(newCustomInput);
Při nastavování marginu nového objektu jsme použili metodu
dipToPx()
. Protože ve třídě našeho objektu View
pracujeme se základní rozměrovou jednotkou (pixel) a v kódu chceme pracovat
s jednotkou dip, je nutné zajistit převod hodnoty dip na pixel, což zajistí
tato metoda:
public int dipToPx(float dpValue) { final float scale = getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }
Uložení stavu vlastního
View
Předpokládám, že vám není úplně cizí problematika ukládání stavu
aplikace, například, během otočení telefonu (tabletu). Ukládání stavu
vlastního View
je velice podobné. Platí pravidlo, že systém
Android automaticky ukládá stav defaultních View
. Ale pouze
pokud mají nastavené ID. Když ukázkové zadávací pole vložíme do
nějakého projektu, spustíme aplikaci a zadáme do něj nějaký text, bude si
EditText
, který je součástí zadávacího pole, obsah pamatovat
i po otočení telefonu. Pokud ale vytvoříme více instancí vytvořeného
View
do jednoho layoutu, nastane problém.
Této problematice budeme věnovat samostatný článek, protože informací je poměrně hodně. Zde problém otočení zmiňuji pro ty, kteří se hned pustí do testování a do popsané situace se dostanou. Není to tedy chyba
Vysvětlení naleznete na konci tohoto kurzu, v samostatných článcích, ve
kterých si tuto problematiku podrobně vysvětlíme a naučíme se ovládat
uchovávání stavu objektů, které jsou potomky třídy View
.
V další lekci, Vlastní Android komponenta - Kreslený graf, si zkusíme vytvořit nějaký složitější
View
, na který už nám základní sada komponent stačit nebude
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 7x (18.05 MB)
Aplikace je včetně zdrojových kódů v jazyce Java