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

237

Как я могу анимировать элементы RecyclerView, когда они появляются?

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

Есть идеи как этого добиться?

PaulNunezM
источник

Ответы:

42

Сделано просто с XML

Посетите Gist Link

Рез / аним / 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%" />

Рез / аним / 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>

Используйте в макетах и ​​recylcerview, как:

<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" />
iamnaran
источник
1
@ArnoldBrown Изменяя файл анимации. См: stackoverflow.com/questions/5151591/...
iamnaran
Это, безусловно, самый практичный ответ.
Оливер Мец
как заставить это работать каждый раз, когда список открывается, потому что теперь он делает это только в первый раз.
Привет Джалал
7
Вы должны позвонить recyclerView.scheduleLayoutAnimation()после изменения набора данных, в противном случае анимация не будет работать.
Зельвен
Должно ли это работать, когда предметы перерабатываются и возвращаются в поле зрения? Я попробовал это решение, и оно отлично работает для начальной анимации, когда вы можете сначала увидеть макет. После прокрутки элементы не имеют анимации при возвращении к просмотру.
Джейсон р
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.

MathieuMaree
источник
1
Я видел это, но это для добавления и удаления элементов после их появления. Мне нужно запустить анимацию прямо перед их появлением. В любом случае, спасибо Матье.
PaulNunezM
1
Насколько я знаю, вам нужно использовать CustomAdapter тогда.
MathieuMaree
20
Мне было интересно, если вы испытали и, возможно, решили "застрял" эффект с этими анимациями в RecyclerView? Я использовал похожий код для ListView, и мои элементы анимировались без проблем, независимо от того, как быстро я прокручивал, но с RecyclerView, если я прокручиваю быстро, некоторые элементы иногда застревали на экране поверх других элементов, и они не скрываются полностью - Как будто анимация была остановлена ​​до того, как она закончилась. На самом деле я пытался закомментировать часть кода, которая заполняет поля (пытаясь ускорить выполнение метода onBindViewHolder), поэтому я оставил код только для аниматора
Томислав
4
@MathieuMaree Спасибо за эту удивительную анимацию. Она выглядит хорошо для медленной прокрутки, но на элементах перемотки с быстрой прокруткой перекрываются элементы. Вы нашли это сообщение? Есть предложения по преодолению этой проблемы.
Гиру Бхай
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
Я сделал небольшую модификацию для вашего ответа, чтобы анимация работала только на прокрутке вниз, проверьте мой ответ
Башир АЛЬ-МОМАНИ
1
это портит макет (
перезаписанные
Это не правильный или рекомендуемый способ анимировать предметы, относящиеся к переработке. Вы должны использовать класс
ItemAnimator
25

Я создал анимацию из ответа pbm с небольшим, modificationчтобы запустить анимацию только один раз

другими словами 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
Что такое lastPosition здесь? Это arrayList.size () - 1
Sumit Shukla
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

Павел Бирюков
источник
Это сработало для меня, когда сначала загружается просмотрщик, но не при добавлении новых предметов. (Хотя я думаю, что это был вопрос такой хорошей работы)
К. Скьердал
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();
}

Моя предпочтительная альтернатива:

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

ШАГ 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();
}

Это оживило бы все ваше представление о переработчике так, чтобы оно отображалось в нижней части экрана.

Саймон
источник
Вы говорите об оживлении всего
взгляда
Да. Вы должны прочитать первый параграф о том, почему я думаю, что анимация всего обзора переработчика - лучшая идея, чем каждый предмет. Вы можете попробовать анимировать каждый элемент, но он не будет хорошо выглядеть.
Симон
Что такое latestPostRecyclerview?
Антонио
1
Читали ли вы первый абзац моего ответа, где я изложил причину, по которой анимация представлений элементов не такая уж хорошая идея? Также я бы предложил попробовать принятое решение, и тогда вы поймете проблему, вернитесь и попробуйте это решение.
Саймон
3

Создать этот метод в ваш recyclerview адаптер

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.

Милан Юркулак
источник