Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

ExpandableListView

Компонент ExpandableListView является расширенным вариантом компонента ListView. Основное отличие - разворачивающий список второго уровня. Получается список в списке. Рассмотрим простейший вариант:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ExpandableListView
        android:id="@+id/expListView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ExpandableListView>

</LinearLayout>

Сам по себе компонент не представляет интереса. Его необходимо заполнить данными. Переключимся в код активности и напишем следующее:


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.testapplication;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter;

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


public class MainActivity extends ActionBarActivity {

    private String[] mGroupsArray = new String[] { "Зима", "Весна", "Лето", "Осень" };

    private String[] mWinterMonthsArray = new String[] { "Декабрь", "Январь", "Февраль" };
    private String[] mSpringMonthsArray = new String[] { "Март", "Апрель", "Май" };
    private String[] mSummerMonthsArray = new String[] { "Июнь", "Июль", "Август" };
    private String[] mAutumnMonthsArray = new String[] { "Сентябрь", "Октябрь", "Ноябрь" };

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        setTitle("Locale Date");

        Map<String, String> map;
        // коллекция для групп
        ArrayList<Map<String, String>> groupDataList = new ArrayList<>();
        // заполняем коллекцию групп из массива с названиями групп

        for (String group : mGroupsArray) {
            // заполняем список атрибутов для каждой группы
            map = new HashMap<>();
            map.put("groupName", group); // время года
            groupDataList.add(map);
        }

        // список атрибутов групп для чтения
        String groupFrom[] = new String[] { "groupName" };
        // список ID view-элементов, в которые будет помещены атрибуты групп
        int groupTo[] = new int[] { android.R.id.text1 };

        // создаем общую коллекцию для коллекций элементов
        ArrayList<ArrayList<Map<String, String>>> сhildDataList = new ArrayList<>();

        // в итоге получится сhildDataList = ArrayList<сhildDataItemList>

        // создаем коллекцию элементов для первой группы
        ArrayList<Map<String, String>> сhildDataItemList = new ArrayList<>();
        // заполняем список атрибутов для каждого элемента
        for (String month : mWinterMonthsArray) {
            map = new HashMap<>();
            map.put("monthName", month); // название месяца
            сhildDataItemList.add(map);
        }
        // добавляем в коллекцию коллекций
        сhildDataList.add(сhildDataItemList);

        // создаем коллекцию элементов для второй группы
        сhildDataItemList = new ArrayList<>();
        for (String month : mSpringMonthsArray) {
            map = new HashMap<>();
            map.put("monthName", month);
            сhildDataItemList.add(map);
        }
        сhildDataList.add(сhildDataItemList);

        // создаем коллекцию элементов для третьей группы
        сhildDataItemList = new ArrayList<>();
        for (String month : mSummerMonthsArray) {
            map = new HashMap<>();
            map.put("monthName", month);
            сhildDataItemList.add(map);
        }
        сhildDataList.add(сhildDataItemList);

        // создаем коллекцию элементов для четвертой группы
        сhildDataItemList = new ArrayList<>();
        for (String month : mAutumnMonthsArray) {
            map = new HashMap<>();
            map.put("monthName", month);
            сhildDataItemList.add(map);
        }
        сhildDataList.add(сhildDataItemList);

        // список атрибутов элементов для чтения
        String childFrom[] = new String[] { "monthName" };
        // список ID view-элементов, в которые будет помещены атрибуты
        // элементов
        int childTo[] = new int[] { android.R.id.text1 };

        SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter(
                this, groupDataList,
                android.R.layout.simple_expandable_list_item_1, groupFrom,
                groupTo, сhildDataList, android.R.layout.simple_list_item_1,
                childFrom, childTo);

        ExpandableListView expandableListView = (ExpandableListView) findViewById(R.id.expListView);
        expandableListView.setAdapter(adapter);
    }
}

ExpandableListView

Сначала мы описываем массивы данных – это названия групп (Времена года) и названия элементов для них (месяцы).

Затем описываем коллекцию для групп, коллекции для элементов и Map для атрибутов.

В методе onCreate() заполняем groupData. Это коллекция групп. Каждая группа представляет собой Map. А в Map мы пишем необходимые нам атрибуты для каждой группы. В нашем случае, для каждой группы мы укажем всего один атрибут groupName - это название из массива groups.

