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

ListView中SwipeDelegate点击展开RowLayout的平滑动画实现问题

QML实现SwipeDelegate展开/收起的平滑动画

问题描述

使用SwipeDelegate作为ListView的代理,contentItem嵌套了包含两个RowLayout的ColumnLayout。点击代理时第二个RowLayout可以正常展开,但给ColumnLayout添加Behaviour on height { NumberAnimation {} }后,平滑动画未生效,如何解决?

原代码:

ListView {
    id: myView
    visible: count > 0

    clip: true
    boundsBehavior: Flickable.StopAtBounds
    model: mymodel

    Component.onCompleted: {
        Layout.preferredHeight = Qt.binding(function() {
            return myView.contentHeight
        })
    }

    spacing: 3

    delegate: SwipeDelegate {
        id: delegate
        width: myView.width
        hoverEnabled: false

        property bool expanded: false

        onClicked: {
            expanded = !expanded
        }


        contentItem: ColumnLayout {

            Behavior on height { NumberAnimation {} }

            RowLayout {
                Layout.fillWidth: true

                CheckBox {
                    id: chkSel
                    Layout.fillHeight: true
                    checked: model.sel
                    padding: 0
                    rightPadding: 5
                    spacing: 0
                }

                Label {
                    Layout.fillWidth: true
                    font.pointSize: 14
                    font.bold: true
                    text: model.nr
                }

                Image {
                    id: swipeTipArrow
                    sourceSize.height: parent.height * 0.9
                    fillMode: Image.PreserveAspectFit
                    opacity: 0
                    source: "left.svg"
                }
            }

            RowLayout {
                Layout.fillWidth: true
                visible: delegate.expanded

                Label {
                    Layout.fillWidth: true
                    font.pointSize: 12
                    text: model.nr2
                }
            }
        }
}

解决方案

问题核心是直接切换visible属性是瞬时状态变化,不会触发布局高度的平滑过渡,且ColumnLayout的height由布局系统自动计算,直接给它加Behavior无法正确触发动画。可以通过控制布局的高度属性来实现平滑效果,以下是两种可行方案:

方案1:给SwipeDelegate的height添加动画

修改代理的height属性动画,让整个代理的高度随内容展开/收起平滑变化:

ListView {
    id: myView
    visible: count > 0

    clip: true
    boundsBehavior: Flickable.StopAtBounds
    model: mymodel

    Component.onCompleted: {
        Layout.preferredHeight = Qt.binding(function() {
            return myView.contentHeight
        })
    }

    spacing: 3

    delegate: SwipeDelegate {
        id: delegate
        width: myView.width
        hoverEnabled: false

        property bool expanded: false

        // 给代理高度添加平滑动画
        Behavior on height { NumberAnimation { duration: 300 } }

        onClicked: {
            expanded = !expanded
        }

        contentItem: ColumnLayout {
            Layout.fillWidth: true

            RowLayout {
                Layout.fillWidth: true

                CheckBox {
                    id: chkSel
                    Layout.fillHeight: true
                    checked: model.sel
                    padding: 0
                    rightPadding: 5
                    spacing: 0
                }

                Label {
                    Layout.fillWidth: true
                    font.pointSize: 14
                    font.bold: true
                    text: model.nr
                }

                Image {
                    id: swipeTipArrow
                    sourceSize.height: parent.height * 0.9
                    fillMode: Image.PreserveAspectFit
                    opacity: 0
                    source: "left.svg"
                }
            }

            RowLayout {
                Layout.fillWidth: true
                // 用preferredHeight替代visible,控制高度从0过渡到固有高度
                Layout.preferredHeight: delegate.expanded ? implicitHeight : 0
                clip: true // 防止高度为0时内容溢出显示

                Label {
                    Layout.fillWidth: true
                    font.pointSize: 12
                    text: model.nr2
                }
            }
        }
    }
}

方案2:给第二个RowLayout的布局高度添加动画

直接给第二个RowLayout的Layout.preferredHeight属性添加动画,单独控制它的展开/收起:

// 仅修改第二个RowLayout部分
RowLayout {
    Layout.fillWidth: true
    Layout.preferredHeight: delegate.expanded ? implicitHeight : 0
    clip: true

    // 给布局高度添加动画
    Behavior on Layout.preferredHeight { NumberAnimation { duration: 300 } }

    Label {
        Layout.fillWidth: true
        font.pointSize: 12
        text: model.nr2
    }
}

关键说明

  • 避免直接使用visible控制显示/隐藏,改用Layout.preferredHeight从0过渡到implicitHeight(控件固有高度),让布局系统感知到高度变化
  • 添加clip: true防止高度为0时内容仍显示
  • 动画时长可根据需求调整duration参数(单位:毫秒)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 15:05:11