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