[英]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.