2015-01-06 26 views
14

Tôi đang cố gắng hiển thị ba (ít nhất là trường hợp tôi có vấn đề với) các mục trong một RecyclerView với một StaggeredGridLayoutManager với hai cột. Mục đầu tiên được kéo dài trên hai hàng. Đây là cách nó trông giống như:RecyclerView StaggeredGridLayoutManager sắp xếp lại vấn đề

Correct initial behaviour

Bây giờ, tôi đang di chuyển các mục "Khoản 2" to top. Dưới đây là đoạn code tôi gọi, trong bộ chuyển đổi (đó là một mẫu tôi đã viết để chứng minh vấn đề tôi có trong một dự án phức tạp hơn):

private int findById(int id) { 
    for (int i = 0; i < items.size(); ++i) { 
     if (items.get(i).title.equals("Item " + id)) { 
      return i; 
     } 
    } 

    return -1; 
} 

// Moving the item "Item 2" with id = 2 and position = 0 
public void moveItem(int id, int position) { 
    final int idx = findById(id); 
    final Item item = items.get(idx); 

    if (position != idx) { 
     items.remove(idx); 
     items.add(position, item); 
     notifyItemMoved(idx, position); 
     //notifyDataSetChanged(); 
    } 
}

Sau đó, mảng là tốt: [Item 2, Item 1, Item 3]. Tuy nhiên, quan điểm là xa tốt:

Layout issue

Nếu tôi chạm vào RecyclerView (đủ để kích hoạt tác dụng thanh cuộn nếu không có đủ các mục để di chuyển), khoản 2 di chuyển sang trái, nơi tôi dự kiến nhìn thấy nó ở nơi đầu tiên (với một hình ảnh động đẹp):

Expected result

Như bạn có thể thấy trong các mã, tôi đã cố gắng để thay thế notifyItemMoved(idx, position) bởi một cuộc gọi đến notifyDataSetChanged(). Nó hoạt động, nhưng thay đổi không phải là hoạt hình.

Tôi đã viết một mẫu hoàn chỉnh để minh họa điều này và đặt nó trên GitHub. Đó là gần như tối thiểu (có các tùy chọn để di chuyển mục và chuyển đổi spanning của họ).

Tôi không thấy những gì tôi có thể làm sai. Đây có phải là lỗi với StaggeredGridLayoutManager không? Tôi muốn tránh notifyDataSetChanged() vì tôi muốn giữ sự nhất quán liên quan đến hoạt ảnh.


Chỉnh sửa: sau một số lần đào, không cần phải có mục toàn bộ để hiển thị sự cố. Tôi đã gỡ bỏ toàn bộ nhịp. Khi tôi cố gắng di chuyển Mục 2 đến vị trí 0, nó không di chuyển: Mục 1 đi sau nó và Mục 3 được di chuyển ở bên phải, vì vậy tôi có: ô trống, Mục 2, dòng mới, Mục 1, Mục 3. Tôi vẫn có bố cục chính xác sau khi cuộn.

Điều thú vị hơn là tôi không gặp sự cố với số GridLayoutManager. Tôi cần một mục toàn bộ vì vậy nó không phải là một giải pháp, nhưng tôi đoán nó thực sự là một lỗi trong StaggeredGridLayoutManager

+0

Tôi không biết đây có phải là lỗi hay không nhưng tôi cũng thấy điều này, mọi thứ dường như chỉ di chuyển khi bạn cuộn và 'khoảng cách chiến lược' được chọn – tyczj

+0

@tyczj:' GAP_HANDLING_NONE' tránh các mục đang di chuyển, nhưng tôi vẫn nhận được một bố trí không chính xác. 'GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS' có lẽ là mặc định, dù sao, tôi có cùng hành vi với nó hơn là không chỉ định bất cứ điều gì. –

+0

Có, tôi biết tôi chỉ nói rằng sau khi bạn cuộn bộ điều hợp kiểm tra các khoảng trống và sau đó di chuyển mọi thứ đến đúng vị trí – tyczj

Trả lời

8

Tôi không có câu trả lời hoàn chỉnh, nhưng tôi có thể chỉ cho bạn cả cách giải quyết và báo cáo lỗi (mà tôi tin là có liên quan).

Bí quyết cập nhật bố cục sao cho nó giống như ảnh chụp màn hình thứ hai của bạn, là gọi invalidateSpanAssignments() trên số StaggeredGridLayoutManger (sglm) sau khi bạn đã gọi notifyItemMoved(). "Thách thức" là nếu bạn gọi nó ngay lập tức sau nIM(), nó sẽ không chạy. Nếu bạn trì hoãn cuộc gọi cho một vài ms, nó sẽ.Vì vậy, trong mã tham chiếu của bạn cho MainActivity, tôi đã thực hiện sglm bạn một lĩnh vực riêng:

private StaggeredGridLayoutManager sglm; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    adapter = new Adapter(); 
    recyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
    sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 
    recyclerView.setLayoutManager(sglm); 
    recyclerView.setItemAnimator(new DefaultItemAnimator()); 
    recyclerView.setAdapter(adapter); 
} 

Và xuống trong khối chuyển đổi, tham khảo nó trong một handler:

 case R.id.move_sec_top: 
      adapter.moveItem(2, 0); 
      new Handler().postDelayed(new Runnable() { 
       @Override 
       public void run() { 
        sglm.invalidateSpanAssignments(); 
       } 
      }, 100); 
      return true; 

Kết quả là hoạt ảnh của bạn vẫn chạy, bố cục sẽ kết thúc theo cách bạn muốn. Đây là một kludge thực, nhưng nó hoạt động. Tôi tin rằng đây là lỗi tương tự mà tôi tìm thấy và báo cáo tại liên kết sau:

https://code.google.com/p/android/issues/detail?id=93156

Trong khi tôi "triệu chứng" và cuộc gọi yêu cầu là khác nhau, vấn đề cơ bản có vẻ là giống hệt nhau.

Chúc may mắn!

EDIT: Không cần phải postDelayed, chỉ cần đăng sẽ làm các trick:

 case R.id.move_sec_top: 
      adapter.moveItem(2, 0); 
      new Handler().post(new Runnable() { 
       @Override 
       public void run() { 
        sglm.invalidateSpanAssignments(); 
       } 
      }); 
      return true; 

lý thuyết ban đầu của tôi là cuộc gọi đã bị chặn cho đến khi vượt qua bố cục đã kết thúc, nhưng tôi tin rằng không phải là trường hợp. Thay vào đó, bây giờ tôi nghĩ rằng nếu bạn gọi invalidateSpanAssignments() ngay lập tức, nó thực sự thực hiện quá sớm (trước khi thay đổi bố trí đã hoàn thành). Vì vậy, bài đăng ở trên (không chậm trễ) chỉ cần thêm cuộc gọi vào cuối hàng đợi hiển thị nơi nó diễn ra sau khi bố cục.

+0

Nghe có giải thích hợp lý. Tôi sẽ xem xét nó vào ngày mai, cảm ơn! –

+0

Lỗi đã được Google thừa nhận: https://code.google.com/p/android/issues/detail?id=93711#c1 Giải pháp của bạn hoạt động tốt trong thời gian chờ đợi, vì vậy tôi sẽ chấp nhận điều này. –

+0

Bất kỳ ý tưởng nào về cách tạo hiệu ứng làm mất hiệu lực phân bổ khoảng thời gian? – mato

0

Tôi đã làm theo cách này.

StaggeredGridLayoutManager gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 
gaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); 
recyclerView.setLayoutManager(gaggeredGridLayoutManager); 

dataList = YourDataList (Your Code for Arraylist); 

recyclerView.setItemAnimator(new DefaultItemAnimator()); 
recyclerAdapter = new DataAdapter(dataList, recyclerView); 
recyclerView.setAdapter(recyclerAdapter); 

// Magic line 
recyclerView.addOnScrollListener(new ScrollListener()); 

Tạo lớp cho Custom RecyclerView Trình xử lý cuộn.

private class ScrollListener extends RecyclerView.OnScrollListener { 
    @Override 
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 
     gaggeredGridLayoutManager.invalidateSpanAssignments(); 
    } 
} 

Hy vọng điều này sẽ giúp bạn.

+0

Cảm ơn bạn, nhưng bản sửa lỗi đã được Google phát hành trong thư viện hỗ trợ 22 và mới hơn. Nó bây giờ làm việc ra khỏi hộp với mã tôi đã viết trong câu hỏi ban đầu. –

Các vấn đề liên quan