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.
Recyclerview scroll to bottom using scrollToPositon. After setting the adapter, then call the scrollToPosition function to scroll the recycler view to the bottom. recyclerView. setAdapter(mAdapter); recyclerView.
To be able to scroll through a vertical list of items that is longer than the screen, you need to add a vertical scrollbar. Inside RecyclerView , add an android:scrollbars attribute set to vertical .
onBindViewHolder. Called by RecyclerView to display the data at the specified position. This method should update the contents of the itemView to reflect the item at the given position. Note that unlike android.
onCreateViewHolder is called when you need a new View. If there is an available Recycled View that can be provided and be bound with new data, then onBindViewHolder is called :) 96. 96. 96.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With