Collapsing CardView Animation работает неправильно

То, что я пытаюсь сделать

У меня есть RecyclerView со многими элементами, которые в основном являются CardView.

У этих карт есть поддерживающий текст в середине их тел, который по умолчанию имеет видимость GONE по умолчанию, и он сделал VISIBLE когда я нажимаю стрелку справа от карты.

Я пытаюсь анимировать карту, пока текст раскрывается, и когда он рухнул.

На приведенном ниже рисунке показана расширенная карта и свернутая карта:

cards

Макет CardView (я удалил некоторые части для удобочитаемости):

<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="3dp"
    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true"
    android:id="@+id/root">

    <LinearLayout
        android:id="@+id/item_ll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_vertical_margin">

        <!-- The header with the title and the item -->


        <TextView
            android:id="@+id/body_content"
            style="@style/TextAppearance.AppCompat.Medium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:layout_marginBottom="8dp"
            android:text="@string/about_page_description"
            android:textColor="@color/secondaryText"
            android:visibility="gone"/>

        <!-- The divider, and the footer with the timestamp -->

    </LinearLayout>
</android.support.v7.widget.CardView>

Эта проблема

Анимация работает, когда карта расширяется и раскрывает тело TextView, однако, когда я пытаюсь свернуть его, карты под анимированным перекрывают первый.

Пример:

card animation

То, что я пробовал до сих пор

Я уже задал аналогичный вопрос об этом поведении здесь раньше, но это решение не работает для TextView в середине карты.

Код, который отвечает за часть анимации, находится внутри адаптера RecyclerView. Стрелка имеет прослушиватель кликов, который вызывает метод ниже:

private fun toggleVisibility() {
    if (bodyContent.visibility == View.GONE || bodyContent.visibility == View.INVISIBLE) {
        btSeeMore.animate().rotation(180f).start()
        TransitionManager.beginDelayedTransition(root, AutoTransition())
        bodyContent.visibility = View.VISIBLE
    }
    else {
        btSeeMore.animate().rotation(0f).start()

        TransitionManager.beginDelayedTransition(root, AutoTransition())
        bodyContent.visibility = View.GONE
    }
}

Где root - мой CardView.

Я также попытался использовать LinearLayout вместо самой карты для задержанного перехода, но это тоже не сработало.

Как я могу достичь этого поведения для моего макета?

Ответ 1

Вам придется выполнить переход на RecyclerView, а не на отдельные элементы. В противном случае изменения макета RecyclerView не учитываются при автоматическом переходе, потому что он будет рассматривать только те изменения в этом очень детском представлении, хотя на самом деле косвенное влияние оказывают другие зрители (параметры макета меняются).

Таким образом, вместо передачи "root" (вид элемента) в TransitionManager # beginDelayedTransition передайте ссылку на ваш RecyclerView

Ответ 2

Я бы посоветовал вам использовать рамки Animator и применить анимацию высоты к вашему TextView.

Вот хорошая библиотека, которую вы можете использовать: https://github.com/cachapa/ExpandableLayout

Я также предлагаю вам проверить его исходный код, он использует аниматоры

Ответ 3

RecyclerView ведет себя странно, если его элементы изменяются вне обратных вызовов RecyclerViews. Попробуйте использовать adapter.notifyItemChanged (позиция, полезная нагрузка) и обновите элемент:

Замените адаптер onclick следующим:

adapter.notifyItemChanged(adapterPosition, true) // needs adapter reference, can use more meaningful payload

Затем внутри вашего адаптера:

override fun onBindViewHolder(holder: Holder, position: Int, payloads: List<Any>) {
    if (payloads.isEmpty())
        onBindViewHolder(holder, position)
    else
        holder.toggleVisibility()
}

Вы также можете увидеть, что происходит при запуске delayedTransition на LinearLayout вместо самой карты.

Это не будет идеально, но это вызовет анимацию следующих предметов, а не прыжков и отсечения.

Ответ 4

Может быть, уже слишком поздно. Внутри onBindViewHolder() есть это

holder.view.btSeeMore.setOnClickListener { view ->
    val seeMore = (bodyContent.visibility != View.VISIBLE)
    view.animate().rotation(if (seeMore) 180f else 0f).start()
    bodyContent.visibility = if (seeMore) View.VISIBLE else View.GONE
}

Ответ 5

Вы должны применить TransitionManager.beginDelayedTransition в корневом представлении, в котором содержится карта

Вы должны удалить android:animateLayoutChanges="true" со всей компоновки

TransitionManager.beginDelayedTransition(the_root_view_where_card_view_exist, new AutoTransition());