-->

QML StackView: Item Destroyed on pop

2020-08-01 07:52发布

问题:

Consider the following example:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2

Window {
    visible: true
    width: 320
    height: 320

    StackView {
        id: stackView
        anchors.fill: parent
        Component.onCompleted: {
            stackView.push( { item: comp1, destroyOnPop:false } )
        }
    }

    Component {
        id: comp1
        Rectangle {
            color: "lightgray"

            Text {
                id: mtext
                anchors.centerIn: parent
                text: "First Page"
            }
        }
    }

    Component {
        id: comp2
        Rectangle {
            color: "lightgreen"

            Text {
                id: mtext
                anchors.centerIn: parent
                text: "Second Page"
            }

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                onClicked: mtext.text += " Clicked"
            }
        }
    }

    Row {
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        Button {
            id: next
            text: "Next"
            onClicked: {
                stackView.push( { item: comp2, destroyOnPop:false } )
                enabled = false
                prev.enabled = true
            }
        }

        Button {
            id: prev
            text: "Prev"
            enabled: false
            onClicked: {
                stackView.pop()
                enabled = false
                next.enabled = true
            }
        }
    }
}

Here I'm pushing 2 Components into the StackView on the each button clicks, i.e. clicking the "Next" button loads the second page. When I click on it, it displays an updated text("Second Page Clicked"). Then clicking "Prev" loads the first page as expected.

Now if I click "Next" again, it should load the second page with that updated text ("Second Page Clicked"), but it doesn't. It shows the initial text ("Second Page").

So the question is whether the Item is destroyed on pop? If not then shouldn't the second page display the updated text ("Second Page Clicked")?

I have even set the flag destroyOnPop to false. Have I misunderstood the concept of StackView? Any possible way to solve this?

I want to load any page from any point in StackView and that the state of Item be as it is where I left it. Just like QStackWidget, where we use setCurrentIndex().

回答1:

Documentation is referring to Item object whereas you are using Components. The documentation is pretty clear about the fact that a component is not an Item since it is not derived from Item. Indeed a Component act like a Class which is instanced to a specific object.

Each time you call

stackView.push( { item: comp2, destroyOnPop:false } )

a new "instance" of the component comp2 is generated and such new instance is used instead of the previous one. I strongly suggest to read this article to better understand the correlation between Component objects and generated instances.

Instances can be generated via createObject function. Hence, you can create an array of Items and use such Items instead of the Components. Final code looks like this:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.2

Window {
    visible: true
    width: 320
    height: 320

    StackView {
        id: stackView
        anchors.fill: parent
        Component.onCompleted: {
            push( { item: items[0], destroyOnPop:false })
        }
        property variant items: [comp1.createObject(), comp2.createObject()]  // objects from the components
    }

    Component {
        id: comp1
        Rectangle {
            color: "lightgray"

            Text {
                id: mtext
                anchors.centerIn: parent
                text: "First Page"
            }
        }
    }

    Component {
        id: comp2
        Rectangle {
            color: "lightgreen"

            Text {
                id: mtext
                anchors.centerIn: parent
                text: "Second Page"
            }

            MouseArea {
                id: mouseArea
                anchors.fill: parent
                onClicked: mtext.text += " Clicked"
            }
        }
    }

    Row {
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        Button {
            id: next
            text: "Next"
            onClicked: {
                stackView.push({ item: stackView.items[1], destroyOnPop:false })
                enabled = false
                prev.enabled = true
            }
        }

        Button {
            id: prev
            text: "Prev"
            enabled: false
            onClicked: {
                stackView.pop()
                enabled = false
                next.enabled = true
            }
        }
    }
}

The same correct result could be achieved by defining directly the Rectagle objects in the source. In this case we have two Items which are reparented to the Window thanks the destroyOnPop set to false.

Rectangle {
    id: comp1
    color: "lightgray"
    visible: false

    Text {
        id: mtext
        anchors.centerIn: parent
        text: "First Page"
    }
}

Rectangle {
   id: comp2
   color: "lightgreen"
   visible: false

   Text {
        id: mtext2
        anchors.centerIn: parent
        text: "Second Page"
   }

   MouseArea {
       id: mouseArea
       anchors.fill: parent
       onClicked: mtext2.text += " Clicked"
   }
}


标签: qml