2014-10-24 12 views
8

Ich versuche, meine GridView durch die neue RecyclerView zu ersetzen (mit GridLayoutManager), aber es scheint, dass es nicht gut mit GridLayoutAnimation (ClassCastException: LayoutAnimationController$AnimationParameters cannot be cast to GridLayoutAnimationController$AnimationParameters) zurecht kommt. Es funktioniert mit einer normalen Layoutanimation, aber da es sich um ein Raster handelt, dauert die Fertigstellung auf Tablets zu lange.Wie verwende ich eine GridLayoutAnimation in einem RecyclerView?

Was ich zu erreichen versuche ist ähnlich wie Hierarchical Timing. Wenn Sie sich das Beispielvideo ansehen, wird die Layoutanimation von oben links nach unten - rechts diagonal angezeigt. Eine reguläre Layoutanimation würde die Animation Reihe um Reihe ausführen, wodurch es zu viel Zeit erfordert, um auf größeren Gittern (z. B. Tablets) zu vervollständigen. Ich habe auch versucht, ItemAnimator zu erkunden, aber das würde nur die Animation auf allen Zellen simultan wie im Beispiel "Do not" ausführen.

Gibt es eine Möglichkeit, diese Rasterlayout-Animation in einem RecyclerView zu erstellen?

Dies ist gridview_layout_animation.xml:

<!-- replace gridLayoutAnimation with layoutAnimation and --> 
<!-- replace column- and rowDelay with delay for RecyclerView --> 

<gridLayoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" 
    android:columnDelay="15%" 
    android:rowDelay="15%" 
    android:animation="@anim/grow_in" 
    android:animationOrder="normal" 
    android:direction="top_to_bottom|left_to_right" 
    android:interpolator="@android:interpolator/linear" 
/> 

Und das ist die grow_in.xml Animation:

<set android:shareInterpolator="false" 
xmlns:android="http://schemas.android.com/apk/res/android"> 
    <scale 
     android:interpolator="@android:interpolator/decelerate_quint" 
     android:fromXScale="0.0" 
     android:toXScale="1.0" 
     android:fromYScale="0.0" 
     android:toYScale="1.0" 
     android:pivotX="50%" 
     android:pivotY="50%" 
     android:fillAfter="true" 
     android:duration="400" 
     android:startOffset="200" 
    /> 
</set> 

EDIT: Basierend auf Galaxas0 Antwort ist here ein Lösung, die nur erfordert, dass Sie eine benutzerdefinierte Ansicht verwenden, die RecyclerView erweitert. Im Grunde nur überschreiben die attachLayoutAnimationParameters() Methode. Damit funktioniert <gridLayoutAnimation> wie bei GridView.

public class GridRecyclerView extends RecyclerView { 

    public GridRecyclerView(Context context) { 
     super(context); 
    } 

    public GridRecyclerView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    public void setLayoutManager(LayoutManager layout) { 
     if (layout instanceof GridLayoutManager){ 
      super.setLayoutManager(layout); 
     } else { 
      throw new ClassCastException("You should only use a GridLayoutManager with GridRecyclerView."); 
      } 
     } 

    @Override 
    protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) { 

     if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager){ 

      GridLayoutAnimationController.AnimationParameters animationParams = 
       (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters; 

      if (animationParams == null) { 
       animationParams = new GridLayoutAnimationController.AnimationParameters(); 
       params.layoutAnimationParameters = animationParams; 
      } 

      int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount(); 

      animationParams.count = count; 
      animationParams.index = index; 
      animationParams.columnsCount = columns; 
      animationParams.rowsCount = count/columns; 

      final int invertedIndex = count - 1 - index; 
      animationParams.column = columns - 1 - (invertedIndex % columns); 
      animationParams.row = animationParams.rowsCount - 1 - invertedIndex/columns; 

     } else { 
      super.attachLayoutAnimationParameters(child, params, index, count); 
     } 
    } 
} 

Antwort

5

LayoutAnimationController gekoppelt ist, in ViewGroup und beide ListView und GridView des Verfahrens erstrecken sich unterhalb der animationParams des Kindes bereitzustellen. Das Problem besteht darin, dass GridLayoutAnimationController eine eigene AnimationParameters erfordert, die nicht in Klassen gegossen werden kann.

/** 
    * Subclasses should override this method to set layout animation 
    * parameters on the supplied child. 
    * 
    * @param child the child to associate with animation parameters 
    * @param params the child's layout parameters which hold the animation 
    *  parameters 
    * @param index the index of the child in the view group 
    * @param count the number of children in the view group 
    */ 
    protected void attachLayoutAnimationParameters(View child, 
      LayoutParams params, int index, int count) { 
     LayoutAnimationController.AnimationParameters animationParams = 
        params.layoutAnimationParameters; 
     if (animationParams == null) { 
      animationParams = new LayoutAnimationController.AnimationParameters(); 
      params.layoutAnimationParameters = animationParams; 
     } 

     animationParams.count = count; 
     animationParams.index = index; 
    } 

Da diese Methode standardmäßig ein fügt LayoutAnimationController.AnimationParameters statt GridLayoutAnimationController.AnimationParameters, sollte das Update sein, eine vorher zu erstellen und zu befestigen. Was müssen wir implementieren ist, was GridView bereits tut:

@Override 
protected void attachLayoutAnimationParameters(View child, 
     ViewGroup.LayoutParams params, int index, int count) { 

    GridLayoutAnimationController.AnimationParameters animationParams = 
      (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters; 

    if (animationParams == null) { 
     animationParams = new GridLayoutAnimationController.AnimationParameters(); 
     params.layoutAnimationParameters = animationParams; 
    } 

    animationParams.count = count; 
    animationParams.index = index; 
    animationParams.columnsCount = mNumColumns; 
    animationParams.rowsCount = count/mNumColumns; 

    if (!mStackFromBottom) { 
     animationParams.column = index % mNumColumns; 
     animationParams.row = index/mNumColumns; 
    } else { 
     final int invertedIndex = count - 1 - index; 

     animationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns); 
     animationParams.row = animationParams.rowsCount - 1 - invertedIndex/mNumColumns; 
    } 
} 

So replizieren GridView, die nächste Sache, die wir tun können, um die Änderungen in onBindViewHolder() Schuhanzieher, die sie vor dispatchDraw, den Anruf ausgeführt werden können, die Animationen auslöst.

ViewGroup.LayoutParams params = holder.itemView.getLayoutParams(); 
     GridLayoutAnimationController.AnimationParameters animationParams = new GridLayoutAnimationController.AnimationParameters(); 
     params.layoutAnimationParameters = animationParams; 

     animationParams.count = 9; 
     animationParams.columnsCount = 3; 
     animationParams.rowsCount = 3; 
     animationParams.index = position; 
     animationParams.column = position/animationParams.columnsCount; 
     animationParams.row = position % animationParams.columnsCount; 

Wenn RecyclerView ‚s neue GridLayoutManager, versuchen mit Hilfe von Parametern aus, dass zu bekommen. Das obige Beispiel ist ein Proof of Concept, um zu zeigen, dass es funktioniert. Ich habe Werte programmiert, die nicht genau für meine Anwendung funktionieren.

Da dies eine API ist, die es seit API 1 gibt, ohne echte Dokumentation oder Beispiele, würde ich dringend davon abraten, sie zu verwenden, da es viele Möglichkeiten gibt, ihre Funktionalität zu replizieren.

+0

Könnten Sie bitte erklären, wie? Ich habe versucht, es mit dem Standard-ItemAnimator sowie das Herunterladen von Beispielen für verschiedene [ItemAnimators] (https://github.com/gabrielemariotti/RecyclerViewItemAnimators/tree/master/library/src/main/java/it/gmariotti/recyclerview/itemanimator), aber ich kann nicht auf die Animation, die ich suche, scheinen. – Musenkishi

+0

Haben Sie sichergestellt, dass Sie 'notifyDataSetChanged()' nicht verwenden und stattdessen 'notifyItemRangeAdded()' und 'notifyItemRangeRemoved()' 'verwenden? Dies sollte immer die erste Lade-Animation auslösen. –

+0

Ja, ich habe versucht, 'notifyItemRangeAdded()' zu verwenden, aber wie ich oben beschrieben habe, gibt es mir nicht das gewünschte Verhalten. 'notifyItemRangeAdded()' wird die Animation in der ersten Zeile ausführen und ** nicht ** in der zweiten Zeile, bis alle Zellen in der ersten Zeile mit ihrer Animation begonnen haben. Die Animation, die ich von einem ' erhalten könnte, könnte diagonal über die Zeilen gehen, was bedeutet, dass sie mehrere Zeilen gleichzeitig animieren könnte. Im Wesentlichen werden alle Zellen im Gitter in einer Welle von oben links nach rechts unten angezeigt. – Musenkishi

0

Zitiert @Musenkishi bei https://gist.github.com/Musenkishi/8df1ab549857756098ba

Keine Ahnung. Rufen Sie nach dem Einstellen des Adapters recyclerView.scheduleLayoutAnimation(); an?Und haben Sie android:layoutAnimation="@anim/your_layout_animation" zu Ihrem <GridRecyclerView> im Layout?

Dies löste mein Problem.

2

Einfachere Lösung

TransitionManager.beginDelayedTransition(moviesGridRecycler); 
gridLayoutManager.setSpanCount(gridColumns); 
adapter.notifyDataSetChanged(); 

Aber vergessen Sie nicht, Ihre RecyclerAdapter setHasStableIds(true); und implementieren zu machen getItemID()

@Override 
public long getItemId(int position) { 
    return yourItemSpecificLongID; 
} 
Verwandte Themen