如何从QML中的GridView或ListView获取实例化的委托组件

cha*_*cus 17 qt qml

我的难题一般来说是:通过GridView之外的一些动作,我想根据之前选择的特定模型项或索引来计算GridView中特定委托项的坐标.

我有一个GridView,其中有许多项目在模型中.GridView的委托创建每个项目的缩略图视图.单击时,它会显示项目的详细全屏视图.我想要一个很好的转换,它显示缩略图从GridView中的位置扩展出来,当详细视图被解除时,缩小回到GridView中.

诀窍是,详细视图本身就是ListView的委托,因此您可以一次在一个屏幕之间在详细视图之间进行分页.这意味着只需调整GridView的委托项目大小或某些内容的解决方案将无效.此外,由于您可以寻呼到ListView中的任何项目,因此必须仅基于模型中可用的信息或模型项目的索引来返回GridView(例如,我无法存储用于启动的模拟项目的鼠标的坐标.详细的观点或东西).

扩展动画相当简单,因为委托项具有一个用于知道其自身位置的单击处理程序的MouseArea,因此可以将其传递给启动动画的函数.这是我无法弄清楚的反过来:从ListView中的模型项/索引,我如何计算GridView中相关项的坐标?

我在文档中找不到任何似乎可以让您从模型项实例甚至索引访问委托项实例的内容.GridView具有indexAt()基于坐标返回索引的功能.我想我可以反过来做,但它似乎不存在.

这是一个更具体的例子.道歉; 这是我能想出的最准确的代码,它准确地描述了我的问题:

import QtQuick 1.1

