У меня есть Fragment, который содержит RecyclerView внутри MotionLayout. Поверх вида переработчика у меня есть вид, который сворачивается и расширяется, и все это делается в моей сцене движения. Он запускается щелчком, а также реагирует на перетаскивание представления ресайклера. Пока все это работает как задумано.
Теперь самое главное: я хотел бы полностью скрыть сворачивающиеся представления для некоторых состояний в моем приложении. Но если я установлю видимость представлений или изменю ее высоту, она все равно вернется в поле зрения, когда я перетащу recyclerview.
Так можно ли полностью отключить MotionLayout (заблокировать ограничения), пока я не включу его снова. Отключение распознавателя перетаскивания также было бы жизнеспособным решением.
Теперь немного упрощенного XML для демонстрации ситуации.
Макет, содержащий макет движения с представлением заголовка и просмотром повторного использования
<?xml version = "1.0" encoding = "utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
xmlns:tools = "http://schemas.android.com/tools"
android:id = "@+id/favouritesMotionLayout"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
app:layoutDescription = "@xml/favourites_motion_scene"
android:animateLayoutChanges = "true">
<ImageView
android:id = "@+id/headerView"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
...
/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id = "@+id/swipeRefreshLayout"
android:layout_width = "match_parent"
android:layout_height = "0dp"
...
>
<androidx.recyclerview.widget.RecyclerVieww
android:id = "@+id/favoritesList"
android:layout_width = "match_parent"
android:layout_height = "match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>
Соответствующая сцена движения:
<?xml version = "1.0" encoding = "utf-8"?>
<MotionScene
xmlns:android = "http://schemas.android.com/apk/res/android"
xmlns:motion = "http://schemas.android.com/apk/res-auto"
>
<Transition
motion:constraintSetStart = "@id/expanded"
motion:constraintSetEnd = "@id/collapsed"
motion:duration = "300">
<OnSwipe
motion:touchAnchorId = "@id/swipeRefreshLayout"
motion:touchAnchorSide = "top"
motion:dragDirection = "dragUp" />
</Transition>
<ConstraintSet android:id = "@+id/expanded">
... Constraints for expanded header ...
</ConstraintSet>
<ConstraintSet android:id = "@+id/collapsed">
... ... Constraints for collapsed header ...
</ConstraintSet>
</MotionScene>
Использование макета ограничения версии "2.0.0-alpha3"
implementation "com.android.support.constraint:constraint-layout:2.0.0-alpha3"
Итак, резюмируем: я хотел бы иметь возможность отключить макет движения, чтобы я мог прокручивать recyclerview без расширения заголовка, пока я не скажу, чтобы он снова был включен.
К сожалению, это не так просто, как favouritesMotionLayout.isEnabled = false, но именно так я и думал.
На данный момент кажется, что невозможно отключить движения в MotionLayout. Я надеюсь, что это будет добавлено в будущей версии. Чтобы решить мой вариант использования, я применил немного грязный хак: я удалил представления, составляющие заголовок.
favouritesMotionLayout.removeView(headerView)
Макет был в порядке с этим, и представление исчезло, а движение не было вызвано. Чтобы вернуть просмотры, я просто обновил весь макет. (Альтернативой может быть повторное добавление представлений с их ограничениями программно).
Я понимаю, что это не самое красивое решение, и в настоящее время я ищу способы решения всего варианта использования без макета движения. Это прискорбно, так как это было очень полезно до 90% моего варианта использования.
В последнее время я много взламывал MotionLayout, и до сих пор я обнаружил, что мой способ отключить Motionlayout выглядит следующим образом.
layoutContainer.setTransitionListener(object: MotionLayout.TransitionListener {
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {}
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {}
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {}
override fun onTransitionChange(p0: MotionLayout?, startedScene: Int, endScene: Int, p3: Float) {
when(currentFragmentTag) {
TAG_DEFAULTBACKGROUND -> {
if ( startedScene==R.id.halfExpanded ) {
layoutContainer.transitionToState(startedScene)
}
}
}
}
})
В общем, я говорю MotionLayout вернуться к определенным ситуациям.
Я чувствую, что onTransitionTrigger или onTransitionStarted должны содержать функцию остановки, но в эти моменты она не слушается.
onTransitionChange это место, где он на самом деле начинает двигаться.
Я думаю, вы поймете смысл этого трюка наоборот.
Удачного кодирования!
Похоже, есть способ взломать ваше состояние, чтобы отключить переход движения. Его нужно добавить после перехода выше в xml файле. Я добавил еще один переход с тем же начальным и конечным состоянием. В твоем случае:
<Transition
motion:constraintSetStart = "@id/collapsed"
motion:constraintSetEnd = "@id/collapsed"
motion:duration = "0">
И в java/котлин
motion_layout.setTransition(R.id.collapsed, R.id.collapsed)
На самом деле, мне удалось заставить его работать, установив начальное состояние моей сцены как для начала, так и для конца. В Котлине:
motionLayout.setTransition(R.id.start, R.id.start)
И когда мне нужно было включить мой макет:
motionLayout.setTransition(R.id.start, R.id.end)
Не нужно менять вашу сцену xml, у меня работало безупречно в alpha-03
лол спасибо чувак. Есть официальный способ сделать это с помощью enableTransition(), но он содержит ошибки.
Меня устраивает:
if (list.isNullOrEmpty()) {
//disable
motionLayout.apply {
setTransition(R.id.expanded, R.id.expanded)
setTransitionDuration(0)
}
} else {
//enable
motionLayout.apply {
setTransition(R.id.expanded, R.id.collapsed)
setTransitionDuration(200)
}
}
p.s.: 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
Обратите внимание, что в последней бета-версии (beta2) можно отключить переход: https://androidstudio.googleblog.com/2019/06/constraintlayout-200-beta-2.html
Пример использования в Котлине:
(view as? MotionLayout)?.getTransition(R.id.swipe_transition)?.setEnable(false)
Это ничего не делает, я единственный, кто не может отключить переход, делая это?
То же самое, нельзя отключить определенный переход и запустить другой с этим фрагментом кода.
Если вы хотите отключить переход после выполнения действия пользователя. Просто установите motionLayout.transitionToState(0). Возможно, вы захотите сделать это в обратном вызове завершения перехода.
Проверьте, работает ли приведенный ниже код для вас.
motionLayout.setTransitionListener(object :
MotionLayout.TransitionListener {
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
}
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
}
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
}
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
(motionLayout as MotionLayout).transitionToState(0)
}
})
Теперь с ConstraintLayout beta 2 это возможно! Это официальный способ сделать это:
motionLayout.getTransition(R.id.yourTransition).setEnable(false)
Я использую 2.0.0-beta7, и я не могу просто запустить 1 из 2 переходов в сцене движения, используя ваш фрагмент
Это не работает с limitedlayout: 2.0.4.
Ну, мне пришлось это сделать, и я попробовал обе библиотеки beta1 и beta2 и не смог отключить motionlayout, а затем, наконец, я сделал это, чтобы отключить анимацию motionlayout, когда мой список пуст: -
РЕШЕНИЕ
// this fix the transition but other parts of layout will still able to trigger transition somehow to R.id.end!!
motionLayout.setTransition(R.id.start, R.id.start);
// disable all other views touch handling except view clicks.
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
motionLayout.findViewById(R.id.view_ids).setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
.... disable all the views touch handling cause they can trigger the motion layout animation!
// And then finally has to disable touch handling of motionlayout itself to avoid the space exposed by motionlayout due to padding or margin which can trigger the transition!
motionLayout.setOnTouchListener(
(v, event) -> event.getActionMasked() != MotionEvent.ACTION_UP);
Короче говоря, нам нужно setTransition для идентификатора начала как для начала, так и для конца. Но я заметил, что этого недостаточно. Каким-то образом motionlayout отключите обработку касания recylerview, но все остальные части motionlayout могут инициировать переход, поэтому мне нужно отключить обработку касания всех других представлений и позволить им обрабатывать только события кликов, а затем, наконец, сделать то же самое для motionlayout тоже. Потому что пространство между представлениями обрабатывается motionlayout, поэтому, когда вы касаетесь тех частей, где находится motionlayout, это вызывает переход. Поэтому необходимо также отключить его сенсорную обработку.
И, наконец, это сработало с этим решением! Надеюсь, этот ответ поможет кому-то! :) наслаждаться!
Принятый ответ LPirro у меня не работал, сейчас я использую бета-версию 3. Скорее я изменил layoutDescription в соответствии с моим требованием:
public void enableTransition(boolean enable) {
if (enable) loadLayoutDescription(R.xml.scene);
else loadLayoutDescription(0);
}
Я использую androidx.constraintlayout:constraintlayout:2.0.0-beta4
и motionLayout.getTransition(R.id.yourTransition).setEnable(false) у меня не работают, но чтобы полностью отключить макет движения, например в моем случае мне нужно скрыть вид в особом случае, но этот вид участвует в анимации. Итак, cardMotionLayout.loadLayoutDescription(0) полностью отключите MotionLayout, а чтобы снова включить, я использую cardMotionLayout.loadLayoutDescription(R.xml.fragment_buy_card_step_one_scene)
Спасибо, моя проблема уже решена, но я рад, что вы нашли и другое решение.
спасибо, motionLayout.getTransition(R.id.yourTransition).setEnable(false) у меня тоже не работает
Я нашел удобное решение для временного отключения перехода макета движения. Установите это в свой MotionLayout в xml:
app:interactionEnabled = "false"
Преимущество решения в том, что оно используется непосредственно в xml-файле, что дает возможность подключения к привязке данных.
app:interactionEnabled = "@{viewModel.shouldInteractionBeActive}"
Работает "из коробки" без дополнительных переходников. Это было добавлено в limitedlayout:2.1.0-beta01.
Да, это возможно первый
motionLayout.getTransition(R.id.yourTransition).setEnable(false)
а теперь замените его на Transistion, который ничего не дозирует
motionLayout.setTransition(R.id.your_second_Transistion);
Самый простой способ отключить анимацию-переход MotionLayout — установить атрибут app:applyMotionScene = "false" в MotionLayout.
Атрибут applyMotionScene имеет логический формат и используется для включения/отключения перехода в MotionLayout.
Это отключит всю сцену движения. Если вы просто хотите отключить один переход, этот хак не сработает.
Забавная идея, но это не будет работать динамически, не так ли?