Qt/QML:在加载程序加载后访问 ListView 以跳转到特定项目/页面

Posted

技术标签:

【中文标题】Qt/QML:在加载程序加载后访问 ListView 以跳转到特定项目/页面【英文标题】:Qt/QML: Accessing a ListView after a Loader has loaded it to jump to a specific item/page 【发布时间】:2015-06-09 09:15:23 【问题描述】:

我有一个 QML 应用程序,它基本上只是一个 ListView,它显示许多“章节”,而每个章节又包含一个或多个“页面”。

由于我不知道 QML 在生产中可能有多少章节和页面,我使用 Loader 按需加载页面,这样可以节省一些内存。

所以问题是我想按一个按钮“跳转”到某个故事和页面。我在示例中添加了一些按钮,它们已经跳转到不同的章节。

您可以在章节之间垂直滑动,也可以在每章的页面之间水平滑动。

我遇到的问题是,在切换/加载特定章节后,我无法弄清楚如何让包含页面的ListView 跳转到特定页面。基本上我错过了pagesView.currentPage = 5 或类似的东西。

什么是让它工作的好方法?

对应的 QML。您可以使用 qmlscene 运行它。

import QtQuick 2.4
import QtQuick.Controls 1.2


ApplicationWindow 
    width: 1024
    height: 768


    Component 
        id: pageViewComponent
        ListView 
            id: pagesView
            property int storyIndex: chapterView.modelData
            orientation: ListView.Horizontal; clip: true
            model: 20; snapMode: ListView.SnapToItem
            delegate: Rectangle 
                width: pagesView.width; height: pagesView.height
                color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
                border.color: "black"
                Text  text: "Page " + modelData; anchors.centerIn: parent; color: "white" 
            

        
    


    Rectangle 
        color: "black"
        anchors.fill: parent

        // Chapters 
        ListView 
            id: chapterView
            model: 8
            anchors.fill: parent
            snapMode: ListView.SnapToItem

            delegate: Rectangle 
                color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
                width: chapterView.width; height: chapterView.height

                Rectangle 
                    width: parent.width * 0.6; height: parent.height * 0.6
                    anchors.centerIn: parent
                    Loader  
                        anchors.fill: parent
                        sourceComponent: pageViewComponent 
                    
                


                Text 
                    x: 50; y: 50
                    color: "white"; font.pointSize: 30
                    text: "Chapter " + modelData
                

                Flow 
                    Button 
                        text: "Go to Chapter 2, Page 7"
                        onClicked:  
                            chapterView.positionViewAtIndex(2, ListView.Beginning)
                            //
                            //
                            // After jumping to the correct chapter, we obviously have to jump
                            // to the correct page after the Loader for that specific chapter has
                            // completed loading the pages of the chapter.
                            //
                            //
                        
                    

                    Button 
                        text: "Go to Chapter 1, Page 1"
                        onClicked: 
                            chapterView.positionViewAtIndex(1, ListView.Beginning)
                            // dito
                        
                    

                    Button 
                        text: "Go to Chapter 5, Page 2"
                        onClicked:  
                            chapterView.positionViewAtIndex(5, ListView.Beginning)
                            // dito
                        
                    
                
            

        
    

【问题讨论】:

使用Loader.item访问动态创建的pagesView。所以你可以idOfTheLoader.item.positionViewAtIndex(7, ListView.Beginning)换页。 @mcchu - 这将访问当前项目的加载器,而不是您要访问的加载器。另外,对它的调用必须安排在加载其内容时运行,并带有 lambda 和参数捕获。 【参考方案1】:

可能是这样的吗?

    // go to chapter 2 page 7
    chapterView.positionViewAtIndex(2, ListView.Beginning)
    var loader = chapterView.currentItem.loader
    loader.loaded.connect(function(l, p) 
        return function() 
           l.item.positionViewAtIndex(p, ListView.Beginning)
    (loader, 7))

这样,当加载器加载项目时,将调用访问项目的函数。

同时公开加载器以便可以访问它:

Rectangle 
    //...
    property Loader loader : l
    Loader  
        id: l
        // ...
    

