Как оживить элементы RecyclerView при инициализации адаптера (в порядке)?

Что я пытаюсь выполнить

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

пример анимации


Что я пробовал

Я пробовал методы, предоставленные в этом вопросе: Как оживить элементы RecyclerView, когда они появляются

Однако это не совсем то, что я пытаюсь выполнить. Это приводит к тому, что все элементы в RecyclerView появляются сразу, а не по одному.


Мой код

public class ParentCommentsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private int lastPosition = -1;

    //constructor and other code not shown...

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        switch (viewHolder.getItemViewType()) {
            case OP:
                OPViewHolder ovh = (OPViewHolder) viewHolder;
                configureOPViewHolder(ovh, position);
                setAnimation(ovh.getContainer(), position);
                break;
            case COMMENT:
                CommentViewHolder cvh = (CommentViewHolder) viewHolder;
                configureCommentViewHolder(cvh, position);
                setAnimation(cvh.getContainer(), position);
                break;
            default:
                RecyclerViewSimpleTextViewHolder vh = (RecyclerViewSimpleTextViewHolder) viewHolder;
                configureDefaultViewHolder(vh, position);
                break;
        }

    }

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder viewHolder)
    {

        switch (viewHolder.getItemViewType()) {
            case OP:
                ((OPViewHolder)viewHolder).clearAnimation();
                break;
            case COMMENT:
                ((CommentViewHolder)viewHolder).clearAnimation();
                break;
            default:
                break;
        }
    }

    private void configureDefaultViewHolder(RecyclerViewSimpleTextViewHolder vh, int position) {
        //code...
    }

    private void configureOPViewHolder(OPViewHolder vh1, int position) {
        //code...
    }

    private void configureCommentViewHolder(CommentViewHolder vh2, int position) {
        //code...
    }

    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(context, R.anim.fade_in);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }

}

Ответ 1

После вызова setAdapter вы можете выполнить следующее:

recyclerView.getViewTreeObserver().addOnPreDrawListener(
        new ViewTreeObserver.OnPreDrawListener() {

            @Override
            public boolean onPreDraw() {
                recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);

                for (int i = 0; i < recyclerView.getChildCount(); i++) {
                    View v = recyclerView.getChildAt(i);
                    v.setAlpha(0.0f);
                    v.animate().alpha(1.0f)
                            .setDuration(300)
                            .setStartDelay(i * 50)
                            .start();
                }

                return true;
            }
        });

Пример показывает простую альфа-анимацию, но вы можете запустить то, что хотите в цикле, и отрегулировать интерполятор, продолжительность и задержку запуска.

Ответ 2

Я решил использовать подход, отличный от того, что предложил Нико ранее. Его метод отлично работает и очень прост, но он вызывал некоторые глючные анимации при добавлении и удалении элементов (как можно увидеть здесь).

Поэтому я решил попробовать другой подход и использовать контроллер анимации макета. Вот код, который я использовал для достижения того же результата.

public void initialRVAnimation() {

    AnimationSet set = new AnimationSet(true);

    // Fade in animation
    Animation fadeIn = new AlphaAnimation(0.0f, 1.0f);
    fadeIn.setDuration(400);
    fadeIn.setFillAfter(true);
    set.addAnimation(fadeIn);

    // Slide up animation from bottom of screen
    Animation slideUp = new TranslateAnimation(0, 0, Utils.getScreenHeight(this), 0);
    slideUp.setInterpolator(new DecelerateInterpolator(4.f));
    slideUp.setDuration(400);
    set.addAnimation(slideUp);

    // Set up the animation controller              (second parameter is the delay)
    LayoutAnimationController controller = new LayoutAnimationController(set, 0.2f);
    rv.setLayoutAnimation(controller);

    // Set the adapter
    adapter = new ItemAdapter(data, this);
    recyclerView.setAdapter(adapter);
}

Ответ 3

Простой способ установить в XML в вашем теге RecyclerView (или в коде, как выше комментарий)

android:layoutAnimation="@anim/slide_left_in_layout_anim"

slide_left_in_layout_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@android:anim/slide_in_left"
    android:delay="10%"
    android:animationOrder="normal"
/>

Примечание: эта анимация оправдывается только при первой загрузке данных, если вы хотите добавить анимацию при добавлении/удалении/перемещении элемента, взгляните на ItemAnimator, и я думаю, что этот пост поможет вам узнать что-то полезное

Ответ 4

В дополнение к @Niko ответу для Kotlin попробуйте это решение.

позвоните после установки адаптера

rvTop!!.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
                        override fun onPreDraw(): Boolean {
                            rvTop!!.getViewTreeObserver().removeOnPreDrawListener(this);

                            for (i in 0 until rvTop!!.getChildCount()) {
                                var v = rvTop!!.getChildAt(i);
                                v.setAlpha(0.0f);
                                v.animate().alpha(1.0f)
                                        .setDuration(300)
                                        .setStartDelay((i * 50).toLong())
                                        .start();
                            }

                            return true
                        }


                    })