文章目錄
- 一、演示
- 二、實作思路
- 三、核心代碼
- 四、Android 配置
- 1. 創建 AndroidManifest.xml
- 2. 添加 icon
- 3. 修改為全屏

羋月鎮樓!
GitHub 地址: QmlLearningPro ,選擇子工程 VirtualJoystick.pro
QML 其它文章請點擊這里: QT QUICK QML 學習筆記
一、演示
本文參考 QGroundControl 地面站的虛擬操作桿部分,它使用了左右兩個遙控共四通道,實作無人機的橫滾、俯仰、航向和油門的控制,
而在本文中,左右遙控分別控制一個小狗狗和小貓咪的移動,而且確保拇指放下的地方為操作桿的中心, 如下演示
Android 平臺下:

Windows 平臺下:

二、實作思路
核心思路如下:

其中核心控制元件為 MultiPointTouchArea:
MultiPointTouchArea 為 qml 中的多點觸摸提供了最基本、最重要的支持,它與TouchPoint及相關域結合,可以說是qml中多點觸摸的基石,
MultiPointTouchArea是不可見元素,它用來跟蹤多點觸摸,從 Item 繼承過來的 enabled 屬性用來標識觸點操作是否有效,如果該屬性為false,則觸摸區域將忽略滑鼠以及觸摸事件,
默認情況下,滑鼠的處理方式與單個觸摸點的處理方式相同,觸摸區域下的專案不會接識訓鼠事件,因為觸摸區域正在處理它們, 但是,如果 mouseEnabled 屬性設定為false,則它對滑鼠事件變得透明,以便可以使用另一個滑鼠敏感項(例如MouseArea)分別處理滑鼠互動,
具體參考 QT 官方幫助檔案,以下為本專案中使用:
///--Multiple Point Touch Area, core: touchPoints
MultiPointTouchArea {
anchors.fill: parent
minimumTouchPoints: 1 //only one
maximumTouchPoints: 1 //only one
touchPoints: [ TouchPoint { id: touchPoint } ]
onPressed: _joyRoot.thumbDown(touchPoints)
onReleased: _joyRoot.reCenter()
//border visible
Rectangle {
border.color: "#A6FFA6"
border.width: 2
color: "transparent"
anchors.fill: parent
}
}
滑鼠或者拇指的觸控的點的輸入坐標為:touchPoint.x、touchPoint.y,
三、核心代碼
遙控中(輸入)核心代碼: JoystickThumbPad.qml
import QtQuick.Window 2.12
import QtQuick 2.12
import QtQuick.Controls 1.2
Item {
id: _joyRoot
///--Input:
property real imageHeight: 10
///--Output:xAxis、yAxis、xPositionDelta、yPositionDelta
property real xAxis: 0 ///< Value range [-1,1], negative values left stick, positive values right stick
property real yAxis: 0 ///< Value range [-1,1], negative values down stick, positive values up stick
property real xPositionDelta: 0 ///< Amount to move the control on x axis ( [-50,50] )
property real yPositionDelta: 0 ///< Amount to move the control on y axis ( [-50,50] )
property real _centerXY: width / 2
property bool _processTouchPoints: false
property color _fgColor: "black"
property real _hatWidth: 15
property real _hatWidthHalf: _hatWidth / 2
property real stickPositionX: _centerXY //Value range [0,width]
property real stickPositionY: _centerXY //Value range [0,height]
onWidthChanged: calculateXAxis()
onStickPositionXChanged: calculateXAxis()
onHeightChanged: calculateYAxis()
onStickPositionYChanged: calculateYAxis()
function calculateXAxis() {
//xAxis = ((stickPositionX / width) * 2 - 1)
xAxis = stickPositionX / width
}
function calculateYAxis() {
//yAxis = (1 - (stickPositionY / height) * 2)
yAxis = stickPositionY / height
}
///--Release the thumb and return to the center position
function reCenter() {
_processTouchPoints = false
// Move control back to original position
xPositionDelta = 0
yPositionDelta = 0
// Re-Center sticks as needed
stickPositionX = _centerXY
stickPositionY = _centerXY
}
///--Where the thumb is pressed, it is the center of the joystick
function thumbDown(touchPoints) {
// Position the control around the initial thumb position
console.log("touchPoints[0].x",touchPoints[0].x)
console.log("touchPoints[0].y",touchPoints[0].y)
xPositionDelta = touchPoints[0].x - _centerXY //[-50,50]
yPositionDelta = touchPoints[0].y - _centerXY //[-50,50]
// We need to wait until we move the control to the right position before we process touch points
_processTouchPoints = true
}
///--stickPositionX = touchPoint.x ; stickPositionY = touchPoint.y
Connections {
target: touchPoint
onXChanged: {
if (_processTouchPoints) {
_joyRoot.stickPositionX = Math.max(Math.min(touchPoint.x, _joyRoot.width), 0)
}
}
onYChanged: {
if (_processTouchPoints) {
_joyRoot.stickPositionY = Math.max(Math.min(touchPoint.y, _joyRoot.height), 0)
}
}
}
///--Multiple Point Touch Area, core: touchPoints
MultiPointTouchArea {
anchors.fill: parent
minimumTouchPoints: 1 //only one
maximumTouchPoints: 1 //only one
touchPoints: [ TouchPoint { id: touchPoint } ]
onPressed: _joyRoot.thumbDown(touchPoints)
onReleased: _joyRoot.reCenter()
//border visible
Rectangle {
border.color: "#A6FFA6"//"#E8FFF5"
border.width: 2
color: "transparent"
anchors.fill: parent
}
}
///--UI: inside circle + outer circle
...
///--UI: Up Down Left Right
...
///--UI: touch points
Rectangle {
width: _hatWidth
height: _hatWidth
radius: _hatWidthHalf
border.color: _fgColor
border.width: 1
color: Qt.rgba(_fgColor.r, _fgColor.g, _fgColor.b, 0.5)
x: stickPositionX - _hatWidthHalf //By default the middle
y: stickPositionY - _hatWidthHalf //By default the middle
}
}
狗狗貓咪(輸出):Output.qml
import QtQuick 2.12
import QtQuick.Controls 1.2
Item {
property real leftX
property real leftY
property real rightX
property real rightY
Rectangle {
id: dogRect
width: (parent.width - 50)/2
height: parent.height
color: "#FFE6D9"
border.width: 2
border.color: "black"
property real imageCenter: dogImage.width / 2
property real moveX: Math.max(Math.min(leftX*width - imageCenter, width - dogImage.width) , 0)
property real moveY: Math.max(Math.min(leftY*height - imageCenter, height - dogImage.height), 0)
Image {
id: dogImage
mipmap: true
fillMode: Image.PreserveAspectFit
source: "/images/Dog.png"
x: dogRect.moveX
y: dogRect.moveY
}
}
Rectangle {
id: catRect
width: (parent.width - 50)/2
height: parent.height
color: "#FFE6D9"
anchors.left: dogRect.right
anchors.leftMargin: 50
border.width: 2
border.color: "black"
property real imageCenter: catImage.width / 2
property real moveX: Math.max(Math.min(rightX*width - imageCenter , width - catImage.width) , 0)
property real moveY: Math.max(Math.min(rightY*height - imageCenter, height - catImage.height), 0)
Image {
id: catImage
mipmap: true
fillMode: Image.PreserveAspectFit
source: "/images/Cat.png"
x: catRect.moveX
y: catRect.moveY
}
}
}
根目錄和驅動器(觸發): main.qml
import QtQuick.Window 2.12
import QtQuick 2.12
import QtQuick.Controls 1.2
Window {
visible: true
width: 192 * 4
height: 108 * 4
color: "grey"
property real _offset: leftStick.width/2
JoystickThumbPad {
id: leftStick
anchors.leftMargin: xPositionDelta + _offset
anchors.bottomMargin: -yPositionDelta + _offset
anchors.left: parent.left
anchors.bottom: parent.bottom
width: 100
height: 100
imageHeight: 20
}
JoystickThumbPad {
id: rightStick
anchors.rightMargin: -xPositionDelta + _offset
anchors.bottomMargin: -yPositionDelta + _offset
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 100
height: 100
imageHeight: 20
}
Output {
id: output
x: 50
y: 10
height: parent.height - leftStick.height*2 - y*2
width: parent.width - x*2
}
///--You can also use signals
Timer {
interval: 50 // 20Hz
running: true
repeat: true
onTriggered: {
output.leftX = leftStick.xAxis
output.leftY = leftStick.yAxis
output.rightX = rightStick.xAxis
output.rightY = rightStick.yAxis
}
}
}
四、Android 配置
1. 創建 AndroidManifest.xml

打開后點擊默認


2. 添加 icon

3. 修改為全屏
然后用 “普通文本編輯器” 打開,修改 android:screenOrientation=“sensorLandscape” 默認為全屏,
小插曲: Android 上貓狗的圖片為 svg 的會報錯,修改為 png 格式就沒問題了,原因未知
GitHub 地址: QmlLearningPro ,選擇子工程 VirtualJoystick.pro
QML 其它文章請點擊這里: QT QUICK QML 學習筆記
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/291680.html
標籤:其他
