Qt/QML: 在 Loader 加载后访问 ListView 以跳转到特定 item/page
Qt/QML: Accessing a ListView after a Loader has loaded it to jump to a specific item/page
我有一个 QML 应用程序,它基本上只是一个显示多个 "chapters" 的 ListView,其中每个又包含一个或多个 "pages"。
由于我不知道 QML 在生产中可能有多少章节和页面,我使用 Loader
按需加载页面,这应该可以节省一些内存。
所以问题是我想 "jump" 按一下按钮就可以转到某个故事和页面。我在示例中添加了几个按钮,它们已经跳转到不同的章节了。
您可以在章节之间垂直轻拂,也可以在每章的页面之间水平轻拂。
我遇到的问题是,我无法弄清楚如何让 ListView
包含的页面在 switching/loading 特定章节之后跳转到特定页面。基本上我遗漏了 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
}
}
}
}
}
}
}
也许是这样的?
// 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
// ...
}
}
这是我的做法:
在 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
滚动到选定的章节,并且当前的委托项目从场景中删除,因为它不再需要。这个"removed from the scene"在技术上是怎么做的我也说不清楚,但是结果是在第一行之后chapterView
就不再定义了。因此需要将这两个赋值包装在一个函数调用中。
我有一个 QML 应用程序,它基本上只是一个显示多个 "chapters" 的 ListView,其中每个又包含一个或多个 "pages"。
由于我不知道 QML 在生产中可能有多少章节和页面,我使用 Loader
按需加载页面,这应该可以节省一些内存。
所以问题是我想 "jump" 按一下按钮就可以转到某个故事和页面。我在示例中添加了几个按钮,它们已经跳转到不同的章节了。
您可以在章节之间垂直轻拂,也可以在每章的页面之间水平轻拂。
我遇到的问题是,我无法弄清楚如何让 ListView
包含的页面在 switching/loading 特定章节之后跳转到特定页面。基本上我遗漏了 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
}
}
}
}
}
}
}
也许是这样的?
// 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
// ...
}
}
这是我的做法:
在
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
滚动到选定的章节,并且当前的委托项目从场景中删除,因为它不再需要。这个"removed from the scene"在技术上是怎么做的我也说不清楚,但是结果是在第一行之后chapterView
就不再定义了。因此需要将这两个赋值包装在一个函数调用中。