Просмотр Recycler добавить элемент Аниматор не анимирующий

#android #android-studio #animation #android-recyclerview #android-animation

#Android #android-studio #Анимация #android-recyclerview #android-анимация

Вопрос:

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

viewholder_add_anim

 <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration = "500"
        android:fromXScale="0%"
        android:fromYScale="0%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="100%"
        android:toYScale="100%"/>
</set>
  

CustomItemAnimator

 import android.view.animation.AnimationUtils;

import com.example.bookui.R;

import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;

public class CustomItemAnimator extends DefaultItemAnimator {
    @Override
    public boolean animateRemove(RecyclerView.ViewHolder holder) {
        return super.animateRemove(holder);
    }

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        holder.itemView.setAnimation(AnimationUtils.loadAnimation(holder.itemView.getContext(), 
                R.anim.viewholder_add_anim));
        return super.animateAdd(holder);
    }
}
  

Основная активность
общедоступный класс MainActivity расширяет AppCompatActivity {

     private RecyclerView recyclerViewBooks;
    private BookAdapter bookAdapter;
    private List<Book> mdata;
    private Button addButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        initmdataBooks();
        setUpBookAdapter();
    }

    private void initViews() {
        addButton = findViewById(R.id.add_btn);
        recyclerViewBooks = findViewById(R.id.recyclerView);
        recyclerViewBooks.setHasFixedSize(true);

        recyclerViewBooks.setItemAnimator(new CustomItemAnimator());

        addButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                addBook();
            }
        });
    }

    private void initmdataBooks() {
        mdata = new ArrayList<>();
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book2));
        mdata.add(new Book(R.drawable.book3));
        mdata.add(new Book(R.drawable.book2));
        mdata.add(new Book(R.drawable.book4));
        mdata.add(new Book(R.drawable.book5));
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book1));
        mdata.add(new Book(R.drawable.book5));
    }

    private void setUpBookAdapter() {
        bookAdapter = new BookAdapter(mdata);
        recyclerViewBooks.setAdapter(bookAdapter);
    }

    private void addBook() {
        Book book = new Book(R.drawable.book4);
        mdata.add(1, book);
        bookAdapter.notifyDataSetChanged();
    }
}
  

Ответ №1:

Вы должны правильно заполнить ItemAnimator контракт, а не слепо переопределять animateAdd :

  1. Не вызывайте через to super.animateAdd(holder) — вы просто столкнетесь с конфликтом с затуханием анимации по умолчанию.
  2. Вы должны отслеживать эту анимацию и вызывать dispatchAddStarting(holder) / dispatchAddFinished(holder) , чтобы сообщить super implementation о текущем состоянии анимации.
  3. Вам нужно переопределить еще несколько методов (в коде ниже), которые должны знать, выполняются ли ваши анимации, и предоставить опцию отмены

Пример DefaultItemAnimator переопределен для запуска анимации увеличения масштаба для вновь добавленных элементов:

 public class AddRecAnimator extends DefaultItemAnimator {
    private final static String TAG = "AddRecAnimator";

    // must keep track of all pending/ongoing animations.
    private final ArrayList<AddHolder> pending = new ArrayList<>();
    private final HashMap<RecyclerView.ViewHolder, AddHolder> additions = new HashMap<>();

    @Override
    public boolean animateAdd(RecyclerView.ViewHolder holder) {
        pending.add(new AddHolder(holder));
        return true;    // return true to receive call to runPendingAnimations
    }

    @Override
    public void runPendingAnimations() {
        for (AddHolder ah : pending) {
            ah.start();
        }
        pending.clear();
        super.runPendingAnimations();
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        AddHolder ah = additions.get(item);
        if (ah != null) {
            ah.endAnimation();
        }
        super.endAnimation(item);
    }

    @Override
    public void endAnimations() {
        for (AddHolder ah : pending) {
            ah.resetViewHolderState();
            dispatchAddFinished(ah.holder);
        }
        for (AddHolder ah : additions.values()) {
            ah.resetViewHolderState();
            dispatchAddFinished(ah.holder);
        }
        pending.clear();
        additions.clear();
        super.endAnimations();
    }

    @Override
    public boolean isRunning() {
        return super.isRunning() amp;amp;
                !pending.isEmpty() amp;amp;
                !additions.isEmpty();
    }

    /**
     * This is container for addition animation. It's also end listener for it.
     */
    private final class AddHolder implements Animation.AnimationListener {
        private final RecyclerView.ViewHolder holder;

        private AddHolder(RecyclerView.ViewHolder holder) {
            this.holder = holder;
            Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
                    ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
                    ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(500);
            anim.setAnimationListener(this);
            holder.itemView.setAnimation(anim);
            dispatchAddStarting(holder);
        }

        void start() {
            View itemView = holder.itemView;
            Animation a = itemView.getAnimation();
            if (a != null) {
                a.start();
                additions.put(holder, this);
            } else {
                endAnimation(); // invalid state, animation missing
            }
        }

        private void resetViewHolderState() {
            // reset state as if no animation was ran
            Animation a = holder.itemView.getAnimation();
            if (a != null) {
                a.setAnimationListener(null);
                a.cancel();
                holder.itemView.clearAnimation();
            }
            holder.itemView.setScaleX(1f);
            holder.itemView.setScaleY(1f);
        }

        // called when animation ends or is manually cancelled
        protected void endAnimation(){
            additions.remove(holder);
            resetViewHolderState();
            dispatchAddFinished(holder);
            // if all animations in animator are done dispatch they're finished
            if (!isRunning()) dispatchAnimationsFinished();
        }

        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            endAnimation();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    }
}