Android: obrázky a dve podpoložky v zozname cez vlastný layout

Úvod

Minulý diel sme vylepšovali históriu volaní o zobrazovanie dátumu a času pre jednotlivé hovory. V tomto dieli vo vylepšovaní pokračujeme: dodáme si ikonky, ktoré budú indikovať typ hovoru a povedia nám, či bol hovor prichádzajúci, odchádzajúci alebo zmeškaný.

Výsledok by mal vyzerať nasledovne:

Vlastný layout pre položky

V tomto prípade potrebujeme zobraziť v každej položke zoznamu už tri podpoložky. Náčrtok výzoru môže byť takýto:

Kým v predošlých prípadoch sme využívali zabudované layouty pre položky (napr. android.R.layout.simple_list_item_2), teraz už nemáme k dispozícii žiadnu vhodnú predlohu. To nevadí, vytvoríme si vlastnú.

Vytvorme nový layout súbor (File | Android XML File) a nazvime ho list_callog.xml.

Pri vytváraní použijeme horizontálny lineárny layout (LinearLayout), kde vľavo umiestnime obrázok a vpravo budeme mať dvojpoložku TwoLineListItem. Tá by mohla vyzerať presne tak ako v prípade klasického dvojpoložkového zoznamu z minulého dielu. Nehanbime sa a “vykradnime” zdrojový kód layoutu android.R.layout.simple_list_item_2, prirodzene, s vhodnými úpravami.

Dôležité je, aby sme v layoute mali tri komponenty:

  • jeden ImageView pre obrázok s identifikátorom calllog_image
  • jedno textové políčko pre horný riadok: lež namiesto zabudovaného identifikátora musíme použiť vlastný calllog_text1
  • jedno textové políčko pre spodný riadok, s menším fontom, kde takisto použijeme vlastný identifikátor calllog_text2

Výsledný layout nech vyzerá nasledovne:

<?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="horizontal" >

    <ImageView
        android:id="@+id/calllog_image"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:paddingRight="6dip"
         />

    <TwoLineListItem 
        android:paddingTop="2dip"
        android:paddingBottom="2dip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?android:attr/listPreferredItemHeight"
        android:mode="twoLine"
    >

        <TextView android:id="@+id/calllog_text1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dip"
            android:textAppearance="?android:attr/textAppearanceLarge"
        />

        <TextView android:id="@+id/calllog_text2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/calllog_text1"
            android:textAppearance="?android:attr/textAppearanceSmall"
        />

    </TwoLineListItem>
</LinearLayout>

Úprava aktivity

Úprava stĺpcov a widgetov

V layoute sme si vytvorili priestor na zobrazovanie troch položiek. Upravme teda kurzorový adaptér tak, aby zobrazoval dáta z troch stĺpcov (čísla, dátumu a typu hovoru.). Typ hovoru získame z content providera histórie volaní, kde sa skrýva pod stĺpcom CallLog.Calls.TYPE.

Upravme teda stĺpce v poliach from:

String[] from = { CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.TYPE };

Položky v poli to, ktoré obsahujú widgety, upravíme tak, aby obsahovali identifikátory z nášho čerstvého layout súboru. (Nebudeme teda používať zabudované identifikátory, ktoré sme doteraz uvádzali.)

int[] to = { R.id.calllog_text1, R.id.calllog_text2, R.id.calllog_image};

Zmena layoutu v kurzorovom adaptéri

Zmenu preddefinovaného layoutu položiek na náš vlastný layout vykonáme v konštruktore kurzorového adaptéra:

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.list_calllog, cursor, from, to);

Celý kód metódy onCreate()

