Qt 如何在QML中重新创建Android的Pull to Refresh图标?

Qt 如何在QML中重新创建Android的Pull to Refresh图标?,qt,qml,Qt,Qml,这是Android中用于刷新视图的拉刷新图标 我一直试图把它带到qml,但这并不容易。 有如此多的转换,它很快变得非常复杂 在QML中重新创建此项应该有多困难? 使用画布是更好的解决方案吗 正如我第一次看到的,当箭头旋转时,滑动以与滑动不同的速度向下移动箭头。如果此箭头来自画布,它如何与外部事件(即滑动)关联?问题在于,在禁用视觉行为的情况下,Flickable和派生的ListView实际上不提供任何过度拖动或过度拍摄信息 如果在开始处拖动视图对您来说不是问题,那么您可以简单地使用content

这是Android中用于刷新视图的拉刷新图标

我一直试图把它带到qml,但这并不容易。 有如此多的转换,它很快变得非常复杂

在QML中重新创建此项应该有多困难? 使用画布是更好的解决方案吗

正如我第一次看到的,当箭头旋转时,滑动以与滑动不同的速度向下移动箭头。如果此箭头来自画布,它如何与外部事件(即滑动)关联?

问题在于,在禁用视觉行为的情况下,Flickable和派生的ListView实际上不提供任何过度拖动或过度拍摄信息

如果在开始处拖动视图对您来说不是问题,那么您可以简单地使用contentY的否定值,如果在视图开始之前拖动视图,则该值将变为否定值

我能想到的唯一解决方案是,不进行任何视觉过度拖动,但仍然获得过度拖动信息,以便驱动刷新程序,就是将view interactive属性设置为false,并在其上放置另一个鼠标区域,然后手动将拖动和轻弹重定向到现在的非交互视图

最后一部分听起来可能很复杂,但它并没有那么复杂,我碰巧知道它工作得很好,因为我已经使用了这种方法

因此,一旦您访问了控制视图的鼠标区域,您就可以跟踪负片的数量,并使用该信息驱动刷新器的逻辑和动画

链接答案中的实现与您需要的实现之间的显著区别在于,由于我想要解决的特定问题的要求,链接答案在每个代理中都有鼠标区域。您不需要这样做,您只需要一个覆盖视图的鼠标区域。

问题是,在禁用视觉行为的情况下,Flickable和派生的ListView实际上不提供任何过度拖动或过度拍摄信息

如果在开始处拖动视图对您来说不是问题,那么您可以简单地使用contentY的否定值,如果在视图开始之前拖动视图,则该值将变为否定值

我能想到的唯一解决方案是,不进行任何视觉过度拖动,但仍然获得过度拖动信息,以便驱动刷新程序,就是将view interactive属性设置为false,并在其上放置另一个鼠标区域,然后手动将拖动和轻弹重定向到现在的非交互视图

最后一部分听起来可能很复杂,但它并没有那么复杂,我碰巧知道它工作得很好,因为我已经使用了这种方法

因此,一旦您访问了控制视图的鼠标区域,您就可以跟踪负片的数量,并使用该信息驱动刷新器的逻辑和动画


链接答案中的实现与您需要的实现之间的显著区别在于,由于我想要解决的特定问题的要求,链接答案在每个代理中都有鼠标区域。你不需要这样做,你只需要一个覆盖视图的鼠标区域。

我找到了一个更简单的解决方案,它涉及多个Flickable元素,基本上包括用鼠标earea填充Flickable,将其boundsBehavior属性设置为Flickable.StopAtBounds,如果它位于顶部,基于鼠标值做事情

我可以在下面的代码中得到更好的近似值。一个可能的缺点是对角滑动也算作刷新意图。它可以改进,但我太懒了,现在还没弄到手

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Window 2.2

