5

I know that this question was asked many times before, but i'm confused why sometimes the data is loaded and sometimes data isn't loaded once i get to the end of list. Also when i go fast scrolling through the list, and the new data has been loaded, but immediately it returns me to the first item in list and remove all new loaded items from the next page from server. So that is the second problem and the third problem is that when i load items using SwipeRefreshLayout, i'm also not getting new items when i reach the end of the list.

I have implemented this in my project: https://gist.github.com/ssinss/e06f12ef66c51252563e

list.setLayoutManager(manager);
    list.setEmptyView(emptyView);
    list.setItemAnimator(new DefaultItemAnimator());
    list.setAdapter(mAdapter);

    loadJokes(1);

    list.addOnScrollListener(new EndlessRecyclerOnScrollListener(manager) {
        @Override
        public void onLoadMore(final int current_page) {
            loadMoreJokes(current_page);
        }
    });

Here is the method where i'm loading more items from server:

private void loadMoreJokes(int current_page) {
    StringRequest request = new StringRequest(Request.Method.GET, AppConfig.URL_GET_ALL_JOKES + current_page,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    hideDialog();
                    try {
                        JSONObject object = new JSONObject(response);
                        boolean error = object.getBoolean("error");
                        JSONArray jokes = object.getJSONArray("jokes");
                        if (!error) {
                            for (int i = 0; i < jokes.length(); i++) {
                                JSONObject object1 = jokes.getJSONObject(i);
                                Joke joke = new Joke();
                                joke.setId(object1.optInt("id"));
                                joke.setLikes(object1.optInt("likes"));
                                joke.setComments(object1.optInt("comments"));
                                joke.setJoke(object1.optString("joke"));
                                joke.setCreatedAt(object1.optString("created_at"));
                                joke.setName(object1.optString("user_name"));
                                joke.setImagePath(object1.optString("image_path"));
                                joke.setFacebookUserId(object1.optString("facebook_user_id"));
                                joke.setCategory(object1.optString("category"));
                                mJokes.add(joke);
                            }
                            menu.showMenu(true);
                        }

                        // Notify adapter that data has changed
                        mAdapter.notifyDataSetChanged();
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            hideDialog();
            Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });

    AppController.getInstance().addToRequestQueue(request);
}

And here is the method where i'm loading first visible items when someone launch the app:

private void loadJokes(int page) {
    pDialog.setMessage("Loading..");
    showDialog();

    StringRequest request = new StringRequest(Request.Method.GET, AppConfig.URL_GET_ALL_JOKES + page,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    mJokes.clear();
                    hideDialog();
                    try {
                        JSONObject object = new JSONObject(response);
                        boolean error = object.getBoolean("error");
                        JSONArray jokes = object.getJSONArray("jokes");
                        if (!error) {
                            for (int i = 0; i < jokes.length(); i++) {
                                JSONObject object1 = jokes.getJSONObject(i);

                                Joke joke = new Joke();
                                joke.setId(object1.optInt("id"));
                                joke.setLikes(object1.optInt("likes"));
                                joke.setComments(object1.optInt("comments"));
                                joke.setJoke(object1.optString("joke"));
                                joke.setCreatedAt(object1.optString("created_at"));
                                joke.setName(object1.optString("user_name"));
                                joke.setImagePath(object1.optString("image_path"));
                                joke.setFacebookUserId(object1.optString("facebook_user_id"));
                                joke.setCategory(object1.optString("category"));
                                mJokes.add(joke);
                            }
                            menu.showMenu(true);
                        }

                        // Notify adapter that data has changed
                        mAdapter.notifyDataSetChanged();
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            hideDialog();
            menu.showMenu(true);
            Toast.makeText(getActivity(), error.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });

    AppController.getInstance().addToRequestQueue(request);
}

And this is onRefresh() method:

@Override
public void onRefresh() {
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            refreshItems();
        }
    }, 5000);
}

private void refreshItems() {
    loadJokes(1);

    mSwipeRefreshLayout.setRefreshing(false);
}

If i need to post more code, let me know. I really need to solve this problem as soon as i can. So again, the problems are the following:

  • When fast scrolling through the list, new items are being loaded, but immediately after that it returns me to the beginning of the list and when i go to the end of list again, load more doesn't respond.

  • After refreshing the list with SwipRefreshLayout, also scrolling doesn't respond at the end.

Note: The scrolling and loading new items is working only if i go slowly through the list and if i didn't swipe to refresh list.

EDIT:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_jokes, container, false);

    mContext = getActivity();
    mView = (CoordinatorLayout) view.findViewById(R.id.coordinatorLayout);

    TextView tvEmptyText = (TextView) view.findViewById(R.id.tv_empty);
    ImageView ivSignal = (ImageView) view.findViewById(R.id.iv_signal);

    if (!ConnectionDetector.getInstance(getActivity()).isOnline() && mAdapter == null) {
        tvEmptyText.setVisibility(View.VISIBLE);
        ivSignal.setVisibility(View.VISIBLE);
        showNoInternetSnackbar();
    }

    // INITIALIZE RECYCLER VIEW
    EmptyRecyclerView list = (EmptyRecyclerView) view.findViewById(R.id.list);
    mJokes = new ArrayList<>();
    mAdapter = new RecyclerJokesAdapter(getActivity(), mJokes, JokesFragment.this, null);

    // Progress dialog
    pDialog = new ProgressDialog(getActivity());
    pDialog.setMessage("Please wait");
    pDialog.setIndeterminate(true);
    pDialog.setCancelable(false);

    showDialog();

    View emptyView = inflater.inflate(R.layout.layout_empty_view, container, false);

    FloatingActionButton fab1 = (FloatingActionButton) view.findViewById(R.id.fab_funny);
    FloatingActionButton fab2 = (FloatingActionButton) view.findViewById(R.id.fab_good_morning);
    FloatingActionButton fab3 = (FloatingActionButton) view.findViewById(R.id.fab_good_night);
    FloatingActionButton fab4 = (FloatingActionButton) view.findViewById(R.id.fab_all);
    menu = (FloatingActionMenu) view.findViewById(R.id.menu_sort_jokes);

    fab1.setOnClickListener(this);
    fab2.setOnClickListener(this);
    fab3.setOnClickListener(this);
    fab4.setOnClickListener(this);

    mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_container);
    mSwipeRefreshLayout.setOnRefreshListener(this);
    mSwipeRefreshLayout.setColorSchemeResources(
            R.color.refresh_progress_1,
            R.color.refresh_progress_2,
            R.color.refresh_progress_3);

    LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);

    list.setLayoutManager(manager);
    list.setEmptyView(emptyView);
    list.setItemAnimator(new DefaultItemAnimator());
    list.setAdapter(mAdapter);

    if (ConnectionDetector.getInstance(mContext).isOnline()) {
        loadJokes(1);
    } else {
        showNoInternetSnackbar();
        hideDialog();
    }

    list.addOnScrollListener(new EndlessRecyclerOnScrollListener(manager) {
        @Override
        public void onLoadMore(final int current_page) {
            loadMoreJokes(current_page);
        }
    });

    return view;
}
Dusan Dimitrijevic
  • 2,971
  • 5
  • 20
  • 42

4 Answers4

1

In onCreate method initialize your adapter, recyclerView and List

List<MyObject> myList = new ArrayList<>();
recyclerViewAdapter = new RecyclerViewAdapter(context, myList)
myRecyclerView.setAdapter(recyclerViewAdapter);

Now, whenever you load data. add the data to your myList and call notifyDataSetChange on your adpater

myList.add(data);
recyclerViewAdapter.notifyDataSetChange();
Arshad
  • 1,235
  • 13
  • 23
0

Use this wrapper class

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;

import java.util.List;

