Unexplainable forced scroll down in RecyclerView after notifyItemMoved() is called

好久不见. 提交于 2019-12-10 13:36:01

问题


In my app I have a list of items implemented with RecyclerView. Under certain conditions items might be moved to the very end of the list and I need that to be animated. So first I move the items to the end in the data source (ArrayList in my case) and call adapter's notifyItemMoved(oldPosition, dataSet.size() - 1) method. Everything works ok except the case when the element I move to the end (it doesn't actually matter if it's the very end of the list or just a bit lower position) is either the very top in the list or the very first partly visible. In such cases not only moved item is animated but entire list scrolls down with it.

I thought it might be some kind of a mess in my code so I created a clean test application with a RecyclerView component and the result was the same.

I think it's a bug of RecyclerView because it's still very green component and Goggle continues to develop it, although I'm pretty sure I'm not the only one who faced such a problem so I hope somebody has a workaround for that.

Here's code snippets of my Activity, RecyclerView.Adapter and a layout.

Activity

public class RecyclerViewActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);

        RecyclerView.Adapter adapter = new RecyclerViewAdapter(recyclerView);
        recyclerView.setAdapter(adapter);
        recyclerView.addItemDecoration(new    SimpleDividerItemDecoration(RecyclerViewActivity.this));
    }
}

Adapter

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
    private ArrayList<String> mDataSet;
    private RecyclerView mRecyclerView;

    public RecyclerViewAdapter(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;

        mDataSet = new ArrayList<String>();
        mDataSet.add("San Francisco");
        mDataSet.add("Los Angeles");
        mDataSet.add("Seattle");
        mDataSet.add("Portland");
        mDataSet.add("Sacramento");
        mDataSet.add("San Diego");
        mDataSet.add("Chicago");
        mDataSet.add("Boston");
        mDataSet.add("New York");
        mDataSet.add("New Jersey");
        mDataSet.add("Washington");
        mDataSet.add("Miami");
        mDataSet.add("New Orlean");
    }

    public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_recycler_view_item, parent, false);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int itemPosition = mRecyclerView.getChildAdapterPosition(v);
                String item = mDataSet.get(itemPosition);
                mDataSet.remove(itemPosition);
                mDataSet.add(item);
                notifyItemMoved(itemPosition, mDataSet.size() - 1);

            }
        });
        return new ViewHolder(view);
    }

    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.itemName.setText(mDataSet.get(position));
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {
        protected TextView itemName;

        public ViewHolder(View v) {
            super(v);

            itemName = (TextView) v.findViewById(R.id.recycler_view_item_text);
        }
    }
}

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"/>

</LinearLayout>

And here's a video of how it looks: https://youtu.be/kjG4oFE7o0w

1) Portland - ok.

2) Seattle - ok.

3) Los Angleles - ok.

4) San Francisco - NOT OK - entire list is scrolling down with the item.

And further in the video can see examples of the same behavior with the elements which aren't at the top of the list but are the very top visible ones and partly hovered by the list's top border.


回答1:


My temporary solution:

// mLayoutManager is LinearLayoutManager from RecyclerView
int visible = mLayoutManager.findFirstVisibleItemPosition();
int offset = mLayoutManager.getChildAt(visible).getTop();

... remove, add and call notifyItemMoved

mLayoutManager.scrollToPositionWithOffset(visible, offset);


来源:https://stackoverflow.com/questions/30697939/unexplainable-forced-scroll-down-in-recyclerview-after-notifyitemmoved-is-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!