ApplicationWindow {
    property real mm: Screen.pixelDensity
    property real margins: 2 * mm
    id: mainWindow
    visible: true
    width: 60 * mm
    height: 120 * mm
    title: qsTr("Hello World")

    ListModel {
        id: myModel
        Component.onCompleted: {
            for(var i = 0; i <= 100; ++i) {
                myModel.append({num: i})
            }
        }
    }
    ListView {
        id: view
        boundsBehavior: Flickable.StopAtBounds
        interactive: true
        anchors.fill: parent
        model: myModel
        spacing: 4
        delegate: Rectangle {
            width: parent.width
            height: 25 * mm
            border.color: 'red'
            Text {
                id: name
                text: num
                anchors.centerIn: parent
            }
        }
        Rectangle {
            signal follow
            id: swatch
            width: 15 * mm
            height: width
            radius: width / 2
            color: 'lightgray'
            anchors.horizontalCenter: parent.horizontalCenter
            y: - height
        }
        MouseArea {
            property int mouseYSart
            property int biggerMouseY
            anchors.fill: view
            onPressed: {
                mouseYSart = mouseY
                biggerMouseY = 0
            }
            onMouseYChanged: {
                if(view.contentY == 0) {
                    var currentMouseY = mouseY
                    if(currentMouseY > biggerMouseY) {
                        biggerMouseY = currentMouseY
                        swatch.y += 1
                    }
                    if(currentMouseY < biggerMouseY) {
                        biggerMouseY = currentMouseY
                        swatch.y -= 1
                    }
                }
            }
            onReleased: swatch.y = - swatch.height
        }
    }
}

我找到了一个更简单的解决方案,它涉及到多个Flickable元素,基本上包括用MouseArea填充Flickable,将其boundsBehavior属性设置为Flickable.StopAtBounds,然后,如果它位于顶部,则根据mouseY值进行操作

我可以在下面的代码中得到更好的近似值。一个可能的缺点是对角滑动也算作刷新意图。它可以改进,但我太懒了,现在还没弄到手

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Window 2.2

ApplicationWindow {
    property real mm: Screen.pixelDensity
    property real margins: 2 * mm
    id: mainWindow
    visible: true
    width: 60 * mm
    height: 120 * mm
    title: qsTr("Hello World")

    ListModel {
        id: myModel
        Component.onCompleted: {
            for(var i = 0; i <= 100; ++i) {
                myModel.append({num: i})
            }
        }
    }
    ListView {
        id: view
        boundsBehavior: Flickable.StopAtBounds
        interactive: true
        anchors.fill: parent
        model: myModel
        spacing: 4
        delegate: Rectangle {
            width: parent.width
            height: 25 * mm
            border.color: 'red'
            Text {
                id: name
                text: num
                anchors.centerIn: parent
            }
        }
        Rectangle {
            signal follow
            id: swatch
            width: 15 * mm
            height: width
            radius: width / 2
            color: 'lightgray'
            anchors.horizontalCenter: parent.horizontalCenter
            y: - height
        }
        MouseArea {
            property int mouseYSart
            property int biggerMouseY
            anchors.fill: view
            onPressed: {
                mouseYSart = mouseY
                biggerMouseY = 0
            }
            onMouseYChanged: {
                if(view.contentY == 0) {
                    var currentMouseY = mouseY
                    if(currentMouseY > biggerMouseY) {
                        biggerMouseY = currentMouseY
                        swatch.y += 1
                    }
                    if(currentMouseY < biggerMouseY) {
                        biggerMouseY = currentMouseY
                        swatch.y -= 1
                    }
                }
            }
            onReleased: swatch.y = - swatch.height
        }
    }
}

我用了这样的方法:

 //
 //  Slot called when the flick has started
 //
 onFlickStarted: {
     refreshFlik = atYBeginning
 }

 //
 //  Slot called when the flick has finished
 //
 onFlickEnded: {
     if ( atYBeginning && refreshFlik )
     {
         refresh()
     }
 }

它似乎如预期的那样工作,并且很容易实现

我使用了类似这样的方法:

 //
 //  Slot called when the flick has started
 //
 onFlickStarted: {
     refreshFlik = atYBeginning
 }

 //
 //  Slot called when the flick has finished
 //
 onFlickEnded: {
     if ( atYBeginning && refreshFlik )
     {
         refresh()
     }
 }

