針對折疊式裝置設計

ConstraintLayout 2.1 版本中,新增多項功能來協助管理折疊式裝置,包括 SharedValuesReactiveGuide,以及加強對 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);

您可以查看 FoldableExperiments 範例,瞭解我們如何使用 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 適用於折疊式裝置

我們在 MotionLayout 2.1 版中新增了多項功能,協助調整變形狀態,特別適合折疊式裝置使用,因為我們通常必須處理不同可能版面配置之間的動畫。

折疊式裝置可使用以下兩種方式:

  • 在執行階段更新目前的版面配置 (ConstraintSet) 以顯示或隱藏折疊。
  • 針對您要支援的每個折疊式裝置狀態 (closedfoldedfully open) 使用獨立的 ConstraintSet

建立 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) 執行)。這可讓您根據變更 (例如目前所在的折疊式狀態) 即時更新 UI。

MotionLayout 中的 ReactiveGuide

ReactiveGuide 也支援在 MotionLayout 中使用時有兩個實用的屬性:

  • app:reactiveGuide_animateChange="true|false"

  • app:reactiveGuide_applyToAllConstraintSets="true|false"

第一個選項會修改目前的 ConstraintSet,並自動為變更加上動畫效果。第二個值會將 ReactiveGuide 位置的新值套用至 MotionLayout 中的所有 ConstraintSet。折疊式裝置的常見做法是使用代表折疊位置的 ReactiveGuide,設定相對於 ReactiveGuide 的版面配置元素。

使用多個 ConstraintSet 代表折疊式裝置狀態

如要架構支援折疊式裝置的 UI,與其更新目前的 MotionLayout 狀態,另一種做法是建立特定的獨立狀態 (包括 closedfoldedfully open)。

在這種情況下,您可能仍希望使用 ReactiveGuide 代表折疊位置,但相較於更新目前的 ConstraintSet 時的自動化動畫,您可以進一步掌控各狀態如何轉換為其他狀態。

使用這種方法時,在 DeviceState 事件監聽器中,您只須透過 MotionLayout.transitionToState(stateId) 方法,將 MotionLayout 導向特定狀態。