public abstract class RecyclerWrapperAdapter<E> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    protected Context context;
    protected List<E> objects;

    public void setContext(Context context) {
        this.context = context;
    }

    public void setObjects(List<E> objects) {
        this.objects = objects;
        notifyDataSetChanged();
    }

    public void add(@NonNull E object) {
        objects.add(object);
        notifyDataSetChanged();
    }

    public void add(int position, @NonNull E object) {
        if (position < objects.size() && position >= 0) {
            objects.add(position, object);
            notifyItemChanged(position);
            notifyDataSetChanged();
        } else if (position >= objects.size()) {
            objects.add(object);
            notifyDataSetChanged();
        }
    }

    public void set(int position, @NonNull E object) {
        if (position < objects.size() && position >= 0) {
            objects.set(position, object);
            notifyItemChanged(position);
        } else if (position >= objects.size()) {
            objects.add(object);
            notifyDataSetChanged();
        }
    }

    public void remove(@NonNull E object) {
        objects.remove(object);
        notifyDataSetChanged();
    }

    public void remove(int position) {
        if (position >=0 && position < objects.size()) {
            objects.remove(position);
            notifyDataSetChanged();
        }
    }

    public void removeAll() {
        objects.clear();
        notifyDataSetChanged();
    }

    public E getItem(int position) {
        return objects.get(position);
    }

    @Override
    public int getItemCount() {
        return objects.size();
    }
}
Jimit Patel
  • 3,985
  • 2
  • 30
  • 50
0

Well I have done this way:

MainActivity .java

public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener, onRecyclerViewListener {

    private RecyclerView mRecyclerView;
    private TextView tvEmptyView;
    private LinearLayoutManager mLayoutManager;
    private List<Object> studentList;
    protected Handler handler;
    private int count = 0;
    private SwipeRefreshLayout swipeRefreshLayout;
    private MyRecycleAdapter myRecycleAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        swipeRefreshLayout = (SwipeRefreshLayout)findViewById(R.id.swiperefresh);

        swipeRefreshLayout.setOnRefreshListener(this);

        swipeRefreshLayout.setColorSchemeResources(R.color.blue, R.color.purple, R.color.green, R.color.orange);

        tvEmptyView = (TextView) findViewById(R.id.empty_view);
        mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
        studentList = new ArrayList<Object>();
        handler = new Handler();
        loadData();

        mRecyclerView.setHasFixedSize(true);

        mLayoutManager = new LinearLayoutManager(this);
        mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);

        // use a linear layout manager
        mRecyclerView.setLayoutManager(mLayoutManager);

        myRecycleAdapter = new MyRecycleAdapter(mRecyclerView, studentList, R.layout.list_row, R.layout.progressbar_item, this);
        myRecycleAdapter.setLoadMoreEnable(true);
        myRecycleAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
            @Override
            public void onLoadMore() {
                count++;
                if (count == 4) {
                    count = 0;
                    myRecycleAdapter.setLoadMoreEnable(false);
                } else {
                    myRecycleAdapter.setLoadMoreEnable(true);
                }

                //add null , so the adapter will check view_type and show progress bar at bottom
                studentList.add(null);
                myRecycleAdapter.notifyItemInserted(studentList.size() - 1);

                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //   remove progress item
                        studentList.remove(studentList.size() - 1);
                        myRecycleAdapter.notifyItemRemoved(studentList.size());
                        //add items one by one
                        int start = studentList.size();
                        int end = start + 20;

                        for (int i = start + 1; i <= end; i++) {
                            studentList.add(new Student("Student " + i, "AndroidStudent" + i + "@gmail.com"));
                            myRecycleAdapter.notifyItemInserted(studentList.size());
                        }
                        myRecycleAdapter.setLoaded();
                        //or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
                    }
                }, 1000);
            }
        });

        ItemViewHolderNew.setRecyclerListener(this);

        mRecyclerView.setAdapter(myRecycleAdapter);

        if (studentList.isEmpty()) {
            mRecyclerView.setVisibility(View.GONE);
            tvEmptyView.setVisibility(View.VISIBLE);

        } else {
            mRecyclerView.setVisibility(View.VISIBLE);
            tvEmptyView.setVisibility(View.GONE);
        }
    }

    private void loadData() {
        for (int i = 1; i <= 20; i++) {
            studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
        }
    }

    @Override
    public void onRefresh() {

        swipeRefreshLayout.setRefreshing(true);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(false);
            }
        },5000);
    }

    @Override
    public void onBindView(View view,  final ItemViewHolderNew itemViewHolder) {
        itemViewHolder.tvName = (TextView) view.findViewById(R.id.tvName);
        itemViewHolder.tvEmailId = (TextView) view.findViewById(R.id.tvEmailId);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, Object object, ItemViewHolderNew itemViewHolder) {
        Student studentObj = (Student)object;
        itemViewHolder.tvName.setText(studentObj.getName());
        itemViewHolder.tvEmailId.setText(studentObj.getEmailId());
        itemViewHolder.student= studentObj;
    }

    @Override
    public void setClickListener(View view, ItemViewHolderNew itemViewHolder) {
        Toast.makeText(view.getContext(), "OnClick :" + itemViewHolder.student.getName() + " \n " + itemViewHolder.student.getEmailId(), Toast.LENGTH_SHORT).show();
    }

    public static class ItemViewHolderNew extends RecyclerView.ViewHolder{
        public TextView tvName, tvEmailId;
        public Student student;
        private static onRecyclerViewListener mListener;

        public static void setRecyclerListener(onRecyclerViewListener listener){
            mListener = listener;
        }

        public ItemViewHolderNew(View itemView) {
            super(itemView);
            mListener.onBindView(itemView, this);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    setClick(v);
                }
            });
        }
        private void setClick(View v) {
            mListener.setClickListener(v, this);
        }
    }
}

