Support RecyclerView doesn't show anything until touched
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);
}
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));
}
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 .
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.