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

如何实现包含多个GridView的Fragment页面整体连贯滚动

这是ScrollView与可滚动控件GridView嵌套的典型滚动冲突问题,系统默认无法正确测量GridView的完整高度,才会出现你遇到的两种异常表现,可按以下步骤解决:

步骤1:修正布局XML问题

你现有XML存在语法错误:ScrollView标签误加了自闭合标记/>,需先改为正常开放标签,同时把原生ScrollView替换为NestedScrollView,并给所有GridView关闭嵌套滚动,核心修改后的布局如下:

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable
            name="viewModel"
            type="com.volvo.mechatronicsstudio.showcarcontrol.ui.main.viewmodels.MainViewModel" />

    </data>

    <!-- 替换为NestedScrollView,删除错误的自闭合标记和nestedScrollingEnabled属性 -->
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/main"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:context=".ui.main.fragments.MainFragment">

            <!--        HANDLES & DOORS-->
            <TextView
                android:id="@+id/tv_title_handles_doors"
                style="@style/Widget.Theme.ShowcarControl.text.title.category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Handles and Doors"
                android:layout_marginTop="10dp"
                android:layout_marginStart="20dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <!-- 所有GridView添加android:nestedScrollingEnabled="false" -->
            <GridView
                android:id="@+id/gridview_handles_doors"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:columnWidth="120dp"
                android:numColumns="auto_fit"
                android:stretchMode="spacingWidth"
                android:horizontalSpacing="30dp"
                android:verticalSpacing="30dp"
                android:layout_margin="20dp"
                android:nestedScrollingEnabled="false"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_title_handles_doors" />

            <!--        LIGHTS-->
            <TextView
                android:id="@+id/tv_title_lights"
                style="@style/Widget.Theme.ShowcarControl.text.title.category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Lights"
                android:layout_marginTop="15dp"
                android:layout_marginStart="20dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/gridview_handles_doors" />

            <GridView
                android:id="@+id/gridview_lights"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:columnWidth="120dp"
                android:numColumns="auto_fit"
                android:stretchMode="spacingWidth"
                android:horizontalSpacing="30dp"
                android:verticalSpacing="30dp"
                android:layout_margin="20dp"
                android:nestedScrollingEnabled="false"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_title_lights" />

            <!--        DECOR-->
            <TextView
                android:id="@+id/tv_title_decor"
                style="@style/Widget.Theme.ShowcarControl.text.title.category"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text=""
                android:layout_marginTop="15dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/gridview_lights" />

            <GridView
                android:id="@+id/gridview_decor"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:columnWidth="120dp"
                android:numColumns="auto_fit"
                android:stretchMode="spacingWidth"
                android:horizontalSpacing="30dp"
                android:verticalSpacing="30dp"
                android:layout_margin="20dp"
                android:nestedScrollingEnabled="false"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_title_decor" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.core.widget.NestedScrollView>
</layout>

步骤2:添加GridView高度动态计算工具方法

系统默认无法根据子项数量计算GridView完整高度,需手动实现测量逻辑:

Kotlin扩展函数版本

fun GridView.setGridViewFullHeight() {
    val adapter = this.adapter ?: return
    val itemCount = adapter.count
    if (itemCount == 0) return
    val columns = this.numColumns
    // 计算总共有多少行
    val rows = if (itemCount % columns == 0) itemCount / columns else (itemCount / columns) + 1
    var totalHeight = 0
    // 累加每行高度
    for (i in 0 until rows) {
        val itemView = adapter.getView(i * columns, null, this)
        itemView.measure(0, 0)
        totalHeight += itemView.measuredHeight
    }
    // 加上垂直间距和padding
    totalHeight += verticalSpacing * (rows - 1) + paddingTop + paddingBottom
    // 重设高度
    layoutParams.height = totalHeight
    requestLayout()
}

Java静态方法版本

public static void setGridViewFullHeight(GridView gridView) {
    ListAdapter adapter = gridView.getAdapter();
    if (adapter == null || adapter.getCount() == 0) return;
    int columns = gridView.getNumColumns();
    int itemCount = adapter.getCount();
    int rows = itemCount % columns == 0 ? itemCount / columns : (itemCount / columns) + 1;
    int totalHeight = 0;
    for (int i = 0; i < rows; i++) {
        View itemView = adapter.getView(i * columns, null, gridView);
        itemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        totalHeight += itemView.getMeasuredHeight();
    }
    totalHeight += gridView.getVerticalSpacing() * (rows - 1) + gridView.getPaddingTop() + gridView.getPaddingBottom();
    ViewGroup.LayoutParams params = gridView.getLayoutParams();
    params.height = totalHeight;
    gridView.setLayoutParams(params);
    gridView.requestLayout();
}

步骤3:数据更新时调用高度计算方法

每次通过MQTT拿到JSON数据、更新完GridView的适配器数据后,调用上述方法即可:

// 示例:更新完适配器数据后调用
gridview_handles_doors.adapter?.notifyDataSetChanged()
gridview_handles_doors.setGridViewFullHeight()

gridview_lights.adapter?.notifyDataSetChanged()
gridview_lights.setGridViewFullHeight()

gridview_decor.adapter?.notifyDataSetChanged()
gridview_decor.setGridViewFullHeight()

可选优化方案

如果网格项数量较多,更推荐使用单个RecyclerView作为整个页面的容器,通过多ItemType实现「标题+网格区域」的结构,网格区域用GridLayoutManager设置对应列数,不需要嵌套滚动容器,性能更优,也不会出现滚动冲突问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:24:08