針對折疊式裝置設計
透過集合功能整理內容
你可以依據偏好儲存及分類內容。

在 ConstraintLayout
2.1 版本,新增幾項功能以協助
管理摺疊式裝置,包括 SharedValues
ReactiveGuide
,並強化支援 MotionLayout
的動畫。
共用值
我們新增了在 ConstraintLayout
中插入執行階段值的新機制:
這個模式用於全系統的值,因為
「ConstraintLayout
」可以存取這個值。
在摺疊式裝置上,我們可以使用這項機制
折疊位置:
Kotlin
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
Java
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
在自訂輔助程式中,只要新增
任何變更:
Kotlin
val sharedValues: SharedValues = ConstraintLayout.getSharedValues()
sharedValues.addListener(mAttributeId, this)
Java
SharedValues sharedValues = ConstraintLayout.getSharedValues();
sharedValues.addListener(mAttributeId, this);
查看 FoldableExperimentals 範例
看看如何運用
Jetpack WindowManager 程式庫和插入
位置放入 ConstraintLayout
。
Kotlin
inner class StateContainer : Consumer<WindowLayoutInfo> {
override fun accept(newLayoutInfo: WindowLayoutInfo) {
// Add views that represent display features
for (displayFeature in newLayoutInfo.displayFeatures) {
val foldFeature = displayFeature as? FoldingFeature
if (foldFeature != null) {
if (foldFeature.isSeparating &&
foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
) {
// The foldable device is in tabletop mode
val fold = foldPosition(motionLayout, foldFeature)
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
} else {
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);
}
}
}
}
}
Java
class StateContainer implements Consumer<WindowLayoutInfo> {
@Override
public void accept(WindowLayoutInfo newLayoutInfo) {
// Add views that represent display features
for (DisplayFeature displayFeature : newLayoutInfo.getDisplayFeatures()) {
if (displayFeature instanceof FoldingFeature) {
FoldingFeature foldFeature = (FoldingFeature)displayFeature;
if (foldFeature.isSeparating() &&
foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL
) {
// The foldable device is in tabletop mode
int fold = foldPosition(motionLayout, foldFeature);
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
} else {
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);
}
}
}
}
}
fireNewValue()
會使用代表值的 ID 做為第一個參數,
要插入為第二個參數的值
ReactiveGuide

在版面配置中利用 SharedValue
的其中一種方式,而不必
就是使用 ReactiveGuide
輔助程式。即可根據
已連結「SharedValue
」。
<androidx.constraintlayout.widget.ReactiveGuide
android:id="@+id/fold"
app:reactiveGuide_valueId="@id/fold"
android:orientation="horizontal" />
這樣就能像一般指南一樣運用它。
MotionLayout
適用於摺疊式裝置
我們在 2.1 的 MotionLayout
中新增了幾項有助於變形的功能
這對摺疊式裝置特別有用,因為我們通常
必須處理各種可能的版面配置之間動畫效果
折疊式裝置有兩種方法:
- 在執行階段,更新目前的版面配置 (
ConstraintSet
),即可顯示或隱藏
摺疊位置。
- 對於每個需要的摺疊式裝置狀態,請使用個別的
ConstraintSet
支援 (closed
、folded
或 fully open
)。
為 ConstraintSet
建立動畫效果
MotionLayout
中的 updateStateAnimate()
函式已於 2.1 版中新增
版本:
Kotlin
fun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)
Java
void updateStateAnimate(int stateId, ConstraintSet set, int duration);
這個函式會在更新指定的
ConstraintSet
,這樣就不必執行立即更新 (也可透過
updateState(stateId, constraintset)
)。這樣一來,您就能更新
套用變更,例如當下的摺疊式裝置狀態。
MotionLayout
中的 ReactiveGuide
ReactiveGuide
在
MotionLayout
:
第一個指令會修改目前的 ConstraintSet
並加上動畫效果
。第二個做法是套用 ReactiveGuide
的新值
移至 MotionLayout
中的所有 ConstraintSet
。一般來說
摺疊式裝置建議您使用 ReactiveGuide
代表摺疊位置
設定相對於 ReactiveGuide
的版面配置元素。
使用多個 ConstraintSet
表示摺疊式裝置狀態
無須更新目前的 MotionLayout
狀態,改採其他架構的方式
建立特定獨立狀態 (包括
closed
、folded
和 fully open
)。

