Я использую MotionLayout и пытаюсь применить серию переходов (определено в файле MotionScene). Например, Transition1 изменяет положение View1, а затем Transition2 последовательно меняет положение View2. Проблема, которую я вижу, заключается в том, что Transition2 заставляет View1 вернуться к исходному макету. Я запускаю переходы, используя вызовы transitionToState() (я также пытался явно указать начальное/конечное состояние для перехода, используя setTransition(), а затем вызывая transitionToEnd()).
Я видел комментарий в https://www.raywenderlich.com/8883-motionlayout-tutorial-for-android-getting-started, который
if you don’t provide an end constraint for a view, it will disappear. This happens because the library doesn’t know which constraints it should apply at the end of the animation
Нужно ли также включать информацию о макете View1 в ConstraintSet для Transition2?
В https://github.com/googlesamples/android-ConstraintLayoutExamples/blob/master/README.md есть пример «Многосостояние», но похоже, что все включенные переходы начинаются с одного базового состояния (а не «кумулятивно»)
Переходы не являются дельтой состояния. Они переходят из штата в штат. ConstraintSets определяют эти состояния.
ConstraintSets (в контексте MotionScene) определяют состояние, которое является базовым макетом + вашими изменениями, описанными в теге.
Как сказал @хофорд, наборы ограничений не объединяются при каждом изменении. Судя по всему, это тоже невозможно без использования рефлексии, хотя она точно была бы полезна. Во всяком случае, вот как это сделать с помощью отражения (через несколько функций расширения).
Я создал свой базовый класс собственный TransitionListener для поддержки аннотаций и удобочитаемых имен значений.
Функции расширения можно найти здесь.
Что вы можете сделать, так это объединить два ConstraintSets, а затем видоизменить endConstraintSet, который можно получить с помощью TransitionListener#onTransitionChange:
class AccumulativeTransitionListener: TransitionListener() {
var didApplyConstraintSet = false
override fun onTransitionChange(view: MotionLayout, @IdRes startConstraintSetId: Int, @IdRes endConstraintSetId: Int, progress: Float) {
if (!didApplyConstraintSet) {
// Let's retrieve our ConstraintSets first
val startConstraintSet = view.getConstraintSet(startConstraintSetId)
val endConstraintSet = view.getConstraintSet(endConstraintSetId)
// Merge them (using an extension function)
val mergedConstraintSet = startConstraintSet + endConstraintSet
// Clear + Set them
endConstraintSet.setConstraints(mergedConstraintSet)
didApplyConstraintSet = true
}
}
override fun onTransitionCompleted(view: MotionLayout, @IdRes constraintSetId: Int) {
didApplyConstraintSet = false
}
}
Чтобы не объединять и не применять ConstraintSets каждый раз, когда вызывается TransitionListener#onTransitionChange, есть простая вспомогательная переменная.
Наконец, вы должны прикрепить слушателя к вашему MotionLayout:
val accumulativeListener = AccumulativeTransitionListener()
motionLayout.setTransitionListener(accumulativeListener)
Дайте мне знать, если это работает или есть какие-либо ошибки!
Итак, в моем случае мне также нужно будет определить текущий макет View1 (после Transition1) в ConstraintSet для Transitions2 (а также другой ConstraintSet, если Transitions1 не произошло)? Моя проблема заключается в том, что у нас есть несколько переходов, изменяющих несколько разных представлений... и нет гарантии, что конкретный переход произошел раньше... поэтому перестановки становятся довольно большими (если все комбинации измененных макетов представления должны быть определены для каждого перехода )