Адаптер обычно использует layout-ресурс для отображения пункта списка. В нашем случае пунктами ExpandableListView являются и группа и элемент. В layout-ресурсе могут быть какие-либо TextView. Мы можем заполнить их значениями из атрибутов элементов или групп, которые собраны в Map. Для этого нам надо указать сначала имена атрибутов, которые хотим использовать, а затем идентификаторы TextView, в которые хотим поместить значения этих атрибутов.

Для связки атрибутов и TextView мы используем два массива:

  • groupFrom – список имен атрибутов, которые будут считаны. В нашем случае – это groupName, который мы добавили к группе с помощью Map чуть выше в коде, когда собирали группы в groupData.
  • groupTo – список ID View-элементов, в которые будут помещены считанные значения атрибутов. Наш используемый layout будет содержать TextView с ID = android.R.id.text1.

Два этих массива сопоставляются по порядку элементов. В итоге, в layout-ресурсе группы найдется элемент с ID = android.R.id.text1 и в него запишется текст из атрибута groupName. Тем самым мы получим отображение имени группы в списке.

Далее формируем коллекции элементов. Создаем общую коллекцию коллекций. А затем создаем коллекции элементов каждой группы. Принцип тот же, что и с группами – создаем Map и в него пишем атрибут monthName со значением равным имени элемента. Коллекцию элементов для каждой группы добавляем в общую коллекцию.

Формируем два массива для сопоставления TextView из layout и атрибутов элементов. Полностью аналогично, как выше мы уже проделали с группами. В итоге при отображении элемента, найдется TextView с ID = android.R.id.text1 и туда запишется текст из атрибута monthName. И мы увидим текст нашего элемента (месяца) в списке.

В конце кода создаем адаптер SimpleExpandableListAdapter и присваиваем его списку.

Параметры для адаптера:

  • this – контекст
  • groupData – коллекция групп
  • android.R.layout.simple_expandable_list_item_1 – layout-ресурс, который будет использован для отображения группы в списке. Соответственно, запросто можно использовать свой layout-файл
  • groupFrom – массив имен атрибутов групп
  • groupTo – массив ID TextView из layout для групп
  • childData – коллекция коллекций элементов по группам
  • android.R.layout.simple_list_item_1 - layout-ресурс, который будет использован для отображения элемента в списке. Можно использовать свой layout-файл childFrom – массив имен атрибутов элементов
  • childTo - массив ID TextView из layout для элементов

Layout simple_expandable_list_item_1, который мы использовали для отображения групп – это TextView с отступом от левого края, чтобы осталось место для кнопки раскрытия/сворачивания списка. Для эксперимента вы можете попробовать использовать для групп layout simple_list_item_1, который мы использовали для элементов. В этом случае текст будет пересекаться с кнопкой.

А вообще вы можете создать для элементов свою разметку, например, с тремя TextView. И к каждому элементу списка (Map) добавить еще по два атрибута. Далее указываете вашу разметку в конструкторе, формируете соответственно массивы childFrom и childTo чтобы сопоставить атрибуты и TextView, и получится, что каждый элемент группы содержит более подробную информацию.

ExpandableListView редко используется в составе разметки с другими элементами. Обычно такой список занимает весь экран, поэтому для подобных целей лучше использовать специальный класс ExpandableListViewActivity, который уже содержит в своём составе компонент ExpandableListView.

Размещение индикаторов групп в заданном месте

Вы можете указать другое расположение индикаторов групп, например, справа. Для этого используются методы setIndicatorBounds() или setIndicatorBoundsRelative():


ExpandableListView expandableListView = (ExpandableListView) findViewById(R.id.expListView);
expandableListView.setAdapter(adapter);

if (android.os.Build.VERSION.SDK_INT <
        android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
    expandableListView.setIndicatorBounds(170, 200);
} else {
    expandableListView.setIndicatorBoundsRelative(170, 200);
}

В примере использованы "магические числа". Вам лучше самостоятельно вычислить нужные значения.

Собственные индикаторы

Для создания собственных индикаторов приготовьте файлы *.9.png в двух состояниях (обычный и раскрытый) и пропишите их в drawable-ресурсах.


<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_expanded="true" android:drawable="@drawable/group_indicator_expanded" />
    <item android:drawable="@drawable/group_indicator" />
</selector>

Пропишите селектор в атрибуте groupIndicator.


<ExpandableListView
        android:id="@+id/expandableListView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:groupIndicator="@drawable/group_indicator_selector" >
</ExpandableListView>

Дополнительное чтение

Фильтруем базар. Поиск по тексту (ExpandableListView, SearchView) (Часть 3) (закрытая зона/3-й курс)

Кастомный ExpandableListView в Android

Реклама