在這種情況下,建議您還是使用 ReactiveGuide
來代表
但相較於自動化功能
更新目前 ConstraintSet
時的動畫),其中說明各狀態會如何
轉換至其他容器
使用這個方法時,只要在 DeviceState
事件監聽器中將
使用 MotionLayout
轉換為特定狀態
MotionLayout.transitionToState(stateId)
敬上
方法。
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-27 (世界標準時間)。
[[["容易理解","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-07-27 (世界標準時間)。"],[],[],null,["# Designing for foldables\n\nIn the [`ConstraintLayout`](/reference/androidx/constraintlayout/widget/ConstraintLayout)\n2.1 release, several features were added to help\nmanage [foldable devices](/guide/topics/ui/foldables), including [`SharedValues`](/reference/androidx/constraintlayout/widget/SharedValues),\n[`ReactiveGuide`](https://github.com/androidx/constraintlayout/blob/d89c45dbb74bf19ad4834198a04af306696357bc/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java), and enhanced support for animation with [`MotionLayout`](/reference/androidx/constraintlayout/motion/widget/MotionLayout).\n\nShared Values\n-------------\n\nWe added a new mechanism to inject runtime values in `ConstraintLayout` --\nthis is intended to be used for system-wide values, as all instances of\n`ConstraintLayout` are able to access the value.\n\nIn the context of foldable devices, we can use this mechanism to inject the\nposition of the fold at runtime: \n\n### Kotlin\n\n```kotlin\nConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)\n```\n\n### Java\n\n```java\nConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);\n```\n\nIn a custom helper, you can access the shared values by adding a listener for\nany changes: \n\n### Kotlin\n\n```kotlin\nval sharedValues: SharedValues = ConstraintLayout.getSharedValues()\nsharedValues.addListener(mAttributeId, this)\n```\n\n### Java\n\n```java\nSharedValues sharedValues = ConstraintLayout.getSharedValues();\nsharedValues.addListener(mAttributeId, this);\n```\n\nYou can look at the [FoldableExperiments example](https://github.com/androidx/constraintlayout/blob/main/projects/FoldableExperiments/app/src/main/java/com/example/experiments/MainActivity.kt)\nto see how we capture the position of the fold using the\n[Jetpack WindowManager](/jetpack/androidx/releases/window) library and inject\nthe position into `ConstraintLayout`. \n\n### Kotlin\n\n```kotlin\ninner class StateContainer : Consumer\u003cWindowLayoutInfo\u003e {\n\n override fun accept(newLayoutInfo: WindowLayoutInfo) {\n\n // Add views that represent display features\n for (displayFeature in newLayoutInfo.displayFeatures) {\n val foldFeature = displayFeature as? FoldingFeature\n if (foldFeature != null) {\n if (foldFeature.isSeparating &&\n foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL\n ) {\n // The foldable device is in tabletop mode\n val fold = foldPosition(motionLayout, foldFeature)\n ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)\n } else {\n ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);\n }\n }\n }\n }\n}\n```\n\n### Java\n\n```java\nclass StateContainer implements Consumer\u003cWindowLayoutInfo\u003e {\n\n @Override\n public void accept(WindowLayoutInfo newLayoutInfo) {\n\n // Add views that represent display features\n for (DisplayFeature displayFeature : newLayoutInfo.getDisplayFeatures()) {\n if (displayFeature instanceof FoldingFeature) {\n FoldingFeature foldFeature = (FoldingFeature)displayFeature;\n if (foldFeature.isSeparating() &&\n foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL\n ) {\n // The foldable device is in tabletop mode\n int fold = foldPosition(motionLayout, foldFeature);\n ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);\n } else {\n ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, 0);\n }\n }\n }\n }\n}\n```\n\n`fireNewValue()` takes an ID representing the value as the first parameter and\nthe value to inject as the second parameter.\n\n`ReactiveGuide`\n---------------\n\nOne way to take advantage of a `SharedValue` in a layout, without having to\nwrite any code, is to use the [`ReactiveGuide`](https://github.com/androidx/constraintlayout/blob/main/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java)\nhelper. This will position a horizontal or vertical guideline according to the\nlinked `SharedValue`. \n\n \u003candroidx.constraintlayout.widget.ReactiveGuide\n android:id=\"@+id/fold\"\n app:reactiveGuide_valueId=\"@id/fold\"\n android:orientation=\"horizontal\" /\u003e\n\nIt can then be used as a you would with a normal guideline.\n\n`MotionLayout` for foldables\n----------------------------\n\nWe added several features in `MotionLayout` in 2.1 that helps morphing\nstate -- something particularly useful for foldables, as we typically\nhave to handle animating between the different possible layouts.\n\nThere are two approaches available for foldables:\n\n- At runtime, update your current layout (`ConstraintSet`) to show or hide the fold.\n- Use a separate `ConstraintSet` for each of the foldable states you want to support (`closed`, `folded`, or `fully open`).\n\n### Animating a `ConstraintSet`\n\nThe function `updateStateAnimate()` in `MotionLayout` was added in the 2.1\nrelease: \n\n### Kotlin\n\n```kotlin\nfun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)\n```\n\n### Java\n\n```java\nvoid updateStateAnimate(int stateId, ConstraintSet set, int duration);\n```\n\nThis function will automatically animate the changes when updating a given\n`ConstraintSet` instead of doing an immediate update (which you can do with\n`updateState(stateId, constraintset)`). This allows you to update your UI on\nthe fly, depending on changes, such as which foldable state you are in.\n\n### `ReactiveGuide` inside a `MotionLayout`\n\n`ReactiveGuide` also supports two useful attributes when used inside a\n`MotionLayout`:\n\n- `app:reactiveGuide_animateChange=\"true|false\"`\n\n- `app:reactiveGuide_applyToAllConstraintSets=\"true|false\"`\n\nThe first one will modify the current `ConstraintSet` and animate the change\nautomatically. The second one will apply the new value of the `ReactiveGuide`\nposition to all `ConstraintSet`s in the `MotionLayout`. A typical approach for\nfoldables would be to use a `ReactiveGuide` representing the fold position,\nsetting up your layout elements relative to the `ReactiveGuide`.\n\n### Using multiple `ConstraintSet`s to represent foldable state\n\nInstead of updating the current `MotionLayout` state, another way to architect\nyour UI to support foldables is to create specific separate states (including\n`closed`, `folded`, and `fully open`).\n\nIn this scenario, you might still want to use a `ReactiveGuide` to represent the\nfold, but you would have a lot more control (compared to the automated\nanimation when updating the current `ConstraintSet`) on how each state would\ntransition into another.\n\nWith this approach, in your `DeviceState` listener, you would simply direct the\n`MotionLayout` to transition to specific states through the\n[`MotionLayout.transitionToState(stateId)`](/reference/androidx/constraintlayout/motion/widget/MotionLayout#transitionToState(int))\nmethod."]]