You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Android健身APP的Navigation Drawer添加展开/收起功能?

实现Navigation Drawer中训练分类的展开/收起功能

我来帮你一步步搞定这个需求!其实用ExpandableListView比普通ListView更适配这种分组展开收起的场景,操作起来也更清晰,下面是具体的实现步骤:

1. 调整抽屉布局(activity_main_drawer.xml)

首先把原来的抽屉菜单布局改成包含ExpandableListView的结构,替换掉原来的menu或普通列表:

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

    <!-- 保留原来的导航头部 -->
    <include layout="@layout/nav_header_main" />

    <!-- 展开/收起列表 -->
    <ExpandableListView
        android:id="@+id/expandable_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:groupIndicator="@null" <!-- 隐藏默认展开箭头,我们自定义 -->
        android:divider="@null"
        android:dividerHeight="0dp"/>

</LinearLayout>

接着修改activity_main.xml,将NavigationView的默认menu替换为上面的自定义布局:

<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <!-- 主内容布局 -->
    <include layout="@layout/app_bar_main" />

    <!-- 抽屉侧栏 -->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main">

        <!-- 引入自定义的ExpandableListView布局 -->
        <include layout="@layout/activity_main_drawer" />

    </com.google.android.material.navigation.NavigationView>

</androidx.drawerlayout.widget.DrawerLayout>

2. 创建数据模型类

新建TrainingGroup.java,用来存储每个训练分类的信息:

public class TrainingGroup {
    private String groupName; // 分类名称(比如"胸部训练")
    private List<String> trainingItems; // 分类下的训练项目
    private boolean isExpanded; // 是否处于展开状态

    public TrainingGroup(String groupName, List<String> trainingItems) {
        this.groupName = groupName;
        this.trainingItems = trainingItems;
        this.isExpanded = false; // 默认收起
    }

    // Getter和Setter方法
    public String getGroupName() { return groupName; }
    public List<String> getTrainingItems() { return trainingItems; }
    public boolean isExpanded() { return isExpanded; }
    public void setExpanded(boolean expanded) { isExpanded = expanded; }
}

3. 自定义ExpandableListView适配器

新建TrainingExpandableAdapter.java,继承自BaseExpandableListAdapter,实现组和子项的视图渲染:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

public class TrainingExpandableAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List<TrainingGroup> trainingGroups;

    public TrainingExpandableAdapter(Context context, List<TrainingGroup> trainingGroups) {
        this.context = context;
        this.trainingGroups = trainingGroups;
    }

    @Override
    public int getGroupCount() {
        return trainingGroups.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        // 组展开时返回子项数量,收起时返回0
        return trainingGroups.get(groupPosition).isExpanded() ? trainingGroups.get(groupPosition).getTrainingItems().size() : 0;
    }

    @Override
    public Object getGroup(int groupPosition) {
        return trainingGroups.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return trainingGroups.get(groupPosition).getTrainingItems().get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    // 渲染组视图(分类标题+自定义展开箭头)
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        GroupViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.list_group_item, parent, false);
            holder = new GroupViewHolder();
            holder.groupTitle = convertView.findViewById(R.id.group_title);
            holder.expandIcon = convertView.findViewById(R.id.expand_icon);
            convertView.setTag(holder);
        } else {
            holder = (GroupViewHolder) convertView.getTag();
        }

        TrainingGroup group = trainingGroups.get(groupPosition);
        holder.groupTitle.setText(group.getGroupName());
        // 根据展开状态切换箭头图标
        holder.expandIcon.setImageResource(group.isExpanded() ? R.drawable.ic_expand_less : R.drawable.ic_expand_more);

        return convertView;
    }

    // 渲染子项视图(训练项目)
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ChildViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.list_child_item, parent, false);
            holder = new ChildViewHolder();
            holder.childTitle = convertView.findViewById(R.id.child_title);
            convertView.setTag(holder);
        } else {
            holder = (ChildViewHolder) convertView.getTag();
        }

        String item = trainingGroups.get(groupPosition).getTrainingItems().get(childPosition);
        holder.childTitle.setText(item);

        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true; // 允许子项被点击
    }

    // 组视图的ViewHolder
    private static class GroupViewHolder {
        TextView groupTitle;
        ImageView expandIcon;
    }

    // 子项视图的ViewHolder
    private static class ChildViewHolder {
        TextView childTitle;
    }
}

4. 创建组和子项的布局文件

  • 组布局list_group_item.xml:
<?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:padding="16dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/group_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="16sp"
        android:textStyle="bold"/>

    <ImageView
        android:id="@+id/expand_icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@drawable/ic_expand_more"/>

</LinearLayout>
  • 子项布局list_child_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/child_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:paddingLeft="32dp"
    android:textSize="14sp"/>

5. 在MainActivity中初始化和设置逻辑

修改MainActivity.java,完成数据填充、适配器绑定和点击事件处理:

import androidx.appcompat.app.AppCompatActivity;
import androidx.drawerlayout.widget.DrawerLayout;
import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private DrawerLayout drawerLayout;
    private ExpandableListView expandableListView;
    private TrainingExpandableAdapter adapter;
    private List<TrainingGroup> trainingGroups;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化视图
        drawerLayout = findViewById(R.id.drawer_layout);
        expandableListView = findViewById(R.id.expandable_list);

        // 填充训练分类数据
        initTrainingData();

        // 设置适配器
        adapter = new TrainingExpandableAdapter(this, trainingGroups);
        expandableListView.setAdapter(adapter);

        // 处理组点击事件,切换展开/收起状态
        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                TrainingGroup group = trainingGroups.get(groupPosition);
                group.setExpanded(!group.isExpanded());
                adapter.notifyDataSetChanged(); // 更新视图
                return true; // 拦截默认展开行为,用自定义逻辑
            }
        });

        // 处理训练项目点击事件(根据需求添加跳转逻辑)
        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                String selectedItem = trainingGroups.get(groupPosition).getTrainingItems().get(childPosition);
                // 这里添加点击训练项目后的逻辑,比如跳转到对应训练页面
                return false;
            }
        });
    }

    // 初始化训练分类数据
    private void initTrainingData() {
        trainingGroups = new ArrayList<>();

        // 胸部训练分类
        List<String> chestItems = new ArrayList<>();
        chestItems.add("杠铃卧推");
        chestItems.add("哑铃飞鸟");
        chestItems.add("双杠臂屈伸");
        trainingGroups.add(new TrainingGroup("胸部训练", chestItems));

        // 背部训练分类
        List<String> backItems = new ArrayList<>();
        backItems.add("引体向上");
        backItems.add("高位下拉");
        backItems.add("俯身划船");
        trainingGroups.add(new TrainingGroup("背部训练", backItems));

        // 可继续添加其他训练分类...
    }
}

补充说明

  • 你需要自行准备展开/收起的图标(ic_expand_more和ic_expand_less),可以使用Material Design官方图标或自定义绘制。
  • 子项的点击逻辑可以根据你的需求修改,比如跳转到对应的训练内容页面。

内容的提问来源于stack exchange,提问作者user9313301

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:23:37