【讨论】:

【参考方案2】:

这是我的做法:

    pagesView 中定义属性,使其能够更新其外观和状态:

    Component 
        id: pageViewComponent
        ListView 
            id: pagesView
    
            // Begin inserted code
            property int chapterIndex: -1
            property ListView chapterView: null
            Connections 
                target: chapterView
                onSelectedPageChanged: 
                    if (chapterIndex === chapterView.selectedChapter)
                        pagesView.positionViewAtIndex(chapterView.selectedPage, ListView.Beginning)
                
            
            onChapterIndexChanged: 
                if (chapterView && chapterIndex === chapterView.selectedChapter)
                    pagesView.positionViewAtIndex(chapterView.selectedPage, ListView.Beginning)
            
            // End inserted code
    
            orientation: ListView.Horizontal; clip: true
            model: 20; snapMode: ListView.SnapToItem
            delegate: Rectangle 
                width: pagesView.width; height: pagesView.height
                color: Qt.rgba(Math.random(),Math.random(),Math.random(),1)
                border.color: "black"
                Text  text: "Page " + modelData; anchors.centerIn: parent; color: "white" 
            
        
    
    

    定义将存储整个事物状态(即当前章节和页面)的属性:

    property int selectedChapter: 0
    property int selectedPage: 0
    

    根据selectedChapter属性更新***ListView位置:

    onSelectedChapterChanged: positionViewAtIndex(selectedChapter, ListView.Beginning)
    

    在创建 pagesView 时设置其属性:

    Loader 
        id: pagesViewLoader
        anchors.fill: parent
        sourceComponent: pageViewComponent
        onLoaded: 
            item.chapterIndex = Qt.binding(function()  return modelData )
            item.chapterView = chapterView
        
    
    

    Button触发章节和页面更改:

    // Define method in top-level ListView
    ListView 
        id: chapterView
        function goTo(chapter, page) 
            selectedChapter = chapter
            selectedPage = page
        
    // ...
    
    // Call method from onClicked handler
    Button 
        text: "Go to Chapter 2, Page 7"
        onClicked: 
            chapterView.goTo(/* chapter */ 2, /* page */ 7)
        
    
    

重要提示

注 1

在第 4 步中,modelData 没有直接设置,而是被包装到 Qt.binding 函数调用中。这就是你从 javascript 绑定到值的方式。这是必要的,因为 ListView 重用了它的委托实例,并且 pagesView 实例可以在单个 onLoaded 消息之后以不同的 modelData 值重用。

注2

起初我在onClicked 中使用了两个属性赋值,而没有将它们包装在goTo 函数中:

chapterView.selectedChapter = chapter
chapterView.selectedPage = page

但这导致第二行出现错误 (chapterView is not defined)。当第一行执行时,chapterView 滚动到选定的章节,并且当前的委托项目从场景中移除,因为它不再需要。我不知道这个“从场景中移除”在技术上是如何完成的,但结果是在第一行之后chapterView 不再定义。因此需要将这两个赋值封装在一个函数调用中。

【讨论】:

不错的答案。我会试一试,然后回复你!如果您习惯于使用传统工具开发应用程序,QML 有时可能有点难以理解。 ;) 更新:从我所看到的情况来看。我从你的回答中学到了一些对我未来有帮助的东西:1)Qt.binding()——我怎么会错过这个? 2) 在 QML 对象中自定义 functions。谢谢! 为了完整起见,我用一个工作示例来说明这个要点:gist.github.com/bastibense/7f949e2f791b4db27c6f

以上是关于Qt/QML:在加载程序加载后访问 ListView 以跳转到特定项目/页面的主要内容,如果未能解决你的问题,请参考以下文章

QML - 图像不会加载到应用程序的部署版本中

Linux 共享库被加载两次

QT QML - 从另一个类访问 qml 模型

如果数据库可访问,如何快速检查? (Qt、QML、C++)- Linux

更改后,QT QML资源文件不会重新编译

在 Qt/QML 中访问/修改一个类型的所有实例