QML - 自定义 PageIndicator 以显示它指示的每个页面的图像

QML - Customizing PageIndicator to show an image for each page it indicates

更新:SwipeView

添加了代码

我正在尝试自定义 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")
            }
        }
    }
}

SwipeViewPageIndicator(见下文)都是 ColumnLayout 的一部分并且是子代:

ColumnLayout {
        id: layoutDetailsAndMenu
        spacing: 0
        SwipeView { ... }
        PageIndicator { ... }
}

首先我研究了 PageIndicatordelegate 属性 是如何工作的。例如下面的 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 是:

来源如下:

            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 是我的 PageIndicatorvariant 属性:

    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,因此生成了 Imagesource: "qrc:/icons/qtlogo.png",所有页面指示器项目都填充了它。如果我 select 其他两个中的一个作为初始 selected 页面,我将分别得到 qrc:/icons/qtlogo1.pngqrc:/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 设置为 PageIndicatorcurrentIndex。您可以这样做:

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.bottomanchors.top)将不起作用。在垂直布局的直接子级中只能使用水平锚点,反之亦然。