在Android中,可以連接藍牙搖桿,也就是能把搖桿當作輸入裝置來使用。
Android本身就有支援搖桿,已經內建XYAB等按鍵資訊,所以我們可以快速的對應按鍵,並執行相對的功能。
通常搖桿有分方向鍵與其他的XYAB等按鍵,這邊在Android中是分開處理的,因為方向鍵有些包含類比墊,類比墊會是一個方向值,所以會與一般按鈕分開處理。
基本按鍵控制(A B X Y L R 等):
1.使用dispatchKeyEvent攔截事件
//攔截一般按鍵
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
//攔截按鍵自己處理
if(event?.source == 1281){//source是控制器編號,每種搖桿編號不一定一樣,所以內建的不一定適用
when(event.keyCode){//按下哪個鍵
KEYCODE_BUTTON_A -> {
if(event.action == ACTION_DOWN){//按下
}else if(event.action == ACTION_UP){//放開
}
}
KEYCODE_BUTTON_B -> {
}
KEYCODE_BUTTON_X -> {
}
KEYCODE_BUTTON_Y -> {
}
KEYCODE_BUTTON_L1 -> {
}
KEYCODE_BUTTON_R1 -> {
}
KEYCODE_BUTTON_SELECT -> {
}
KEYCODE_BUTTON_START -> {
}
}
return true
}else{//其他按鍵(如系統,pass)
return super.dispatchKeyEvent(event)
}
}
在這邊我們攔截KEYCODE_BUTTON_A等的事件,並且可以進行ACTION_DOWN按鈕按下或彈起的事件。
我們要在event?.source判斷事件的來源(來自哪個控制器),防止抓到不該抓的事件來源。
如果不知道控制器的ID,可以用以下方法把按鍵資訊全部印出來:
LogUtil.d("按鍵資訊", event.toString())
會顯示如下:
D/11:06:21.149/按鍵資訊: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_BUTTON_B, scanCode=305, metaState=0, flags=0x8, repeatCount=0, eventTime=496377584, downTime=496377584, deviceId=34, source=0x501, displayId=-1 }
其中,source為來源的控制器,可以使用這項資訊來過濾來源。
另外有個好用的repeatCount資訊,因有些手把支援按住連點,repeatCount可以計算目前這次按住的連點次數為多少!
另外還有按鈕按下時間downTime等資訊可以使用。
方向鍵(類比墊)控制:
1.建立onGenericMotionEvent攔截方向鍵事件
//攔截搖桿(或方向鍵)
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
// Check that the event came from a game controller
return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK && event.action == MotionEvent.ACTION_MOVE) {
// Process the movements starting from the
// earliest historical position in the batch
(0 until event.historySize).forEach { i ->
// Process the event at historical position i
processJoystickInput(event, i)
}
// Process the current movement sample in the batch (position -1)
processJoystickInput(event, -1)
true
} else {
super.onGenericMotionEvent(event)
}
}
2.對方向事件進行處理
private fun processJoystickInput(event: MotionEvent, historyPos: Int) {
val inputDevice = event.device
// Calculate the horizontal distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat axis, or the right control stick.
var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
}
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
}
// Calculate the vertical distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat switch, or the right control stick.
var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
}
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
}
// Update the ship object based on the new x and y values
// 按下方向鍵後的判斷結論,會有XY軸向的比例(-1~1,0就是沒動)
Log.e("process", "x=$x y=$y")
//這邊以一般按鍵型十字鍵為例,十字鍵只有-1,0,1
if(x == -1f){//按下上
//動作
}else if(x == 0f){//放開
//動作
}else if(x == 1f){//按下下
//動作
}
}
其中使用getCenteredAxis判斷移動範圍程度,程式碼如下:
private fun getCenteredAxis(event: MotionEvent, device: InputDevice, axis: Int, historyPos: Int): Float {
val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)
// A joystick at rest does not always report an absolute position of
// (0,0). Use the getFlat() method to determine the range of values
// bounding the joystick axis center.
range?.apply {
val value: Float = if (historyPos < 0) {
event.getAxisValue(axis)
} else {
event.getHistoricalAxisValue(axis, historyPos)
}
// Ignore axis values that are within the 'flat' region of the
// joystick axis center.
if (Math.abs(value) > flat) {
return value
}
}
return 0f
}
在某些場景中,有時使用onGenericMotionEvent時會攔截不到完整的按鍵訊息,有可能是因為被其他View吃掉事件了,例如網頁太大,就會吃掉方向鍵來用作移動網頁畫面,這時就需要在WebView做setOnGenericMotionListener來監聽onGenericMotion即可,其他元件意思也是一樣。
-END-
發佈留言