处理音频眼镜和带屏眼镜的实体输入

适用的 XR 设备
本指南可帮助您为以下类型的 XR 设备打造优质体验。
音频和
显示眼镜

如需捕获并处理传递到应用投影 activity 的用户输入,或实现自定义触控板行为,请使用间接指针输入。如需处理其他类型的事件(例如相机操作),请参阅本指南中的以下部分。

处理相机操作

每当用户按下设备的相机按钮时,系统都会将输入事件作为 ProjectedInputEvent.ProjectedInputAction.TOGGLE_APP_CAMERA 操作传递给应用的投影 activity。您的应用可以使用专用调度程序来观察此操作:

@OptIn(ExperimentalProjectedApi::class)
class CameraActionInputSnippetActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe hardware camera action events when RESUMED
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.RESUMED) {
                withContext(Dispatchers.Default) {
                    try {
                        val projectedActivity = ProjectedActivityCompat.create(this@CameraActionInputSnippetActivity)
                        try {
                            projectedActivity.projectedInputEvents.collect { inputEvent ->
                                if (inputEvent.inputAction == TOGGLE_APP_CAMERA) {
                                    withContext(Dispatchers.Main) {
                                        onCameraActionTriggered()
                                    }
                                }
                            }
                        } finally {
                            projectedActivity.close()
                        }
                    } catch (e: CancellationException) {
                        throw e
                    } catch (e: Exception) {
                        Log.w("CameraActionSnippet", "ProjectedActivityCompat unavailable: ${e.message}")
                    }
                }
            }
        }

        setContent {
            GlimmerTheme {
                TouchpadInputSnippet(
                    onTap = { Log.i("CameraActionSnippet", "Touchpad tapped") }
                )
            }
        }
    }

    private fun onCameraActionTriggered() {
        Log.i("CameraActionSnippet", "Camera action button double-pressed!")
    }
}

检查通用动作事件

使用 dispatchGenericMotionEvent 检查传递到应用投影 activity 的通用动作事件。通用运动事件在 activity 边界处传递,不需要聚焦的 Jetpack Compose Glimmer 组件。

MotionEvent 可以包括:

并非每个值都对每项操作有意义。在将原始事件视为已完成的手势之前,检查操作和来源。

class MotionGestureInputSnippetActivity : ComponentActivity() {

    override fun dispatchGenericMotionEvent(ev: MotionEvent): Boolean {
        val actionName = when (ev.actionMasked) {
            MotionEvent.ACTION_DOWN -> "ACTION_DOWN" // First contact with the glasses touchpad
            MotionEvent.ACTION_MOVE -> "ACTION_MOVE" // Contact moving across the glasses touchpad
            MotionEvent.ACTION_UP -> "ACTION_UP"     // Contact lifted from the glasses touchpad
            else -> "ACTION_${ev.actionMasked}"
        }

        Log.d(
            "MotionGestureSnippet",
            "MotionEvent: action=$actionName x=${ev.x} y=${ev.y}"
        )

        // Delegate to super unless intentionally consuming the event
        return super.dispatchGenericMotionEvent(ev)
    }
}

仅当 activity 有意消耗事件时才返回 true。 否则,返回 super.dispatchGenericMotionEvent(ev),以便事件可以通过正常调度继续进行。

处理系统返回手势

当屏幕需要自定义返回行为时,您的应用应使用与其他 activity 相同的 Android API(例如 OnBackPressedDispatcherOnBackPressedCallback)来处理眼镜的返回手势。

用户手势取决于眼镜类型:

  • 在显示型眼镜上,在触控板上向下滑动会执行系统返回手势。
  • 在无屏眼镜上,在触控板上向后滑动会执行系统“返回”手势。

当当前屏幕需要拦截返回操作时(例如关闭叠加层或显示退出确认对话框),注册返回回调。

class BackGestureInputSnippetActivity : ComponentActivity() {

    private val backCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            onBackGestureReceived()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Register back gesture callback with Activity's onBackPressedDispatcher
        onBackPressedDispatcher.addCallback(this, backCallback)
    }

    private fun onBackGestureReceived() {
        Log.i("BackGestureSnippet", "System back gesture intercepted")
    }
}

仅当活动屏幕应拦截返回时,才应启用回调。当默认系统或导航行为应处理返回时,停用或移除回调。

后续步骤

为这些事件实现输入处理后,您可以在以下主题中详细了解如何处理其他类型的输入事件: