簡體   English   中英

通過 MouseArea Qt QML 檢測向左或向右滑動

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

我有問題,如何檢測 qml 中的 MouseArea 滑動?

此代碼,來自文檔:

    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 ?
        }
    }
}

但我不明白如何向左或向右滑動,我在移動應用程序(ios 和 android)中得到了這個用途。

有人可以幫助我嗎? 謝謝。 如何檢測用手指向左或向右滑動? 謝謝。

就像 derM 解釋了一系列事件: Touch -> Movement -> Release
這是我對這個概念的解決方案。 工作很好!

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")
                }
            }
        }
    }

滑動是一個簡單的事件鏈:

Touch -> Movement -> Release

所以這正是你檢測它的方式:

您初始化一些變量(例如 originX/Y) onPressed ,然后檢測運動onPositionChanged ,計算原點和當前位置之間的向量,分析長度和方向以評估方向和速度。 originX/Y設置為新位置並繼續直到onReleased 然后您可以確定它是否是滑動(取決於最后一個向量或運動的歷史 - 為此以某種方式存儲或累積計算的向量)

你需要考慮的事情:最后一個動作可能很短,因為用戶在釋放之前放慢了速度,或者因為在兩個步驟之間他釋放了。 所以只考慮最后一個向量可能會產生不好的結果。

相反,您可能會累積最后的 n 個向量,並應用一些權重。

此外,如果您將onPositionChanged替換為 Timer 以獲得更長的分析間隔,您可能會有所改善。 您可以使用區間來找到最佳行為。

因為為檢測實現一個微調算法並非易事,我建議重新考慮,是否有必要自己實現檢測,或者是否有許多Item實現了滑動行為就足夠了。

有一個更簡單的方法,你可以只使用一個空的 Flickable。 然后,您可以查看輕彈標志以查看用戶是否輕彈它(刷過它)。

這是我使用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
}

只需確保您擁有的任何控件都是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");
            }
        }
    }

這是我的五分錢:

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");
}

我也遇到了 Drawer item 的問題(看起來像QTBUG-59141 ),所以我實現了一個簡單的可滑動區域,可以在 QML 中注冊和創建(qmlRegisterType ...)。 請注意,它只檢測一般的輕彈方向,即水平/垂直。 實現對從左到右或從上到下(反之亦然)檢測的支持應該很簡單。 下面的基本示例。

// 標題部分

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;
};

// 實現部分

#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();
}

編輯:為了在具有不同像素密度的設備上獲得最佳性能,最好使用與密度無關的像素而不是實際像素作為速度閾值。

滑動檢測也可以使用DragHandler來實現。 就我而言,這是檢測垂直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