I'm fairly new to Android and I'm building and app with a group of friends from college . The problem that we have run into is that when filling the list of elements to display by getting data from a server the data loads and gets added to a list but the recyclerView never displays it (even after calling NotifyDataSetChanged from my custom Adapter) . Back when we used hardcoded data to test the app , this never happened.
The strange part is that the list of elements get displayed on the recyclerView after I turn off the display and then turn it back on (onBindViewHolder of my custom Adapter gets called when I turn the screen back on).Moreover , if I replace the fragment that contains the recyclerView with another and then return to it the data gets loaded to the list but not shown.
What I want to know is why is this happening and what can I do to solve it (and by solve it I mean showing the recyclerView populated with the correct items right as I launch the fragment and updating the recyclerView when I add more elements to the list)
We are using a custom adpater and Retrofit to get data from the server.
Here is the code
Fragment that contains the recyclerView
public class EventViewListFragment extends Fragment implements EventListAdapter.ClickListener{
private RestClient restClient;
private Builder builder;
private String token ;
private boolean userScrolled = true;
int pastVisiblesItems, visibleItemCount, totalItemCount;
private LinearLayoutManager mLayoutManager;
private RecyclerView recyclerView;
private EventListAdapter eventListAdapter;
private Button btnNewEvent;
FloatingActionButton suggestFAB;
private int currentPage=1;
public EventViewListFragment(){
restClient = new RestClient();
}
@Override
public void onCreate(Bundle savedInstanceState) {
System.out.println("Se llamo al onCreate de EventViewListFragment");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println("se llamo al onCreateView de EvENT List Fragment");
// Inflate the layout for this fragment
View layout =inflater.inflate(R.layout.fragment_events_list, container, false);
setUpElements(layout);
addListeners();
return layout;
}
private void setUpElements(View layout)
{
recyclerView = (RecyclerView) layout.findViewById(R.id.eventList);
eventListAdapter = new EventListAdapter(getActivity());
eventListAdapter.setClickListener(this);
eventListAdapter.setData(getInitialData());
recyclerView.setAdapter(eventListAdapter);
mLayoutManager=new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
suggestFAB = (FloatingActionButton) layout.findViewById(R.id.suggestFAB);
builder = new Builder();
}
private void addListeners()
{
addNewEventListener();
addScrollBottomListener();
}
private void addNewEventListener()
{
/*btnNewEvent.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view)
{
EventsActivity.getInstance().toNewEventForm();
}
});*/
suggestFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventsActivity.getInstance().toNewEventForm();
currentPage=1;
}
});
}
public List<Event> getInitialData()
{
List<Event> data=new ArrayList<>();
data = getEvents(data);
return data;
}
@Override
public void itemClicked(View view, int position) {
EventsActivity.getInstance().toEventPage(eventListAdapter.getItemAtPos(position));
currentPage=1;
}
private void addScrollBottomListener() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
userScrolled = true;
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx,
int dy) {
super.onScrolled(recyclerView, dx, dy);
// Here get the child count, item count and visibleitems
// from layout manager
visibleItemCount = mLayoutManager.getChildCount();
totalItemCount = mLayoutManager.getItemCount();
pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
if (userScrolled && (visibleItemCount + pastVisiblesItems) == totalItemCount) {
userScrolled = false;
addNewElementsToList();
}
}
});
}
private void addNewElementsToList()
{
Toast.makeText(getActivity(), "Cargando Mas Elementos", Toast.LENGTH_SHORT).show();
eventListAdapter.setData(getEvents(eventListAdapter.getData()));
}
private List<Event> getEvents(final List<Event> eventsList)
{
System.out.println("Asignando CALL");
Call<JsonElement> eventPage = restClient.getConsumerService().getEvents(token, "", currentPage, 10);
System.out.println("Enquequeing");
eventPage.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(Response<JsonElement> response) {
JsonObject responseBody = response.body().getAsJsonObject();
if (responseBody.has("events")) {
JsonArray jsonArray = responseBody.getAsJsonArray("events");
System.out.println(jsonArray.size());
for (int i = 0; i < jsonArray.size(); i++) {
JsonObject storedObject = jsonArray.get(i).getAsJsonObject();
Event current = new Event();
current.setEventId(storedObject.get("id").getAsInt());
current.setName(storedObject.get("title").getAsString());
Calendar startCal = new GregorianCalendar();
startCal.setTimeInMillis((storedObject.get("starts_at").getAsLong()) * 1000);
current.setStartDateTime(startCal);
Calendar endCal = new GregorianCalendar();
endCal.setTimeInMillis((storedObject.get("ends_at").getAsLong()) * 1000);
current.setFinishDateTime(endCal);
current.setImgUrl(storedObject.get("image").getAsString());
eventsList.add(current);
}
} else {
if (responseBody.has("error")) {
System.out.println("ERROR");
wan.wanmarcos.models.Error error = builder.buildError(responseBody.get("error").getAsJsonObject());
Toast.makeText(getActivity(), "Error : " + error.toString(), Toast.LENGTH_SHORT).show();
} else {
}
}
}
@Override
public void onFailure(Throwable t) {
}
});
eventListAdapter.notifyDataSetChanged();
currentPage++;
return eventsList;
}
}
Custom Adapter for RecyclerView
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.EventListViewHolder>{
private LayoutInflater inflater;
private List<Event> data = Collections.emptyList();
private Context context;
private ClickListener clickListener;
public EventListAdapter(Context context){
inflater = LayoutInflater.from(context);
this.context=context;
}
@Override
public EventListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.event_list_item, parent, false);
EventListViewHolder holder = new EventListViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(EventListViewHolder holder, int position) {
System.out.println("se llamo al onBindViewHolder de ELA");
Event current = getData().get(position);
holder.title.setText(current.getName());
Picasso.with(context)
.load(current.getImgUrl())
.into(holder.img);
String startDateAndTime = current.CalendarToString(current.getStartDateTime())+" - "+current.CalendarToString(current.getFinishDateTime());
holder.dateAndTime.setText(startDateAndTime);
}
public void setClickListener(ClickListener clickListener){
this.clickListener=clickListener;
}
@Override
public int getItemCount() {
return getData().size();
}
public List<Event> getData() {
return data;
}
public void setData(List<Event> data) {
this.data = data;
}
class EventListViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView title;
TextView dateAndTime;
ImageView img;
public EventListViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
title = (TextView) itemView.findViewById(R.id.eventListTitle);
img = (ImageView) itemView.findViewById(R.id.eventListImage);
dateAndTime =(TextView) itemView.findViewById(R.id.eventListDateAndTime);
}
@Override
public void onClick(View v) {
if(clickListener!=null)
{
clickListener.itemClicked(v,getPosition());
}
}
}
public Event getItemAtPos(int pos)
{
return getData().get(pos);
}
public interface ClickListener{
public void itemClicked(View view,int position);
}
}
Fragment XML File
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
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">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="wan.wanmarcos.fragments.EventViewListFragment">
<android.support.v7.widget.RecyclerView
android:id="@+id/eventList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
>
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/suggestFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
app:layout_anchorGravity="bottom|right|end"
android:layout_margin="16dp"
android:clickable="true"
android:src="@mipmap/ic_add_white_48dp" />
</android.support.design.widget.CoordinatorLayout>
What does notifyDataSetChanged() do on recyclerview ? Notify any registered observers that the data set has changed. There are two different classes of data change events, item changes and structural changes. Item changes are when a single item has its data updated but no positional changes have occurred.
It's pretty simple, simply set the RecyclerView 's height to wrap_content . That's right.
eventsList in your Activity and data in your Adapter are two different collections. You are filling up the former but not the latter.
Intialize data with new ArrayList<> instead of Collections.emptyList(); and then add a method in your Adapter, call addAll, like that:
public void addAll(final List<Event> new events) {
final int currentCount = data.size();
synchronized(data) {
data.addAll(events);
}
if (Looper.getMainLooper() == Looper.myLooper()) {
notifyItemRangeInserted(currentCount, events.size());
} else {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
notifyItemRangeInserted(currentCount, events.size());
}
});
}
}
the if else checks if you are calling addAll from the ui thread or from a different thread and call notifyItemRangeInserted in a safe way. In the Activity when onResponse is invoked, after you filled up completely eventsList, just call eventListAdapter.addAll(eventsList)
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