在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-
發佈留言