6

Its the first time I am using onscroll listener. My problem is how to fetch 20 new rows every time I scroll down ?.

I understand when I scroll down the grid view this code will be executed.

public void onScroll(AbsListView view, int firstVisibleItem,
                             int visibleItemCount, int totalItemCount) {
            // TODO Auto-generated method stub

            this.currentFirstVisibleItem = firstVisibleItem;
            this.currentVisibleItemCount = visibleItemCount;
            this.totalItem = totalItemCount;

            if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + 20)) {
               // the below when executed will get all the rows ,I only need 20 rows
             handler.execute().get();

            }
        }

this is my code

// the load more in the grid view, fetch new images.
mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
    private int currentVisibleItemCount;
    private int currentScrollState;
    private int currentFirstVisibleItem;
    private int totalItem;
    private LinearLayout lBelow;

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        this.currentScrollState = scrollState;
        this.isScrollCompleted();
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub

        this.currentFirstVisibleItem = firstVisibleItem;
        this.currentVisibleItemCount = visibleItemCount;
        this.totalItem = totalItemCount;

        if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + 20)) {
           // load new 20 row but how to do that

        }
    }

    private void isScrollCompleted() {
        if (totalItem - currentFirstVisibleItem == currentVisibleItemCount
                && this.currentScrollState == SCROLL_STATE_IDLE) {
            Log.d("a", "poooppii");

        }
    }

    ;
});

this is where the connection happens

 protected void showList(){
        try {

            JSONObject jsonObj = new JSONObject(myJSON);
            peoples = jsonObj.getJSONArray("result");
            System.out.println("Length:"+peoples.length());
            int J_length=peoples.length()-1;


            jsonObj= peoples.getJSONObject(J_length);


                Listitem = new ArrayList<Listitem>();

                for (int i = 0; i < peoples.length(); i++) {
                    JSONObject c = peoples.getJSONObject(i);

                    String id = c.getString("id");
                    String url = c.getString("url");

                    int intid = 0;
                    try {
                        intid = Integer.parseInt(id.toString());
                    } catch (NumberFormatException nfe) {
                        System.out.println("Could not parse " + nfe);
                    }

                    Listitem.add(new Listitem(id, url));
                    System.out.println(Listitem);
                }
            //}
            if (mListener != null)
                  mListener.myMethod(Listitem);


        } catch (JSONException e) {
            e.printStackTrace();
        }

    }
Moudiz
  • 7,211
  • 22
  • 78
  • 156
  • why you are not using adapter instead of scroll listeners. I recommend adapter, because here this problem can be solved very easily and you will also get better experience. – Neo Jun 30 '16 at 08:44
  • can you please me show an example of how using it on an adapter ? – Moudiz Jun 30 '16 at 09:30
  • I have posted my working code for the similar problem solving with the help of adapter, hope it will help you :) – Neo Jun 30 '16 at 11:33

2 Answers2

5

You need to maintain base and limit for data load. Initialize your base and limit variable.

int base = 0, limit = 20;

Increase your base variable whenever loading new items.

base = base + limit;
handler.execute().get();

Pass base and limit variable in your API call, so that you can use it in query from API side.

Note : base is a value from which position you need to start fetching values. limit is a value for how many rows you need to fetch.

Refer this for limit query in sqlite.
Refer this for limit query in MySql.
Refer this for limit query in SQL.

Community
  • 1
  • 1
Ravi
  • 34,851
  • 21
  • 122
  • 183
  • 1
    you were right i as missing the limit and the base , however after testing someexamples i noticed my above code wont work because setOnScrollListener is depricated for recycler view. ill search for examples arround the web. do you have a recomanded example for recyclerview and loading on scroll ? – Moudiz Jul 02 '16 at 07:00
3

Here is an working code, Kindly modify as per your use -

Let assume following is your model -

public class MyModel {
    String name;
}

You need one interface -

public interface PagingListener<T> {
    void onItemUpdate(ArrayList<T> allData);
}

Design your adapter like this -

public class CouponsAdapter extends RecyclerView.Adapter<CouponsAdapter.CouponHolder> implements View.OnClickListener, PagingListener<MyModel> {
    private final static int TYPE_LOADING = 0;
    private final static int TYPE_DATA = 1;
    private FragmentActivity activity;
    private LayoutInflater inflater;
    private PagingRequestHandler<MyModel> pagingRequestHandler;
    private ArrayList<MyModel> data = null;

    public CouponsAdapter(FragmentActivity activity) {
        this.activity = activity;
        inflater = LayoutInflater.from(activity);
        this.data = new ArrayList<>();
        this.pagingRequestHandler = new PagingRequestHandler<>(data, this);
    }

