IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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 souboru attrs.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 6x (18.05 MB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Vlastní Android komponenta - Vytvoření a usazení do layoutu
Všechny články v sekci
Vlastní vzhled Android komponent
Přeskočit článek
(nedoporučujeme)
Vlastní Android komponenta - Kreslený graf
Článek pro vás napsal Pavel
Avatar
Uživatelské hodnocení:
4 hlasů
Autor se věnuje programování v Javě, hlavně pro Android. Mezi jeho další zájmy patří Arduino, Minecraft.
Aktivity