简体   繁体   English

通过 MouseArea Qt QML 检测向左或向右滑动

[英]Detect left or right swipe through MouseArea Qt QML

I've the problem, how to detect swipe through MouseArea in qml ?我有问题,如何检测 qml 中的 MouseArea 滑动?

This code, from documentation:此代码,来自文档:

    Rectangle {
    id: container
    width: 600; height: 200

    Rectangle {
        id: rect
        width: 500; height: 500

        MouseArea {
            anchors.fill: parent
            drag.target: rect
            drag.axis: Drag.XAxis
            drag.minimumX: 0
            drag.maximumX: container.width - rect.width

            //event slide here ?
        }
    }
}

but i didn't understood how to get left or right swipe, i get this use in the mobile application (ios & android).但我不明白如何向左或向右滑动,我在移动应用程序(ios 和 android)中得到了这个用途。

Somebody can help me ?有人可以帮助我吗? Thanks.谢谢。 How to detect left or right swipe with finger?如何检测用手指向左或向右滑动? Thanks.谢谢。

Just like derM explained the series of an event : Touch -> Movement -> Release就像 derM 解释了一系列事件: Touch -> Movement -> Release
Here is my solution to that concept.这是我对这个概念的解决方案。 Working Pretty Fine!!工作很好!

MouseArea {
            width: 100
            height: 50
            preventStealing: true
            property real velocity: 0.0
            property int xStart: 0
            property int xPrev: 0
            property bool tracing: false
            onPressed: {
                xStart = mouse.x
                xPrev = mouse.x
                velocity = 0
                tracing = true
                console.log( " pressed  "+xStart)
                console.log( " pressed  "+xPrev)
            }
            onPositionChanged: {
                if ( !tracing ) return
                var currVel = (mouse.x-xPrev)
                velocity = (velocity + currVel)/2.0
                xPrev = mouse.x
                if ( velocity > 15 && mouse.x > parent.width*0.2 ) {
                    tracing = false
                    console.log( " positioned  ")
                    // SWIPE DETECTED !! EMIT SIGNAL or DO your action
                }
            }
            onReleased: {
                tracing = false
                if ( velocity > 15 && mouse.x > parent.width*0.2 ) {
                    // SWIPE DETECTED !! EMIT SIGNAL or DO your action
                    console.log("abcccccccccc")
                }
            }
        }
    }

A swipe is a simple chain of events:滑动是一个简单的事件链:

Touch -> Movement -> Release

So this is exactly how you detect it:所以这正是你检测它的方式:

You initialize some variables (eg originX/Y) onPressed , then you detect movement onPositionChanged , calculate the vector between origin and current position, analyze length and direction to evaluate the direction and velocity.您初始化一些变量(例如 originX/Y) onPressed ,然后检测运动onPositionChanged ,计算原点和当前位置之间的向量,分析长度和方向以评估方向和速度。 Set originX/Y to the new position and continue until onReleased .originX/Y设置为新位置并继续直到onReleased Then you can determine whether it was a swipe (depending on the last vector or on the history of the movement - store or accumulate the calculated vectors in some way for this)然后您可以确定它是否是滑动(取决于最后一个向量或运动的历史 - 为此以某种方式存储或累积计算的向量)

Things you need to consider: The last movement might be short for the user slows down just before release or because in between two steps he releases.你需要考虑的事情:最后一个动作可能很短,因为用户在释放之前放慢了速度,或者因为在两个步骤之间他释放了。 So considering only the last vector might yield bad results.所以只考虑最后一个向量可能会产生不好的结果。

Instead you might accumulate the last n vectors, applying some weight.相反,您可能会累积最后的 n 个向量,并应用一些权重。

Also you might improve if you replace the onPositionChanged by a Timer to have longer intervals to analyze.此外,如果您将onPositionChanged替换为 Timer 以获得更长的分析间隔,您可能会有所改善。 You can play with the interval to find a optimal behavior.您可以使用区间来找到最佳行为。

For it is not trivial to implement a finely tuned algorithm for the detection, I recommend to reconsider, whether it is necessary to implement the detection yourself, or whether on of the many Item s that have a swipe behavior implemented might suffice.因为为检测实现一个微调算法并非易事,我建议重新考虑,是否有必要自己实现检测,或者是否有许多Item实现了滑动行为就足够了。

There's an easier way, you can just use an empty Flickable.有一个更简单的方法,你可以只使用一个空的 Flickable。 You can then look at the flicking flags to see if the user has flicked it (swiped it).然后,您可以查看轻弹标志以查看用户是否轻弹它(刷过它)。

Here is my implementation using Flickable (inspired by Stuart's answer):这是我使用Flickable的实现(受 Stuart 的回答启发):

Flickable {
    id: swipeArea
    ...
    
    flickableDirection: Flickable.HorizontalFlick
    onFlickStarted: {
        if (horizontalVelocity < 0) {
            console.log("swiped right")
        }
        if (horizontalVelocity > 0) {
            console.log("swiped left")
        }
    }
    boundsMovement: Flickable.StopAtBounds
    pressDelay: 0
    
    // children
}

Just make sure any controls you have are children of the Flickable , since press events won't be propagated to items below a Flickable .只需确保您拥有的任何控件都是Flickable的子级,因为新闻事件不会传播到Flickable下的项目。

    MouseArea {
        property variant previousPosition: 0
        property variant direction: 0

        anchors.fill: parent;

        onPressed: {
            previousPosition = mouseX;
            direction = 0;
            console.debug("onPressed mouseX:" + mouseX);
        }

        onPositionChanged: {
            if(previousPosition > mouseX){
                direction = 1;
            }
            else if(previousPosition < mouseX){
                direction = -1;
            }
            else{
                direction = 0;
            }
            previousPosition = mouseX;
        }

        onReleased: {
            if(direction > 0){
                console.debug("swipe to right");
            }
            else if(direction < 0){
                console.debug("swipe to left");
            }
            else{
                console.debug("swipe no detected");
            }
        }
    }

Here my five cents:这是我的五分钱:

MouseArea {

signal sgSwipeLeft();
signal sgSwipeRight();
signal sgSwipeDown();
signal sgSwipeUp();

QtObject {

    property bool pTracing: false;
    property real pXVelocity: 0.0;
    property real pYVelocity: 0.0;
    property int pXPrev: 0;
    property int pYPrev: 0;

    id: oPrivate;
}

id: oRoot;
preventStealing: true;

onPressed: {

    oPrivate.pTracing = true;
    oPrivate.pXVelocity = 0;
    oPrivate.pYVelocity = 0;
    oPrivate.pXPrev = mouse.x;
    oPrivate.pYPrev = mouse.y;
}

onPositionChanged: {

    if (!oPrivate.pTracing) return;

    var oCurrentXVelocity = (mouse.x - oPrivate.pXPrev);
    oPrivate.pXVelocity = (oPrivate.pXVelocity + oCurrentXVelocity) / 2.0;
    oPrivate.pXPrev = mouse.x;

    var oCurrentYVelocity = (mouse.y - oPrivate.pYPrev);
    oPrivate.pYVelocity = (oPrivate.pXVelocity + oCurrentYVelocity) / 2.0;
    oPrivate.pYPrev = mouse.y;

    if (oPrivate.pXVelocity > 15 && mouse.x > parent.width * 0.2) {
        oPrivate.pTracing = false;
        oRoot.sgSwipeRight();
    } else if (oPrivate.pXVelocity < -15 && mouse.x > parent.width * 0.2) {
        oPrivate.pTracing = false;
        oRoot.sgSwipeLeft();
    } else if (oPrivate.pYVelocity > 15 && mouse.y > parent.height * 0.2) {
        oPrivate.pTracing = false;
        oRoot.sgSwipeDown();
    } else if (oPrivate.pYVelocity < -15 && mouse.y < parent.height * 0.2) {
        oPrivate.pTracing = false;
        oRoot.sgSwipeUp();
    }
}

onClicked: console.log("onClicked");
onPressAndHold: console.log("onPressAndHold");
onSgSwipeLeft: console.log("onSgSwipeLeft");
onSgSwipeDown: console.log("onSgSwipeDown");
onSgSwipeRight: console.log("onSgSwipeRight");
onSgSwipeUp: console.log("onSgSwipeUp");
onReleased: console.log("onReleased");
}

I also had a problem with Drawer item (seems like QTBUG-59141 ) , so I implemented a simple flickable area which can be registered and created in QML (qmlRegisterType...).我也遇到了 Drawer item 的问题(看起来像QTBUG-59141 ),所以我实现了一个简单的可滑动区域,可以在 QML 中注册和创建(qmlRegisterType ...)。 Note that it only detects general flick direction, ie horizontal / vertical.请注意,它只检测一般的轻弹方向,即水平/垂直。 It should be simple to implement support for left-to-right or up-to-down (and vice versa) detection.实现对从左到右或从上到下(反之亦然)检测的支持应该很简单。 Basic example below.下面的基本示例。

// header part // 标题部分

class FrontendFlickArea : public QQuickItem
{
    Q_OBJECT

public:
    explicit FrontendFlickArea(QQuickItem *parent = nullptr);

protected:
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

signals:
    void horizontalFlick();
    void verticalFlick();

private:
    QElapsedTimer m_timer;

    QPoint m_pos;
    qint64 m_elapsedTime;

    QPair<qreal, qreal> m_velocitySum;
    int m_velocitySz;
};

// implementation part // 实现部分

#define DEBUG 0
#define VELOCITY_THRESH 2000 // pix / s
#define NSEC_TO_SEC_CONST 1E-9

#if DEBUG
#include <QDebug>
#endif

FrontendFlickArea::FrontendFlickArea(QQuickItem *parent)
    : QQuickItem(parent),
      m_timer(),
      m_pos(),
      m_elapsedTime(),
      m_velocitySum(),
      m_velocitySz(0)
{
    setAcceptedMouseButtons(Qt::LeftButton);
}

void FrontendFlickArea::mousePressEvent(QMouseEvent *event)
{
    m_pos = event->pos();

    if(m_timer.isValid())
        m_timer.invalidate();
    m_timer.start();

    m_velocitySum.first = m_velocitySum.second = 0.0;
    m_velocitySz = 0;
    m_elapsedTime = m_timer.nsecsElapsed();
}

void FrontendFlickArea::mouseMoveEvent(QMouseEvent *event)
{
    const QPoint diffPos = event->pos() - m_pos;
    const qint64 elapsed = m_timer.nsecsElapsed();

    const qreal timeDiff = static_cast<qreal>(elapsed - m_elapsedTime) * NSEC_TO_SEC_CONST;

    m_velocitySum.first += diffPos.x() / timeDiff;
    m_velocitySum.second += diffPos.y() / timeDiff;
    m_velocitySz++;

#if DEBUG
    qInfo() << "VelocityX: " << diffPos.x() / timeDiff << ", VelocityY: " << diffPos.y() / timeDiff;
#endif

    m_elapsedTime = elapsed;
    m_pos = event->pos();
}

void FrontendFlickArea::mouseReleaseEvent(QMouseEvent *event)
{
    if(m_velocitySz == 0)
        return;

    bool eventAccept = false;

    const qreal velocityX = m_velocitySum.first / m_velocitySz;
    const qreal velocityY = m_velocitySum.second / m_velocitySz;

#if DEBUG
    qInfo() << "Avg VelocityX: " << velocityX << ", Avg VelocityY: " << velocityY;
#endif

    if(qAbs(velocityX) > VELOCITY_THRESH) {
        eventAccept = true;
        emit horizontalFlick();
    }

    if(qAbs(velocityY) > VELOCITY_THRESH) {
        eventAccept = true;
        emit verticalFlick();
    }

    if(event->button() == Qt::LeftButton && eventAccept)
        event->accept();
}

EDIT: For best performance across devices with different pixel densities, it would be better to use density independent pixels instead of actual pixels for velocity threshold.编辑:为了在具有不同像素密度的设备上获得最佳性能,最好使用与密度无关的像素而不是实际像素作为速度阈值。

Swipe detection can also be achieved using DragHandler .滑动检测也可以使用DragHandler来实现。 In my case it was the simplest way to detect horizontal swipes on a vertical ScrollView / Flickable (I've added DragHandler as next sibling of ScrollView , not as its child).就我而言,这是检测垂直ScrollView / Flickable上水平滑动的最简单方法(我已将DragHandler添加为ScrollView的下一个兄弟,而不是其子项)。

DragHandler {
    property real lastHorizontalVelocity

    target: null
    xAxis.enabled: true
    yAxis.enabled: false

    // add this to ignore vertical scrolling of your Flickable, flick is id of the Flickable
    //enabled: !flick.moving

    onCentroidChanged: {
        if (centroid.position.x !== 0 || centroid.velocity.x !== 0) {
            lastHorizontalVelocity = centroid.velocity.x
            return
        }
        // 0.4 threshold was obtained by making lots of swipes to find an appropriate value
        if (Math.abs(lastHorizontalVelocity) < 0.4)
            return
        if (lastHorizontalVelocity < 0) {
            // handle swipe from right to left
        } else {
            // handle swipe from left to right
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM