如何为Android健身APP的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
相关产品推荐
相关产品推荐

