Mendesain aplikasi untuk perangkat foldable

Di ConstraintLayout 2.1, beberapa fitur ditambahkan untuk membantu mengelola perangkat foldable, termasuk SharedValues, ReactiveGuide, dan dukungan yang disempurnakan untuk animasi dengan MotionLayout.

Nilai Bersama

Kami menambahkan mekanisme baru untuk memasukkan nilai runtime di ConstraintLayout – ini dimaksudkan untuk digunakan untuk nilai seluruh sistem, karena semua instance ConstraintLayout dapat mengakses nilai.

Dalam konteks perangkat foldable, kita bisa menggunakan mekanisme ini untuk memasukkan posisi lipatan saat runtime:

Kotlin

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

Java

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

Pada helper kustom, Anda bisa mengakses nilai yang dibagikan dengan menambahkan pemroses untuk setiap perubahan:

Kotlin

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

Java

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

Anda dapat melihat contoh FoldableExperiments untuk melihat bagaimana kita menangkap posisi lipatan menggunakan Library dan memasukkan Jetpack WindowManager posisi ke dalam 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() mengambil ID yang mewakili nilai sebagai parameter pertama dan nilai yang diinjeksikan sebagai parameter kedua.

ReactiveGuide

Salah satu cara untuk memanfaatkan SharedValue dalam tata letak, tanpa harus menulis kode apa pun, adalah dengan menggunakan ReactiveGuide . Cara ini akan menempatkan garis panduan horizontal atau vertikal sesuai dengan SharedValue tertaut.

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

Langkah ini dapat digunakan sebagaimana yang Anda lakukan dengan pedoman normal.

MotionLayout untuk perangkat foldable

Kami menambahkan beberapa fitur di MotionLayout pada versi 2.1 yang membantu proses morphing status – sesuatu yang sangat berguna untuk perangkat foldable, seperti yang biasanya harus menangani animasi di antara berbagai tata letak yang mungkin.

Ada dua pendekatan yang tersedia untuk perangkat foldable:

  • Saat runtime, perbarui tata letak Anda saat ini (ConstraintSet) untuk menampilkan atau menyembunyikan lipat.
  • Gunakan ConstraintSet terpisah untuk setiap status perangkat foldable yang ingin Anda mendukung (closed, folded, atau fully open).

Menganimasikan ConstraintSet

Fungsi updateStateAnimate() di MotionLayout telah ditambahkan di versi 2.1 rilis:

Kotlin

fun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)

Java

void updateStateAnimate(int stateId, ConstraintSet set, int duration);

Fungsi ini akan secara otomatis menganimasikan perubahan saat memperbarui elemen ConstraintSet daripada melakukan update langsung (yang dapat Anda lakukan dengan updateState(stateId, constraintset)). Hal ini memungkinkan Anda untuk memperbarui UI pada dengan cepat, tergantung perubahannya, seperti status perangkat foldable yang Anda gunakan.

ReactiveGuide di dalam MotionLayout

ReactiveGuide juga mendukung dua atribut yang berguna saat digunakan di dalam MotionLayout:

  • app:reactiveGuide_animateChange="true|false"

  • app:reactiveGuide_applyToAllConstraintSets="true|false"

Yang pertama akan memodifikasi ConstraintSet saat ini dan menganimasikan perubahan secara otomatis. Yang kedua akan menerapkan nilai baru ReactiveGuide ke semua ConstraintSet di MotionLayout. Pendekatan yang khas untuk perangkat foldable akan menggunakan ReactiveGuide yang mewakili posisi lipat, menyiapkan elemen tata letak yang relatif terhadap ReactiveGuide.

Menggunakan beberapa ConstraintSet untuk merepresentasikan status perangkat foldable

Daripada memperbarui status MotionLayout saat ini, cara lain untuk merancang UI untuk mendukung perangkat foldable adalah membuat status terpisah tertentu (termasuk closed, folded, dan fully open).

Dalam skenario ini, Anda mungkin masih perlu menggunakan ReactiveGuide untuk mewakili lipat, tetapi Anda akan memiliki lebih banyak kontrol (dibandingkan dengan animasi saat memperbarui ConstraintSet saat ini) mengenai bagaimana setiap status akan untuk beralih ke proyek lain.

Dengan pendekatan ini, dalam pemroses DeviceState, Anda cukup mengarahkan MotionLayout untuk bertransisi ke status tertentu melalui MotionLayout.transitionToState(stateId) .