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
相关产品推荐
相关产品推荐

