2015-01-06 26 views
14

RecyclerViewに2つの列を持つStaggeredGridLayoutManagerの項目を3つ(少なくとも問題がある場合)表示しようとしています。最初の項目は2つの行にまたがっています。今、私は先頭に「アイテム2」の項目を移動していRecyclerView StaggeredGridLayoutManager並べ替えの問題

Correct initial behaviour

:ここでは、それがどのように見えるかです。ここでは、アダプタ(それは私が、より複雑なプロジェクトでは、私が持っている問題を示すために書いたサンプルです)で、私は呼んコードです:その後

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

、配列は結構です:[Item 2, Item 1, Item 3]が。しかし、ビューには、はるかに細かいからである:

Layout issue

私はRecyclerView(スクロールするのに十分な項目がない場合はオーバースクロール効果を誘発するのに十分な)をタッチすると、私は予想左へ2項の移行(素敵なアニメーションで)最初の場所でそれを参照してください。

Expected result

あなたは多分コードで見たように、私はnotifyDataSetChanged()への呼び出しによってnotifyItemMoved(idx, position)を交換しようとしました。それは動作しますが、変更はアニメ化されません。

これを実証し、GitHubに載せるための完全なサンプルを書きました。それはほとんど最小です(項目を移動し、スパニングを切り替えるオプションがあります)。

私は何が間違っているのか分かりません。これはStaggeredGridLayoutManagerのバグですか? notifyDataSetChanged()を避けたいのですが、アニメーションに関して一貫性を保ちたいと思います。


編集:一部を掘った後、完全にスパンしたアイテムを表示する必要はありません。私はフルスパンを削除しました。アイテム2をポジション0に移動しようとするとは移動しません:アイテム1が後になり、アイテム3が右に移動します。空のセル、アイテム2、改行、アイテム1、私はまだスクロール後に正しいレイアウトを持っています。

さらに興味深いのは、GridLayoutManagerの問題がないことです。私は解決策ではありませんので、フルスパンのアイテムが必要ですが、実際にはバグだと思います。StaggeredGridLayoutManager ...

+0

これはバグかどうかわかりませんが、これも見たことがあります。スクロールしてギャップストラテジーをチェックしたように見えます。 – tyczj

+0

@tyczj: 'GAP_HANDLING_NONE'は動いているアイテムを避けますが、依然として誤った処分を受ける。 'GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS'はおそらくデフォルトですが、とにかく何も指定しない場合と同じ動作をします。 –

+0

はい私はちょうどあなたがスクロールした後にアダプターがギャップをチェックしてから、物事を正しい場所に移動すると言っていることを知っています。 – tyczj

答えて

8

私は完全な答えはありませんが、回避策とバグレポート(私はそれが関係していると信じている)。

notifyItemMoved()を呼び出した後、2番目のスクリーンショットのようにレイアウトを更新して、StaggeredGridLayoutManger(sglm)のinvalidateSpanAssignments()を呼び出してください。 「挑戦」とは、nIM()の直後に呼び出すと実行されないということです。通話を数ミリ秒遅らせると、それが実行されます。ハンドラの中でそれを参照、スイッチブロック内

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

そしてダウン::だから、MainActivityのためのあなたの参照のコードでは、私はあなたのsglmプライベートフィールドを作った

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

結果はということですあなたのアニメーションはまだ実行され、レイアウトはあなたが望むように終わります。これは本当のクルージングですが、うまくいきます。私の「症状」と、必要な呼び出しが異なっていたものの、根本的な問題は同じであるように思わ

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

:私はこれは私が発見し、以下のリンクから報告された同じバグであると信じています。

幸運を祈る!

EDIT:postDelayedする必要はありません、単にトリックを行います投稿:

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

私の元理論コールがレイアウトパスが終わるまでブロックされたが、私はそれがないと信じていたということでした場合。代わりに、invalidateSpanAssignments()をすぐに呼び出すと、(レイアウトの変更が完了する前に)実際に実行するのが早すぎると思います。したがって、上記の投稿(遅延なし)は、レンダリングキューの終わりにコールを追加するだけです。レンダリングキューは、レイアウトの後に発生します。

+0

合理的な説明が聞こえます。私は明日それを見てみましょう、ありがとう! –

+0

バグはGoogleによって認識されています:https://code.google.com/p/android/issues/detail?id=93711#c1 あなたの回避策はその間に問題なく動作しますので、これを受け入れます。 –

+0

スパン割り当ての無効化をアニメーション化する方法に関するアイデアはありますか? – mato

0

私はこのようにしました。

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

カスタムRecyclerViewスクロールリスナーのためのクラスを作成します。

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

希望すると、これが役立ちます。

+0

ありがとうございますが、サポートライブラリ22以降でGoogleによって修正がリリースされました。元の質問に書いたコードをそのまま使っています。 –

関連する問題