I am using RecyclerView to view my data, but when there are many items ,RecyclerView will auto scroll to the bottom everytime a new item is inserted. how to prevent it ?
this is the insert code:
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
getOneMessage(dataSnapshot.getKey(), new OneMessageCallBack() {
#Override
public void OnCallBack(Object_Message message) {
Object_Conversation conversation=new Object_Conversation(dataSnapshot.getKey(),message);
mConvers_List.add(conversation);
mConvers_Adapter.notifyItemInserted(mConvers_List.size()-1);
}
});
}
this the the XML :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_conversations"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:background="#color/white">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
Try using
android:descendantFocusability="blocksDescendants"
in recyclerview. This will help you avoid autoscroll
I think in order to disable the autoscroll you need to use notifyItemRangeInserted instead of notifyItemInserted. This should not make your RV scroll to the bottom.
final int positionStart = mConvers_List.size() + 1;
mConvers_List.add(conversation);
notifyItemRangeInserted(positionStart, mConvers_List.size());
You can use scrollToPosition(int position) method to scroll back to zero position.
RecyclerView.scrollToPosition(0)
Related
I am trying to list items on my recycler view using the grid layout manager with two columns.
However, the first row of the grid seems to be having width issues.
When I scroll down until the first row is invisible, then scrolled back up, it will fix itself and show the correct width.
My items are retrieved from themoviedb API,
adapter.setMovieWrapper(body); // body contains data from the API
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerView.setAdapter(adapter);
Here is the layout code for the grid item:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:padding="2dp"
android:orientation="vertical">
<ImageView
android:id="#+id/movie_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:contentDescription="#string/movie_thumbnail_description"/>
<TextView
android:background="#drawable/bg_shade"
android:id="#+id/movie_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textColor="#FFF"
android:ellipsize="end"
android:padding="8dp"/>
</FrameLayout>
Here is the adapter code:
public class MovieGridAdapter extends RecyclerView.Adapter<MovieGridItemViewHolder> {
MovieWrapper movieWrapper;
private Context context;
public MovieGridAdapter(Context context) {
this.context = context;
}
public MovieWrapper getMovieWrapper() {
return movieWrapper;
}
public void setMovieWrapper(MovieWrapper movieWrapper) {
this.movieWrapper = movieWrapper;
this.notifyDataSetChanged();
}
#Override
public MovieGridItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new MovieGridItemViewHolder(
LayoutInflater.from(context)
.inflate(R.layout.movie_grid_item, parent, false)
);
}
#Override
public void onBindViewHolder(MovieGridItemViewHolder holder, int position) {
Movie movie = movieWrapper.getResults().get(position);
Uri uri = Uri.parse("https://image.tmdb.org/t/p/w185");
uri = uri.buildUpon().appendEncodedPath(movie.getPosterPath()).build();
Picasso.with(context)
.load(uri)
.into(holder.getThumbnail());
holder.getTitle().setText(movie.getTitle());
}
#Override
public int getItemCount() {
if (movieWrapper != null && movieWrapper.getResults() != null) {
return movieWrapper.getResults().size();
}
return 0;
}
}
and here is the activity that host the recycler view XML layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.adityapurwa.popularmovies.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/movie_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
Anyone know what is causing this issue and how to resolve it?
Thanks!
There seems to be an issue with my ConstraintLayout hosting the RecyclerView, I changed it to FrameLayout and the issue disappeared. Even though the layout and the RecyclerView both have its dimension set to match_parent, somehow it fails to detect the dimension.
Note: Changing the dimension to be absolute (e.g 100dp) also solved the problem, however it makes the layout unresponsive.
I have used FrameLayout around Recyclerview but did not work, I used this work around solution like this, for first 2 items i added dummy items and hidden the views, this worked for me , although this is not a perfect solution
This question has been asked many times but it just doesn't seem to have a solution that fits my requirement.
I have a Recipe that has instructions. Each instruction is ordered in a RecyclerView. The order must be from top to bottom. It cannot be reversed like many answers to this question.
I have an Add button that when clicked a new item is added at the bottom of the list. I need to scroll to the new item after the insert. So I employed the following code in the button's onClick event:
mRecyclerAdapter.addItem(newInstruction);
mRecyclerView.smoothScrollToPosition(mRecyclerAdapter.getItemCount() - 1);
This scrolls to the second last item because in the adapter's addItem() method I have this:
mItems.add(item);
notifyItemInserted(getItemCount() - 1);
I don't know the details of the notifyItemInserted() call, but it appears to be on a separate thread, which means that smoothScrollToPosition() is called before the views have been laid out after the insertion. Hence, it only scrolls to the second last item.
I have tried looking for a "callback" function along the lines of onItemInserted but I did not have any luck. eg, no answer Here.
How do I solve this problem?
EDIT START
I've simplified the code in a new project in hope to find the problem, and found out what the problem is. It appears to be a problem caused by CoordinatorLayout/AppBarLayout when scrolling flag is enabled. It seems as though the scrolling did not take into consideration the extra scrolling space added by the AppBarLayout.
In the sample codes below, if you disable the scrolling flag for the app bar, scrolling to the bottom of the screen works perfectly as intended. However, if you enable the AppBarLayout scrolling behavior, and change its height, you'll notice that scrolling to the bottom is off by the given height.
I need the scrolling behavior, so how do I make them play nicely with each other? Please note that I've changed the question to reflect the problem.
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int ITEMS_COUNT = 50;
private FloatingActionButton mFloatingActionButton;
private RecyclerView mRecyclerView;
private RecyclerAdapter mRecyclerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFloatingActionButton = (FloatingActionButton) findViewById(R.id.fabButton);
mFloatingActionButton.setOnClickListener(this);
setupRecyclerView();
}
private void setupRecyclerView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerAdapter = new RecyclerAdapter(createItemList());
mRecyclerView.setAdapter(mRecyclerAdapter);
}
private List<String> createItemList() {
List<String> itemList = new ArrayList<>();
for (int i = 0; i < ITEMS_COUNT; i++) {
itemList.add("Item " + i);
}
return itemList;
}
#Override
public void onClick(View v) {
mRecyclerView.scrollToPosition(49);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- changing the height affects the scrolling distance -->
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Header"
app:layout_scrollFlags="scroll|enterAlways"/> <!-- this is the problem -->
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fabButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="20dp"/>
</android.support.design.widget.CoordinatorLayout>
RecyclerAdapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<String> mItemList;
public RecyclerAdapter(List<String> itemList) {
mItemList = itemList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return RecyclerItemViewHolder.newInstance(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
String itemText = mItemList.get(position);
holder.setItemText(itemText);
}
#Override
public int getItemCount() {
return mItemList == null ? 0 : mItemList.size();
}
}
recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="#+id/itemTextView"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:padding="8dp"
style="#style/Base.TextAppearance.AppCompat.Body2"/>
</android.support.v7.widget.CardView>
EDIT END
Doing a bit more research I found this question/answer which has a workaround to this problem. Although not ideal, it will have to do for now. Though the real solution shouldn't have to collapse the AppBarLayout, the scrolling should take into consideration the extra scrolling space needed. Perhaps this is just a bug that is overlooked.
In your addItem() method change this
notifyItemInserted(getItemCount() - 1);
to
notifyItemInserted(mItems.size() - 1);
This will solve the problem!
add this after adding item
notifyItemRangeChanged(0, items.size());
Have you tried
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
You can also put it in a runnable like this to make sure item has added before you scroll to it.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());
}
}, 1000);
Before you add list to adapter in Recycleview do some sorting by using Collection method according to your need,use this code may help you
Comparator<Long> comparator = Collections.reverseOrder();
Collections.sort(arrayList, comparator);
I use the following Code in my RecyclerView.Adapter to remove an Item. If the current scroll-position is at the top, everything works fine.
If the scroll-position is somewhere in the middle of the List, the List automatically scrolls to the Top of the View.
How to avoid this behaviour?
public class MyAdapter extends RecyclerView.Adapter {
...
public void removeItem(int pos){
mDataset.remove(pos); //List with Items
notifyItemRemoved(pos);
notifyItemRangeChanged(pos,mDataset.size());
}
}
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
...
</FrameLayout>
Adding the following Line solved it for me
recyclerView.setHasFixedSize(true);
for recycler view you use
yourRecylerView.scrollToPosition(position);
Just check whether this position is present in the list.
Use this :
mDataset.remove(pos);
adapter.notifyItemRemoved(pos);
adapter.notifyDataSetChanged();
You need to scroll to the position removed after notifying the dataset associated with the RecyclerView is changed.
And yes, you just have to call notifyDataSetChanged() after removing an item from the mDataset. So your adapter will look like this.
public class MyAdapter extends RecyclerView.Adapter {
//... Code goes here
public void removeItem(int pos){
mDataset.remove(pos);
notifyDataSetChanged();
recyclerView.scrollToPosition(pos - 1);
}
}
Try this
mDataset.remove(pos);
adapter.notifyItemRemoved(pos);
adapter.notifyDataSetChanged();
This should work.
Try this code:
public void removeItem(int position){
mdata.remove(position);
notifyItemRangeChanged(position,mdata.size());
}
I use recyclerView inside nestedScrollView and had the same issue. Here is the code to call to remove an item
mDataset.remove(pos) // Remove from dataset
notifyItemRemoved(pos) // Show animation of removing
And here are lines to be added to XML to make it scroll correctly (RecyclerView is focused with higher priority than its viewHolders so it won't scroll)
android:descendantFocusability="beforeDescendants"
android:nestedScrollingEnabled="false"
After that everything was ok.
I have a recyclerview inside SwipeRefreshLayout. and at the end of recyclerview, I'd like to add a button, but I'm unable to do so. Here the code :
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/refresh"
android:layout_width="wrap_content"
android:layout_below="#+id/toolbar"
android:paddingBottom="50dp"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"/>
<Button
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="#+id/load_more"
android:layout_alignParentBottom="true"
android:textColor="#color/md_black_1000"
android:text="TEST"/>
</android.support.v4.widget.SwipeRefreshLayout>
The button does not appear at the end of the recyclerview.
SwipeRefreshLayout could have only one child.
Add RecyclerView and Button to vertical LinearLayout and put it into SwipeRefreshLayout.
<android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout>
<android.support.v7.widget.RecyclerView>
<Button>
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
To add Button as last item in RecyclerView you need edit adapter:
1) increase number of rows
#Override
public int getItemCount() {
return pictureArrayList.size() + 1;
}
2) show Button when list ends
#Override
public void onBindViewHolder(final ExampleHolder holder, final int position) {
final Picture picture = pictureArrayList.get(position);
if (position <= pictureArrayList.size()) {
holder.title.setVisibility(View.VISIBLE);
holder.button.setVisibility(View.GONE);
holder.title.setText(picture.getName());
holder.imageView.setImageResource(picture.getImage());
} else {
holder.title.setVisibility(View.GONE);
holder.button.setVisibility(View.VISIBLE);
}
}
You need to create a RecyclerView with different view items depending on what you want to do may be an option to use a view button at the bottom of the list, I have a blog post where I wrote a blog about the RecyclerView(Spanish).
http://erikcaffrey.github.io/2015/10/05/recyclerview/
You can see the code!
https://github.com/erikcaffrey/RecyclerView-Examples
I'm looking for a solution to this that will allow me to expand a cardview to see more information and then easily collapse it. Google Keep has examples of cards like this. Anyone know how they do this? Would I create 2 versions of my cardview (one collapsed and one expanded) and then use an Animator class coupled with gesture methods to transition between the two views? I'm using a Recyclerview to hold my cardviews.
I found this if it is at all relevant: http://developer.android.com/training/animation/layout.html
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:animateLayoutChanges="true"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
//here put the view which is always visible
<LinearLayout
android:layout_width="match_parent"
android:visibilty="gone"
android:id="#+id/expandableLayout"
android:layout_height="wrap_content">
//here put which will collapse and expand
</LinearLayout>
</android.support.v7.widget.CardView>
take a boolean isexpanded in you arraylist object class
if (listobj.isexpanded)
{
holder.expandableLayout.setVisibility(View.VISIBLE);
}
else {
holder.expandableLayout.setVisibility(View.GONE);
}
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listobj.isexpanded)
{
holder.expandableLayout.setVisibility(View.GONE);
listobj.isexpanded=false;
notifyItemChanged(position);
}
else {
holder.expandableLayout.setVisibility(View.VISIBLE);
listobj.isexpanded=true;
notifyItemChanged(position);
}
}
});
try someting like this