Как передать прогресс от корневого MotionLayout к его дочернему RecyclerView, который содержит элемент MotionLayout?

У меня есть фрагмент с MotionLayout (1). Один из дочерних макетов можно развернуть, проведя по нему вниз (настраиваемая панель инструментов). Этот дочерний макет содержит RecyclerView с другим MotionLayout (2) внутри каждого элемента.

TransitionListener подключен к (1).

Когда MotionLayout (1) обнаруживает переход смахивания (с TransitionListener), он должен распространить свой progress на MotionLayout (2) видимого в данный момент элемента RecyclerView, чтобы синхронизировалась анимация развернутой панели инструментов и анимация видимого элемента RecyclerView.

Проблема в том, что хотя элемент RecyclerView получает значение progress, отправленное вручную методом TransitionListener.onTransitionChange, анимация элемента RecyclerView фиксируется в одном из начальных / конечных состояний, и анимация не видна.

За пределами RecyclerView настраиваемой панели инструментов смахивание вниз отправляет прогресс в MotionLayout (2), и изменение состояния и анимация происходят нормально.

MotionScene добавляется программно к MotionLayout каждого RecyclerView элемента, потому что данные являются динамическими и не могут быть описаны только в motion_scene.xml MotionLayout (2). (Я использовал https://medium.com/androiddevelopers/working-with-dynamic-data-in-motionlayout-9dbbcfe5ff75 для программного добавления переходов и состояний MotionScene).

Как получить MotionLayout (1), который передает прогресс в RecyclerView элемент «MotionLayout» (2)?


person Mateo Kutnjak    schedule 22.12.2020    source источник


Ответы (1)


В настоящее время это невозможно сделать с RecyclerView без некоторого Kotlin. Ниже я подробно расскажу, как это сделать.


Я распространил прогресс для каждого ViewHolder, установив RecyclerView.OnScrollListener и внутри onScrolled:

  • Получите ссылку на текущий LayoutManager RecyclerView
  • Используйте findFirstVisibleItemPosition и findLastVisibleItemPosition, чтобы получить диапазон ViewHolder, который я должен искать.
  • Используйте LayoutManager.findViewByPosition, чтобы получить View ссылку, и RecyclerView.getChildViewHolder, чтобы получить ViewHolder ссылку.
  • Отфильтровать нулевые результаты
  • Рассчитайте прогресс для каждого конкретного ViewHolder для моего варианта использования.
  • Обновите прогресс каждого ViewHolder

На практике (и с некоторыми функциями расширения, которые обрабатывают эти биты) общий поток в Kotlin должен выглядеть следующим образом:

recyclerView.activeViewHolders(onlyVisible = true)
    .mapNotNull { viewHolder -> viewHolder as? WidgetViewHolder }
    .forEach { viewHolder ->
        val position = viewHolder.adapterPosition
        val awayFromCenter = recyclerView.calculateHorizontalOffsetPercentage(position)
        viewHolder.resize(awayFromCenter)
    }

Это решение отлично работало для меня как с синтетикой Kotlin, так и с ViewBinding.


Поскольку вы упомянули, что у вас довольно динамические данные, похоже, что Carousel не может сработать для вас, но вы должны проверить его, поскольку он в настоящее время находится в разработке: https://github.com/androidx/constraintlayout/wiki/Carousel

person Jason Pearson    schedule 27.12.2020