Item {
    id: window
    width: 400
    height: 200

    state: "summary"
    states: [
        State { name: "summary"; },
        State { name: "details"; }
    ]
    transitions: [
        Transition { from: "summary"; to: "details";
            SequentialAnimation {
                PropertyAction { target: animationRect; property: "visible"; value: true; }
                ParallelAnimation {
                    NumberAnimation { target: animationRect; properties: "x,y"; to: 0; duration: 200; }
                    NumberAnimation { target: animationRect; property: "width"; to: 400; duration: 200; }
                    NumberAnimation { target: animationRect; property: "height"; to: 200; duration: 200; }
                }
                PropertyAction { target: detailsView; property: "visible"; value: true; }
                PropertyAction { target: summaryView; property: "visible"; value: false; }
                PropertyAction { target: animationRect; property: "visible"; value: false; }
            }
        },
        Transition { from: "details"; to: "summary";
            SequentialAnimation {
                PropertyAction { target: summaryView; property: "visible"; value: true; }

                // How to animate animationRect back down to the correct item?

                PropertyAction { target: detailsView; property: "visible"; value: false; }
            }
        }
    ]

    Rectangle {
        id: animationRect
        z: 1
        color: "gray"
        visible: false

        function positionOverSummary(summaryRect) {
            x = summaryRect.x; y = summaryRect.y;
            width = summaryRect.width; height = summaryRect.height;
        }
    }

    ListModel {
        id: data
        ListElement { summary: "Item 1"; description: "Lorem ipsum..."; }
        ListElement { summary: "Item 2"; description: "Blah blah..."; }
        ListElement { summary: "Item 3"; description: "Hurf burf..."; }
    }

    GridView {
        id: summaryView
        anchors.fill: parent
        cellWidth: 100
        cellHeight: 100

        model: data
        delegate: Rectangle {
            color: "lightgray"
            width: 95; height: 95;

            Text { text: summary; }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    var delegateRect = mapToItem(window, x, y);
                    delegateRect.width = width; delegateRect.height = height;

                    animationRect.positionOverSummary(delegateRect);
                    detailsView.positionViewAtIndex(index, ListView.Beginning);
                    window.state = "details";
                }
            }
        }
    }

    ListView {
        id: detailsView
        anchors.fill: parent
        visible: false
        orientation: ListView.Horizontal
        snapMode: ListView.SnapOneItem

        model: data
        delegate: Rectangle {
            color: "gray"
            width: 400; height: 200;

            Column {
                Text { text: summary; }
                Text { text: description; }
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    // How do I get the coordinates to where animationRect should return?

                    summaryView.positionViewAtIndex(index, GridView.Visible);
                    window.state = "summary";
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?我可能只是以错误的方式解决这个问题.如果我想要做的具体是不可能的,还有其他方法我应该设计这个吗?谢谢!


编辑:我有过的一些想法(我认为其中没有一个是可行的):

  • 使用Component.onCompleted和保留创建的所有委托项的列表Component.onDestruction.为了有用,它应该是模型项或index =>委托项的映射.麻烦的是,基本类型文档(特别是变体)似乎表明这种地图不可能用纯QML创建.所以听起来这意味着将这个地图创建为C++类,并在QML中使用它和onCompleted/ onDestruction或委托组件中的内容,以使其保持最新.对于应该简单的事情来说,似乎有点危险和苛刻.

  • 此邮件列表帖子似乎表明Flickable的contentItem属性可用于枚举委托项目.然后我发现这篇帖子称这是一种不好的做法.我仍在调查,但我怀疑这将是一个合法的解决方案.看起来太可靠了,无法可靠地工作.

这就是我到目前为止所做的一切.

cha*_*cus 10

经过一些调查后,事实证明它contentItem确实为Flickable保存了实例化的代理.正如我上面所说,我怀疑这是真正做到这一点的最佳方式,甚至是一个好的方式,但它似乎确实有效.我将在下面发布这个hacky解决方案的完整代码,但我仍然希望有更好的方法.非常重要的一点是getDelegateInstanceAt()GridView中的新功能.

import QtQuick 1.1

Item {
    id: window
    width: 400
    height: 200

    state: "summary"
    states: [
        State { name: "summary"; },
        State { name: "details"; }
    ]
    transitions: [
        Transition { from: "summary"; to: "details";
            SequentialAnimation {
                PropertyAction { target: animationRect; property: "visible"; value: true; }
                ParallelAnimation {
                    NumberAnimation { target: animationRect; properties: "x,y"; to: 0; duration: 200; }
                    NumberAnimation { target: animationRect; property: "width"; to: 400; duration: 200; }
                    NumberAnimation { target: animationRect; property: "height"; to: 200; duration: 200; }
                }
                PropertyAction { target: detailsView; property: "visible"; value: true; }
                PropertyAction { target: summaryView; property: "visible"; value: false; }
                PropertyAction { target: animationRect; property: "visible"; value: false; }
            }
        },
        Transition { from: "details"; to: "summary";
            id: shrinkTransition
            property variant destRect: {"x": 0, "y": 0, "width": 0, "height": 0}

            SequentialAnimation {
                PropertyAction { target: summaryView; property: "visible"; value: true; }
                PropertyAction { target: animationRect; property: "visible"; value: true; }
                PropertyAction { target: detailsView; property: "visible"; value: false; }
                ParallelAnimation {
                    NumberAnimation { target: animationRect; property: "x"; to: shrinkTransition.destRect.x; duration: 200; }
                    NumberAnimation { target: animationRect; property: "y"; to: shrinkTransition.destRect.y; duration: 200; }
                    NumberAnimation { target: animationRect; property: "width"; to: shrinkTransition.destRect.width; duration: 200; }
                    NumberAnimation { target: animationRect; property: "height"; to: shrinkTransition.destRect.height; duration: 200; }
                }
                PropertyAction { target: animationRect; property: "visible"; value: false; }
            }
        }
    ]

    Rectangle {
        id: animationRect
        z: 1
        color: "gray"
        visible: false

        function positionOverSummary(summaryRect) {
            x = summaryRect.x; y = summaryRect.y;
            width = summaryRect.width; height = summaryRect.height;
        }

        function prepareForShrinkingTo(summaryRect) {
            x = 0; y = 0;
            width = 400; height = 200;
            shrinkTransition.destRect = summaryRect;
        }
    }

    ListModel {
        id: data
        ListElement { summary: "Item 1"; description: "Lorem ipsum..."; }
        ListElement { summary: "Item 2"; description: "Blah blah..."; }
        ListElement { summary: "Item 3"; description: "Hurf burf..."; }
    }

    GridView {
        id: summaryView
        anchors.fill: parent
        cellWidth: 100
        cellHeight: 100

        model: data
        delegate: Rectangle {
            // These are needed for getDelegateInstanceAt() below.
            objectName: "summaryDelegate"
            property int index: model.index

            color: "lightgray"
            width: 95; height: 95;

            Text { text: summary; }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    var delegateRect = mapToItem(window, x, y);
                    delegateRect.width = width; delegateRect.height = height;

                    animationRect.positionOverSummary(delegateRect);
                    detailsView.positionViewAtIndex(index, ListView.Beginning);
                    window.state = "details";
                }
            }
        }

        // Uses black magic to hunt for the delegate instance with the given
        // index.  Returns undefined if there's no currently instantiated
        // delegate with that index.
        function getDelegateInstanceAt(index) {
            for(var i = 0; i < contentItem.children.length; ++i) {
                var item = contentItem.children[i];
                // We have to check for the specific objectName we gave our
                // delegates above, since we also get some items that are not
                // our delegates here.
                if (item.objectName == "summaryDelegate" && item.index == index)
                    return item;
            }
            return undefined;
        }
    }

    ListView {
        id: detailsView
        anchors.fill: parent
        visible: false
        orientation: ListView.Horizontal
        snapMode: ListView.SnapOneItem

        model: data
        delegate: Rectangle {
            color: "gray"
            width: 400; height: 200;

            Column {
                Text { text: summary; }
                Text { text: description; }
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    summaryView.positionViewAtIndex(index, GridView.Visible);

                    var delegateInstance = summaryView.getDelegateInstanceAt(index);
                    var delegateRect = window.mapFromItem(summaryView,
                        delegateInstance.x - summaryView.contentX,
                        delegateInstance.y - summaryView.contentY
                    );
                    delegateRect.width = delegateInstance.width;
                    delegateRect.height = delegateInstance.height;

                    animationRect.prepareForShrinkingTo(delegateRect);
                    window.state = "summary";
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请告诉我有一个更健壮的方式!