Some of the new features in the project some time ago require three levels of nested list implementation, although it is ugly on mobile, but requirements are requirements. I was trying to nest all kinds of views, and then I found that the system had an ExpandableListView. I just used it.

In theory, there is no essential difference between level 2 and level 3 nesting of ExpandableListView. If the child of level 2 nesting is replaced by a new ExpandableListView, level 3 nesting can be implemented.

With the idea of ExpandableListView on the three layer nesting directly hand implementation

So my requirement here is that some of the data is only level 2, and some of the data is level 3. If your requirement is only three levels, there is no need to consider the mix of three and two levels.

rendering

ExpandableListView

ExpandableListView is an official control that displays collapsed lists. Official document straight link

Its basic usage is as follows

Basic usage

The basic usage of ExpandableListView is very simple, it is essentially a ListView, so the usage is similar, so I won’t cover it here.

If necessary, you can refer to the rookie tutorial ExpandableListView basic usage below to get started.

Layout file

First, four layout files are required because of tertiary nesting, one layout file is required for the Activity page itself, and then three layout files for tertiary nesting.

  • Activity layout file
<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ExpandableListView android:id="@+id/expand_view" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="#00000000" android:childIndicator="@color/white" android:divider="@null" android:fadeScrollbars="false" android:groupIndicator="@null" android:listSelector="#00000000" android:scrollbars="none"  /> </LinearLayout>Copy the code

We can control some of the styles with the default properties of ExpandableListView, pasted here with the properties of the beginner tutorial

  • Level 1 menu layout file
<? The XML version = "1.0" encoding = "utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="44dp" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@drawable/chapter_gradient_group"> <TextView android:id="@+id/adapter_title" android:layout_width="0dp" android:layout_height="match_parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" android:layout_marginHorizontal="10dp" android:paddingStart="20dp" android:singleLine="true" android:ellipsize="end" android:text="@string/groupName" android:textColor="@color/white" android:textSize="16sp" android:gravity="start|center_vertical" /> </androidx.constraintlayout.widget.ConstraintLayout>Copy the code
  • Level 2 menu layout file
<? The XML version = "1.0" encoding = "utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/chapter_gradient_child"> <TextView android:id="@+id/adapter_child_title" android:layout_width="match_parent" android:layout_height="40dp" android:ellipsize="end" android:gravity="start|center_vertical" android:paddingStart="30dp" android:paddingEnd="10dp" android:singleLine="true" android:text="@string/childName" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>Copy the code
  • Three-level menu layout file
<? The XML version = "1.0" encoding = "utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/chapter_gradient_grandson"> <TextView android:id="@+id/adapter_grandson_title" android:layout_width="match_parent" android:layout_height="40dp" android:ellipsize="end" android:gravity="start|center_vertical" android:paddingStart="40dp" android:paddingEnd="10dp" android:singleLine="true" android:text="@string/grandsonName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>Copy the code

Adapter

ExpandableListView inherits from ListView, so we need adapters, level 3 nesting, we need two adapters.

Here, need to say why are two Adapter, the Adapter ExpandableListView inherited from BaseExpandableListAdapter. You need to override getGroupView and getChildView. The View in these two methods is the layout of the parent menu and the layout file of the child menu respectively.

So our three level menu layout files above are connected through two Adapters. These are the Adapter for the first-level menu and the Adapter for the third-level menu.

The following is a detailed description of the two Adapters. Notes have been made on the points needing attention. Please read the notes carefully

  • The first-level menu Adapter is most notable for its getChildView method and getChildrenCount. Because some data does not contain tertiary menus, and some do. In addition, this place needs to handle the nested ExpandableListView.
