Android uses expandablelistview to implement three-tier nested folding menus

Time:2022-1-3

Some pages in the new functions of the project some time ago need to be implemented by three-tier nested lists. Although this is ugly on the mobile end, the demand is the demand.
Originally, I wanted to nest with various views, and then found that the system has an expandablelistview. Just use it.

Theoretically, there is no essential difference between two-level nesting and three-level nesting of expandablelistview. If the child of two-level nesting is replaced with a new expandablelistview, three-level nesting can be realized.

With the idea, the three-tier nesting of expandablelistview can be implemented directly

Here, my requirement is that some data are only level 2 and some data are level 3. If your demand is only three levels, you don’t need to consider the situation of three-level and two-level mixing. The following describes how to deal with it.

design sketch

ExpandableListView

Expandablelistview is an officially provided control that can display collapsed lists.

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. I won’t introduce it here.

Let’s get to the point.

Layout file

First, because it is three-level nesting, four layout files are required. The activity page itself needs one layout file, and then three layout files are three-level nesting.

Activity layout file


<?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>

We can control some styles through the default properties of expandablelistview. Here are the attribute pictures of rookie tutorial

First level menu layout file


<?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>

Secondary menu layout file


<?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>

Three level menu layout file


<?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>

Adapter

As mentioned above, expandablelistview inherits from listview, so we need adapters, three-level nesting, and we need two adapters.

It is necessary to explain why there are two adapters. The expandablelistview adapter inherits from the baseexpandablelistadapter. Getgroupview and getchildview need to be overridden. The views in the two methods respectively inflate the layout of the parent menu and the layout file of the child menu.

Therefore, the above three levels of menu layout files are connected through two adapters. They are respectively the adapter of the first level menu and the adapter of the third level menu.

The following is a detailed description of the two adapters. Notes have been made where you need to pay attention. Please read the notes carefully

First level menu adapter
The most noteworthy are the getchildview method and getchildrencount of the adapter. Because some data do not contain three-level menus, some contain three-level menus. The listexpandview needs to be nested in other places.

/**
 *First level adapter of three-level folding menu
 *
 * @author StarryRivers
 */
public class ChapterExpandableAdapter extends BaseExpandableListAdapter {
    
    ...

    @Override
    public int getGroupCount() {
        //Parent menu length
        return fatherChapterList.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        //Submenu length, nested, so only 1 can be returned
        return 1;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        GroupViewHolder groupHolder;
        //Reuse old view processing as much as possible
        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 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 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);
        }
        //The parent of itself is equivalent to the child listening of the three-level directory
        expandableListView.setOnGroupClickListener((parent12, v, groupPosition12, id) -> {
            //If the size of the third layer is 0, it means that there is no three-level menu
            if (fatherChapterList != null && fatherChapterList.size() > 0 && fatherChapterList.get(groupPosition).getSec().get(groupPosition12).getThird().size() == 0) {
                //Todo business processing
            }
            //There is third level data, and the event distribution mechanism continues to want to transfer it
            return false;
        });
        expandableListView.setOnChildClickListener((parent1, v, groupPosition1, childPosition1, id) -> {
            //Business processing of three-level menu
            return true;
        });
        return expandableListView;
    }

    /**
     *Whether the sub list is optional. If false, the sub item cannot trigger a click event. The default is false
     *
     * @param groupPosition groupPosition
     * @param childPosition childPosition
     * @return result
     */
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    /**
     *Viewholder of parent menu
     */
    static class GroupViewHolder {
        TextView groupTitle;
    }
}

Three level menu adapter
The adapter of the three-level menu is the same as that of the ordinary two-level nesting. There is nothing to pay attention to, so only the getgroupview and getchildview method codes are listed

@Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        ChapterExpandableLowAdapter.GroupViewHolder groupHolder;
        //Reuse old view processing as much as possible
        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 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;
    }

use

After we have completed the above steps, we will finally use it in the activity. The method of use is super simple

Just set the adapter for expandablelistview


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

Write at the end

Because it is a three-level nesting, the expandablelistview needs to be rewritten to redraw the height. Otherwise, the page display will be incomplete or incomplete.

The above is the details of Android using expandablelistview to realize three-tier nested folding menu. For more information about Android expandablelistview three-tier nested folding menu, please pay attention to other related articles of developeppaer!

Recommended Today

Vue2 technology finishing 3 – Advanced chapter – update completed

3. Advanced chapter preface Links to basic chapters:https://www.cnblogs.com/xiegongzi/p/15782921.html Link to component development:https://www.cnblogs.com/xiegongzi/p/15823605.html 3.1. Custom events of components 3.1.1. Binding custom events There are two implementation methods here: one is to use v-on with vuecomponent$ Emit implementation [PS: this method is a little similar to passing from child to parent]; The other is to use ref […]