valnewConfig=session.config.copy(handTracking=Config.HandTrackingMode.BOTH)when(valresult=session.configure(newConfig)){isSessionConfigureConfigurationNotSupported->
TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)isSessionConfigureSuccess->TODO(/* Success! */)else->
TODO(/* A different unhandled exception was thrown. */)}
Hand.left(session)?.state?.collect{handState->// or Hand.right(session)// Hand state has been updated.// Use the state of hand joints to update an entity's position.renderPlanetAtHandPalm(handState)}
valpalmPose=leftHandState.handJoints[HandJointType.PALM]?:return// the down direction points in the same direction as the palmvalangle=Vector3.angleBetween(palmPose.rotation*Vector3.Down,Vector3.Up)palmEntity.setEnabled(angle > Math.toRadians(40.0))valtransformedPose=session.scene.perceptionSpace.transformPoseTo(palmPose,session.scene.activitySpace,)valnewPosition=transformedPose.translation+transformedPose.down*0.05fpalmEntity.setPose(Pose(newPosition,transformedPose.rotation))
valtipPose=rightHandState.handJoints[HandJointType.INDEX_TIP]?:return// the forward direction points towards the finger tip.valangle=Vector3.angleBetween(tipPose.rotation*Vector3.Forward,Vector3.Up)indexFingerEntity.setEnabled(angle > Math.toRadians(40.0))valtransformedPose=session.scene.perceptionSpace.transformPoseTo(tipPose,session.scene.activitySpace,)valposition=transformedPose.translation+transformedPose.forward*0.03fvalrotation=Quaternion.fromLookTowards(transformedPose.up,Vector3.Up)indexFingerEntity.setPose(Pose(position,rotation))
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-08-25 (世界標準時間)。"],[],[],null,["# Work with hands using ARCore for Jetpack XR\n\nARCore for Jetpack XR can provide information about the user's detected hands,\nand gives pose information for hands and their associated joints. This hand data\ncan be used to attach entities and models to a user's hands, for example, a tool\nmenu:\n\n\nYour browser doesn't support HTML video. Here is a\n[link to the video](/static/develop/xr/jetpack-xr-sdk/videos/watch.mp4) instead.\n\nObtain a session\n----------------\n\nAccess hand information through an Android XR `Session`. See [Understand a\nSession's lifecycle](/develop/xr/jetpack-xr-sdk/work-with-arcore#session-lifecycle) to obtain a [`Session`](/reference/kotlin/androidx/xr/runtime/Session).\n\nConfigure the session\n---------------------\n\nHand tracking is not enabled by default on XR sessions. To receive hand data,\nconfigure the session and set the [`HandTrackingMode.BOTH`](/reference/kotlin/androidx/xr/runtime/Config.HandTrackingMode#BOTH()) mode:\n\n\n```kotlin\nval newConfig = session.config.copy(\n handTracking = Config.HandTrackingMode.BOTH\n)\nwhen (val result = session.configure(newConfig)) {\n is SessionConfigureConfigurationNotSupported -\u003e\n TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)\n is SessionConfigureSuccess -\u003e TODO(/* Success! */)\n else -\u003e\n TODO(/* A different unhandled exception was thrown. */)\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L39-L48\n```\n\n\u003cbr /\u003e\n\n| **Note:** Tracking the user's hands requires the `android.permission.HAND_TRACKING` [runtime permission](/training/permissions/requesting) to be granted to your app.\n\nRetrieve hand data\n------------------\n\nHand data is available for left and right hands separately. Use each hand's\n`state` to access pose positions for each joint:\n\n\n```kotlin\nHand.left(session)?.state?.collect { handState -\u003e // or Hand.right(session)\n // Hand state has been updated.\n // Use the state of hand joints to update an entity's position.\n renderPlanetAtHandPalm(handState)\n}https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L55-L59\n```\n\n\u003cbr /\u003e\n\nHands have the following properties:\n\n- `trackingState`: whether or not the hand is being tracked.\n- `handJoints`: a map of hand joints to poses. Hand joint poses are specified\n by the [OpenXR standards](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#convention-of-hand-joints).\n\n\n Your browser doesn't support HTML video. Here is a\n [link to the video](/static/develop/xr/develop/videos/hand-debug.mp4) instead.\n\nUse hand data in your app\n-------------------------\n\nThe positions of a user's hand joints can be used to [anchor 3D objects](/develop/xr/jetpack-xr-sdk/work-with-arcore#attach-entity) to a\nuser's hands, for example, to attach a model to the left palm:\n\n\n```kotlin\nval palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return\n\n// the down direction points in the same direction as the palm\nval angle = Vector3.angleBetween(palmPose.rotation * Vector3.Down, Vector3.Up)\npalmEntity.setEnabled(angle \u003e Math.toRadians(40.0))\n\nval transformedPose =\n session.scene.perceptionSpace.transformPoseTo(\n palmPose,\n session.scene.activitySpace,\n )\nval newPosition = transformedPose.translation + transformedPose.down * 0.05f\npalmEntity.setPose(Pose(newPosition, transformedPose.rotation))https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L83-L95\n```\n\n\u003cbr /\u003e\n\n\nYour browser doesn't support HTML video. Here is a\n[link to the video](/static/develop/xr/jetpack-xr-sdk/videos/left.mp4) instead.\n\nOr to attach a model to your right hand's index finger tip:\n\n\n```kotlin\nval tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return\n\n// the forward direction points towards the finger tip.\nval angle = Vector3.angleBetween(tipPose.rotation * Vector3.Forward, Vector3.Up)\nindexFingerEntity.setEnabled(angle \u003e Math.toRadians(40.0))\n\nval transformedPose =\n session.scene.perceptionSpace.transformPoseTo(\n tipPose,\n session.scene.activitySpace,\n )\nval position = transformedPose.translation + transformedPose.forward * 0.03f\nval rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up)\nindexFingerEntity.setPose(Pose(position, rotation))https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L104-L117\n```\n\n\u003cbr /\u003e\n\n\nYour browser doesn't support HTML video. Here is a\n[link to the video](/static/develop/xr/jetpack-xr-sdk/videos/right.mp4) instead.\n\nDetect basic hand gestures\n--------------------------\n\nUse the poses of joints in the hand to detect basic hand gestures. Consult the\n[Conventions of hand joints](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#convention-of-hand-joints) to determine which range of poses\nthe joints should be in to register as a given pose.\n\nFor example, to detect a pinch with the thumb and the index finger, use the\ndistance between the two tip joints:\n\n\n```kotlin\nval thumbTip = handState.handJoints[HandJointType.THUMB_TIP] ?: return false\nval thumbTipPose = session.scene.perceptionSpace.transformPoseTo(thumbTip, session.scene.activitySpace)\nval indexTip = handState.handJoints[HandJointType.INDEX_TIP] ?: return false\nval indexTipPose = session.scene.perceptionSpace.transformPoseTo(indexTip, session.scene.activitySpace)\nreturn Vector3.distance(thumbTipPose.translation, indexTipPose.translation) \u003c 0.05https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L123-L127\n```\n\n\u003cbr /\u003e\n\nAn example of a more complicated gesture is the \"stop\" gesture. In this gesture,\neach finger should be outstretched, that is, each joint in each finger should\nroughly be pointing in the same direction:\n\n\n```kotlin\nval threshold = toRadians(angleInDegrees = 30f)\nfun pointingInSameDirection(joint1: HandJointType, joint2: HandJointType): Boolean {\n val forward1 = handState.handJoints[joint1]?.forward ?: return false\n val forward2 = handState.handJoints[joint2]?.forward ?: return false\n return Vector3.angleBetween(forward1, forward2) \u003c threshold\n}\nreturn pointingInSameDirection(HandJointType.INDEX_PROXIMAL, HandJointType.INDEX_TIP) &&\n pointingInSameDirection(HandJointType.MIDDLE_PROXIMAL, HandJointType.MIDDLE_TIP) &&\n pointingInSameDirection(HandJointType.RING_PROXIMAL, HandJointType.RING_TIP)https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L133-L141\n```\n\n\u003cbr /\u003e\n\nKeep the following points in mind when developing custom detection for hand\ngestures:\n\n- Users may have a different interpretation of any given gesture. For example, some may consider a \"stop\" gesture to have the fingers splayed out, while others may find it more intuitive to have the fingers close together.\n- Some gestures may be uncomfortable to maintain. Use intuitive gestures that don't strain a user's hands.\n\nDetermine the user's secondary hand\n-----------------------------------\n\nThe Android system places system navigation on the user's primary hand, as\nspecified by the user in system preferences. Use the secondary hand for your\ncustom gestures to avoid conflicts with system navigation gestures:\n\n\n```kotlin\nval handedness = Hand.getPrimaryHandSide(activity.contentResolver)\nval secondaryHand = if (handedness == Hand.HandSide.LEFT) Hand.right(session) else Hand.left(session)\nval handState = secondaryHand?.state ?: return\ndetectGesture(handState)https://github.com/android/snippets/blob/dd30aee903e8c247786c064faab1a9ca8d10b46e/xr/src/main/java/com/example/xr/arcore/Hands.kt#L72-L75\n```\n\n\u003cbr /\u003e\n\n*** ** * ** ***\n\nOpenXR™ and the OpenXR logo are trademarks owned\nby The Khronos Group Inc. and are registered as a trademark in China,\nthe European Union, Japan and the United Kingdom."]]