В выпуске ConstraintLayout
2.1 было добавлено несколько функций, помогающих управлять складными устройствами , включая SharedValues
, ReactiveGuide
и расширенную поддержку анимации с помощью MotionLayout
.
Общие ценности
Мы добавили новый механизм для внедрения значений времени выполнения в ConstraintLayout
— он предназначен для использования для общесистемных значений, поскольку все экземпляры ConstraintLayout
могут получить доступ к этому значению.
В контексте складных устройств мы можем использовать этот механизм для определения положения сгиба во время выполнения:
Котлин
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold)
Ява
ConstraintLayout.getSharedValues().fireNewValue(R.id.fold, fold);
В пользовательском помощнике вы можете получить доступ к общим значениям, добавив прослушиватель любых изменений:
Котлин
val sharedValues: SharedValues = ConstraintLayout.getSharedValues() sharedValues.addListener(mAttributeId, this)
Ява
SharedValues sharedValues = ConstraintLayout.getSharedValues(); sharedValues.addListener(mAttributeId, this);
Вы можете посмотреть пример FoldableExperiments , чтобы увидеть, как мы фиксируем положение сгиба с помощью библиотеки Jetpack WindowManager и вводим это положение в ConstraintLayout
.
Котлин
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); } } } } }
Ява
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
. Это позволит расположить горизонтальную или вертикальную направляющую в соответствии со связанным 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:
Котлин
fun updateStateAnimate(stateId: Int, set: ConstraintSet, duration: Int)
Ява
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)
.