Celá metóda onCreate() v aktivite bude vyzerať nasledovne:

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

    String[] from = { CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.TYPE };
    int[] to = { R.id.calllog_text1, R.id.calllog_text2, R.id.calllog_image};

    Uri contentProviderUri = CallLog.Calls.CONTENT_URI;

    Cursor cursor = getContentResolver().query(contentProviderUri, NO_PROJECTION, NO_SELECTION, NO_SELECTION_ARGS, NO_SORT_ORDER);
    startManagingCursor(cursor);

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.list_calllog, cursor, from, to);
    adapter.setViewBinder(new CallLogEntryViewBinder());
    setListAdapter(adapter);
}

Ešte nespúšťame aplikáciu, potrebujeme upraviť ViewBinder!

Upravený ViewBinder

Pripomeňme, že ViewBinder hovorí, ako sa má previesť mapovanie dát z kurzora na jednotlivé widgety. Iste si pamätáme na dátum, ktorý bol v tabuľke uložený ako počet milisekúnd od počtu éry, a museli sme ho previesť na čitateľný text.

Prevod typu hovoru na zodpovedajúci obrázok

Podobná situácia platí aj pre typ hovoru, ktorý je uložený ako integer. V tomto prípade ho však budeme prevádzať na konkrétny obrázok. Odkiaľ ho však zoberieme? Opäť vieme využiť preddefinované obrázky (utešený zoznam možno nájsť na AndroidDrawableExplorer). Vyrobme si metódu, ktorá pre daný typ hovoru z tabuľky vráti identifikátor obrázku (zhodou okolností tiež integer).

private int getCallLogEntryImage(int type) {
    switch (type) {
    case CallLog.Calls.INCOMING_TYPE:
        return android.R.drawable.sym_call_incoming;
    case CallLog.Calls.OUTGOING_TYPE:
        return android.R.drawable.sym_call_outgoing;
    case CallLog.Calls.MISSED_TYPE:
        return android.R.drawable.sym_call_missed;
    }
    return 0;
}

Všimnime si, ako vieme využiť konštanty pre hodnoty jednotlivých typov súborov z triedy CallLog.Calls a ako vraciame identifikátory zo zabudovaných obrázkov.

Metóda pre mapovanie dát na widgety

Upravme teraz metódu setViewValue(), ktorá prevádza dáta z kurzora na jednotlivé widgety v podpoložkách. Kód sme trochu upratali a sprehľadnili podľa filozofie: ak máme stĺpec pre dátumy, prevedieme počet milisekpnd na text a vrátime ho. Ak budeme mať stĺpec pre typ hovoru, pretypujeme všeobecný widget view na obrázkový widget ImageView a nastavíme mu obrázok cez setImageResource(). Samotný obrázok vráti vyššie uvedenou pomocnou metódou.

Čo s ostatnými stĺpcami? O tie sa starať nebudeme, jednoducho vrátime false a povieme kurzorovému adaptéru, nech sa o ne postará sám.

Výsledný kód teda vyzerá:

public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
    if(CallLog.Calls.DATE.equals(cursor.getColumnName(columnIndex))) {
        TextView textView = (TextView) view;

        String value = new Date(cursor.getLong(columnIndex)).toLocaleString();
        textView.setText(value);
        return true;
    }
    if(CallLog.Calls.TYPE.equals(cursor.getColumnName(columnIndex))) {
        ImageView imageView = (ImageView) view;
        if(CallLog.Calls.TYPE.equals(cursor.getColumnName(columnIndex))) {
            int type = cursor.getInt(columnIndex);
            imageView.setImageResource(getCallLogEntryImage(type));
            return true;
        }           
    }
    return false;
}

A to je všetko, aplikáciu stačí spustiť a máme to.

Sumár

  • vytvorili sme si vlastný layout pre položky
  • upravili sme zoznam stĺpcov z tabuľky content providera
  • uviedli sme zoznam identifikátorov widgetov pre podpoložky z nášho vlastného layoutu
  • vieme ho asociovať s kurzorovým adaptérom
  • upravíme ViewBinder tak, aby vracal správne zabudované obrázky

Výsledný kód

Stiahnite si výsledný projekt.

Pridaj komentár

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