В этом уроке:

- обрабатываем события дерева-списка

 

Дерево-список строить мы умеем, теперь посмотрим, как с ним можно взаимодействовать. Нам предоставлена возможность обрабатывать следующие события: нажатие на группу, нажатие на элемент, сворачивание группы, разворачивание группы.

 

Создадим проект:

Project name: P0461_ExpandableListEvents
Build Target: Android 2.3.3
Application name: ExpandableListEvents
Package name: ru.startandroid.develop.p0461expandablelistevents
Create Activity: MainActivity

 

Экран main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tvInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </TextView>
        <ExpandableListView
            android:id="@+id/elvMain"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </ExpandableListView>
    </LinearLayout>
</LinearLayout>

TextView для вывода информации и ExpandableListView.

 

В проекте, рядом с классом MainActivity создадим (не Activity) класс AdapterHelper. В него поместим код для заполнения списка, чтобы разгрузить MainActivity.java.

Код AdapterHelper.java:

package ru.startandroid.develop.p0461expandablelistevents;

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

import android.content.Context;
import android.widget.SimpleExpandableListAdapter;

public class AdapterHelper {
  
  final String ATTR_GROUP_NAME= "groupName";
  final String ATTR_PHONE_NAME= "phoneName";

  
  // названия компаний (групп)
  String[] groups = new String[] {"HTC", "Samsung", "LG"};
  
  // названия телефонов (элементов)
  String[] phonesHTC = new String[] {"Sensation", "Desire", "Wildfire", "Hero"};
  String[] phonesSams = new String[] {"Galaxy S II", "Galaxy Nexus", "Wave"};
  String[] phonesLG = new String[] {"Optimus", "Optimus Link", "Optimus Black", "Optimus One"};
  
  // коллекция для групп
  ArrayList<Map<String, String>> groupData;
  
  // коллекция для элементов одной группы
  ArrayList<Map<String, String>> childDataItem;

  // общая коллекция для коллекций элементов
  ArrayList<ArrayList<Map<String, String>>> childData;
  // в итоге получится childData = ArrayList<childDataItem>
  
  // список аттрибутов группы или элемента
  Map<String, String> m;
  
  Context ctx;
  
  AdapterHelper(Context _ctx) {
    ctx = _ctx;
  }
  
  SimpleExpandableListAdapter adapter;
  
  
  SimpleExpandableListAdapter getAdapter() {
    
    // заполняем коллекцию групп из массива с названиями групп
        groupData = new ArrayList<Map<String, String>>();
        for (String group : groups) {
          // заполняем список аттрибутов для каждой группы
          m = new HashMap<String, String>();
            m.put(ATTR_GROUP_NAME, group); // имя компании
            groupData.add(m);  
        }
        
        // список аттрибутов групп для чтения
        String groupFrom[] = new String[] {ATTR_GROUP_NAME};
        // список ID view-элементов, в которые будет помещены аттрибуты групп
        int groupTo[] = new int[] {android.R.id.text1};
        

        // создаем коллекцию для коллекций элементов 
        childData = new ArrayList<ArrayList<Map<String, String>>>(); 
        
        // создаем коллекцию элементов для первой группы
        childDataItem = new ArrayList<Map<String, String>>();
        // заполняем список аттрибутов для каждого элемента
        for (String phone : phonesHTC) {
          m = new HashMap<String, String>();
            m.put(ATTR_PHONE_NAME, phone); // название телефона
            childDataItem.add(m);  
        }
        // добавляем в коллекцию коллекций
        childData.add(childDataItem);

        // создаем коллекцию элементов для второй группы        
        childDataItem = new ArrayList<Map<String, String>>();
        for (String phone : phonesSams) {
          m = new HashMap<String, String>();
            m.put(ATTR_PHONE_NAME, phone);
            childDataItem.add(m);  
        }
        childData.add(childDataItem);

        // создаем коллекцию элементов для третьей группы        
        childDataItem = new ArrayList<Map<String, String>>();
        for (String phone : phonesLG) {
          m = new HashMap<String, String>();
            m.put(ATTR_PHONE_NAME, phone);
            childDataItem.add(m);  
        }
        childData.add(childDataItem);

        // список аттрибутов элементов для чтения
        String childFrom[] = new String[] {ATTR_PHONE_NAME};
        // список ID view-элементов, в которые будет помещены аттрибуты элементов
        int childTo[] = new int[] {android.R.id.text1};
        
        adapter = new SimpleExpandableListAdapter(
            ctx,
            groupData,
            android.R.layout.simple_expandable_list_item_1,
            groupFrom,
            groupTo,
            childData,
            android.R.layout.simple_list_item_1,
            childFrom,
            childTo);
        
    return adapter;
  }
  
