06
Grafické prvky v okně (tlačítka, textová pole atd.) v Androidu vkládáme do komponent, kterým se říká layout. Layout grafické prvky shromažduje (funguje jako kontejner grafických prvků) a především určuje uspořádání prvků v okně a jejich vzájemnou polohu (funguje jako správce rozložení). V českém textu budeme používat buď slovo kontejner, nebo anglické slovo layout tam, kde by překlad byl zavádějící. Termín layout se běžně používá i v českých textech o programování a tvorbě grafického rozhraní.
Zatím jsme prvky v okně skládali pod sebe nebo vedle sebe a používali k jejich uspořádání kontejner LinearLayout
. Tento kontejner je pro naše účely nejjednodušší za předpokladu, že nám stačí skládat prvky pod sebe nebo vedle sebe. Pro složitější rozložení se sice kontejnery dají vnořovat, ale to vede k výpočetně náročnějšímu a méně spolehlivému kódu.
Výchozí volbou v Android Studiu je RelativeLayout
. Ten určuje pozici grafických prvků ve vztahu k ostatním grafickým prvkům. Pro komplexnější rozložení je tak méně výpočetně náročný než vnořované kontejnery LinearLayout
, ale zase je obtížnější si rozložení prvků představit a upravovat.
Dalším základním kontejnerem je WebView
, který slouží k jednoduchému zobrazení dokumentu zapsaného v jazyce HTML.
Existují ale i další kontejnery (layouts). Zmiňme GridView
, který uspořádává prvky do pravidelné mřížky a hodí se tedy typicky pro náhledy obrázků a další podobné situace, a ListView
, který umožňuje jednoduše řešit dynamické přidávání textových položek. Tyto kontejnery používají třídu Adapter
k získání položek, které mají zobrazovat. Kontejnerem ListView
se budeme podrobněji zabývat v této kapitole.
ListView
ListView
slouží k zobrazení několika textových položek, mezi kterými můžeme skrolovat. Je tak vhodný zejména tehdy, kdy počet položek předem neznáme a přidáváme položky teprve za běhu aplikace.
ListView
do oknaKontejner vkládáme stejně jako ostatní kontejnery v XML popisu vzhledu okna. Používáme element <ListView>
.
ListView
Ke vkládání položek používá layout ListView
instance tříd, implementujících rozhraní (interface) Adapter
. Nejběžnější je použití třídy ArrayAdapter
, která umožňuje vkládat položky z pole či Listu
(například z kontejneru ArrayList
), nebo použití třídy Cursor
a SimpleCursorAdapter
. My se omezíme na použití ArrayAdapteru
.
ListView lvSeznam = (ListView) this.findViewById(R.id.prehled_zaznamu); lvSeznam.setAdapter(new ArrayAdapter<Zaznam>(this, android.R.layout.simple_list_item_1, zaznamy));
Adaptér nastavíme metodou setAdapter()
. Při vytváření ArrayAdapteru
zadáváme parametry:
Context
, tedy aktivitu, ve které bude Adapter
použitandroid.R.layout.simple_list_item_1
List
či pole s daty.Data navíc musí mít vhodně překrytu metodu toString()
, která bude použita pro nastavení textů jednotlivých položek ListView
.
Pokud dojde ke změně dat v ArrayListu
, musíme o tom informovat ArrayAdapter
tak, že zavoláme jeho metodu notifyDataSetChange()
. ArrayAdapter
zařídí aktualizaci zobrazených dat.
((ArrayAdapter) seznam.getAdapter()).notifyDataSetChanged();
Reakci na stisk některé z položek ListView
nastavujeme pomocí mechanismu posluchačů (Listener
). V našem případě použijeme metodu setOnClickListener()
třídy ListView
a předáme jí jako parametry instanci potomka třídy OnItemClickListener
.
Při použití anonymního posluchače (Listener
) může kód vypadat například takto:
lvSeznam.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View itemView, int poradiPolozkyVAdapteru, long cisloRadkuVListView) { uprav(adapterView, itemView, poradiPolozkyVAdapteru, cisloRadkuVListView); } });
Metoda uprav(…)
je v tomto příkladě metoda třídy, která reprezentuje okno, ve kterém je ListView
vnořen. V této metodě realizujeme samotnou reakci na stisk položky.
Jaké parametry dostaneme k dispozici:
adapterView
je v našem případě instance ListView
, ve které byla položka zvolena. Je tady proto, abychom mohli mít listener společný pro více ListView
.itemView
je položka v ListView
, která byla zvolena.poradiPolozkyVAdapteru
v parametru metody onItemClick(…)
udává číslo položky, na kterou uživatel klikl. Jedná se o pořadí položky v adaptéru, tedy v našem případě v ArrayListu
. Položky jsou číslovány od 0
. Pod tímto číslem lze položku získat z ArrayListu
metodou get(…)
.cisloRadkuVListView
udává číslo řádku v komponentě ListView
, který byl zvolen. Řádky jsou také číslovány od 0
a v našem případě to tedy bude stejná hodnota jako poradiPolozkyVAdapteru
.Pokud tedy chceme získat záznam, který odpovídá zvolené položce, použijeme metody getItemAtPosition() třídy ListView (zděděnou ze třídy AdapterView, která je nepřímým předkem ListView):
Zaznam zaznam = (Zaznam) zvolenyView.getItemAtPosition(poziceVAdapteru);
Vytvoříme jednoduchou aplikaci, která bude umožňovat zaznamenat, co nového jsme zažili/naučili se/vyzkoušeli si. Budeme zaznamenávat jednotlivé poznámky. U každé poznámky uvedeme datum, co zajímavého se stalo a případně další údaje, komentáře atd. Při kliknutí na poznámku se poznámka zvýrazní jako důležitá, druhé kliknutí příznak důležitosti zruší.
Aplikaci si můžete snadno upravit na jednoduchý tréninkový deník, ...
Jednotlivé položky/záznamy budou v aplikaci načteny do komponenty ListView
. V dalších kapitolách doplníme možnost přidávat záznamy, upravovat je a mazat.
Celá aplikace bude ve výsledku vypadat takto:
ListView
, která umožňuje zobrazit více položek najednou:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" ... android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Uložené záznamy:" /> <ListView android:id="@+id/lv_seznam" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </LinearLayout>
Zaznam
s atributy nazev
a popis
(oba budou textové). Přidáme atribut dulezite
(logická hodnota) a případně další atributy dle potřeby. Překryjeme metodu toString()
, čímž umožníme správné zobrazení v ListView
.public class Zaznam { String nazev; String popis; boolean dulezite; public Zaznam(String nazev, String popis, boolean dulezite) { this.nazev = nazev; this.popis = popis; this.dulezite = false; } public String toString() { String vysledek = this.nazev + "\n" + this.popis.substring(0,25) + "..."; if (this.dulezite) vysledek = "[!] " + vysledek; return vysledek; } }
Data
, která bude zastřešovat operace nad kontejnerem jednotlivých záznamů. Zároveň ji v dalších kapitolách doplníme tak, aby zasílala informace o změnách v záznamech komponentě ListView
. K tomu použijeme implementaci návrhového vzoru Pozorovatel (observer) v Javě.
Třída Data
zatím vytvoří několik záznamů „ručně“, později ji ale doplníme tak, aby data četla ze souboru.public class Data { ArrayList<Zaznam> data = new ArrayList<Zaznam>(); protected void nactiData() { this.data = new ArrayList(); Zaznam z; z = new Zaznam("Programování pro Android", "Tvorba aplikací pro Android.", true); this.data.add(z); z= new Zaznam("Hra na kytaru", "Základní akordy na kytaru.", false); this.data.add(z); z = new Zaznam("Terra Mystica", "Naučil jsem se novou deskovou hru.", false); this.data.add(z); } public ArrayList<Zaznam> getData() { return this.data; } }
Data
a inicializujeme ji. ListView
, bude nejprve potřeba vytvořit datový adaptér, který se nám bude starat o data v ListView
. Dále našemu seznamu (ListView
) tento adaptér přiřadíme.public class MainActivity extends AppCompatActivity { Data zaznamy; ListView seznam; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Vytvoření a inicializace třídy Data: this.zaznamy = new Data(); this.zaznamy.nactiData(); // Vytvoření ListView a propojení s daty: this.seznam = (ListView) this.findViewById(R.id.lv_seznam); this.seznam.setAdapter(new ArrayAdapter<Zaznam>(this, android.R.layout.simple_list_item_1, this.zaznamy.getData())); } }
aktualizuj()
, která zařídí aktualizaci obsahu ListView
při změně obsahu. notifyDataSetChanged()
adaptéru ArrayAdapter
:private void aktualizuj() { ListView seznam = (ListView) this.findViewById(R.id.lv_seznam); ArrayAdapter<Zaznam> adapter = (ArrayAdapter<Zaznam>) seznam.getAdapter(); adapter.notifyDataSetChanged(); }
ListView
. Zatím nereaguje na volbu položky, ale položky by se již měly zobrazovat.Položky tedy máme načtené v komponentě ListView
. Nyní budeme chtít zařídit, aby se při volbě položky změnila její důležitost. Projeví se to změnou popisu položky.
zvolenaPolozka()
do hlavní aktivity. Ta získá zvolený záznam z ListView a zavolá jeho metodu invertujDulezite()
(tu jsme si již ve třídě Zaznam
vytvořili):private void zvolenaPolozka(AdapterView<?> zvolenyView, View polozka, int poziceVAdapteru, long cisloRadkuUvnitrView) { Zaznam zaznam = (Zaznam) zvolenyView.getItemAtPosition(poziceVAdapteru); zaznam.invertujDulezite(); }
onCreate()
hlavní aktivity položku ListView OnClickListener
. zvolenaPolozka()
:protected void onCreate(Bundle savedInstanceState) { ... this.seznam.setAdapter(new ArrayAdapter<Zaznam>(this, android.R.layout.simple_list_item_1, this.zaznamy.getData())); this.seznam.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick( AdapterView<?> zvolenyView, View polozka, int poziceVAdapteru, long cisloRadkuUvnitrView) { zvolenaPolozka(zvolenyView, polozka, poziceVAdapteru, cisloRadkuUvnitrView); } }); }
LinearLayout
, který je vhodný pro jednoduché uspořádání prvků pod sebou nebo vedle sebe.RelativeLayout
, nejlépe s grafickým návrhářem vzhledu – například tím, který je součástí vývojového prostředí Android Studio.ListView
, který je vhodný pro zobrazení většího počtu položek, které se navíc mohou dynamicky měnit. ListView
položkami jsme použili ArrayAdapter
, který bere položky z ArrayListu
.ArrayListu
, informujeme ArrayAdapter
metodou notifyDataSetChanged()
a ten zajistí aktualizaci ListView
.ListView
zajistí OnClickListener
. Konkrétní zvolenou položku získáme metodou getItemAtPosition()
třídy ListView
.