RecyclerView和ItemTouchHelper滑动以删除问题

时间:2016-01-12 03:52:57

标签: android android-recyclerview

我正在尝试通过RecyclerView和ItemTouchHelper实现“滑动删除”功能。我有一个奇怪的问题,我无法找到我的生活问题。我将物品从顶部(不是第一个)上移开,它消失了,到目前为止一切都很好。当我滚动并回来时,在上面的行中有一个人工制品。看起来那行没有绘制(或者可能是x翻译?)。视频显示了这个问题。

视频的步骤:

  1. 我轻扫第2项
  2. 向下滚动到底部
  3. 回来
  4. 第1项不再可见
  5. 我再次向下滚动
  6. 我向上滚动
  7. 现在一切都很好
  8. 同样,但是从顶部开始的第三个(不是第二个)项目,同样的问题
  9. 再次,使用第一个项目,没有问题
  10. enter image description here

    相关代码:(整个github示例应用here

    0

    编辑:

    我有一个黑客可以消除这个故障,但我仍然想知道原因,我怎么能真正解决这个问题。黑客正在调用notifyDataSetChanged(),但是在动画完成后(否则动画终止)。基本上我添加一个ItemDecorator并找出动画结束。

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

5 个答案:

答案 0 :(得分:8)

尝试在删除方法中添加notifyDataSetChanged()

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

notifyItemRemoved(position)通知RecyclerView适配器已在特定位置删除适配器中的数据。

notifyDataSetChanged()通知附加的观察者基础数据已被更改,反映数据集的任何视图都应自行刷新。

<强>更新

尝试在mRecyclerView.removeViewAt(position);之前添加notifyItemRemoved(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 :(得分:3)

更新: 23.1.1修复了该错误。不要使用23.1.0。

看起来此错误是回收站视图支持库中的回归。我认为这是导致它的commit,但仍然不能100%理解这种情况,所以不要指责我。

该错误表现为com.android.support:recyclerview-v7版本23.1.0但不包含23.0.122.2.1

我会尝试找到一个正确的位置进行举报,并将此链接发布在此答案的评论中。

答案 2 :(得分:1)

我想建议您看到该链接,因为这是实现此功能的最简单方法 Drag and swipe with RecyclerView using ItemTouchHelper

答案 3 :(得分:0)

我在滑动到重新排序时遇到了类似的问题。 RecyclerView正在留下视图的“幽灵”,只是移动到移动到该位置的视图下方。 Random给我的onItemMoved方法添加'notifyDataSetChanged()'的答案有效,但这会破坏交换动画。我在短暂的延迟后运行notifyDataSetChanged时解决了这个问题:

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

答案 4 :(得分:0)

我能够通过在recyclerview上再次设置适配器来解决此问题。从列表中删除后,只需....

RecyclerView.setAdapter(this);