  String getGroupText(int groupPos) {
    return ((Map<String,String>)(adapter.getGroup(groupPos))).get(ATTR_GROUP_NAME);
  }
  
  String getChildText(int groupPos, int childPos) {
    return ((Map<String,String>)(adapter.getChild(groupPos, childPos))).get(ATTR_PHONE_NAME);
  }
  
  String getGroupChildText(int groupPos, int childPos) {
    return getGroupText(groupPos) + " " +  getChildText(groupPos, childPos);
  }
}

Код создания адаптера полностью заимствован с прошлого урока. Чтобы получить адаптер нам надо будет просто вызвать метод getAdapter.

У класса есть конструктор, через который мы передаем объекту ссылку на context. Context нам понадобится, чтобы создать адаптер. Адаптеру же в свою очередь context нужен, например, для доступа к LayoutInflater.

В конце класса находятся методы, которые возвращают нам названия групп и элементов из коллекций по номеру группы или номеру элемента. Для этого используем методы адаптера getGroup и getChild, приводим их к Map и извлекаем значение атрибута с именем компании или телефона.

 

Пишем код в MainActivity.java:

package ru.startandroid.develop.p0461expandablelistevents;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.ExpandableListView.OnGroupCollapseListener;
import android.widget.ExpandableListView.OnGroupExpandListener;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;


public class MainActivity extends Activity {
  
  final String LOG_TAG = "myLogs";
  
  ExpandableListView elvMain;
  AdapterHelper ah;
  SimpleExpandableListAdapter adapter;
  TextView tvInfo;
  
    /** Called when the activity is first created. */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        tvInfo = (TextView) findViewById(R.id.tvInfo);
        
        // создаем адаптер
        ah = new AdapterHelper(this);
        adapter = ah.getAdapter();
        
        elvMain = (ExpandableListView) findViewById(R.id.elvMain);
        elvMain.setAdapter(adapter);
        
        // нажатие на элемент
        elvMain.setOnChildClickListener(new OnChildClickListener() {
      public boolean onChildClick(ExpandableListView parent, View v,
          int groupPosition,   int childPosition, long id) {
        Log.d(LOG_TAG, "onChildClick groupPosition = " + groupPosition + 
                " childPosition = " + childPosition + 
                " id = " + id);
        tvInfo.setText(ah.getGroupChildText(groupPosition, childPosition));
        return false;
      }
    });
        
        // нажатие на группу
        elvMain.setOnGroupClickListener(new OnGroupClickListener() {
      public boolean onGroupClick(ExpandableListView parent, View v,
          int groupPosition, long id) {
        Log.d(LOG_TAG, "onGroupClick groupPosition = " + groupPosition + 
                " id = " + id);
        // блокируем дальнейшую обработку события для группы с позицией 1
        if (groupPosition == 1) return true;
        
        return false;
      }
    });
        
        // сворачивание группы        
        elvMain.setOnGroupCollapseListener(new OnGroupCollapseListener() {
      public void onGroupCollapse(int groupPosition) {
        Log.d(LOG_TAG, "onGroupCollapse groupPosition = " + groupPosition);
        tvInfo.setText("Свернули " + ah.getGroupText(groupPosition));
      }
    });
        
