2015-07-14 21 views
15

Mam Usuń w machnięcia, który rysuje tło (podobnie jak w aplikacji Inbox), realizowany przez ItemTouchHelper - poprzez nadpisanie metody onChilDraw i rysując prostokąt na dostarczonych płótnie:RecyclerView ItemTouchHelper swipe usunąć animacja

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); 
      } 
     }); 

metoda remove nazywa powyżej jest w adapterze:

public void remove(int position) { 
     items.remove(position); 
     notifyItemRemoved(position); 
    } 

w tle rysuje się ładnie, ale gdy notifyItemRemoved nazywa (według pana Debugger), przy czym RecyclerView najpierw usuwa moją ładną zielonym tle, a następnie popycha dwa a sąsiadujące przedmioty razem.

enter image description hereenter image description here

chciałbym go zachować tam tło podczas gdy to robi, że (podobnie jak w aplikacji Inbox). Czy istnieje jakiś sposób, aby to zrobić?

Odpowiedz

2

Miałem ten sam problem i nie chciałem wprowadzać nowej biblioteki, żeby to naprawić. RecyclerView nie usuwa twojego całkiem zielonego tła, po prostu przerysowuje się, a Twój ItemTouchHelper nie rysuje. W rzeczywistości rysuje, ale dX ma wartość 0 i rysuje od itemView.getLeft() (co odpowiada 0) do dX (co oznacza 0), więc nic nie widać. I to za dużo, ale wrócę do tego później.

W każdym razie z powrotem do tła podczas animacji wierszy: Nie mogłem tego zrobić w ramach ItemTouchHelper i onChildDraw. W końcu musiałem dodać kolejnego dekoratora przedmiotów, żeby to zrobić.Jest zgodny z następującymi liniami:

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 
    if (parent.getItemAnimator().isRunning()) { 
     // find first child with translationY > 0 
     // draw from it's 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); 
    } 
} 

Ten kod uwzględnia tylko wiersze animowane, ale powinieneś również rozważyć pominięcie wierszy. Tak się dzieje, gdy przesuniesz palcem usuń ostatni wiersz, wiersze powyżej będą animowane w dół do tego miejsca.

Kiedy powiedziałem, że ItemTouchHelper rysuje za dużo, co miałem na myśli było: Wygląda na to, że ItemTouchHelper zachowuje ViewHolder s usuniętych wierszy na wypadek konieczności ich przywrócenia. Oprócz tych, które przesyłają VH, wywoływane są także onChildDraw. Nie jestem pewien co do implikacji związanych z zarządzaniem pamięcią tego zachowania, ale potrzebowałem dodatkowej kontroli na początku onChildDraw, aby uniknąć rysowania wierszy "fantom".

if (viewHolder.getAdapterPosition() == -1) { 
    return; 
} 

W twoim przypadku rysuje od lewej = 0 do prawej = 0, więc nie widzisz niczego poza napowietrznym. Jeśli zaczynasz widzieć poprzednio przesuwane rzędy, rysując ich tło, to jest powód.

EDYCJA: Miałem iść na to, zobacz ten blog post i ten github repo.

1

Udało mi się uruchomić go przy użyciu biblioteki Wasabeefs's recyclerview-animators.

My ViewHolder teraz rozciąga przewidzianego AnimateViewHolder Biblioteki:

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

} 

W overrided implementacje funkcji są identyczne, co jest w readme recyclerview-animatorów na github.

konieczne wydaje się również, aby zmienić ItemAnimator do niestandardowej jeden i ustawić removeDuration 0 (lub inny niska wartość - jest to, aby zapobiec migotanie obrazu):

recyclerView.setItemAnimator(new SlideInLeftAnimator()); 
    recyclerView.getItemAnimator().setRemoveDuration(0); 

nie powoduje żadnych problemów jako normalna (bez przesuwania) usuwana animacja jest używana w AnimateViewHolder.

Cały kod pozostał taki sam jak w pytaniu. Nie miałem czasu, aby rozgryźć wewnętrzne działanie tego, ale jeśli ktoś ma ochotę to zrobić, zachęcamy do aktualizacji tej odpowiedzi.

Aktualizacja: Ustawienie recyclerView.getItemAnimator().setRemoveDuration(0); w rzeczywistości powoduje przerwanie animacji "ponownego wiązania" przesunięcia. Na szczęście usunięcie tej linii i ustawienie dłuższego czasu trwania w animateRemoveImpl (500 działa dla mnie) również rozwiązuje problem migotania.

Aktualizacja 2: Okazuje się, że ItemTouchHelper.SimpleCallback wykorzystuje trwania animacji ItemAnimator, która jest dlaczego powyżej setRemoveDuration (0) łamie animacje machnięcia. Po prostu przesłonięcie metody 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; 
} 

rozwiązuje ten problem.