我正在寻求实现一个锁屏模式,可用于发现其他设置。为此,我使用
RoundButtons
生成了 3x3 的 Repeater
网格,如下所示:
Grid {
anchors.centerIn: parent
spacing: 10
columns: 3
rows: 3
Repeater {
id: rpt
model: 9
delegate: RoundButton {
radius: Math.max(width, height) / 2
}
}
}
如何检测手指被拖动到哪个按钮,以便将其添加到图案中?我看过
TapHandler
和 MouseArea
但我似乎不明白我可以使用哪些属性来检查按下的手指是否被拖动到按钮上。
如果缺少我应该添加的任何其他信息,请告诉我。谢谢。
这是用 QML
编写的
模式锁的示例。
我发现使用单个
PointHandler
(或 MouseArea
)并创建网格状触摸部分更简单,而不是找到一种方法来连续检测多个 MouseAreas
上的鼠标移动。可以将剩余的鼠标位置获取到图块的width
,然后获取鼠标相对于每个图块的位置,并检测鼠标是否进入内圈。
const [x,y] = [mousePos.x % tile.width, mousePos.y % tile.height];
/// Now, you have to find the distance of the x/y position from the tile center.
然后将鼠标位置除以图块宽度即可得到当前鼠标位置的区域。
const cIndex = Math.floor(mousePos.x / tile.width);
const rIndex /// same as cIndex..
array.push(rIndex * 3 + cIndex); /// j * 3 + i
最后,您可以将索引保存在数组中作为输入的模式。
注意:另外,值得一提的是,您应该考虑不要访问一个点两次。
上面的描述对于简单的功能来说已经足够了,但我提供了一个完整的示例。 您只需将其复制到
.qml
文件中并在程序中使用该组件即可。
您还可以在此处找到完整的源代码。
组件的用法如本例所示。
PatternLock {
anchors.fill: parent
onRelease: {
print(path);
reset();
}
}
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Shapes 1.15
Control {
id: control
component Circle: Rectangle {
x: (parent.width - width)/2
y: (parent.height - height)/2
width: 5; height: width; radius: width
color: control.palette.button
opacity: 0.8
}
signal press()
signal release()
property alias shapePath: shapePath
property real snapMargin: 10
property int columns: 3
property int rows: 3
/// Path of pattern
property var path: []
function reset() {
internals.visited = [];
control.path = [];
pathpoly.path = [];
}
/// Reposition points in case
onWidthChanged: Qt.callLater(internals.reposition);
onHeightChanged: Qt.callLater(internals.reposition);
QtObject {
id: internals
property var visited: Array(columns * rows).fill(false);
readonly property alias pos: pointhandler.point.position;
readonly property size subgrid: Qt.size(availableWidth/columns, availableHeight/rows);
/// Current location of the mouse.
readonly property point region: Qt.point(Math.floor(pos.x / subgrid.width),
Math.floor(pos.y / subgrid.height));
/// Mouse position relative to the center of the circles.
readonly property point subpos: Qt.point(pos.x % subgrid.width - subgrid.width/2,
pos.y % subgrid.height - subgrid.height/2);
/// NOTE: There is no way to hide the last PathLine item when the mouse is not pressed,
/// so it currently points to the end of the path, if it exists.
readonly property point trail: pointhandler.active ? pos : (pathpoly.path.slice(-1)[0] ?? Qt.point(0,0));
readonly property int currentIndex: distance(subpos) < snapMargin ? region.y * columns + region.x : -1
/// Returns the length of the point.
function distance(p: point): real {
return Math.sqrt(p.x * p.x + p.y * p.y);
}
/// Reposition all points of polypath if there is any resizing.
function reposition() {
if(path.length) {
const newPath = path.map(idx => {
const [i, j] = [idx % columns, Math.floor(idx/columns)];
return Qt.point((i + 0.5) * subgrid.width, (j + 0.5) * subgrid.height);
});
pathpoly.path = newPath;
}
}
}
contentItem: Item {
/// Grid of circles
Grid {
columns: control.columns
Repeater {
model: control.rows * control.columns
Item {
width: internals.subgrid.width
height: internals.subgrid.height
Circle {
width: index === internals.currentIndex ? 15 : 5
Behavior on width {NumberAnimation{}}
}
}
}
}
PointHandler {
id: pointhandler
onActiveChanged: {
if(active) control.press();
else control.release();
}
onPointChanged: {
if(active && internals.distance(internals.subpos) < control.snapMargin) {
const region = internals.region;
const index = region.y * columns + region.x;
const sg = internals.subgrid;
/// Check if the mouse position is valid and the point hasn't been visited yet.
if(!internals.visited[index] && 0 <= index && region.x < columns && region.y < rows) {
const point = Qt.point((region.x + 0.5) * sg.width, (region.y + 0.5) * sg.height);
internals.visited[index] = true;
control.path.push(index);
/// BUG: pathpoly.path.push() doesn't work on Qt 5.15.2
pathpoly.path = [...pathpoly.path, point];
}
}
}
}
Shape {
width: parent.width
height: parent.height
smooth: true
visible: pathpoly.path.length
ShapePath {
id: shapePath
strokeWidth: 1
strokeColor: control.palette.button
fillColor: 'transparent'
startX: (pathpoly.path[0] ?? {x:0}).x
startY: (pathpoly.path[0] ?? {y:0}).y
PathPolyline {
id: pathpoly
path: []
}
/// PathLine to the current mouse position
PathLine {
x: internals.trail.x
y: internals.trail.y
}
}
}
}
background: Rectangle {
color: palette.window
}
}