#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
:
- Не вызывайте через to
super.animateAdd(holder)
— вы просто столкнетесь с конфликтом с затуханием анимации по умолчанию. - Вы должны отслеживать эту анимацию и вызывать
dispatchAddStarting(holder)
/dispatchAddFinished(holder)
, чтобы сообщить super implementation о текущем состоянии анимации. - Вам нужно переопределить еще несколько методов (в коде ниже), которые должны знать, выполняются ли ваши анимации, и предоставить опцию отмены
Пример 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) {
}
}
}