QML - 自定义 PageIndicator 以显示它指示的每个页面的图像
QML - Customizing PageIndicator to show an image for each page it indicates
更新: 为 SwipeView
添加了代码
我正在尝试自定义 PageIndicator
来执行我目前使用三个按钮执行的操作,即
- 可以通过单击
PageIndicator
中的相应项目来更改页面(目前我有 3 页,每页一个按钮 onClicked
设置为更改 SwipeView
到预定义的索引 - 没有 PageIndicator
)
- 为每个页面指示器项目显示自定义图像(目前我有 3 页,每页一个按钮带有自定义图像 - 无
PageIndicator
)
我的 SwipeView
具有以下结构:
SwipeView {
id: detailsView
Layout.fillWidth: true
Layout.preferredHeight: layoutTopLevel.height * .9
Page {
id: captureViewPage
header: Text {
text: "Capture view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
}
Page {
id: helpViewPage
header: Text {
text: "Help view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: helpViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Gestures")
}
TabButton {
text: qsTr("General")
}
}
}
Page {
id: settingsViewPage
header: Text {
text: "Settings view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: settingsViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Language")
}
TabButton {
text: qsTr("Device")
}
}
}
}
SwipeView
和 PageIndicator
(见下文)都是 ColumnLayout
的一部分并且是子代:
ColumnLayout {
id: layoutDetailsAndMenu
spacing: 0
SwipeView { ... }
PageIndicator { ... }
}
首先我研究了 PageIndicator
的 delegate
属性 是如何工作的。例如下面的 QML 代码
PageIndicator {
id: detailsViewIndicator
count: detailsView.count
currentIndex: detailsView.currentIndex
interactive: true
anchors.bottom: detailsView.bottom
anchors.bottomMargin: -40
anchors.horizontalCenter: parent.horizontalCenter
delegate: Rectangle {
implicitWidth: 15
implicitHeight: 15
radius: width
color: "#21be2b"
opacity: index === detailsView.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
}
产生以下绿色结果:
可以通过单击 PageIndicator
中的相应项目来更改页面
出于某种原因,inactive
根本不起作用。我将引用文档这个 属性 应该做什么:
interactive : bool
This property holds whether the control is interactive. An interactive
page indicator reacts to presses and automatically changes the current
index appropriately. The default value is false.
我不知道这是一个错误还是我遗漏了一些东西,但即使没有通过 delegate
进行自定义,点击页面指示器项目也会 NOT更改当前页面(这些项目的数量确实等于页面数量,因此点击没有分配页面的索引是不可能的)。
所以为了实现第一个愿望我加了一个MouseArea
:
PageIndicator {
// ...
delegate: Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
if(index !== detailsView.currentIndex) {
detailsView.setCurrentIndex(index);
}
}
}
}
}
如您所见,我正在使用 onClicked
事件处理程序(或任何这些东西的名称)并检查 PageIndicator
的当前索引是否等于我的 [=19] 中的页面=].如果不是这种情况,我使用 setCurrentIndex(index)
将我的 SwipeView
设置为 selected 索引。
在我写完这篇文章后,我很满意它能像我想象的那样工作(尽管 interactive
的事情仍然困扰着我)。接下来是添加图片...
为每个页面指示器项目显示自定义图像
首先让我向您展示我想要的外观(这实际上是最终结果,但更多内容 - 稍后再说):
注意:我使用了不属于我的 Qt
徽标。用于演示目的
从左到右 QRC
个 URL 是:
qrc:/icons/qtlogo.png
qrc:/icons/qtlogo1.png
qrc:/icons/qtlogo2.png
来源如下:
delegate: Image {
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
height: 30
width: 30
opacity: index === detailsView.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
MouseArea {
anchors.fill: parent
onClicked: {
if(index !== detailsView.currentIndex) {
detailsView.setCurrentIndex(index);
source = detailsViewIndicator.indicatorIcons[detailsView.currentIndex];
}
}
}
}
其中 indicatorIcons
是我的 PageIndicator
的 variant
属性:
property variant indicatorIcons: [
"qrc:/icons/qtlogo.png",
"qrc:/icons/qtlogo1.png",
"qrc:/icons/qtlogo2.png"
]
我为 QRC
URL 使用了一个 string
对象数组,因为这似乎不可能做到
delegate: detailsViewIndicator.indicatorImages[detailsView.currentIndex]
其中 indicatorImages
为
property list<Image> indicatorImages: [
Image { source: "..." },
Image { source: "..." },
Image { source: "..." }
]
我遇到的问题与图像的实际加载有关,我感觉它与我上面描述的 list
问题有关。使用代码
delegate: Image {
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
// ...
}
我第一次 运行 我的申请得到:
这是因为最初 selected 的索引是 0
,因此生成了 Image
和 source: "qrc:/icons/qtlogo.png"
,所有页面指示器项目都填充了它。如果我 select 其他两个中的一个作为初始 selected 页面,我将分别得到 qrc:/icons/qtlogo1.png
和 qrc:/icons/qtlogo2.png
。
在 SwipeView
中滑动( 而不是 单击 PageIndicator
)会导致
和
这仅在一个方向上(从左到右按索引)。如果我倒退,我会得到相同的结果,但顺序相反。
点击让事情变得更有趣。在下面的屏幕截图中,我在初始化后单击了第二页指示器项目(qrc:/icons/qtlogo1.png
作为 Image
的来源):
接下来我点击了第三页指标项:
再点击之后我得到:
显然这不是事情应该如何运作的。我希望从头到尾始终获得最终结果,无论哪个页面被滑走或页面指示器项目被点击。
有人做过这样的事吗?这可能吗?
For some reason the inactive doesn't work at all
我假设这是一个打字错误,因为使用 interactive
属性 对我有用 (Qt 5.7):
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
PageIndicator {
count: 3
interactive: true
onCurrentIndexChanged: print(currentIndex)
}
}
第一个示例的简化版本也是如此:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
PageIndicator {
id: detailsViewIndicator
count: 3
currentIndex: 0
interactive: true
delegate: Rectangle {
implicitWidth: 15
implicitHeight: 15
radius: width
color: "#21be2b"
opacity: index === detailsViewIndicator.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
}
}
因此,如果 SwipeView
没有更改页面,则可能存在问题,我们需要查看该代码。
I'd like to have the final result all the time from start to end and no matter which page has been swiped away or page indicator item clicked.
我认为问题在于您为图标数组使用的索引:
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
那是在使用当前选定的索引,而您似乎想使用该委托的索引:
source: detailsViewIndicator.indicatorIcons[index]
顺便说一句,指定图标源的一种更简单的方法是使用字符串连接(并为您的文件提供适当的命名以匹配):
source: "qrc:/icons/qtlogo" + index + ".png"
在您添加的代码中,SwipeView
没有将其 currentIndex
设置为 PageIndicator
的 currentIndex
。您可以这样做:
currentIndex: detailsViewIndicator.currentIndex
完整示例:
import QtQuick 2.5
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.fill: parent
SwipeView {
id: detailsView
currentIndex: detailsViewIndicator.currentIndex
Layout.fillWidth: true
Layout.fillHeight: true
Page {
id: captureViewPage
header: Text {
text: "Capture view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
}
Page {
id: helpViewPage
header: Text {
text: "Help view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: helpViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Gestures")
}
TabButton {
text: qsTr("General")
}
}
}
Page {
id: settingsViewPage
header: Text {
text: "Settings view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: settingsViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Language")
}
TabButton {
text: qsTr("Device")
}
}
}
}
PageIndicator {
id: detailsViewIndicator
count: detailsView.count
currentIndex: detailsView.currentIndex
interactive: true
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
另请注意:在 ColumnLayout
中使用垂直锚点(anchors.bottom
、anchors.top
)将不起作用。在垂直布局的直接子级中只能使用水平锚点,反之亦然。
更新: 为 SwipeView
我正在尝试自定义 PageIndicator
来执行我目前使用三个按钮执行的操作,即
- 可以通过单击
PageIndicator
中的相应项目来更改页面(目前我有 3 页,每页一个按钮onClicked
设置为更改SwipeView
到预定义的索引 - 没有PageIndicator
) - 为每个页面指示器项目显示自定义图像(目前我有 3 页,每页一个按钮带有自定义图像 - 无
PageIndicator
)
我的 SwipeView
具有以下结构:
SwipeView {
id: detailsView
Layout.fillWidth: true
Layout.preferredHeight: layoutTopLevel.height * .9
Page {
id: captureViewPage
header: Text {
text: "Capture view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
}
Page {
id: helpViewPage
header: Text {
text: "Help view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: helpViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Gestures")
}
TabButton {
text: qsTr("General")
}
}
}
Page {
id: settingsViewPage
header: Text {
text: "Settings view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: settingsViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Language")
}
TabButton {
text: qsTr("Device")
}
}
}
}
SwipeView
和 PageIndicator
(见下文)都是 ColumnLayout
的一部分并且是子代:
ColumnLayout {
id: layoutDetailsAndMenu
spacing: 0
SwipeView { ... }
PageIndicator { ... }
}
首先我研究了 PageIndicator
的 delegate
属性 是如何工作的。例如下面的 QML 代码
PageIndicator {
id: detailsViewIndicator
count: detailsView.count
currentIndex: detailsView.currentIndex
interactive: true
anchors.bottom: detailsView.bottom
anchors.bottomMargin: -40
anchors.horizontalCenter: parent.horizontalCenter
delegate: Rectangle {
implicitWidth: 15
implicitHeight: 15
radius: width
color: "#21be2b"
opacity: index === detailsView.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
}
产生以下绿色结果:
可以通过单击 PageIndicator
出于某种原因,inactive
根本不起作用。我将引用文档这个 属性 应该做什么:
interactive : bool
This property holds whether the control is interactive. An interactive page indicator reacts to presses and automatically changes the current index appropriately. The default value is false.
我不知道这是一个错误还是我遗漏了一些东西,但即使没有通过 delegate
进行自定义,点击页面指示器项目也会 NOT更改当前页面(这些项目的数量确实等于页面数量,因此点击没有分配页面的索引是不可能的)。
所以为了实现第一个愿望我加了一个MouseArea
:
PageIndicator {
// ...
delegate: Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
if(index !== detailsView.currentIndex) {
detailsView.setCurrentIndex(index);
}
}
}
}
}
如您所见,我正在使用 onClicked
事件处理程序(或任何这些东西的名称)并检查 PageIndicator
的当前索引是否等于我的 [=19] 中的页面=].如果不是这种情况,我使用 setCurrentIndex(index)
将我的 SwipeView
设置为 selected 索引。
在我写完这篇文章后,我很满意它能像我想象的那样工作(尽管 interactive
的事情仍然困扰着我)。接下来是添加图片...
为每个页面指示器项目显示自定义图像
首先让我向您展示我想要的外观(这实际上是最终结果,但更多内容 - 稍后再说):
注意:我使用了不属于我的 Qt
徽标。用于演示目的
从左到右 QRC
个 URL 是:
qrc:/icons/qtlogo.png
qrc:/icons/qtlogo1.png
qrc:/icons/qtlogo2.png
来源如下:
delegate: Image {
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
height: 30
width: 30
opacity: index === detailsView.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
MouseArea {
anchors.fill: parent
onClicked: {
if(index !== detailsView.currentIndex) {
detailsView.setCurrentIndex(index);
source = detailsViewIndicator.indicatorIcons[detailsView.currentIndex];
}
}
}
}
其中 indicatorIcons
是我的 PageIndicator
的 variant
属性:
property variant indicatorIcons: [
"qrc:/icons/qtlogo.png",
"qrc:/icons/qtlogo1.png",
"qrc:/icons/qtlogo2.png"
]
我为 QRC
URL 使用了一个 string
对象数组,因为这似乎不可能做到
delegate: detailsViewIndicator.indicatorImages[detailsView.currentIndex]
其中 indicatorImages
为
property list<Image> indicatorImages: [
Image { source: "..." },
Image { source: "..." },
Image { source: "..." }
]
我遇到的问题与图像的实际加载有关,我感觉它与我上面描述的 list
问题有关。使用代码
delegate: Image {
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
// ...
}
我第一次 运行 我的申请得到:
这是因为最初 selected 的索引是 0
,因此生成了 Image
和 source: "qrc:/icons/qtlogo.png"
,所有页面指示器项目都填充了它。如果我 select 其他两个中的一个作为初始 selected 页面,我将分别得到 qrc:/icons/qtlogo1.png
和 qrc:/icons/qtlogo2.png
。
在 SwipeView
中滑动( 而不是 单击 PageIndicator
)会导致
和
这仅在一个方向上(从左到右按索引)。如果我倒退,我会得到相同的结果,但顺序相反。
点击让事情变得更有趣。在下面的屏幕截图中,我在初始化后单击了第二页指示器项目(qrc:/icons/qtlogo1.png
作为 Image
的来源):
接下来我点击了第三页指标项:
再点击之后我得到:
显然这不是事情应该如何运作的。我希望从头到尾始终获得最终结果,无论哪个页面被滑走或页面指示器项目被点击。
有人做过这样的事吗?这可能吗?
For some reason the inactive doesn't work at all
我假设这是一个打字错误,因为使用 interactive
属性 对我有用 (Qt 5.7):
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
PageIndicator {
count: 3
interactive: true
onCurrentIndexChanged: print(currentIndex)
}
}
第一个示例的简化版本也是如此:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
PageIndicator {
id: detailsViewIndicator
count: 3
currentIndex: 0
interactive: true
delegate: Rectangle {
implicitWidth: 15
implicitHeight: 15
radius: width
color: "#21be2b"
opacity: index === detailsViewIndicator.currentIndex ? 0.95 : pressed ? 0.7 : 0.45
Behavior on opacity {
OpacityAnimator {
duration: 100
}
}
}
}
}
因此,如果 SwipeView
没有更改页面,则可能存在问题,我们需要查看该代码。
I'd like to have the final result all the time from start to end and no matter which page has been swiped away or page indicator item clicked.
我认为问题在于您为图标数组使用的索引:
source: detailsViewIndicator.indicatorIcons[detailsView.currentIndex]
那是在使用当前选定的索引,而您似乎想使用该委托的索引:
source: detailsViewIndicator.indicatorIcons[index]
顺便说一句,指定图标源的一种更简单的方法是使用字符串连接(并为您的文件提供适当的命名以匹配):
source: "qrc:/icons/qtlogo" + index + ".png"
在您添加的代码中,SwipeView
没有将其 currentIndex
设置为 PageIndicator
的 currentIndex
。您可以这样做:
currentIndex: detailsViewIndicator.currentIndex
完整示例:
import QtQuick 2.5
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.fill: parent
SwipeView {
id: detailsView
currentIndex: detailsViewIndicator.currentIndex
Layout.fillWidth: true
Layout.fillHeight: true
Page {
id: captureViewPage
header: Text {
text: "Capture view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
}
Page {
id: helpViewPage
header: Text {
text: "Help view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: helpViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Gestures")
}
TabButton {
text: qsTr("General")
}
}
}
Page {
id: settingsViewPage
header: Text {
text: "Settings view"
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 20
}
footer: TabBar {
id: settingsViewSubCategories
currentIndex: 0
TabButton {
text: qsTr("Language")
}
TabButton {
text: qsTr("Device")
}
}
}
}
PageIndicator {
id: detailsViewIndicator
count: detailsView.count
currentIndex: detailsView.currentIndex
interactive: true
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
另请注意:在 ColumnLayout
中使用垂直锚点(anchors.bottom
、anchors.top
)将不起作用。在垂直布局的直接子级中只能使用水平锚点,反之亦然。