ออกแบบมาเพื่ออุปกรณ์แบบพับได้

ในConstraintLayout รุ่น 2.1 มีการเพิ่มฟีเจอร์หลายอย่างเพื่อช่วย จัดการอุปกรณ์แบบพับได้ ซึ่งรวมถึง SharedValues ReactiveGuide และการรองรับภาพเคลื่อนไหวที่ดียิ่งขึ้นด้วย MotionLayout

ค่าที่ใช้ร่วมกัน

เราได้เพิ่มกลไกใหม่เพื่อแทรกค่ารันไทม์ใน ConstraintLayout – ใช้สำหรับค่าสำหรับทั้งระบบ ดังเช่น ConstraintLayout มีสิทธิ์เข้าถึงค่าดังกล่าว

ในบริบทของอุปกรณ์แบบพับได้ เราสามารถใช้กลไกนี้เพื่อแทรก ตำแหน่งของเส้นแบ่งหน้าขณะรันไทม์:

Kotlin

ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)

Java

ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);

ในผู้ช่วยที่กำหนดเอง คุณสามารถเข้าถึงค่าที่แชร์ได้โดยการเพิ่ม Listener สำหรับ การเปลี่ยนแปลงใดๆ:

Kotlin

val sharedValues: SharedValues = ConstraintLayout.getSharedValues()
sharedValues.addListener(mAttributeId, this)

Java

SharedValues sharedValues = ConstraintLayout.getSharedValues();
sharedValues.addListener(mAttributeId, this);

คุณสามารถดูตัวอย่าง Foldable Experiments เพื่อดูว่าเราจับภาพตำแหน่งเส้นแบ่งหน้าโดยใช้ ไลบรารี 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() จะใช้รหัสที่แสดงถึงค่านั้นเป็นพารามิเตอร์แรก และ ค่าที่จะแทรกเป็นพารามิเตอร์ที่ 2

ReactiveGuide

วิธีหนึ่งในการใช้ประโยชน์จาก SharedValue ในเลย์เอาต์โดยไม่ต้อง เขียนโค้ด คือการใช้ ReactiveGuide ผู้ช่วยอัตโนมัติ ซึ่งจะวางหลักเกณฑ์เป็นแนวนอนหรือแนวตั้งตาม ลิงก์เมื่อ SharedValue

    <androidx.constraintlayout.widget.ReactiveGuide
        android:id="@+id/fold"
        app:reactiveGuide_valueId="@id/fold"
        android:orientation="horizontal" />

ซึ่งสามารถใช้ในลักษณะเดียวกับการใช้หลักเกณฑ์ปกติได้

MotionLayout สำหรับอุปกรณ์แบบพับได้

เราได้เพิ่มฟีเจอร์หลายอย่างใน MotionLayout ในเวอร์ชัน 2.1 ที่ช่วยในการปรับเปลี่ยนรูปแบบ สถานะ – เป็นอุปกรณ์ที่มีประโยชน์อย่างยิ่งสำหรับอุปกรณ์แบบพับได้ ตามที่เรามัก ต้องจัดการกับภาพเคลื่อนไหว ระหว่างเลย์เอาต์ที่เป็นไปได้ต่างๆ

อุปกรณ์แบบพับได้มี 2 วิธีให้เลือกดังนี้

  • เมื่อรันไทม์ ให้อัปเดตเลย์เอาต์ปัจจุบัน (ConstraintSet) เพื่อแสดงหรือซ่อน เส้นแบ่งหน้า
  • ใช้ ConstraintSet แยกกันสำหรับแต่ละอุปกรณ์แบบพับได้ที่ต้องการ การสนับสนุน (closed, folded หรือ fully open)

กำลังทำให้ ConstraintSet เคลื่อนไหว

เพิ่มฟังก์ชัน updateStateAnimate() ใน MotionLayout แล้วในเวอร์ชัน 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 ได้ใน ขึ้นอยู่กับการเปลี่ยนแปลง เช่น สถานะของคุณเป็นแบบพับได้

ReactiveGuide ใน MotionLayout

ReactiveGuide ยังรองรับแอตทริบิวต์ที่มีประโยชน์ 2 รายการเมื่อใช้ภายใน MotionLayout:

  • app:reactiveGuide_animateChange="true|false"

  • app:reactiveGuide_applyToAllConstraintSets="true|false"

รายการแรกจะแก้ไข ConstraintSet ปัจจุบันและทำให้การเปลี่ยนแปลงเคลื่อนไหว โดยอัตโนมัติ ตัวระบุที่ 2 จะใช้ค่าใหม่ของ ReactiveGuide ตำแหน่งเป็น ConstraintSet ทั้งหมดใน MotionLayout แนวทางทั่วไปสำหรับ อุปกรณ์แบบพับได้จะใช้ ReactiveGuide ที่แทนตำแหน่งเส้นแบ่ง ตั้งค่าองค์ประกอบเลย์เอาต์ให้สัมพันธ์กับ ReactiveGuide

การใช้ ConstraintSet หลายชิ้นเพื่อแสดงสถานะอุปกรณ์พับได้

อีกวิธีหนึ่งในการออกแบบสถาปัตยกรรม แทนที่จะอัปเดตสถานะ MotionLayout ปัจจุบัน UI เพื่อรองรับอุปกรณ์แบบพับได้คือการสร้างสถานะเฉพาะ (รวมถึง closed, folded และ fully open)

ในสถานการณ์นี้ คุณอาจยังคงต้องการใช้ ReactiveGuide เพื่อแสดง แต่คุณจะควบคุมได้มากขึ้นมาก (เมื่อเทียบกับ ภาพเคลื่อนไหวเมื่ออัปเดต ConstraintSet ปัจจุบัน) ว่าแต่ละสถานะจะ เปลี่ยนไปเป็นอีกเครื่องหนึ่งได้

ด้วยวิธีการนี้ ใน Listener DeviceState คุณเพียงแค่กำหนด MotionLayoutเพื่อเปลี่ยนเป็นรัฐที่เจาะจงผ่าน MotionLayout.transitionToState(stateId)