        // разворачивание группы
        elvMain.setOnGroupExpandListener(new OnGroupExpandListener() {
      public void onGroupExpand(int groupPosition) {
        Log.d(LOG_TAG, "onGroupExpand groupPosition = " + groupPosition);
        tvInfo.setText("Развернули " + ah.getGroupText(groupPosition));
      }
    });
        
        // разворачиваем группу с позицией 2
        elvMain.expandGroup(2);
    }
}

 

Благодаря классу AdapterHelper, код создания адаптера занял всего две строчки: создание объекта и вызов метода getAdapter. Далее присваиваем адаптер списку и добавляем обработчики:

1) OnChildClickListener - нажатие на элемент

Метод
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id), где

parent – ExpandableListView с которым работаем
v – View элемента
groupPosition – позиция группы в списке
childPosition – позиция элемента в группе
id – id элемента

Мы выводим в лог позицию и id. А в TextView сверху от списка выводим текст нажатого элемента и его группы, который получаем с помощью методов AdapterHelper.

Метод должен вернуть boolean. Если мы возвращаем true – это значит, мы сообщаем, что сами полностью обработали событие и оно не пойдет в дальнейшие обработчики (если они есть). Если возвращаем false – значит, мы позволяем событию идти дальше.

 

2) OnGroupClickListener – нажатие на группу

Метод
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id), где

parent – ExpandableListView с которым работаем
v – View элемента
groupPosition – позиция группы в списке
id – id группы

Мы выводим в лог позицию и id группы.

Этот метод также должен вернуть boolean. Мы будет возвращать true, если позиция группы = 1, иначе - false. Т.е. для этой группы мы блокируем дальнейшую обработку события. Далее увидим, что нам это даст.

 

3) OnGroupCollapseListener – сворачивание группы

Метод
onGroupCollapse(int groupPosition), где groupPosition – позиция группы, которую свернули

 

4) OnGroupExpandListener – разворачивание группы

Метод
onGroupExpand(int groupPosition), где groupPosition – позиция группы, которую развернули

 

И в конце кода MainActivity мы разворачиваем группу с позицией 2, используя метод expandGroup.

 

Все сохраним и запускаем.

 

Как видим, группа LG сразу развернута. Это сработала команда expandGroup в конце кода.

Если посмотреть в лог, то видим

onGroupExpand groupPosition = 2

Т.е. отработало событие разворачивания группы с позицией 2.

 

Нажмем, например, на Optimus Link. Смотрим лог:

onChildClick groupPosition = 2 childPosition = 1 id = 1

Не забываем, что позиция считается с нуля. Группа с позицией 2 – LG, элемент с позицией 1 – Optimus Link, все верно.

Смотрим TextView сверху экрана, он считал из адаптера значение атрибута и отобразил его.

 

Теперь попробуем свернуть группу LG, нажмем на нее. Смотрим лог:

onGroupClick groupPosition = 2 id = 2
onGroupCollapse groupPosition = 2

Сначала отработал onGroupClick – нажатие на группу, а потом onGroupCollapse – сворачивание группы. TextView наверху экрана оповестил о том, что свернули группу LG.

 

Снова развернем группу LG. Лог:

onGroupClick groupPosition = 2 id = 2
onGroupExpand groupPosition = 2

Нажатие на группу и разворачивание. Обратите внимание, что при программном разворачивании, события нажатия не было, только разворот.

 

Теперь попробуем развернуть группу с позицией 1 – Samsung. Группа не разворачивается. Смотрим лог:

onGroupClick groupPosition = 1 id = 1

Событие нажатия есть, а вот обработчик разворачивания не вызывается. Это происходит из-за строчки

// блокируем дальнейшую обработку события для группы с позицией 1
if (groupPosition == 1) return true;

Мы для группы с позицией 1 блокируем дальнейшую обработку события и оно не уходит в обработчики разворачивания или сворачивания. Поэтому и не срабатывает onGroupExpand.

 

В итоге эти 4 обработчика позволяют вам определять, как пользователь взаимодействует с деревом.


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal