2014-10-24 20 views
8

Tôi đang cố gắng thay thế GridView bằng RecyclerView mới (sử dụng GridLayoutManager) nhưng có vẻ như nó không tương thích tốt với gridLayoutAnimation (ClassCastException: LayoutAnimationController$AnimationParameters cannot be cast to GridLayoutAnimationController$AnimationParameters). Nó hoạt động với một hoạt hình bố trí thường xuyên, nhưng vì nó là một mạng lưới, phải mất quá nhiều thời gian để hoàn thành trên máy tính bảng.Làm cách nào để sử dụng GridLayoutAnimation trong RecyclerView?

Điều tôi đang cố thực hiện tương tự với Hierarchical Timing. Nếu bạn nhìn vào video ví dụ, nó sẽ hiển thị hoạt ảnh bố cục từ trên xuống bên trái theo chiều dọc theo đường chéo. Một hoạt ảnh bố cục thông thường sẽ thực thi hàng hoạt ảnh sau hàng, do đó mất quá nhiều thời gian để hoàn thành trên các lưới lớn hơn (ví dụ: máy tính bảng). Tôi cũng đã cố gắng khám phá ItemAnimator, nhưng điều đó sẽ chỉ chạy hoạt ảnh trên tất cả các ô cùng một lúc giống như trong ví dụ "Don't".

Có cách nào để thực hiện hoạt ảnh bố cục lưới này trong RecyclerView không?

Đây là 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" 
/> 

Và đây là grow_in.xml hoạt hình:

<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: Dựa trên câu trả lời của Galaxas0, here là một giải pháp chỉ yêu cầu bạn sử dụng chế độ xem tùy chỉnh mở rộng RecyclerView. Về cơ bản chỉ ghi đè phương thức attachLayoutAnimationParameters(). Với công cụ <gridLayoutAnimation> hoạt động như với 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); 
     } 
    } 
} 

Trả lời

5

LayoutAnimationController được kết hợp vào ViewGroup và cả ListViewGridView mở rộng phương pháp dưới đây để cung cấp của animationParams đứa trẻ. Vấn đề là GridLayoutAnimationController yêu cầu AnimationParameters riêng của mình mà không thể được đúc lớp.

/** 
    * 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; 
    } 

Vì phương pháp này theo mặc định thêm một LayoutAnimationController.AnimationParameters thay vì GridLayoutAnimationController.AnimationParameters, sửa chữa nên để tạo ra và đính kèm một trước. Những gì chúng ta cần phải thực hiện là gì GridView đã thực hiện:

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

Để tái tạo GridView, điều gần gũi nhất chúng ta có thể làm là bót đi giày những sửa đổi vào onBindViewHolder() cho phép họ chạy trước dispatchDraw, cuộc gọi kích hoạt hình ảnh động.

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; 

Nếu sử dụng RecyclerView 's mới GridLayoutManager, hãy thử nhận được các thông số từ đó. Mẫu trên là bằng chứng về khái niệm cho thấy nó hoạt động. Tôi đã hardcoded giá trị mà không chính xác làm việc cho ứng dụng của tôi là tốt.

Vì đây là API được sử dụng từ API 1 không có tài liệu hoặc mẫu thực, tôi rất khuyên bạn không nên sử dụng nó, xem xét có nhiều cách để nhân rộng chức năng của nó.

+0

Ông có thể vui lòng giải thích như thế nào? Tôi đã thử làm điều đó với ItemAnimator mặc định cũng như tải xuống các ví dụ về [ItemAnimators] khác nhau (https://github.com/gabrielemariotti/RecyclerViewItemAnimators/tree/master/library/src/main/java/it/gmariotti/recyclerview/itemanimator) nhưng tôi không thể có vẻ như là hình ảnh động mà tôi đang tìm kiếm. – Musenkishi

+0

Bạn đã chắc chắn không sử dụng 'notifyDataSetChanged()', và thay vào đó sử dụng 'notifyItemRangeAdded()' và 'notifyItemRangeRemoved()'? Điều này sẽ luôn kích hoạt hoạt ảnh đầu tiên tải. –

+0

Có, tôi đã thử sử dụng 'notifyItemRangeAdded()' nhưng như tôi đã mô tả ở trên, nó không cho tôi hành vi mong muốn. 'notifyItemRangeAdded()' sẽ chạy hoạt ảnh trên hàng đầu tiên và ** không ** bắt đầu trên hàng thứ hai cho đến khi tất cả các ô trong ô đầu tiên đã bắt đầu hoạt ảnh của nó. Hình ảnh động mà tôi có thể nhận được từ '' có thể đi theo đường chéo trên các hàng, có nghĩa là nó có thể bắt đầu làm động một số hàng cùng một lúc. Về cơ bản làm cho tất cả các ô trong lưới xuất hiện trong một làn sóng từ trên xuống dưới bên phải. – Musenkishi

0

Trích dẫn @Musenkishi tại https://gist.github.com/Musenkishi/8df1ab549857756098ba

Không đầu mối. Bạn có đang gọi recyclerView.scheduleLayoutAnimation(); sau khi đặt bộ điều hợp không?Và bạn đã đặt android:layoutAnimation="@anim/your_layout_animation" thành <GridRecyclerView> trong bố cục chưa?

Điều này giải quyết được vấn đề của tôi.

2

đơn giản giải pháp

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

Nhưng đừng quên để làm cho RecyclerAdapter bạn setHasStableIds(true); và thực hiện getItemID()

@Override 
public long getItemId(int position) { 
    return yourItemSpecificLongID; 
} 
Các vấn đề liên quan