我想要一个类似于Qt 文档中 TabBar 示例的 GUI :
如果我使用 SwipeView,则在页面之间移动时会有过渡(内容向左或向右移动)。使用 StackLayout,变化是立竿见影的。
我想在页面之间淡入淡出。我将如何使用 StackLayout 做到这一点?这甚至可能还是我需要编写自己的容器元素?
我想要一个类似于Qt 文档中 TabBar 示例的 GUI :
如果我使用 SwipeView,则在页面之间移动时会有过渡(内容向左或向右移动)。使用 StackLayout,变化是立竿见影的。
我想在页面之间淡入淡出。我将如何使用 StackLayout 做到这一点?这甚至可能还是我需要编写自己的容器元素?
滥用StackView::replace()
在代码方面看起来并不算太糟糕:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: window
width: 360
height: 360
visible: true
header: TabBar {
TabButton { text: "Tab1" }
TabButton { text: "Tab2" }
TabButton { text: "Tab3" }
onCurrentIndexChanged: stackView.replace(pages.itemAt(currentIndex))
}
Repeater {
id: pages
model: ["red", "green", "blue"]
delegate: Page {
opacity: index > 0 ? 0 : 1 // ideally "opacity: 0" => QTBUG-60670
background: Rectangle { color: modelData }
Text { text: "Page" + (index + 1); color: "white"; anchors.centerIn: parent }
}
}
StackView {
id: stackView
anchors.fill: parent
replaceEnter: Transition {
NumberAnimation { property: "opacity"; to: 1.0; duration: 500 }
}
replaceExit: Transition {
NumberAnimation { property: "opacity"; to: 0.0; duration: 500 }
}
}
Component.onCompleted: stackView.push(pages.itemAt(0))
}
查看StackView
相关的 pushTransition、popTransition 和 replaceTransition。
一个简单的淡入淡出过渡:
StackView {
delegate: StackViewDelegate {
function transitionFinished(properties)
{
properties.exitItem.opacity = 1
}
pushTransition: StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
}
PropertyAnimation {
target: exitItem
property: "opacity"
from: 1
to: 0
}
}
}
}
如果这不适合您,从头开始实施它仍然是一种选择:
// StackWidget.qml
Item {
width: 300
height: 500
clip: true
property Item activeItem: null
property int activeIndex: -1
onActiveIndexChanged: {
if (activeIndex > -1) {
animout.target = activeItem
activeItem = children[activeIndex]
animin.target = activeItem
}
}
PropertyAnimation {
id: animin
property: "x"
from: target ? -target.width : 0
to: 0
onTargetChanged: if (target) start()
easing.type: Easing.InOutQuad
}
PropertyAnimation {
id: animout
property: "x"
from: 0
to: target ? target.width : 0
onTargetChanged: if (target) start()
easing.type: Easing.InOutQuad
}
Component.onCompleted: {
for (var i = 0; i < children.length; ++i) {
if (i) children[i].x = -children[i].width
}
if (children.length) {
activeIndex = 0
activeItem = children[0]
}
}
}
// test it out
StackWidget {
id: stack
Rectangle {
width: parent.width
height: parent.height
color: "blue"
Text {
anchors.centerIn: parent
text: "blue widget"
}
}
Rectangle {
width: parent.width
height: parent.height
color: "red"
Text {
anchors.centerIn: parent
text: "red widget"
}
}
Rectangle {
width: parent.width
height: parent.height
color: "green"
Text {
anchors.centerIn: parent
text: "green widget"
}
}
}
MouseArea {
anchors.fill: parent
onClicked: stack.activeIndex = (stack.activeIndex + 1) % stack.children.length
}
我一直在寻找相同问题的解决方案,但找不到好的解决方案。因此,即使这是一个旧帖子,也可以在这里解决我的解决方案。
CrossfadeStackLayout.qml
import QtQuick 2.15
import QtQuick.Layouts 1.15
StackLayout {
id: root
property int previousIndex: 0
property var items: children
property Item previousItem: items[previousIndex]
property Item currentItem: items[currentIndex]
Component.onCompleted: {
previousIndex = currentIndex
for(var i = 1; i < count; ++i) {
children[i].opacity = 0
}
}
Component {
id: crossFader
ParallelAnimation {
property Item fadeOutTarget
property Item fadeInTarget
NumberAnimation {
target: fadeOutTarget
property: "opacity"
to: 0
duration: 300
}
NumberAnimation {
target: fadeInTarget
property: "opacity"
to: 1
duration: 300
}
}
}
onCurrentIndexChanged: {
items = root.children
if(previousItem && currentItem && (previousItem != currentItem)) {
previousItem.visible = true
currentItem.visible = true
var crossFaderAnim = crossFader.createObject(parent, {fadeOutTarget: previousItem, fadeInTarget: currentItem})
crossFaderAnim.restart()
}
previousIndex = currentIndex
}
}
您可以使用 aStackView
而不是 a轻松地进行淡入淡出过渡StackLayout
。这是一个例子:
import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Stack animations example")
StackView {
id: stackView
anchors.fill: parent
// Applied to the item that enters the stack when the item is pushed.
pushEnter: Transition {
PropertyAnimation {
property: "opacity"
from: 0
to: 1
duration: 1000
}
}
// Applied to the item that exits the stack when another item is pushed.
pushExit: Transition {
PropertyAnimation {
property: "opacity"
from: 1
to: 0
duration: 1000
}
}
// Applied to the item that enters the stack when another item is popped.
popEnter: Transition {
PropertyAnimation {
property: "opacity"
from: 0
to: 1
duration: 1000
}
}
// Applied to the item that exits the stack when the item is popped.
popExit: Transition {
PropertyAnimation {
property: "opacity"
from: 1
to: 0
duration: 1000
}
}
initialItem: bluePageComponent
}
Component {
id: bluePageComponent
Page {
id: bluePage
background: Rectangle { color: "blue" }
ColumnLayout {
anchors.centerIn: parent
Button {
id: bluePopButton
text: "Pop page"
onClicked: stackView.pop();
}
Button {
text: "Add Red Page"
onClicked: bluePage.StackView.view.push(redPageComponent);
}
}
}
}
Component {
id: redPageComponent
Page {
id: redPage
background: Rectangle { color: "red" }
ColumnLayout {
anchors.centerIn: parent
Button {
id: redPopButton
text: "Pop page"
onClicked: stackView.pop();
}
Button {
text: "Add Blue Page"
onClicked: redPage.StackView.view.push(bluePageComponent);
}
}
}
}
}
注意不要混淆 Quick Controls 和 Quick Controls 2 StackViews,它们是不同的。
可能有一种方法可以为此使用 a StackLayout
,但我会尝试进行一些调查。
编辑:我设法使它与 a 一起工作,StackLayout
在我看来这不是一个很好的解决方案,但它可以按您的预期工作。StackView
即使它不是最好的用例,我还是会使用它。
import QtQuick 2.7
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.1
Page {
header: TabBar {
id: bar
width: parent.width
currentIndex: 0
TabButton {
text: qsTr("Home")
}
TabButton {
text: qsTr("Discover")
}
TabButton {
text: qsTr("Activity")
}
}
StackLayout {
id: stackLayout
anchors.fill: parent
currentIndex: bar.currentIndex
// When changing index we set new index opacity to 1 to let it fade in
// and other items in StackView to 0 so that they fade out. We also set
// all items to visible since StackLayout makes the old item mmediately
// invisible and crossfade wouldn't work
onCurrentIndexChanged: {
switch (currentIndex) {
case 0:
homeTab.opacity = 1;
homeTab.visible = true;
discoverTab.opacity = 0;
discoverTab.visible = true;
activityTab.opacity = 0;
activityTab.visible = true;
break;
case 1:
homeTab.opacity = 0;
homeTab.visible = true;
discoverTab.opacity = 1;
discoverTab.visible = true;
activityTab.opacity = 0;
activityTab.visible = true;
break;
case 2:
homeTab.opacity = 0
homeTab.visible = true;
discoverTab.opacity = 0;
discoverTab.visible = true;
activityTab.opacity = 1;
activityTab.visible = true;
break;
}
}
// Home Page
Rectangle {
id: homeTab
color: "blue"
// Starting item should be visible and completely opaque
opacity: 1
visible: true
Label {
anchors.centerIn: parent
font.pixelSize: 30
text: qsTr("Home")
}
// Animates opacity change
Behavior on opacity {
NumberAnimation { duration: 1000 }
}
}
// Discover Page
Rectangle {
id: discoverTab
color: "green"
// Items not visible from the start should be completely invisible
// so that crossfade will work correctly when clicking TabBar for
// the first time
opacity: 0
visible: false
Label {
anchors.centerIn: parent
font.pixelSize: 30
text: qsTr("Discover")
}
// Animates opacity change
Behavior on opacity {
NumberAnimation { duration: 1000 }
}
}
// Activity Page
Rectangle {
id: activityTab
color: "pink"
// Items not visible from the start should be completely invisible
// so that crossfade will work correctly when clicking TabBar for
// the first time
opacity: 0
visible: false
Label {
anchors.centerIn: parent
font.pixelSize: 30
text: qsTr("Activity")
}
// Animates opacity change
Behavior on opacity {
NumberAnimation { duration: 1000 }
}
}
}
}
我在 Github 上推送了一个示例,请随意使用并进行试验。 https://github.com/Alien1993/stacks-animation-example