You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何使用onItemClickListener实现ListView点击展开效果?

实现可展开的工程列表方案

嘿,这个需求我之前刚好做过,给你两个靠谱的实现思路,一步步来就能搞定:

方案一:用系统自带的ExpandableListView(最省心)

系统已经封装好了展开/收起的逻辑,直接用就行,步骤如下:

  1. 替换布局组件
    把原来布局里的ListView换成ExpandableListView,比如:
<ExpandableListView
    android:id="@+id/expandable_project_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  1. 整理解析后的数据
    自定义一个Project实体类,把XML里解析出的标题和工程信息存起来:
public class Project {
    private String title; // 比如"A78 Seamill"
    private String projectDetails; // 对应的工程信息

    public Project(String title, String projectDetails) {
        this.title = title;
        this.projectDetails = projectDetails;
    }

    // 别忘了添加getter和setter方法
    public String getTitle() { return title; }
    public String getProjectDetails() { return projectDetails; }
}

解析XML的时候,把每个项目都封装成Project对象,存入List<Project>

  1. 自定义ExpandableListAdapter
    继承BaseExpandableListAdapter,实现组视图(标题)和子视图(工程信息)的渲染:
public class ProjectExpandAdapter extends BaseExpandableListAdapter {
    private Context mContext;
    private List<Project> mProjectList;

    public ProjectExpandAdapter(Context context, List<Project> projectList) {
        mContext = context;
        mProjectList = projectList;
    }

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

    @Override
    public int getChildrenCount(int groupPosition) {
        return 1; // 每个标题对应一个工程信息子项
    }

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

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

    @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) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_project_group, parent, false);
        }
        TextView titleTv = convertView.findViewById(R.id.tv_group_title);
        ImageView arrowIv = convertView.findViewById(R.id.iv_group_arrow);
        
        Project project = (Project) getGroup(groupPosition);
        titleTv.setText(project.getTitle());
        // 切换箭头方向,提示展开/收起状态
        arrowIv.setRotation(isExpanded ? 180 : 0);
        return convertView;
    }

    // 渲染工程信息视图
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_project_child, parent, false);
        }
        TextView detailTv = convertView.findViewById(R.id.tv_child_detail);
        detailTv.setText((String) getChild(groupPosition, childPosition));
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false; // 子项不需要可点击,只做展示
    }
}
  1. 在MainActivity中绑定数据
    解析完XML拿到List<Project>后,把Adapter设置给ExpandableListView:
// 假设你已经完成XML解析,得到projectList
ExpandableListView expandableListView = findViewById(R.id.expandable_project_list);
ProjectExpandAdapter adapter = new ProjectExpandAdapter(this, projectList);
expandableListView.setAdapter(adapter);

// 可选:监听展开/收起事件,做额外逻辑
expandableListView.setOnGroupExpandListener(groupPosition -> {
    // 比如记录展开的位置,或者做一些UI调整
});

对应的组视图布局item_project_group.xml

<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/tv_group_title"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"/>

    <ImageView
        android:id="@+id/iv_group_arrow"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@drawable/ic_arrow_down"/>
</LinearLayout>

子视图布局item_project_child.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:background="#f5f5f5">

    <TextView
        android:id="@+id/tv_child_detail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"/>
</LinearLayout>

方案二:自定义可展开的ListView Item(更灵活)

如果不想用ExpandableListView,想完全自定义展开逻辑,也可以这么做:

  1. 自定义Item布局
    布局里包含标题栏和默认隐藏的工程信息区域:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <!-- 标题栏 -->
    <LinearLayout
        android:id="@+id/layout_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:orientation="horizontal"
        android:gravity="center_vertical">

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

        <ImageView
            android:id="@+id/iv_item_arrow"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@drawable/ic_arrow_down"/>
    </LinearLayout>

    <!-- 工程信息区域,默认隐藏 -->
    <LinearLayout
        android:id="@+id/layout_detail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:background="#f5f5f5"
        android:visibility="gone">

        <TextView
            android:id="@+id/tv_item_detail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="14sp"/>
    </LinearLayout>
</LinearLayout>
  1. 修改Project类,添加展开状态
public class Project {
    private String title;
    private String projectDetails;
    private boolean isExpanded; // 记录当前Item是否展开

    // 构造方法、getter和setter
    public boolean isExpanded() { return isExpanded; }
    public void setExpanded(boolean expanded) { isExpanded = expanded; }
}
  1. 自定义ListView Adapter
    getView方法中处理标题点击事件,切换工程信息区域的可见性,还可以加动画让交互更流畅:
public class ProjectListAdapter extends BaseAdapter {
    private Context mContext;
    private List<Project> mProjectList;

    public ProjectListAdapter(Context context, List<Project> projectList) {
        mContext = context;
        mProjectList = projectList;
    }

    @Override
    public int getCount() {
        return mProjectList.size();
    }

    @Override
    public Object getItem(int position) {
        return mProjectList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_project_custom, parent, false);
            holder = new ViewHolder();
            holder.titleLayout = convertView.findViewById(R.id.layout_title);
            holder.titleTv = convertView.findViewById(R.id.tv_item_title);
            holder.arrowIv = convertView.findViewById(R.id.iv_item_arrow);
            holder.detailLayout = convertView.findViewById(R.id.layout_detail);
            holder.detailTv = convertView.findViewById(R.id.tv_item_detail);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Project project = mProjectList.get(position);
        holder.titleTv.setText(project.getTitle());
        holder.detailTv.setText(project.getProjectDetails());

        // 恢复之前的展开状态
        boolean isExpanded = project.isExpanded();
        holder.detailLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
        holder.arrowIv.setRotation(isExpanded ? 180 : 0);

        // 标题栏点击事件
        holder.titleLayout.setOnClickListener(v -> {
            boolean newState = !isExpanded;
            project.setExpanded(newState);
            // 切换可见性并添加动画
            if (newState) {
                expandView(holder.detailLayout);
                holder.arrowIv.animate().rotation(180).setDuration(300).start();
            } else {
                collapseView(holder.detailLayout);
                holder.arrowIv.animate().rotation(0).setDuration(300).start();
            }
        });

        return convertView;
    }

    // 展开动画
    private void expandView(View view) {
        view.setVisibility(View.VISIBLE);
        view.animate().alpha(1).setDuration(300).start();
    }

    // 收起动画(动画结束后隐藏)
    private void collapseView(View view) {
        view.animate().alpha(0).setDuration(300).setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {}

            @Override
            public void onAnimationEnd(Animator animation) {
                view.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationCancel(Animator animation) {}

            @Override
            public void onAnimationRepeat(Animator animation) {}
        }).start();
    }

    static class ViewHolder {
        LinearLayout titleLayout;
        TextView titleTv;
        ImageView arrowIv;
        LinearLayout detailLayout;
        TextView detailTv;
    }
}
  1. 在MainActivity中绑定Adapter
    和原来的ListView用法一样,把解析好的projectList传给Adapter即可:
ListView listView = findViewById(R.id.project_list);
ProjectListAdapter adapter = new ProjectListAdapter(this, projectList);
listView.setAdapter(adapter);

注意事项

  • 解析XML时要确保每个标题和对应的工程信息一一对应,避免数据错乱;
  • 如果数据量较大,要注意Adapter的视图复用,避免内存泄漏和性能问题;
  • 动画可以提升用户体验,不需要的话可以直接切换visibility即可;
  • 如果需要保存展开状态(比如旋转屏幕后不丢失),可以把状态存在ViewModel或者用onSaveInstanceState保存。

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

火山引擎 最新活动