Support RecyclerView doesn't show anything until touched
AndroidAndroid RecyclerviewAndroid Problem Overview
I'm using the support RecyclerView in my app, and I see the most bizarre thing. It doesn't display any items until I touch to scroll. Then, all of a sudden, the RecyclerView populates itself. I have verified that the list backing the adapter is populated, and that onCreatViewHolder and onBindViewHolder are never called until a touch event.
Here's how I set up the recyclerview:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
Drawable divider = new ColorDrawable(ProfileInfo.getCurrentProfileColor());
mInboxList.addItemDecoration(new DividerItemDecoration(getActivity(), divider, false));
mInboxList.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
mInboxList.setAdapter(new InboxAdapter(getActivity(), new ArrayList<Conversation>()));
new AsyncTask<Void, Void, List<Conversation>{
public List<Conversation> doInBackground(...){
//load the conversations
return conversations;
}
public void onPostExecute(List<Conversation> conversations){
((InboxAdapter) mInboxList.getAdapter()).clear(false);
((InboxAdapter) mInboxList.getAdapter()).addAll(conversations);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
Here's a gist of my adapter:
public class InboxAdapter extends RecyclerView.Adapter<InboxAdapter.ViewHolder> {
List<Conversation> mConversations; //initialized in contructor
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = mInflater.inflate(R.layout.inbox_item, parent, false);
ViewHolder holder = new ViewHolder(v);
Log.d(LOG_TAG, "oncreateviewholder : " + viewType); //never called when I first bind the adapter
return holder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final Conversation item = mConversations.get(position);
Log.d(LOG_TAG, "binding " + position);
...
}
@Override
public int getItemCount() {
Log.d(LOG_TAG, "item count: " + mConversations.size());
return mConversations.size();
}
/**
* Empties out the whole and optionally notifies
*/
public void clear(boolean notify) {
mConversations.clear();
if (notify) {
notifyDataSetChanged();
}
}
public void addAll(List<Conversation> conversations) {
mConversations.addAll(conversations);
notifyDataSetChanged();
}
}
Here's the layout file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
<android.support.v7.widget.RecyclerView
android:id="@+id/lv_inbox"
android:layout_width="match_parent"
android:layout_height="match_parent"
></android.support.v7.widget.RecyclerView>
</RelativeLayout>
I'm using recyclerview-v7:21.0.3 running on a Moto X with version 4.4.4.
EDIT: Smooth scrolling at the end of the onPostExecute seems to resolve the issue:
if (mInboxList.getAdapter().getItemCount() > 0) {
mInboxList.smoothScrollToPosition(0);
}
Android Solutions
Solution 1 - Android
If anyone else is having this issue using RxJava and Retrofit, I solved this issue by adding the .observeOn(AndroidSchedulers.mainThread())
operator into my method chain before subscribing. I had read this was taken care of by default, and thus not explicitly necessary, but I guess not. Hope this helps.
Example:
public void loadPhotos() {
mTestPhotoService.mServiceAPI.getPhotos()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(photoList -> mRecyclerActivity.OnPhotosLoaded(photoList));
}
Solution 2 - Android
In my case only this worked:
recyclerView.smoothScrollToPosition(arrayModel.size-1); // I am passing last position here you can pass any existing position
RecyclerView was showing data only when I scroll it. So instead of user to manually scroll I have added above line which will scroll the recyclerView programmatically .
Solution 3 - Android
So at first, just like everybody else I just added:
recyclerView.smoothScrollToPosition(0)
and it worked pretty much good enough, however it wasn't nice and you had to remember to add it every time. and I then I followed @SudoPlz comment and answer on another question and it also worked too, you have to extend RecyclerView and override requestLayout:
private boolean mRequestedLayout = false;
@SuppressLint("WrongCall")
@Override
public void requestLayout() {
super.requestLayout();
// We need to intercept this method because if we don't our children will never update
// Check https://stackoverflow.com/questions/49371866/recyclerview-wont-update-child-until-i-scroll
if (!mRequestedLayout) {
mRequestedLayout = true;
this.post(() -> {
mRequestedLayout = false;
layout(getLeft(), getTop(), getRight(), getBottom());
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
});
}
}
while still, I would have preferred this to fixed after 4, 5 years, however, this was a good workaround, and you won't forget about them in your view.
Solution 4 - Android
i'm also suffering from same issue for 2 days now its done after add this line.
adapter.notifyDataSetChanged() //after notify adapter add this one mRecyclerView.smoothScrollToPosition(list.size-1)
Solution 5 - Android
In my case it's only happen on Android 6. above this version I didn't had any problems. So what I've found to work is to use recyclerView.scrollBy(0, 0). Check it maybe it will work for you too.
Solution 6 - Android
Since this is still happening, here is an specific fixed instance in case it helps someone:
- It happened only when the cell view contains a WebView.
The fix:
- Call setIsRecyclable(false) from the ViewHolder
Solution 7 - Android
This is a bit late, but in my case I had a recylerview inside a viewpager (TabView) that was inside another viewpager (using BottomNavigationView) and smooth scrolling didn't change anything.
However, I noticed that I had the recylerview set with Visibility GONE when the layout was drawn, so the solution was:
- Setting the recyclerview to Visibility VISIBLE on xml layout (so it's draw completely when the TabView is set);
- When the adapter is set (linked to observable with room), i do:
new Handler(Looper.getMainLooper()).postDelayed(() -> {
binding.recyclerView.scrollToPosition(0);
}, getResources().getInteger(android.R.integer.config_shortAnimTime));
Solution 8 - Android
You would have to run the stuff you do in onPostExecute on the UI thread so that the RecyclerView gets redrawn. That's why the smooth scrolling works, because that is running on the UI thread and therefore causing the view to redraw.
Solution 9 - Android
It's most likely because you're not calling the correct notification methods of RecyclerView.Adapter. You have a much more granular interface for this than you previously had in ListAdapters. For example, in addAll() you should call notifyItemRangeInserted(oldConversationsSize, conversations.size())
instead of notifyDataSetChanged