Add OnLoadMoreListener.java interface

public interface OnLoadMoreListener {
    void onLoadMore();
}

Add Student.java as Model class

public class Student implements Serializable {

    private static final long serialVersionUID = 1L;
    private String name;
    private String emailId;
    public Student(String name, String emailId) {
        this.name = name;
        this.emailId = emailId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailId() {
        return emailId;
    }

    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

}

activity_main.xml

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">


    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical">

        </android.support.v7.widget.RecyclerView>


    </android.support.v4.widget.SwipeRefreshLayout>

    <TextView
        android:id="@+id/empty_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="No Records Here !"
        android:visibility="gone" />
</RelativeLayout>

list_row.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:padding="5dp"
        android:background="?android:selectableItemBackground">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="Name"
            android:textColor="@android:color/black"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tvEmailId"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvName"
            android:layout_margin="5dp"
            android:text="Email Id"
            android:textColor="@android:color/black"
            android:textSize="12sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="Name"
            android:textColor="@android:color/black"
            android:textSize="18sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvName"
            android:layout_margin="5dp"
            android:text="Email Id"
            android:textColor="@android:color/black"
            android:textSize="12sp" />
    </LinearLayout>

</android.support.v7.widget.CardView>

progressbar_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <ProgressBar
        android:id="@+id/progressBar1"
        android:layout_width="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_height="wrap_content" />


</LinearLayout>

Working fine with Endless, swipe to refresh, load more RecyclerView.

Hope this would help you.

Hiren Patel
  • 48,538
  • 20
  • 161
  • 144
0

Volley RequestQueue uses a thread pool for network requests.

/** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

Obviously, when you do fast scrolling through the list multiple requests are generated in quick succession.

There is a possibility that the responses are received asynchronously / out of sequence. Also, its possible that the "No more data" responses / error responses etc arrive before the responses with next page of data, which may lead to unexpected behaviour by your app.

Specially watch out for how this would effect your mJokes ArrayList member variable.

Chebyr
  • 2,133
  • 1
  • 16
  • 29