android - RecyclerView 和 ItemTouchHelper 滑动以删除问题

标签 android android-recyclerview

我正在尝试通过 RecyclerView 和 ItemTouchHelper 实现“滑动以删除”功能。我有一个奇怪的问题,我一辈子都找不到问题所在。我从顶部滑动一个项目(不是第一个),它消失了,到目前为止一切顺利。当我滚动离开并返回时,在扫过的项目上方的行中有一个 Artifact 。看起来该行未绘制(或者 x 是否已翻译?)。视频显示了问题。

视频步骤:

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

最佳答案

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

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

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

notifyDataSetChanged() 通知附加的观察者底层数据已更改,任何反射(reflect)数据集的 View 都应自行刷新。

更新

尝试在 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);
        }

    }

}

关于android - RecyclerView 和 ItemTouchHelper 滑动以删除问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34735297/

相关文章:

android - 在 ActionBar 中为 MenuItem 使用选择器

android - 仅在我自己的应用程序中发送和接收 SMS,而不是在使用 native SMS API 的 android 中的 native Message 应用程序中

Android RecyclerView 切换布局(网格/列表)旧的 viewHolders 使用

Android Studio - 没有将 C++ 链接到 Gradle 的选项

android - 如何在 Android 的 webView 中加载 img 标签?

android - 未找到可绘制资源,但它们存在于可绘制文件夹中

android - RecyclerView 在第一次加载时不显示数据

java - 使用 RecyclerAdapter 的 Facebook 原生广告

android - RecyclerView 是否为空

android - 为什么在 RecyclerView 中 findViewByPosition() 在 EditText 上返回 null