Progettazione di pieghevoli

In ConstraintLayout 2.1, sono state aggiunte diverse funzionalità per gestire dispositivi pieghevoli, tra cui SharedValues, ReactiveGuide e supporto migliorato per le animazioni con MotionLayout.

Valori condivisi

Abbiamo aggiunto un nuovo meccanismo per inserire i valori di runtime in ConstraintLayout: questo è destinato a essere utilizzato per i valori a livello di sistema, poiché tutte le istanze ConstraintLayout sono in grado di accedere al valore.

Nel contesto dei dispositivi pieghevoli, possiamo usare questo meccanismo per inserire posizione della piegatura in fase di esecuzione:

Kotlin

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

Java

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

In un helper personalizzato, puoi accedere ai valori condivisi aggiungendo un listener per modifiche:

Kotlin

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

Java

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

Puoi esaminare l'esempio di Foldable Experiments per vedere come afferriamo la posizione del fold usando Jetpack WindowManager e inserisci posizione in 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() prende un ID che rappresenta il valore come primo parametro e il valore da inserire come secondo parametro.

ReactiveGuide

Un modo per sfruttare SharedValue in un layout, senza dover scrivere alcun codice, è utilizzare ReactiveGuide come un assistente per le app. In questo modo, una linea guida orizzontale o verticale verrà posizionata in base alla collegato SharedValue.

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

Puoi quindi utilizzarlo come faresti con una normale linea guida.

MotionLayout per pieghevoli

Nella versione 2.1 abbiamo aggiunto diverse funzionalità in MotionLayout che aiutano il morphing particolarmente utile per i pieghevoli, come devono gestire l'animazione tra i diversi layout possibili.

Esistono due approcci disponibili per i pieghevoli:

  • In fase di runtime, aggiorna il layout corrente (ConstraintSet) per mostrare o nascondere piega.
  • Usa un attributo ConstraintSet separato per ciascuno degli stati pieghevoli che vuoi assistenza (closed, folded o fully open).

Animazione di un ConstraintSet

La funzione updateStateAnimate() in MotionLayout è stata aggiunta nella versione 2.1 release:

Kotlin

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

Java

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

Questa funzione animerà automaticamente le modifiche durante l'aggiornamento di una determinata ConstraintSet anziché eseguire un aggiornamento immediato (operazione che puoi eseguire updateState(stateId, constraintset)). In questo modo puoi aggiornare la UI al volo, a seconda dei cambiamenti, ad esempio lo stato del pieghevole in cui ti trovi.

ReactiveGuide in un MotionLayout

ReactiveGuide supporta anche due attributi utili quando viene utilizzato all'interno di un MotionLayout:

  • app:reactiveGuide_animateChange="true|false"

  • app:reactiveGuide_applyToAllConstraintSets="true|false"

Il primo modificherà il valore ConstraintSet corrente e animerà la modifica automaticamente. La seconda applica il nuovo valore dell'attributo ReactiveGuide posizione per tutti i ConstraintSet in MotionLayout. Un approccio tipico i pieghevoli dovrebbero usare ReactiveGuide che rappresenta la posizione di piegatura, la configurazione degli elementi di layout in relazione a ReactiveGuide.

Utilizzo di più ConstraintSet per rappresentare lo stato pieghevole

Invece di aggiornare lo stato attuale di MotionLayout, un altro modo di progettare la UI per supportare i pieghevoli è la creazione di stati separati specifici (inclusi closed, folded e fully open).

In questo scenario, potresti comunque voler utilizzare ReactiveGuide per rappresentare ma avresti un controllo molto maggiore (rispetto alle durante l'aggiornamento dell'attuale ConstraintSet) su come ogni stato sarebbe passare a un altro.

Con questo approccio, nel tuo listener DeviceState, dovrai semplicemente indirizzare la MotionLayout per passare a stati specifici tramite MotionLayout.transitionToState(stateId): .