    @Override
    public int getItemViewType(int position) {
        return position >= data.size() ? TYPE_LOADING : TYPE_DATA;
    }

    @Override
    public CouponHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        if (viewType == TYPE_DATA) {
            view = inflater.inflate(R.layout.item_coupons_card_block, parent, false);
        } else {
            view = inflater.inflate(R.layout.item_drawer_payment_loading, parent, false);
        }
        return new CouponHolder(view, viewType);
    }

    @Override
    public void onBindViewHolder(CouponHolder holder, int position) {
        if (getItemViewType(position) == TYPE_DATA) {
            //Bind your view here
        }
        pagingRequestHandler.checkAndMakeRequestForMoreData(position); //Will check and make request
    }

    @Override
    public int getItemCount() {
        return data.size() + (pagingRequestHandler.isNoMoreDataLeft() ? 0 : 1); // If no data then it will return 1 to show loading
    }

    @Override
    public void onClick(View v) {
    }

    @Override
    public void onItemUpdate(ArrayList<MyModel> allData) {
        this.data = allData; // will update data with updated content
        notifyDataSetChanged();

    }


    public class CouponHolder extends RecyclerView.ViewHolder {
        public CouponHolder(View itemView, int itemType) {
            super(itemView);
            if (itemType == TYPE_DATA) {
            }
        }
    }
}

And now this is the paging request handler, that will keep track on page number and will make request for next page -

public class PagingRequestHandler<T> {
    public static final int PAGE_SIZE = 10;
    private final PagingListener listener;
    private final ArrayList<MyModel> arrayList;
    private final String url = "Your Url here";
    private boolean noMoreDataLeft;
    private int pagingIndex = 0;
    private boolean requestGoingOn;


    public PagingRequestHandler(ArrayList<MyModel> data, PagingListener<MyModel> listener) {
        this.listener = listener;
        this.arrayList = data;
        fetchMoreData();

    }

    private void fetchMoreData() {
        requestGoingOn = true;
        Call<MyModel[]> getCoupons = Utils.getRetrofit().getCoupons(url + "/" + pagingIndex); // I am using retrofit for network call, you can use your own
        getCoupons.enqueue(new Callback<MyModel[]>() {
            @Override
            public void onResponse(Response<MyModel[]> response, Retrofit retrofit) {
                try {
                    requestGoingOn = false;
                    MyModel[] couponModelDses = response.body();
                    if (couponModelDses != null) {
                        for (MyModel couponModelDse : couponModelDses) {
                            arrayList.add(couponModelDse);
                        }
                        if (couponModelDses.length < PAGE_SIZE)
                            noMoreDataLeft = true;
                        listener.onItemUpdate(arrayList);
                    }
                } catch (Exception e) {
                    e.printStackTrace();

                }
            }

            @Override
            public void onFailure(Throwable t) {
                requestGoingOn = false;
            }
        });
    }

    public boolean isNoMoreDataLeft() {
        return noMoreDataLeft;
    }

    public void checkAndMakeRequestForMoreData(int position) {
        if (!noMoreDataLeft && !requestGoingOn && position > (PAGE_SIZE * pagingIndex)) {
            if ((position % PAGE_SIZE) > (int) (PAGE_SIZE * .7)) {
                pagingIndex++;
                fetchMoreData();
            }
        }
    }
}

How it works - The adapter is initialising the Handler, which make network call for first time. The adapter now calls handler api to check if 70% of page is scrolled on every onBind. Now if 70% of data has been shown then it makes network call againe and makes a variable called requestGoingOn true, so you can show a loader in list. Wher request completed the handler is adding data to the arraylist and updating adapter by calling the callback onItemUpdate.

Hope it will help, please let me know if you need further assistance :)

Neo
  • 3,546
  • 1
  • 24
  • 31
  • still not yet i was busy lately, tomorow i will check it thanks you – Moudiz Jul 01 '16 at 21:21
  • helllo you there ? – Moudiz Jul 02 '16 at 18:23
  • Yes, need any help? – Neo Jul 02 '16 at 18:25
  • can you please post to me the xml for the view? I am testing your code – Moudiz Jul 02 '16 at 18:28
  • You can use your own xml if it is type data, in case of loading just put a circular progress bar with text loading in a linear layout. Let me know if you still want it :) – Neo Jul 02 '16 at 18:33
  • well still reading your code , I noticed your using connection with retrofit ` public void onResponse(Response response, Retrofit retrofit) {` can you give the dependecy to put on gradle ? I usealy use okhttp connection – Moudiz Jul 02 '16 at 18:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/116272/discussion-between-neo-and-moudiz). – Neo Jul 02 '16 at 18:35