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

Android Studio中如何构建带图标的侧边栏及下拉子项?

嘿,我刚好之前折腾过类似的侧边栏需求,给你两个实用的解决思路,应该能完美匹配你要的「带图标+下拉子项」效果!


方案一:用官方NavigationView(适配Android Studio模板布局)

Android Studio默认的侧边栏模板大多用的是NavigationView,它本身就支持下拉子项+图标,不用额外造轮子,步骤超简单:

  1. 创建侧边栏菜单文件
    在res/menu目录下新建nav_menu.xml,通过<item>嵌套<menu>实现下拉子项,每个项都可以用android:icon设置图标:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 带下拉子项的主菜单 -->
    <item
        android:id="@+id/nav_main_1"
        android:title="我的项目"
        android:icon="@drawable/ic_folder">
        <menu>
            <item
                android:id="@+id/nav_sub_1"
                android:title="未完成"
                android:icon="@drawable/ic_unfinished"/>
            <item
                android:id="@+id/nav_sub_2"
                android:title="已完成"
                android:icon="@drawable/ic_finished"/>
        </menu>
    </item>

    <!-- 普通单级菜单 -->
    <item
        android:id="@+id/nav_main_2"
        android:title="设置"
        android:icon="@drawable/ic_setting"/>
</menu>
  1. 在布局中引用菜单
    找到模板里的NavigationView,添加app:menu属性关联刚才的菜单文件:
<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"
    app:menu="@menu/nav_menu"/>
  1. 处理点击事件
    NavigationView会自动处理主项的展开/折叠,你只需要监听点击逻辑:
NavigationView navView = findViewById(R.id.nav_view);
navView.setNavigationItemSelectedListener(item -> {
    switch (item.getItemId()) {
        case R.id.nav_main_1:
            // 主菜单点击逻辑(可选,比如手动控制展开状态)
            break;
        case R.id.nav_sub_1:
            // 子项1的业务逻辑
            break;
        case R.id.nav_sub_2:
            // 子项2的业务逻辑
            break;
    }
    // 点击后关闭侧边栏
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
});

这个方案的优势是代码少、符合Material Design规范,完全适配模板布局,不用自己写展开折叠逻辑。


方案二:自定义ExpandableListView(高度定制需求)

如果觉得NavigationView的样式不够灵活,比如要调整图标大小、添加额外控件,就用ExpandableListView自定义布局:

  1. 准备主项和子项的布局文件
  • 主项布局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">

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

    <TextView
        android:id="@+id/group_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:textSize="16sp"/>
</LinearLayout>
  • 子项布局list_child_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="12dp"
    android:paddingStart="32dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <ImageView
        android:id="@+id/child_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:src="@drawable/ic_child"/>

    <TextView
        android:id="@+id/child_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:textSize="14sp"/>
</LinearLayout>
  1. 自定义适配器加载布局
    继承BaseExpandableListAdapter,在getGroupView和getChildView中绑定图标和文字:
public class CustomExpandAdapter extends BaseExpandableListAdapter {
    private Context mContext;
    private List<String> mGroupTitles;
    private Map<String, List<ChildItem>> mChildData;

    // 子项数据模型:存图标ID和标题
    public static class ChildItem {
        int iconRes;
        String title;
        public ChildItem(int iconRes, String title) {
            this.iconRes = iconRes;
            this.title = title;
        }
    }

    public CustomExpandAdapter(Context context, List<String> groupTitles, Map<String, List<ChildItem>> childData) {
        mContext = context;
        mGroupTitles = groupTitles;
        mChildData = childData;
    }

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

    @Override
    public int getChildrenCount(int groupPosition) {
        return mChildData.get(mGroupTitles.get(groupPosition)).size();
    }

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

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return mChildData.get(mGroupTitles.get(groupPosition)).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) {
        String groupTitle = (String) getGroup(groupPosition);
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.list_group_item, parent, false);
        }
        TextView tvTitle = convertView.findViewById(R.id.group_title);
        ImageView ivIcon = convertView.findViewById(R.id.group_icon);
        tvTitle.setText(groupTitle);
        // 可选:根据展开状态切换图标(比如展开用向下箭头,折叠用向右箭头)
        ivIcon.setImageResource(isExpanded ? R.drawable.ic_expand : R.drawable.ic_collapse);
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        ChildItem childItem = (ChildItem) getChild(groupPosition, childPosition);
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.list_child_item, parent, false);
        }
        TextView tvTitle = convertView.findViewById(R.id.child_title);
        ImageView ivIcon = convertView.findViewById(R.id.child_icon);
        tvTitle.setText(childItem.title);
        ivIcon.setImageResource(childItem.iconRes);
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}
  1. 在Activity中初始化列表
ExpandableListView expandListView = findViewById(R.id.expand_list);
// 准备测试数据
List<String> groupTitles = new ArrayList<>();
groupTitles.add("我的收藏");
groupTitles.add("最近浏览");

Map<String, List<CustomExpandAdapter.ChildItem>> childData = new HashMap<>();
List<CustomExpandAdapter.ChildItem> collectList = new ArrayList<>();
collectList.add(new CustomExpandAdapter.ChildItem(R.drawable.ic_article, "技术文章"));
collectList.add(new CustomExpandAdapter.ChildItem(R.drawable.ic_video, "教学视频"));
childData.put(groupTitles.get(0), collectList);

// 设置适配器
CustomExpandAdapter adapter = new CustomExpandAdapter(this, groupTitles, childData);
expandListView.setAdapter(adapter);

// 监听子项点击
expandListView.setOnChildClickListener((parent, v, groupPos, childPos, id) -> {
    CustomExpandAdapter.ChildItem item = (CustomExpandAdapter.ChildItem) adapter.getChild(groupPos, childPos);
    // 子项点击逻辑
    return true;
});

这个方案适合需要高度自定义UI的场景,比如要给图标加圆角、添加 badge 标记等。


总结

  • 如果只是基础需求,优先用方案一的NavigationView,省心省力还符合官方规范;
  • 如果要做特殊样式,再用方案二的自定义ExpandableListView。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:27:15