它看起来像预期的那样工作,而且很容易实现

这实际上相当容易实现。如果您在设想实现方面有困难,我想说,在开始使用它之前,您还有一些需要学习的地方。我不是要代码,我甚至不喜欢它。只是需要一些关于实现的一般性解释。。。一些方向。我现在是个新手,你知道当你不知道做smth需要什么工具时,学习起来很难。这里有经验的开发人员给出的答案帮助很大。
顺便说一句,谢谢你的建议。只需使用z定位和锚定将图像放置在列表或任何东西上。将MouseArea放入其中以获取onClick事件并刷新列表。OP询问的是这样一个动画:,虽然它在QML中是可行的,但它肯定不是微不足道的。检查是一个很好的起点。至于箭头的渲染,无论是画布还是Qt5.10中的新形状模块看起来都不错。谢谢@GrecKo。不幸的是,我的Qt版本是5.7,但Flickable.verticalOverShoot是在v中引入的。5.9. 我真的在考虑升级。到目前为止,尽管将boundsBehavior设置为stopAtBounds,但我无法检查ListView的隐式垂直位置,因为它总是在顶部有边界。知足也变得毫无用处。这实际上相当容易实现。如果您在设想实现方面有困难,我想说,在开始使用它之前,您还有一些需要学习的地方。我不是要代码,我甚至不喜欢它。只是需要一些关于实现的一般性解释。。。一些方向。我现在是个新手,你知道当你不知道做smth需要什么工具时,学习起来很难。这里有经验的开发人员给出的答案帮助很大。顺便说一句,谢谢你的建议。只需使用z定位和锚定将图像放置在列表或任何东西上。将MouseArea放入其中以获取onClick事件并刷新列表。OP询问的是这样一个动画:,虽然它在QML中是可行的,但它肯定不是微不足道的。检查是一个很好的起点。至于箭头的渲染,无论是画布还是Qt5.10中的新形状模块看起来都不错。谢谢@GrecKo。不幸的是,我的Qt版本是5.7,但Flickable.verticalOverShoot是在v中引入的。5.9. 我真的在考虑升级。到目前为止,尽管将boundsBehavior设置为stopAtBounds,但我无法检查ListView的隐式垂直位置,因为它总是在顶部有边界。contentY也变得毫无用处。不过它确实有超调:即使boundsMovement是可Flickable,也会报告超调距离。StopAtBounds.OK,与文档所述相反,StopAtBounds不会报告超调。至少在我使用的5.9.1中没有。当超调球出现时会报告,但仅用于轻弹,不用于拖动,对于DragOverBounds会报告,但不用于轻弹,内容不会停留在边界上,当然,对于DragAndOvershootBounds会报告,但StopAtBounds是需要的,超调值不会随它而变化,它始终为0。看起来它与boundsMovement有关,boundsMovement是5.10版本的东西。事实上,它是在5.10版本中添加的,对于任何使用5.10+的人来说,它将提供一种简单的方法来获得90%的期望行为。对我来说,这不是一个选择,因为回归。当然,如果没有对用户交互的明确控制,就不可能获得准确的行为。不过,它确实有过冲:即使boundsMovement是Flickable,也会报告过冲距离。StopAtBounds.OK,与文档所述相反,StopAtBounds不会报告过冲。至少在我使用的5.9.1中没有。当超调球出现时会报告,但仅用于轻弹,不用于拖动,对于DragOverBounds会报告,但不用于轻弹,内容不会停留在边界上,当然,对于DragAndOvershootBounds会报告,但StopAtBounds是需要的,超调值不会随它而变化,它始终为0。看起来它与boundsMovement有关,boundsMovement是5.10版本的东西。事实上,它是在5.10版本中添加的,对于任何使用5.10+的人来说,它将提供一种简单的方法来获得90%的期望行为。对我来说,这不是一个选择,因为回归。当然,如果没有对用户交互的明确控制,就不可能获得准确的行为。