Як анімувати RecyclerView елементи, коли вони з'являються


237

Як я можу оживити елементи RecyclerView, коли вони з’являються?

Аніматор елементів за замовчуванням анімує лише тоді, коли дані додані або видалені після встановлення даних переробника. Я нові розроблюючі програми і не маю підказки з чого почати.

Будь-які ідеї, як цього досягти?

Відповіді:


42

Зроблено просто з XML

Відвідайте посилання Gist

res / anim / layout_animation.xml

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

res / anim / item_animation_fall_down.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromYDelta="-20%"
        android:toYDelta="0"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <scale
        android:fromXScale="105%"
        android:fromYScale="105%"
        android:toXScale="100%"
        android:toYScale="100%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

</set>

Використовуйте у макетах та рециркуляціях, як-от:

<android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layoutAnimation="@anim/layout_animation"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

1
@ArnoldBrown Змінивши файл анімації. Див: stackoverflow.com/questions/5151591 / ...
iamnaran

Це, безумовно, найбільш практична відповідь.
Олівер Мец

як змусити цю роботу кожен раз, коли список відкривається, тому що зараз це робиться лише вперше.
Hiwa Jalal

7
Вам потрібно зателефонувати recyclerView.scheduleLayoutAnimation()після зміни набору даних, якщо ні, анімація не працюватиме.
zeleven

Чи має це спрацювати, коли предмети переробляються та повертаються назад? Я спробував це рішення, і він чудово підходить для початкової анімації, коли ви вперше можете побачити макет. Після прокрутки елементи не мають анімації при поверненні для перегляду.
Джейсон п

315

Редагувати:

Відповідно до документації ItemAnimator :

Цей клас визначає анімації, які відбуваються на елементах, коли зміни адаптера вносяться.

Тож якщо ви не додасте свої предмети по одному до свого RecyclerViewта не оновлюєте подання на кожній ітерації, я не думаю, що ItemAnimatorце рішення вашої потреби.

Ось як можна анімувати RecyclerViewелементи, коли вони з’являються за допомогою CustomAdapter:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
    private Context context;

    // The items to display in your RecyclerView
    private ArrayList<String> items;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        TextView text;
        // You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
        // It's the view that will be animated
        FrameLayout container;

        public ViewHolder(View itemView)
        {
            super(itemView);
            container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
            text = (TextView) itemView.findViewById(R.id.item_layout_text);
        }
    }

    public CustomAdapter(ArrayList<String> items, Context context)
    {
        this.items = items;
        this.context = context;
    }

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        holder.text.setText(items.get(position));

        // Here you apply the animation when the view is bound
        setAnimation(holder.itemView, position);
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }
}

І ваш custom_item_layout виглядатиме так:

<FrameLayout
    android:id="@+id/item_layout_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_layout_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

</FrameLayout>

Для отримання додаткової інформації про CustomAdapters та RecyclerViewзверніться до цього тренінгу в офіційній документації .

Проблеми з швидкою прокруткою

Використання цього методу може спричинити проблеми з швидкою прокруткою. Погляд можна повторно використовувати під час анімації. Щоб уникнути цього, рекомендується очистити анімацію, коли вона від'єднана.

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
    {
        ((CustomViewHolder)holder).clearAnimation();
    }

На CustomViewHolder:

    public void clearAnimation()
    {
        mRootLayout.clearAnimation();
    }

Стара відповідь:

Погляньте на репортаж Габріелі Маріоти , я впевнений, що ви знайдете те, що вам потрібно. Він надає прості ItemAnimators для RecyclerView, такі як SlideInItemAnimator або SlideScaleItemAnimator.


1
Я бачив це, але це для додавання та видалення елементів після їх появи. Мені потрібно запустити анімацію прямо до того, як вони з’являться. Все одно дякую Матьє.
PaulNunezM

1
Наскільки я знаю, тоді потрібно використовувати CustomAdapter.
MathieuMaree

20
Мені було цікаво, чи ви відчули і, можливо, вирішили ефект «застрягнення» за допомогою цих анімацій у RecyclerView? Я використовував аналогічний код для ListView, і мої предмети анімували без проблем незалежно від того, наскільки швидко я прокручую, але за допомогою RecyclerView, якщо я швидко прокручую, деякі елементи іноді застрягають на екрані вгорі інших елементів, і вони не ховаються повністю - це як анімація була зупинена ще до її закінчення. Я насправді намагався прокоментувати частину коду, яка заповнює поля (намагаючись прискорити виконання методу onBindViewHolder), тому я залишив код тільки для animat
Томіслав

4
@MathieuMaree Дякую за цю дивовижну анімацію. Це добре виглядає для повільної прокрутки, але на елементах швидкого прокрутки reciklierview перекриті. Чи знайшли ви це Issse? Будь-яка пропозиція подолати цю проблему.
Giru Bhai

40
@GiruBhai перевизначте onViewDetachedFromWindowі зателефонуйте clearAnimationна перегляд. Проблема полягає в тому, що існують анімації, коли RecyclerView намагається повторно використовувати перегляд.
Xample

62

Я анімував Recyclerviewелементи завмирання, коли вони вперше з’являються, як показано в коді нижче. Можливо, це комусь буде корисно.

private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    holder.getTextView().setText("some text");

    // Set the view to fade in
    setFadeAnimation(holder.itemView);            
}

private void setFadeAnimation(View view) {
    AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

Ви також можете замінити setFadeAnimation()таким чином, setScaleAnimation()щоб анімувати зовнішній вигляд предметів, масштабуючи їх з точки:

private void setScaleAnimation(View view) {
    ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

У наведеному вище коді є деякі бородавки настільки, що при прокручуванні RecyclerViewелементів завжди вицвітають або набувають масштаб. Якщо ви хочете, ви можете додати код, щоб просто дозволити анімації відбуватися, коли фрагмент або активність, що містить RecyclerViewфайл, вперше створений (наприклад, отримати системний час на створення і дозволити анімацію лише протягом перших мілісекунд FADE_DURATION).


1
Я зробив невелику модифікацію для вашої відповіді, щоб зробити анімаційну роботу лише прокруткою вниз, перевірте свою відповідь
Basheer AL-MOMANI

1
це псує макет (переписані елементи списку, деякі елементи мають неправильний колір тексту) при швидкій прокрутці вгору та вниз
7

Це не належний або рекомендований спосіб анімації рециркуляторних елементів. Ви повинні використовувати клас
ItemAnimator

25

Я створив анімацію з відповідіmodification pbm мало, щоб зробити анімацію лише один раз

іншим словом the Animation appear with you scroll down only

private int lastPosition = -1;

private void setAnimation(View viewToAnimate, int position) {
    // If the bound view wasn't previously displayed on screen, it's animated
    if (position > lastPosition) {
        ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
        viewToAnimate.startAnimation(anim);
        lastPosition = position;
    }
}

і у onBindViewHolderвиклику функції

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

holder.getTextView().setText("some text");

// call Animation function
setAnimation(holder.itemView, position);            
}

1
Що тут останнєПозиція? Це arrayList.size () - 1
Суміт Шукла

1
lastPositionпредставляє кількість наданих переглядів, тому воно починає свою цінність -1, кожного разу, коли створюється новий вид, ми запускаємо анімацію та збільшуємо позицію
Basheer AL-MOMANI

15

Ви можете додати такий android:layoutAnimation="@anim/rv_item_animation"атрибут, RecyclerViewяк це:

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"                                        
    android:layoutAnimation="@anim/layout_animation_fall_down"
    />

дякую за чудову статтю тут: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213


Це працювало для мене, коли рециркулятор спочатку завантажує, але не додаючи нові елементи. (Хоча я гадаю, що це питання було дуже гарною роботою)
C. Skjerdal

8

Гарне місце для початку - це: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

Вам навіть не потрібна повна бібліотека, цього класу достатньо. Тоді якщо ви просто реалізуєте клас Adapter, даючи аніматора, як це:

@Override
protected Animator[] getAnimators(View view) {
    return new Animator[]{
            ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
    };
}

@Override
public long getItemId(final int position) {
    return getWrappedAdapter().getItemId(position);
}

ви побачите елементи, які з’являються знизу під час прокрутки, також уникнувши проблеми зі швидким прокручуванням.


3

Анімаційні елементи в перегляді рециркулятора, коли вони прив’язані до адаптера, можуть бути не найкращою ідеєю, оскільки це може спричинити анімацію елементів у переробці перегляду з різною швидкістю. У моєму випадку, предмет в кінці рециркулятора перетворюється на їхнє положення швидше, ніж ті, що знаходяться вгорі, як ті, хто знаходиться вгорі, далі рухаються, тому він виглядає непристойним.

Оригінальний код, який я використовував для анімації кожного елемента в перегляді рециркулятора, можна знайти тут:

http://frogermcs.github.io/Instagram-with-Material-Design-concept-is-getting-real/

Але я скопіюю і вставте код на випадок, коли посилання розірветься.

КРОК 1: Установіть це всередині вашого методу onCreate, щоб ви гарантували, що анімація запускається лише один раз:

if (savedInstanceState == null) {
    pendingIntroAnimation = true;
}

КРОК 2: Вам потрібно буде ввести цей код у метод, де ви хочете запустити анімацію:

if (pendingIntroAnimation) {
    pendingIntroAnimation = false;
    startIntroAnimation();
}

У посиланні автор пише анімацію піктограм панелі інструментів, тому він помістив її всередині цього методу:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    inboxMenuItem = menu.findItem(R.id.action_inbox);
    inboxMenuItem.setActionView(R.layout.menu_item_view);
    if (pendingIntroAnimation) {
        pendingIntroAnimation = false;
        startIntroAnimation();
    }
    return true;
}

КРОК 3: Тепер запишіть логіку для startIntroAnimation ():

private static final int ANIM_DURATION_TOOLBAR = 300;

private void startIntroAnimation() {
    btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));

    int actionbarSize = Utils.dpToPx(56);
    toolbar.setTranslationY(-actionbarSize);
    ivLogo.setTranslationY(-actionbarSize);
    inboxMenuItem.getActionView().setTranslationY(-actionbarSize);

    toolbar.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(300);
    ivLogo.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(400);
    inboxMenuItem.getActionView().animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(500)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    startContentAnimation();
                }
            })
            .start();
}

Моя бажана альтернатива:

Я б швидше оживив весь recilerview замість елементів всередині recilerview.

КРОК 1 і 2 залишаються колишніми.

На КРОКІ 3, як тільки ваш дзвінок API повернеться з вашими даними, я б запустив анімацію.

private void startIntroAnimation() {
    recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
    recyclerview.setAlpha(0f);
    recyclerview.animate()
            .translationY(0)
            .setDuration(400)
            .alpha(1f)
            .setInterpolator(new AccelerateDecelerateInterpolator())
            .start();
}

Це оживить весь ваш перегляд рециркулятора, щоб він летів у нижній частині екрана.


ви говорите про анімацію всього рециркулятора
Longerian

Так. Ви повинні прочитати перший абзац про те, чому я вважаю, що анімація цілого перегляду рециркулятора є кращою ідеєю, ніж кожен предмет. Ви можете спробувати анімувати кожен елемент, але він виглядатиме не дуже добре.
Simon Simon

Що таке latestPostRecyclerview?
Антоніо

1
Чи читав ви перший абзац своєї відповіді, де я вказав причину, чому анімаційні перегляди предметів не така чудова ідея? Також я б запропонував спробувати прийняте рішення, і тоді ви зрозумієте проблему, поверніться і спробуйте це рішення.
Саймон

3

Створіть цей метод у своєму адаптері перегляду рециркулятора

private void setZoomInAnimation(View view) {
        Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file 
        view.startAnimation(zoomIn);
    }

І нарешті додайте цей рядок коду в onBindViewHolder

setZoomInAnimation(holder.itemView);


2

У 2019 році я б запропонував усі анімації предметів помістити в ItemAnimator.

Почнемо з оголошення аніматора в режимі переробки:

with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}

Потім оголосіть спеціального аніматора,

class CustomAnimator() : DefaultItemAnimator() {

     override fun animateAppearance(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo?,
       postInfo: ItemHolderInfo): Boolean{} // declare  what happens when a item appears on the recycler view

     override fun animatePersistence(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo,
       postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change

}

Подібне до вищезгаданого є одне для зникнення animateDisappearance, додавання animateAdd, зміни animateChangeта переміщення animateMove.

Одним важливим моментом буде викликати правильних анімаційних диспетчерів всередині них.


Чи можете ви надати приклад спеціальної анімації зовнішнього вигляду за допомогою цієї функції заміщення? Я не можу знайти приклад, і я не впевнений, чи потрібно просто вказати анімацію у функції.
мін

1
gist.github.com/tadfisher/120d03f8380bfa8a16bf Я знайшов це в Інтернеті за допомогою швидкого пошуку, це дає уявлення про те, як працює перевантаження
Дінеш

0

Просто розширює адаптер, як показано нижче

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

І додайте супер метод у onBindViewHolder

@Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        super.onBindViewHolder(holder, position);

Це автоматичний спосіб створити анімований адаптер типу "Basheer AL-MOMANI"

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;

import java.util.Random;

/**
 * Created by eliaszkubala on 24.02.2017.
 */
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {


    @Override
    public T onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(T holder, int position) {
        setAnimation(holder.itemView, position);
    }

    @Override
    public int getItemCount() {
        return 0;
    }

    protected int mLastPosition = -1;

    protected void setAnimation(View viewToAnimate, int position) {
        if (position > mLastPosition) {
            ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
            viewToAnimate.startAnimation(anim);
            mLastPosition = position;
        }
    }

}

0

Думаю, краще використовувати його так: (у RecyclerView адаптер замінює лише один метод)

override fun onViewAttachedToWindow(holder: ViewHolder) {
    super.onViewAttachedToWindow(holder)

    setBindAnimation(holder)
}

Якщо ви хочете, щоб кожна анімація на вкладку була там, в RV.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.