Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView doesn't update after NotifyDataSetChanged() , content shows up after turning on and off Screen

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>
like image 968
Francisco M. Avatar asked Nov 09 '15 08:11

Francisco M.


People also ask

What does notifyDataSetChanged do in RecyclerView?

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.

How do you load all items in RecyclerView without scrolling?

It's pretty simple, simply set the RecyclerView 's height to wrap_content . That's right.


1 Answers

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)

like image 110
Blackbelt Avatar answered Sep 21 '22 19:09

Blackbelt