עיצוב למכשירים מתקפלים

ב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);

אפשר לעיין בדוגמה לניסויים מתקפלים כדי לראות איך אנחנו אוספים את מיקום החלק העליון של הקיפול באמצעות ספריית 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() לוקחת מזהה שמייצג את הערך כפרמטר הראשון, את הערך שרוצים להחדיר כפרמטר השני.

ReactiveGuide

אחת מהדרכים להשתמש ב-SharedValue בפריסה, בלי כל קוד שהוא, הוא להשתמש בפונקציה ReactiveGuide Assistant. פעולה זו תמקם קו מנחה אופקי או אנכי בהתאם קושר SharedValue.

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

אחר כך אפשר להשתמש בו בדיוק כמו בהנחיות רגילות.

MotionLayout למכשירים מתקפלים

הוספנו כמה תכונות ב-MotionLayout בגרסה 2.1 שעוזרות לשנות מורפינג הוא שימושי במיוחד למכשירים מתקפלים, מכיוון שאנחנו בדרך כלל צריכים לטפל באנימציה בין הפריסות האפשריות השונות.

יש שתי שיטות זמינות למכשירים מתקפלים:

  • בזמן הריצה, צריך לעדכן את הפריסה הנוכחית (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)). כך אפשר לעדכן את ממשק המשתמש בהתאם לשינויים, כמו המצב המתקפל שבו אתם נמצאים.

ReactiveGuide בתוך MotionLayout

ReactiveGuide תומך גם בשני מאפיינים שימושיים כשנעשה בהם שימוש בתוך MotionLayout:

  • app:reactiveGuide_animateChange="true|false"

  • app:reactiveGuide_applyToAllConstraintSets="true|false"

הראשון ישנה את ConstraintSet הנוכחי ותוסיף אנימציה לשינוי באופן אוטומטי. הערך השני יחיל את הערך החדש של ReactiveGuide המיקום לכל ConstraintSet בMotionLayout. גישה אופיינית במכשירים מתקפלים צריך להשתמש ב-ReactiveGuide שמייצג את מיקום הקיפול, להגדיר את רכיבי הפריסה שלך ביחס ל-ReactiveGuide.

שימוש בכמה מעבדי ConstraintSet לייצוג מצב מתקפל

במקום לעדכן את מצב MotionLayout הנוכחי, אפשר להשתמש בדרך נוספת בארכיטקטורה בממשק המשתמש, שתומך במכשירים מתקפלים, צריך ליצור מצבים נפרדים ספציפיים closed, folded ו-fully open).

בתרחיש הזה, יכול להיות שעדיין תרצו להשתמש ב-ReactiveGuide כדי לייצג את לקפל, אבל תהיה לך הרבה יותר שליטה (בהשוואה אנימציה כשמעדכנים את ה-ConstraintSet הנוכחי), שממחישה איך כל מצב לעבור לחשבון אחר.

בגישה הזו, ב-DeviceState המאזינים שלכם פשוט תגדירו את ה- MotionLayout כדי לעבור למדינות ספציפיות באמצעות MotionLayout.transitionToState(stateId) .