Android: SimpleAdaptéry pre zoznamy s podpoložkami

ArrayAdapter ako trieda pre model zoznamov v aktivitách je skvelá dovtedy, kým nezistíte, že umožňuje zobraziť len jeden reťazec, a ten sa preberá z metódy toString() jednotlivých objektov.

Lenže zoznamy a ListActivity umožňujú zobrazovať v rámci jednej položky aj viacero podpoložiek: zoberte si napríklad také možnosti nastavenia WiFi:

SimpleAdapter je alternatívny adaptér, ktorého veľkou výhodou je možnosť nadefinovať ľubovoľný layout (výzor) pre jednotlivé položky. Na druhej strane, hodí sa len pre statické dáta: dáta musíte dodať v konštruktore, a to v podobe zoznamu máp, ale o tom o chvíľu.

Budeme potrebovať:

  • jeden súbor s XML layoutom, kde definujeme výzor jednej položky
  • vytvoriť dáta
  • prepojiť dáta s widgetami v XML layoute

Súbor s XML layoutom

Našim snom je zobrazovať úlohy z predošlého tasku, ale v podobnom štýle ako nastavenia WiFi: názov úlohy bude zvýrazneným písmom, a pod ním bude voliteľný text “HOTOVO”, ak je úloha už splnená.

Vytvorme XML súbor s definíciou layoutu: File | New | Android XML File. Názov súboru: row_task_list, typ: Linear Layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView android:id="@+id/task_name" 
              android:layout_width="fill_parent" 
              android:layout_height="wrap_content"
              style="@android:style/TextAppearance.Large"
    />

    <TextView android:id="@+id/task_done" 
              android:layout_width="fill_parent" 
              android:layout_height="wrap_content" 

              style="@android:style/TextAppearance.Small"
    />

</LinearLayout>

Tento lineárny layout definuje výzor pre jednu položku, ktorá budem mať dve podpoložky. Prvý TextView bude obsahovať názov úlohy (má ID task_name) a bude zvýraznený, pričom druhá podpoložka je TextView s ID task_done.

Zvýraznenie reprezentované väčším či menším písmom využíva zabudované androidovské štýly (TextAppearance.Large, resp. TextAppearance.Small), čo vedie ku konzistentnému výzoru aplikácií.

Dáta

Hore bolo spomenuté, že dáta do adaptéra musia byť reprezentované zoznamom máp. Každá inštancia mapy Map obsahuje dáta pre jednu položku, kde kľúčom je ľubovoľný názov podpoložky a hodnotou je… samotný zobrazovaný text.

Ak máme tri objekty:

new Task("Zubár", true), 
new Task("Elvisove narodeniny"), 
new Task("Svadba")

potom sa rozhodneme vytvoriť tri mapy a v každej budeme mať nami zvolené podpoložky pomenované napr. name (názov) a done (je hotová). Ak je položka hotová, zobrazovaný text, t. j. text v mape bude reťazec "HOTOVO"; v opačnom prípade dáme do mapy text "".

Všetky tri mapy budú teda:

name -> Zubár, done->"HOTOVO"
name -> Elvisove narodeniny, done->""
name -> Svadba, done->""

Aby sme v tom mali poriadok, vyrobíme si pomocnú metódu, ktorá dostane na vstup List úloh Task a na výstupe bude mať celý zoznam máp. Každý objekt udalosti Task sa prevedie na jednu mapu a vloží do výsledného zoznamu.

private List<Map<String, ?>> getListAdapterData(List<Task> tasks) {
    List<Map<String, ?>> list = new ArrayList<Map<String, ?>>(tasks.size());
    for (Task task : tasks) {
        Map<String, String> itemMap = new HashMap<String, String>();
        itemMap.put("name", task.getName());
        itemMap.put("done", task.isDone() ? "HOTOVO" : "");

        list.add(itemMap);
    }
    return list;
}

Mapovanie stĺpcov na widgety

Teraz musíme namapovať názvy stĺpcov na jednotlivé widgety z layout súboru:

name -> task_name
done -> task_done

Androidovské API je v tomto prípade svojsky podivný: potrebujeme vytvoriť dve polia: jedno s názvami stĺpcov a druhé s identifikátormi widgetov, pričom stĺpec na i-tej pozícii v prvom poli zodpovedá identifikátoru na _i_tej pozícii v druhom poli.

String[] columnNames = { "name",         "done" };
int[] columnIds      = { R.id.task_name, R.id.task_done };

Vytvorenie adaptéra

Máme už všetko: vytvorme adaptér:

SimpleAdapter adapter = new SimpleAdapter(this, getListAdapterData(tasks), R.layout.row_task_list, columnNames, columnIds);

Použité parametre:

  • kontext, tuto vlastniaca aktivita
  • zoznam máp s dátami pre položky a podpoložky
  • identifikátor layoutu s podpoložkami: odkážeme sa na layout row_task_list.xml.
  • pole s názvami stĺpcov
  • pole s identifikátormi widgetov podpoložiek.

Previazanie adaptéra so zoznamom

Adaptér previažeme s aktivitou ListActivity veľmi jednoducho: volaním zdedenej metódy setListAdapter():

setListAdapter(getListAdapter(tasks));

A to je všetko, celý kód vyzerá nasledovne:

Kompletný kód pre aktivitu

package sk.upjs.ics.taskr;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.Menu;
import android.widget.ListAdapter;
import android.widget.SimpleAdapter;

public class TaskListActivity extends ListActivity {
    private static final String LABEL_DONE = "hotovo";

    private static final String COLUMN_NAME = "name";

    private static final String COLUMN_DONE = "done";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        List<Task> tasks = Arrays.asList(new Task("Zubár", true), new Task("Elvisove narodeniny"), new Task("Svadba"));
        setListAdapter(getListAdapter(tasks));
    }

    private ListAdapter getListAdapter(List<Task> tasks) {
        String[] columnNames = { COLUMN_NAME, COLUMN_DONE };
        int[] columnIds = { R.id.task_name, R.id.task_done };
        SimpleAdapter adapter = new SimpleAdapter(this, getListAdapterData(tasks), R.layout.row_task_list, columnNames, columnIds);
        return adapter;
    }

    private List<Map<String, ?>> getListAdapterData(List<Task> tasks) {
        List<Map<String, ?>> list = new ArrayList<Map<String, ?>>(tasks.size());
        for (Task task : tasks) {
            Map<String, String> itemMap = new HashMap<String, String>();
            itemMap.put(COLUMN_NAME, task.getName());
            itemMap.put(COLUMN_DONE, task.isDone() ? LABEL_DONE : "");

            list.add(itemMap);
        }
        return list;
    }

}

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *