2016-01-12 35 views
1

我試圖通過RecyclerView和ItemTouchHelper實現「刷卡刪除」功能。我有一個奇怪的問題,我無法找到我的生活問題。我從頂端(而不是第一個)掃一個項目,它消失了,到目前爲止這麼好。當我滾動回來時,在上面一排擦掉的物品上面有一個人工製品。看起來這行沒有繪製(或者可能是x翻譯?)。視頻顯示了這個問題。視頻RecyclerView和ItemTouchHelper刷卡以消除問題

步驟:

  1. 我滑開的第2項
  2. 向下滾動至底部
  3. 回來
  4. 第1項是不再可見
  5. 我向下滾動到底部再
  6. 我滾動備份
  7. 一切都很好現在
  8. 了,但與第三(而不是秒)從頂部的項目,同樣的問題
  9. 此外,與第一個項目,沒有問題

enter image description here

相關代碼:(全GitHub的示例應用程序here

public class MainActivity extends AppCompatActivity { 

RecyclerView mRecyclerView; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 
    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 
    mRecyclerView.setAdapter(new TestAdapter()); 
    setUpItemTouchHelper(); 

} 

private void setUpItemTouchHelper() { 
    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { 

     @Override 
     public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 
      return false; 
     } 

     @Override 
     public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { 
      int swipedPosition = viewHolder.getAdapterPosition(); 
      TestAdapter adapter = (TestAdapter)mRecyclerView.getAdapter(); 
      adapter.remove(swipedPosition); 
     } 

    }; 
    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); 
    itemTouchHelper.attachToRecyclerView(mRecyclerView); 
} 

static class TestAdapter extends RecyclerView.Adapter { 

    List<String> items; 

    public TestAdapter() { 
     items = new ArrayList<>(); 
     // this should give us a couple of screens worth 
     for (int i=1; i<= 15; i++) { 
      items.add("Item " + i); 
     } 
    } 

    @Override 
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     return new TestViewHolder(parent); 
    } 

    @Override 
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
     TestViewHolder viewHolder = (TestViewHolder)holder; 
     String item = items.get(position); 
     viewHolder.titleTextView.setText(item); 
    } 

    @Override 
    public int getItemCount() { 
     return items.size(); 
    } 

    public void remove(int position) { 
     if (position < 0 || position >= items.size()) { 
      return; 
     } 
     items.remove(position); 
     notifyItemRemoved(position); 
    } 
} 

static class TestViewHolder extends RecyclerView.ViewHolder { 

    TextView titleTextView; 

    public TestViewHolder(ViewGroup parent) { 
     super(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view, parent, false)); 
     titleTextView = (TextView) itemView.findViewById(R.id.title_text_view); 
    } 

} 

} 

編輯:

我有一個刪除這個故障的黑客,但我仍然想知道原因,我怎樣才能真正解決這個問題。黑客正在調用notifyDataSetChanged(),但動畫完成後(否則動畫被終止)。基本上,我添加一個ItemDecorator並找出一個動畫結束。

mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { 

     boolean running; 

     @Override 
     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 
      if (parent.getItemAnimator().isRunning()) { 
       running = true; 
      } 
      if (running == true && !parent.getItemAnimator().isRunning()) { 
       // first time it's not running 
       running = false; 
       parent.getAdapter().notifyDataSetChanged(); 
      } 
      super.onDraw(c, parent, state); 
     } 
}); 

回答

3

UPDATE: 23.1.1修復的bug。不要使用23.1.0。

看起來像這個bug是回收商視圖支持庫中的一個迴歸。我認爲這是導致它的commit,但仍然不能100%地理解情況,所以不要讓我這麼做。

的錯誤常表現爲23.1.0但不com.android.support:recyclerview-v7版本23.0.122.2.1

我會盡力找到一個正確的位置進行舉報,並會張貼在這個答案的評論的鏈接。

6

嘗試在remove方法

public void remove(int position) { 
    if (position < 0 || position >= items.size()) { 
     return; 
    } 
    items.remove(position); 
    notifyItemRemoved(position); 
    notifyDataSetChanged(); 
} 

notifyItemRemoved(position)添加notifyDataSetChanged()通知RecyclerView適配器在適配器的數據已在特定位置被移除。

notifyDataSetChanged()通知附屬的觀察員,底層數據已被更改,任何反映該數據集的視圖都應刷新自身。

UPDATE

嘗試notifyItemRemoved(position);此之前添加mRecyclerView.removeViewAt(position);不會與動畫一塌糊塗。

public class MainActivity extends AppCompatActivity { 

    RecyclerView mRecyclerView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
     setSupportActionBar(toolbar); 
     mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view); 
     mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 
     mRecyclerView.setAdapter(new TestAdapter()); 
     setUpItemTouchHelper(); 

    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     getMenuInflater().inflate(R.menu.menu_main, menu); 
     return true; 
    } 

    @Override 
    public boolean onOptionsItemSelected(MenuItem item) { 
     if (item.getItemId() == R.id.menu_item_add_5_items) { 
      ((TestAdapter)mRecyclerView.getAdapter()).addItems(5); 
     } 
     return super.onOptionsItemSelected(item); 
    } 

    private void setUpItemTouchHelper() { 
     ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) { 

      @Override 
      public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 
       return false; 
      } 

      @Override 
      public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { 
       int swipedPosition = viewHolder.getAdapterPosition(); 
       TestAdapter adapter = (TestAdapter)mRecyclerView.getAdapter(); 
       adapter.remove(swipedPosition); 
      } 

     }; 
     ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); 
     itemTouchHelper.attachToRecyclerView(mRecyclerView); 
    } 

    class TestAdapter extends RecyclerView.Adapter { 

     List<String> items; 
     int lastInsertedIndex; 

     public TestAdapter() { 
      items = new ArrayList<>(); 
      lastInsertedIndex = 15; 
      // this should give us a couple of screens worth 
      for (int i=1; i<= lastInsertedIndex; i++) { 
       items.add("Item " + i); 
      } 
     } 

     @Override 
     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      return new TestViewHolder(parent); 
     } 

     @Override 
     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 
      TestViewHolder viewHolder = (TestViewHolder)holder; 
      String item = items.get(position); 
      viewHolder.titleTextView.setText(item); 
     } 

     @Override 
     public int getItemCount() { 
      return items.size(); 
     } 

     public void addItems(int howMany){ 
      if (howMany > 0) { 
       for (int i = lastInsertedIndex + 1; i <= lastInsertedIndex + howMany; i++) { 
        items.add("Item " + i); 
        notifyItemInserted(items.size() - 1); 
       } 
       lastInsertedIndex = lastInsertedIndex + howMany; 
      } 
     } 

     public void remove(int position) { 
      if (position < 0 || position >= items.size()) { 
       return; 
      } 
      items.remove(position); 
      mRecyclerView.removeViewAt(position); 
      notifyItemRemoved(position); 
     } 
    } 

    static class TestViewHolder extends RecyclerView.ViewHolder { 

     TextView titleTextView; 

     public TestViewHolder(ViewGroup parent) { 
      super(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view, parent, false)); 
      titleTextView = (TextView) itemView.findViewById(R.id.title_text_view); 
     } 

    } 

} 
+1

謝謝,但notifyDataSetChanged()會搞亂去除動畫所以這不是我要找的不幸的解決方案:當我在短暫的延遲後跑notifyDataSetChanged的問題得到解決。工作的黑客是等待動畫完成,然後調用notifyDataSetChanged(),但沒有回調,我需要猜測。請參閱編輯我的問題,瞭解我是如何做到的。這不是我願意留在生產代碼中的東西。 –

+1

好的,看看更新的答案是否有幫助 – random

+0

不完全。適配器中的位置不會映射1到1與回收站視圖的子項中的位置。您可以在適配器中擁有數百個項目,但只能在一個屏幕上添加x個視圖以及多個視圖。你的修補程序可以稍作修改:如果我在onSwipe()中調用它,而不是removeViewAt(position),但removeView(view)像這樣 - mRecyclerView.removeView(viewHolder.itemView);無論如何,這也可能只是一個「黑客」解決方案,最近引入了這個問題,肯定是一個錯誤。看到我的答案。我非常感謝你的幫助! –

0

我是有刷到重新排序類似問題的最簡單的方法。 RecyclerView正在離開一個剛剛移動到視圖下面的視圖的「幽靈」,該視圖移動到該位置。 Random的將'notifyDataSetChanged()'添加到我的onItemMoved方法的答案很有用,但是會破壞交換動畫。

notifyItemMoved(position1, position2); 
    getActivity().getHandler().postDelayed(new Runnable() { 
     @Override 
     public void run() { 
      notifyDataSetChanged(); 
     } 
    }, 300);