В этом уроке мы реализуем двухуровневый список ExpandableListView
. Это своего рода меню из подменю в Android ListView.
Краткий обзор ExpandableListView в Android
Двухуровневый список ExpandableListView
представляет собой объект view, в котором каждый элемент списка содержит вложенный список. За работу списка отвечает адаптер ExpandableListViewAdapter
, который загружает данные по каждому пункту и вложенному списку.
Давайте рассмотрим наиболее полезные методы, которые используются при работе с классом ExpandableListView
:
- Метод
setChildIndicator(Drawable)
используется для отображения индикатора возле каждого элемента списка. - Метод
setGroupIndicator(Drawable)
устанавливает индикатор возле каждого элемента, обозначая развернут он или свернут. Если группа пуста, то будет установлено состояниеstate_empty
. Если группа развернута, будет установлено состояниеstate_expanded
. - Метод
getGroupView()
возвращает view для группы элементов списка - Метод
getChildView()
возвращает view дочернего элемента списка
Для каждого элемента используется свой тип слушателя:
- Интерфейс
ExpandableListView.OnChildClickListener
переопределяется для отслеживания нажатий на дочерние элементы раскрывающегося списка - Интерфейс
ExpandableListView.OnGroupClickListener
переопределяется для отслеживания нажатий на группу элементов - Интерфейс
ExpandableListView.OnGroupCollapseListener
используется для уведомления о том, что группа элементов была свернута - Интерфейс
ExpandableListView.OnGroupExpandListener
используется для уведомления о том, что группа элементов была развернута
Пример создания ExpandableListView. Практика
В нашем приложении будет использоваться всего 3 класса:
- Класс MainActivity — главная активити, в которой будет макет с ExpandableListView
- Класс ListData — класс, в котором будут храниться данные для представления в ExpandableListView. В реальных проектах источником данных конечно будет БД или же какие-то данные из сети.
- Класс ListAdapter — класс, который отвечает за управление данными из ListData в компоновке
ExpandableListView
.
Файл компоновки activity_main.xml будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <ExpandableListView android:id="@+id/expListView" android:layout_height="match_parent" android:layout_width="match_parent" android:indicatorLeft="?android:attr/expandableListPreferredItemIndicatorLeft" android:divider="@android:color/darker_gray" android:dividerHeight="1dp" /> </RelativeLayout> |
Обратите внимание на использование атрибута android:indicatorLeft
— он устанавливает индикатор списка (открыт он или нет) слева от названия пункта.
Также нам понадобится компоновка list_group.xml для дочерних элементов раскрывающегося списка каждого пункта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?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/listTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="10dp" android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft" android:paddingTop="10dp" android:textColor="@android:color/black" /> </LinearLayout> |
Компоновка одного дочернего элемента будет следующей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?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="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/expandedListItem" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="10dp" android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft" android:paddingTop="10dp" /> </LinearLayout> |
Теперь представим класс ListData, в котором будут храниться данные всего списка ExpandableListView
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package ua.com.prologistic.expandablelistviewapp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class ListData { public static HashMap<String, List<String>> loadData() { HashMap<String, List<String>> expDetails = new HashMap<>(); List<String> oopLanguages = new ArrayList<>(); oopLanguages.add("Java"); oopLanguages.add("C++"); oopLanguages.add("C#"); oopLanguages.add("Python"); oopLanguages.add("Scala"); List<String> structuredLanguages = new ArrayList<>(); structuredLanguages.add("ALGOL"); structuredLanguages.add("COBOL"); structuredLanguages.add("QBasic"); structuredLanguages.add("COMAL"); structuredLanguages.add("LEAP"); List<String> functionalLanguages = new ArrayList<>(); functionalLanguages.add("Haskell"); functionalLanguages.add("Miranda"); functionalLanguages.add("Curry"); functionalLanguages.add("Clean"); functionalLanguages.add("Joy"); expDetails.put("OOP языки программирования", oopLanguages); expDetails.put("Structured языки программирования", structuredLanguages); expDetails.put("Functional языки программирования", functionalLanguages); return expDetails; } } |
Как видите, наш HashMap содержит в качестве ключей названия родительских элементов раскрывающегося списка, а в качестве значений — список дочерних элементов (тех элементов, которые будут показаны при нажатии на раскрывающийся родительский элемент списка).
Определяем в классе ListAdapter какие элементы будут показаны в GroupView
, а какие в ChildView
и как будет работать наш адаптер:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
package ua.com.prologistic.expandablelistviewapp; import android.content.Context; import android.graphics.Typeface; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import android.widget.TextView; import java.util.HashMap; import java.util.List; public class ListAdapter extends BaseExpandableListAdapter { private Context context; private List<String> expListTitle; private HashMap<String, List<String>> expListDetail; public ListAdapter(Context context, List<String> expListTitle, HashMap<String, List<String>> expListDetail) { this.context = context; this.expListTitle = expListTitle; this.expListDetail = expListDetail; } @Override public Object getChild(int listPosition, int expListPosition) { return expListDetail.get( expListTitle.get(listPosition) ).get(expListPosition); } @Override public long getChildId(int listPosition, int expandedListPosition) { return expandedListPosition; } @Override public View getChildView(int listPosition, final int expandedListPosition, boolean isLastChild, View convertView, ViewGroup parent) { // получаем дочерний элемент String expListText = (String) getChild(listPosition, expandedListPosition); if (convertView == null) { LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = mInflater.inflate(R.layout.list_item, null); } TextView expListTextView = (TextView) convertView.findViewById(R.id.expandedListItem); expListTextView.setText(expListText); return convertView; } @Override public int getChildrenCount(int listPosition) { return expListDetail.get( expListTitle.get(listPosition) ).size(); } @Override public Object getGroup(int listPosition) { return expListTitle.get(listPosition); } @Override public int getGroupCount() { return expListTitle.size(); } @Override public long getGroupId(int listPosition) { return listPosition; } @Override public View getGroupView(int listPosition, boolean isExpanded, View convertView, ViewGroup parent) { // получаем родительский элемент String listTitle = (String) getGroup(listPosition); if (convertView == null) { LayoutInflater mInflater = (LayoutInflater) context. getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = mInflater.inflate(R.layout.list_group, null); } TextView listTitleTextView = (TextView) convertView .findViewById(R.id.listTitle); listTitleTextView.setTypeface(null, Typeface.BOLD); listTitleTextView.setText(listTitle); return convertView; } @Override public boolean hasStableIds() { return false; } @Override public boolean isChildSelectable(int listPosition, int expandedListPosition) { return true; } } |
Ну и собираем это все в главном классе MainActivity, в котором будет отображен наш ExpandableListView
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package ua.com.prologistic.expandablelistviewapp; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class MainActivity extends AppCompatActivity { ExpandableListView expListView; ExpandableListAdapter expListAdapter; List<String> expListTitle; HashMap<String, List<String>> expListDetail; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); expListView = (ExpandableListView) findViewById(R.id.expListView); expListDetail = ListData.loadData(); expListTitle = new ArrayList<>(expListDetail.keySet()); expListAdapter = new ListAdapter(this, expListTitle, expListDetail); expListView.setAdapter(expListAdapter); expListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() { @Override public void onGroupExpand(int groupPosition) { Toast.makeText(getApplicationContext(), expListTitle.get(groupPosition) + " Список раскрыт.", Toast.LENGTH_SHORT).show(); } }); expListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() { @Override public void onGroupCollapse(int groupPosition) { Toast.makeText(getApplicationContext(), expListTitle.get(groupPosition) + " Список скрыт.", Toast.LENGTH_SHORT).show(); } }); expListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { Toast.makeText(getApplicationContext(), expListTitle.get(groupPosition) + " : " + expListDetail.get(expListTitle.get(groupPosition)) .get(childPosition), Toast.LENGTH_SHORT).show(); return false; } }); } } |
Как видно и кода выше, мы реализовали описанные в начале статьи интерфейсы и показали элемент, на который нажал пользователь в сплывающем окне Toast.
Результат создания ExpandableListView
в Android:
Следите за обновлениями и подписывайтесь на новые статьи!