@author StarryRivers */ public Class ChapterExpandableAdapter extends BaseExpandableListAdapter { ... @override public int getGroupCount() {return fatherchapterlist.size (); } @override public int getChildrenCount(int groupPosition) {return 1; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { GroupViewHolder groupHolder; If (convertView == null) {convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_expandable_group_view, parent, false); groupHolder = new GroupViewHolder(); groupHolder.groupTitle = convertView.findViewById(R.id.adapter_title); convertView.setTag(groupHolder); } else { groupHolder = (GroupViewHolder) convertView.getTag(); } / / set the title groupHolder. GroupTitle. SetText (fatherChapterList. Get (groupPosition). GetName ()); return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { if (convertView == null) { convertView = new CustomExpandableListView(context); } CustomExpandableListView expandableListView = (CustomExpandableListView) convertView; / / load the child Adapter ChapterExpandableLowAdapter lowAdapter = new ChapterExpandableLowAdapter (context); lowAdapter.setTotalList(fatherChapterList.get(groupPosition).getSec()); expandableListView.setAdapter(lowAdapter); if (fatherChapterList.get(groupPosition).getSec().get(childPosition).getThird().size() == 0) { expandableListView.setGroupIndicator(null); } // Parent of itself, Is equivalent to level 3 directory children listening expandableListView. SetOnGroupClickListener ((parent12, v, groupPosition12, id) - > {/ / if the third size is zero, Means there is no three-level menu if (fatherChapterList! = null && fatherChapterList.size() > 0 && Fatherchapterlist.get (groupPosition).getSec().get(groupPosition12).getThird().size() == 0) {// TODO business process} The event distribution mechanism continues to want to pass return false; }); expandableListView.setOnChildClickListener((parent1, v, groupPosition1, childPosition1, Id) -> {return true; }); return expandableListView; } /** * sublists are optional. If false, the subitems cannot trigger click events. Default value: false * * @param groupPosition groupPosition * @param childPosition childPosition * @return result */ @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } /** * parent menu ViewHolder */ static class GroupViewHolder {TextView groupTitle; }}Copy the code
  • The third level menu Adapter is the same as the normal second level nested Adapter, nothing special, so it just lists the getGroupView and getChildView methods. Okay
@Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { ChapterExpandableLowAdapter.GroupViewHolder groupHolder; If (convertView == null) {convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_expandable_child_view, parent, false); groupHolder = new ChapterExpandableLowAdapter.GroupViewHolder(); groupHolder.groupTitle = convertView.findViewById(R.id.adapter_child_title); convertView.setTag(groupHolder); } else { groupHolder = (ChapterExpandableLowAdapter.GroupViewHolder) convertView.getTag(); } / / set the title groupHolder. GroupTitle. SetText (childChapterList. Get (groupPosition). GetName ()); return convertView; } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { ChapterExpandableLowAdapter.ChildViewHolder childHolder; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_expandable_grandson_view, parent, false); childHolder = new ChapterExpandableLowAdapter.ChildViewHolder(); childHolder.childTitle = convertView.findViewById(R.id.adapter_grandson_title); convertView.setTag(childHolder); } else { childHolder = (ChapterExpandableLowAdapter.ChildViewHolder) convertView.getTag(); } if (childChapterList.get(groupPosition).getThird() ! = null && childChapterList.get(groupPosition).getThird().size() > 0) { childHolder.childTitle.setText(childChapterList.get(groupPosition).getThird().get(childPosition).getName()); } return convertView; }Copy the code

use

Once we have completed the above steps, the final step is to use it in the Activity. It’s super easy to use

Set Adapter for ExpandableListView

@BindView(R.id.chapter_elv) ExpandableListView chapterExpandable; private ChapterExpandableAdapter chapterExpandableAdapter; . chapterExpandableAdapter = new ChapterExpandableAdapter(this); chapterExpandable.setAdapter(chapterExpandableAdapter);Copy the code

Write in the last

Oh, I forgot, because it’s tertiary nesting, so ExpandableListView needs to be rewritten to redraw the height. Otherwise, the page display will be incomplete or incomplete.

Complete code in Git, interested can see, with Star food more fragrant!