4

我想要一个类似于Qt 文档中 TabBar 示例的 GUI :

在此处输入图像描述

如果我使用 SwipeView,则在页面之间移动时会有过渡(内容向左或向右移动)。使用 StackLayout,变化是立竿见影的。

我想在页面之间淡入淡出。我将如何使用 StackLayout 做到这一点?这甚至可能还是我需要编写自己的容器元素?

4

4 回答 4

3

滥用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))
}
于 2017-05-08T18:35:13.050 回答
2

查看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
}
于 2017-05-08T12:58:15.240 回答
1

我一直在寻找相同问题的解决方案,但找不到好的解决方案。因此,即使这是一个旧帖子,也可以在这里解决我的解决方案。

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
    }
}
于 2021-06-16T18:10:22.000 回答
0

您可以使用 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

于 2017-05-26T12:34:24.543 回答