RecyclerView ItemTouchHelper салфетки удалить анимацию
У меня есть удаление при удалении, которое рисует фон (как и приложение Inbox), реализованное с помощью ItemTouchHelper - путем переопределения метода onChilDraw и рисования прямоугольника на предоставленном холсте:
ItemTouchHelper mIth = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
remove(viewHolder.getAdapterPosition());
}
public boolean onMove(RecyclerView recyclerview, RecyclerView.ViewHolder v, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
Drawable d = ContextCompat.getDrawable(context, R.drawable.bg_swipe_item_right);
d.setBounds(itemView.getLeft(), itemView.getTop(), (int) dX, itemView.getBottom());
d.draw(c);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
});
Выбранный выше метод удаления находится в адаптере:
public void remove(int position) {
items.remove(position);
notifyItemRemoved(position);
}
Фон отлично рисуется, но когда вызывается notifyItemRemoved (согласно Mr. Debugger), RecyclerView сначала удаляет мой довольно зеленый фон, а затем сжимает два соседних элемента вместе.
![enter image description here]()
![enter image description here]()
Мне бы хотелось, чтобы он сохранял фон там, пока он это делает (как и приложение Inbox). Есть ли способ сделать это?
Ответы
Ответ 1
У меня была такая же проблема, и я не хотел вводить новую библиотеку, чтобы исправить ее. RecyclerView
не удаляет ваш довольно зеленый фон, он просто перерисовывает себя, а ваш ItemTouchHelper
больше не рисуется. На самом деле это рисунок, но dX
равен 0 и рисуется от itemView.getLeft()
(от 0) до dX
(что равно 0), поэтому вы ничего не видите. И это слишком много, но я вернусь к нему позже.
Во всяком случае, вернуться к фону во время анимации строк: я не мог сделать это в пределах ItemTouchHelper
и onChildDraw
. В конце концов мне пришлось добавить еще один декоратор предметов, чтобы сделать это. Он идет по этим строкам:
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getItemAnimator().isRunning()) {
// find first child with translationY > 0
// draw from it top to translationY whatever you want
int top = 0;
int bottom = 0;
int childCount = parent.getLayoutManager().getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getLayoutManager().getChildAt(i);
if (child.getTranslationY() != 0) {
top = child.getTop();
bottom = top + (int) child.getTranslationY();
break;
}
}
// draw whatever you want
super.onDraw(c, parent, state);
}
}
В этом коде учитываются только анимированные строки, но вы также должны учитывать, что строки спускаются. Это произойдет, если вы удалите последнюю строку, строки выше будут анимированы до этого места.
Когда я сказал, что ваш ItemTouchHelper слишком сильно использует то, что я имел в виду, выглядит так: Похоже, что ItemTouchHelper
хранит ViewHolder
удаленных строк в случае, если их нужно восстановить. Он также называет onChildDraw
для этих VH в дополнение к VH, который выкачивается. Не уверен относительно последствий управления памятью этого поведения, но мне нужна дополнительная проверка в начале onChildDraw
, чтобы избежать рисования для строк "fantom".
if (viewHolder.getAdapterPosition() == -1) {
return;
}
В вашем случае это рисование слева = 0 вправо = 0, поэтому вы ничего не видите, кроме накладных расходов. Если вы начинаете видеть ранее прочищенные строки, рисующие их фоны, это и есть причина.
EDIT: я пошел на это, см. это сообщение в блоге и github repo.
Ответ 2
Мне удалось заставить его работать с помощью библиотеки Wasaceefs recyclerview-animators.
Теперь My ViewHolder расширяет библиотеку, предоставленную AnimateViewHolder:
class MyViewHolder extends AnimateViewHolder {
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
this.textView = (TextView) itemView.findViewById(R.id.item_name);
}
@Override
public void animateAddImpl(ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(0)
.alpha(1)
.setDuration(300)
.setListener(listener)
.start();
}
@Override
public void preAnimateAddImpl() {
ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3f);
ViewCompat.setAlpha(itemView, 0);
}
@Override
public void animateRemoveImpl(ViewPropertyAnimatorListener listener) {
ViewCompat.animate(itemView)
.translationY(0)
.alpha(1)
.setDuration(300)
.setListener(listener)
.start();
}
}
Реализации переопределенных функций идентичны тем, что находится в readme-reciler-аниматорах на github.
Также представляется необходимым изменить ItemAnimator на пользовательский и установить для параметра removeDuration значение 0 (или другое низкое значение - это предотвращение некоторых мерцаний):
recyclerView.setItemAnimator(new SlideInLeftAnimator());
recyclerView.getItemAnimator().setRemoveDuration(0);
Это не вызывает никаких проблем, поскольку обычная (не-swiping) удаление анимации используется в AnimateViewHolder.
Все остальные коды остались такими же, как в вопросе. У меня не было времени, чтобы разобраться в внутренней работе этого, но если кто-то чувствует, что делает это, не стесняйтесь обновлять этот ответ.
Обновление: настройка recyclerView.getItemAnimator().setRemoveDuration(0);
на самом деле разбивает анимацию "повторного воспроизведения" салфетки. К счастью, удаление этой строки и установка большей продолжительности в animateRemoveImpl (500 работает для меня) также решает проблему мерцания.
Обновление 2: Оказывается, что ItemTouchHelper.SimpleCallback использует длительность анимации ItemAnimator, поэтому вышеуказанный setRemoveDuration (0) разбивает анимацию салфетки. Просто заменив его методом getAnimationDuration на:
@Override
public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
return animationType == ItemTouchHelper.ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION
: DEFAULT_SWIPE_ANIMATION_DURATION;
}
решает эту проблему.