我正在编写一个QML程序,该程序本质上是一个4x4 GridView,其中填充有编号的矩形。我希望能够:
从网格中交换两个元素,拖放
仅允许直接相邻元素交换
我当前的问题是,一旦我将一个元素拖到另一个元素的上方,整个网格就会调整位置,以填充元素原来所在的间隙。有什么方法可以避免这种类型的网格的自动调整行为?
我知道下面的代码可能是造成这种行为的原因,我只是想不出如何以适当的方式更改它。
DropArea {
anchors { fill: parent; margins: 15 }
onEntered: {visualModel.items.move(drag.source.visualIndex, delegateRoot.visualIndex)}
}
完整代码:
import QtQuick 2.0
import QtQml.Models 2.1
GridView {
id: root
width: 320; height: 480
cellWidth: 80; cellHeight: 80
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }//Animação anima a transicao dos tiles
}
model: DelegateModel {
id: visualModel
model: ListModel {
id: colorModel
ListElement { color: "lightsteelblue" ; text: "1" }
ListElement { color: "lightsteelblue" ; text: "2" }
ListElement { color: "lightsteelblue" ; text: "3" }
ListElement { color: "lightsteelblue" ; text: "4" }
ListElement { color: "lightsteelblue" ; text: "5" }
ListElement { color: "lightsteelblue" ; text: "6" }
ListElement { color: "lightsteelblue" ; text: "7" }
ListElement { color: "lightsteelblue" ; text: "8" }
ListElement { color: "lightsteelblue" ; text: "9" }
ListElement { color: "lightsteelblue" ; text: "10" }
ListElement { color: "lightsteelblue" ; text: "11" }
ListElement { color: "lightsteelblue" ; text: "12" }
ListElement { color: "lightsteelblue" ; text: "13" }
ListElement { color: "lightsteelblue" ; text: "14" }
ListElement { color: "lightsteelblue" ; text: "15" }
ListElement { color: "transparent" }
}
delegate: MouseArea {
id: delegateRoot
property int visualIndex: DelegateModel.itemsIndex
width: 80; height: 80
drag.target: icon
Rectangle {
id: icon
Text {
text: model.text
font.pointSize: 30
anchors.centerIn: parent
}
width: 72; height: 72
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter
}
color: model.color
radius: 3
Drag.active: delegateRoot.drag.active
Drag.source: delegateRoot
Drag.hotSpot.x: 36
Drag.hotSpot.y: 36
states: [
State {
when: icon.Drag.active
ParentChange {
target: icon
parent: root
}
AnchorChanges {
target: icon;
anchors.horizontalCenter: undefined;
anchors.verticalCenter: undefined
}
}
]
}
DropArea {
anchors { fill: parent; margins: 15 }
onEntered: {visualModel.items.move(drag.source.visualIndex, delegateRoot.visualIndex)}
}
}
}
}
答案 0 :(得分:2)
我尝试了一些东西,但是到处都是错误。希望对您有所帮助。
import QtQuick 2.0
import QtQml.Models 2.1
GridView {
id: root
width: 320; height: 480
cellWidth: 80; cellHeight: 80
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }//Animação anima a transicao dos tiles
}
model: DelegateModel {
id: visualModel
model: ListModel {
id: colorModel
ListElement { color: "lightsteelblue" ; text: "1" }
ListElement { color: "lightsteelblue" ; text: "2" }
ListElement { color: "lightsteelblue" ; text: "3" }
ListElement { color: "lightsteelblue" ; text: "4" }
ListElement { color: "lightsteelblue" ; text: "5" }
ListElement { color: "lightsteelblue" ; text: "6" }
ListElement { color: "lightsteelblue" ; text: "7" }
ListElement { color: "lightsteelblue" ; text: "8" }
ListElement { color: "lightsteelblue" ; text: "9" }
ListElement { color: "lightsteelblue" ; text: "10" }
ListElement { color: "lightsteelblue" ; text: "11" }
ListElement { color: "lightsteelblue" ; text: "12" }
ListElement { color: "lightsteelblue" ; text: "13" }
ListElement { color: "lightsteelblue" ; text: "14" }
ListElement { color: "lightsteelblue" ; text: "15" }
ListElement { color: "transparent" ; text:"" }
}
delegate: MouseArea {
id: delegateRoot
property bool held: false
property int visualIndex: DelegateModel.itemsIndex
width: 80; height: 80
drag.target: held ? icon : undefined
drag.axis: Drag.XAndYAxis
drag.minimumX: delegateRoot.x-75
drag.minimumY: delegateRoot.y-75
drag.maximumX: delegateRoot.x + 85
drag.maximumY: delegateRoot.y + 85
onPressed: {
held = true
icon.opacity = 0.5
}
onReleased: {
if (held === true) {
held = false
icon.opacity = 1
icon.Drag.drop()
} else {
//action on release
}
}
Rectangle {
id: icon
Text {
text: model.text
font.pointSize: 30
anchors.centerIn: parent
}
width: 72; height: 72
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter
}
color: model.color
radius: 3
Drag.active: delegateRoot.drag.active
Drag.source: delegateRoot
Drag.hotSpot.x: 36
Drag.hotSpot.y: 36
states: [
State {
when: icon.Drag.active
ParentChange {
target: icon
parent: root
}
AnchorChanges {
target: icon;
anchors.horizontalCenter: undefined;
anchors.verticalCenter: undefined
}
}
]
}
DropArea {
anchors {
fill: parent
margins: 15
}
onDropped: {
var sourceNumber = colorModel.get(drag.source.visualIndex).text;
var targetNumber = colorModel.get(delegateRoot.visualIndex).text;
var sourceColor = colorModel.get(drag.source.visualIndex).color;
var targetColor = colorModel.get(delegateRoot.visualIndex).color;
colorModel.setProperty(drag.source.visualIndex, "text", targetNumber);
colorModel.setProperty(delegateRoot.visualIndex, "text", sourceNumber);
colorModel.setProperty(drag.source.visualIndex, "color", targetColor);
colorModel.setProperty(delegateRoot.visualIndex, "color", sourceColor);
}
}
}
}
}
答案 1 :(得分:1)
这里是DropArea
的实现,专门用于交换4x4网格中的相邻元素。参见下面的说明。
DropArea {
id: dropArea
anchors { fill: parent; margins: 15 }
onEntered: {
// store as local variables
var from = drag.source.visualIndex;
var to = delegateRoot.visualIndex;
console.log(from, "-->", to);
// `isAdjacent` is a function implemented below
if (isAdjacent(from, to))
console.warn("Yes, adjacent.");
else {
console.warn("No, not adjacent.");
// jump the gun, we don't care if they're not adjacent
return;
}
// normal move
visualModel.items.move(from, to);
// visualModel.items.move(drag.source.visualIndex, delegateRoot.visualIndex); // this is the same as the line above
// if `from`/`to` are horizontally adjacent (left/right)
// then the move is already valid
if (from % 4 < 3 && from + 1 === to) // check `to` is right of `from`
return;
if (from % 4 > 0 && from - 1 === to) // check `to` is left of `from`
return;
// move for vertically adjacent
if (from < 12 && from + 4 === to) // check `to` is below `from`
visualModel.items.move(to - 1, from); // CRUCIAL MOVE
if (from >= 4 && from - 4 === to) // check `to` is above `from`
visualModel.items.move(to + 1, from); // CRUCIAL MOVE
}
function isAdjacent(from, to) {
if (from % 4 < 3 && from + 1 === to) // check `to` is right of `from`
return true;
if (from % 4 > 0 && from - 1 === to) // check `to` is left of `from`
return true;
if (from < 12 && from + 4 === to) // check `to` is below `from`
return true;
if (from >= 4 && from - 4 === to) // check `to` is above from
return true;
return false;
}
}
实际的思考过程相当数学化。但是就在这里。
您可能会进行Google搜索并轻松找到一些东西。但我会一一解释这些条件。
// check `to` is right of `from`
from % 4 < 3 // first make sure that `from` is not on the last column
from + 1 === to // then check that `to` is on the next tile
// check `to` is left of `from`
from % 4 > 0 // first make sure that `from` is not on the first column
from - 1 === to // then check that `to` is on the previous tile
// check `to` is below `from`
from < 12 // first make sure that `from` is not on the last row
from + 4 === to // then check that `to` is four tiles to the right
// with the grid's wraparound, this will check if `to` is
// below `from`
// check `to` is above from
from >= 4 // first make sure that `from` is not on the first row
from - 4 === to // then check that `to` is four tiles to the left
// with the grid's wraparound, this will check if `to` is
// above `from`
我首先在纸上画一个2x2的网格会发生什么。对于水平相邻的元素,交换没有问题。唯一的问题是交换垂直相邻的元素。
Let X(i) -> Y(j) denote an object with display X at index i moving to index j, where index j was originally occupied by an object with display Y. The index of X becomes j, making it X(j) and object Y becomes displaced.
Consider if we want to swap B(1) with D(3) in a 2x2 grid.
+------+------+ +------+------+
| A(0) | B(1) | ← ↖ | A(0) | D(1) |
+------+------+ > --> +------+------+
| C(2) | D(3) | ← ↙ | C(2) | B(3) |
+------+------+ +------+------+
>>> User drags B(1) to D(3).
>>> var from = 1;
>>> var to = 3;
Just executing the command
>>> visualModel.items.move(from, to);
will give
B(1) -> D(3) -> C(2) -> (1)
i.e.
Object B goes to index 3. Displaces object D.
Object D goes to index 2. Displaces object C.
Object C goes to index 1, which is empty, since B was already moved.
And the result of the grid is
+------+------+
| A(0) | C(1) |
+------+------+
| D(2) | B(3) |
+------+------+
To achieve the desired result, we need to swap C(1) and D(2).
>>> visualModel.items.move(1, 2);
C(1) -> D(2) -> (1)
The result is shown below.
+------+------+
| A(0) | D(1) |
+------+------+
| C(2) | B(3) |
+------+------+
Now consider swapping in a 3x3 grid.
Consider swapping E(4) with H(7).
+------+------+------+ +------+------+------+
| A(0) | B(1) | C(2) | | A(0) | B(1) | C(2) |
+------+------+------+ +------+------+------+
| D(3) | E(4) | F(5) | --> | D(3) | H(4) | F(5) |
+------+------+------+ +------+------+------+
| G(6) | H(7) | I(8) | | G(6) | E(7) | I(8) |
+------+------+------+ +------+------+------+
>>> User drags E(4) to H(7)
>>> var from = 4;
>>> var to = 7;
>>> visualModel.items.move(from, to);
E(4) -> H(7) -> G(6) -> F(5) -> (4)
This results in
+------+------+------+
| A(0) | B(1) | C(2) |
+------+------+------+
| D(3) | F(4) | G(5) |
+------+------+------+
| H(6) | E(7) | I(8) |
+------+------+------+
To get our desired result, to get H(6) up to (4),
we need to simulate the user dragging H(6) to (4).
>>> visualModel.items.move(6, 4);
H(6) -> F(4) -> G(5) -> (6)
This achieves our desired result and gives us
+------+------+------+
| A(0) | B(1) | C(2) |
+------+------+------+
| D(3) | H(4) | F(5) |
+------+------+------+
| G(6) | E(7) | I(8) |
+------+------+------+
The crucial move here was with that second move command.
>>> visualModel.items.move(6, 4);
We can generalise that...
每当我们将项目向下从
from
向下移动到to
时,from + 1
和to
之间的所有瓦片都会 移至左侧。我们要交换的项目移至
to - 1
。因此,我们将to - 1
移至from
。 因此,我们获得visualModel.items.move(to - 1, from);
用于移动to
在以下from
以下的图块。
We've tried dragging with `from` < `to`.
I.e., we dragged from an upper row to a lower row.
But what if we were to drag from a lower row to an upper row?
I.e. `to` < `from`.
The grid and desired result is the same.
+------+------+------+ +------+------+------+
| A(0) | B(1) | C(2) | | A(0) | B(1) | C(2) |
+------+------+------+ +------+------+------+
| D(3) | E(4) | F(5) | --> | D(3) | H(4) | F(5) |
+------+------+------+ +------+------+------+
| G(6) | H(7) | I(8) | | G(6) | E(7) | I(8) |
+------+------+------+ +------+------+------+
But...
>>> User drags H(7) to E(4)
Note: previously, it was "User drags E(4) to H(7)".
Thus,
>>> var from = 7;
>>> var to = 4;
>>> visualModel.items.move(from, to);
H(7) -> E(4) -> F(5) -> G(6) -> (7)
The grid is then
+------+------+------+
| A(0) | B(1) | C(2) |
+------+------+------+
| D(3) | H(4) | E(5) |
+------+------+------+
| F(6) | G(7) | I(8) |
+------+------+------+
This time, simulating (6) moving up to (4), will gives us an incorrect grid.
We want to move E(5) down to (7).
>>> visualModel.items.move(5, 7);
E(5) -> G(7) -> F(6) -> (5)
This gives us
+------+------+------+
| A(0) | B(1) | C(2) |
+------+------+------+
| D(3) | H(4) | F(5) |
+------+------+------+
| G(6) | E(7) | I(8) |
+------+------+------+
任何时候,我们将一个项目从
from
移到to
上一个上,在to + 1
到from
之间的所有瓦片都会右移以填补空白。我们要交换的项目移至
to + 1
。因此,我们将to - 1
移至from
。 因此,我们得到visualModel.items.move(to + 1, from);
用于移动to
在上方from
的图块。
这已留给读者练习。
开个玩笑,您要做的就是更改条件检查。
if (from % width < width - 1 && from + 1 === to) // check `to` is right of `from`
// ...
if (from % width > 0 && from - 1 === to) // check `to` is left of `from`
// ...
if (from < (width * height - width) && from + width === to) // check `to` is below `from`
// ...
if (from >= width && from - width === to) // check `to` is above from
// ...
并且应该能够使用任何整数的宽度和高度。
垂直交换的动画不如水平交换的平滑或过渡。
乏味的答案。