【问题标题】:How to add a transition to a StackLayout in QML?如何在 QML 中向 StackLayout 添加过渡?
【发布时间】:2017-10-06 11:46:11
【问题描述】:

我想要一个像 TabBar example in the Qt docs 这样的 GUI:

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

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

【问题讨论】:

  • 这似乎是一些人感兴趣的事情(我之前曾被个人问过)——也许值得在 bugreports.qt.io 上提出建议?跨度>

标签: qt qml qtquickcontrols2


【解决方案1】:

滥用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))
}

【讨论】:

    【解决方案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
    }
    

    【讨论】:

    • 我查看了 StackView,尽管它可能会起作用,但它并不是一个理想的解决方案。 StackView 用于拥有一堆视图,而不是同一级别的一堆视图。我可能会滥用它的 replace() 方法在页面之间切换,但我希望有更好的东西,因为 Qt 已经使用它作为 QStackedWidget
    • 嗯,是的,这不是主意,但堆栈布局不支持并且似乎甚至不支持对转换的支持。如果堆栈视图不适用,您应该推出自己的组件。
    • 感谢示例代码。如果我决定自己实现它,我可能会这样做。不过,我会以Container 类为基础。
    【解决方案3】:

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

    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
        }
    }
    

    【讨论】:

      【解决方案4】:

      您可以使用StackView 而不是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,它们是不同的。

      可能有一种方法可以为此使用StackLayout,但我会尝试进行一些调查。

      编辑: 我设法使它与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

      【讨论】:

        猜你喜欢
        • 2019-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-04
        • 1970-01-01
        相关资源
        最近更新 更多