猛禽洛的程式筆記庫

[Android] 接收搖桿按鈕訊號

在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-

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *