Tvorba aplikací pro Android

11

Kontextové menu pro komponentu ListView

Kon­textové menu se velmi často používá v případě, kdy máme sez­nam určitých položek načten v sez­namu (ListView, GridView,…) a chceme přiřadit každé položce z tohoto sez­namu kon­textové menu. Nejlépe si tuto možnost vysvětlíme na jedno­duc­hém příkladu.


Příklad 2 – Kon­textové menu pro Li­stView – Můj pestrý život

Přidej­me k přeh­ledu záznamů v ap­likaci, kterou jsme nap­rogramovali v předchozích kapitolách. Do kon­textového menu pro jedno­tlivé položky přidáme volby „Odstranit“, „Přepni důležitost“ a „Up­ravit“. Položky „Odstranit“ a „Přepni důležitost“ im­plemen­tujeme, volbu „Up­ravit“ reali­zujeme až v příští kapitole, kde se zároveň naučíme vytvářet a volat nové ak­tiv­ity (další ob­razov­ky ap­likace).


Celá ap­likace bude ve výs­ledku vypadat takto:


Obrázek 1 –


Pos­tup:

  1. Otevřeme si pro­jekt Můj pestrý život z předchozích kapitol. (Pokud pro­jekt z nějakého důvodu nemáte, můžete si vytvořit jedno­duc­hou kom­ponen­tu ListView, která bere položky z kon­tejneru ArrayList.)

    Připomeňme, že v pro­jek­tu máme třídu Data (kon­tejn­er záznamů) a třídu Zaznam s tex­tovými at­ributy nazevpopis a at­ributem dulezite, který je datového typu logická hod­nota (boolean). Pro snazší práci a pro dodržení prin­cipu zapouzdř­ení si do třídy Data přidáme metody:

  2. public Zaznam getZaznam(int pozice) {
        return this.data.get(pozice);
    }
    public void odstranZaznam(int pozice) {
        this.data.remove(pozice);
        this.notifyObservers();
    }
    

  3. V souboru re­s/values/strings.xml de­finujeme tex­tové řetězce, popisující texty v menu:

        <string name="app_name">Můj pestrý život</string>
        <string name="menu_load_file">Obnov</string>
        <string name="menu_save_file">Ulož</string>
        <string name="kmenu_remove">Odeber záznam</string>
        <string name="kmenu_change_importance">Přepni důležitost</string>
        <string name="kmenu_edit">Uprav záznam...</string>
    </resources>
    

    Vytvoříme soubor re­s/menu/­menu_prezen­ce.xml a nadefinujeme v něm položky menu. Elemen­ty a význam at­ributů zůstávají stejné jako v předchozích kapitolách při tvorbě hlavního menu a kon­textového menu pro běžné položky:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto" >
        <item android:id="@+id/km_remove" android:title="@string/kmenu_remove"
            android:orderInCategory="100" app:showAsAction="never" />
        <item android:id="@+id/km_change_importance" android:title="@string/kmenu_change_importance"
            android:orderInCategory="100" app:showAsAction="never" />
        <item android:id="@+id/km_edit" android:title="@string/kmenu_edit"
            android:orderInCategory="100" app:showAsAction="never" />
        
    </menu>
    

  4. Sez­namu (ListView) nyní zaregistrujemev metodě onCreate(…) hlavní ak­tiv­ity kon­textové menu – tím řek­neme přek­ladači, že chceme, aby položky Li­stView měly kon­textové menu. To jsme se již naučili v předchozí kapitole:

    this.registerForContextMenu(this.seznam);
    

    Přek­ryjeme metodu onCreateCon­textMenu třídy Ac­tiv­ity (přidáme do naší ak­tiv­ity metodu onCreateCon­textMenu(…):

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.kontext_menu_zaznamy, menu);
    }
    

Ap­likaci můžeme nyní zkusit spus­tit. Kon­textové menu je u každé položky (záznamu), zatím ale nereaguje na výběr.


Rea­kce na volbu položky z menu:

  1. Přek­ryjeme si metodu onContextItemSelected(…) pro rea­kci na výběr položky. Pro určení, na jakou položku v ListView jsme klik­li, využijeme třídu AdapterContextMenuInfo. V případě potřeby o změně dat v ArrayListu in­for­mujeme adaptér metodou notifyDataSetChanged() – to už známe z kapito­ly o ListView. Naše třídy již mají in­for­maci o změně vyřešenu pomocí mech­anis­mu pozorovatele.

  2. @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo infoOZvolenePolozce =
                (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        switch(item.getItemId()) {
            case R.id.km_change_importance:
                Zaznam zaznam = this.zaznamy.getZaznam(infoOZvolenePolozce.position);
                zaznam.invertujDulezite();
                return true;
            case R.id.km_edit:
                Toast.makeText(this, "Editace zatím není implementována", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.km_remove:
                this.zaznamy.odstranZaznam(infoOZvolenePolozce.position);
            default:
                return super.onContextItemSelected(item);
        }
    }
    

Shrnutí:

  1. Při použití kon­textového menu v sez­namu (ListView,…) re­gistrujeme kon­textové menu pro celý sez­nam.

  2. Stejně jako u klasic­kého kon­textového menu nám metoda MenuItem.getItemId() sdělí ID zvol­ené položky menu.

  3. Pozici zvol­ené položky v adaptéru získáme pomocí vnořené třídy AdapterView.AdapterContextMenuInfo. Tato třída nese at­ribut position, který udává pozici zvol­ené položky v adaptéru. In­stan­ci třídy získáme metodou MenuItem.getMenuInfo().