RecyclerView : Pull to refresh and EndlessScroll implementation AKA Lazy loading

1.4k views Asked by At

I am trying to implement pull to refresh and endless scroll on recyclerview. While the endless scroll works very well, if I navigate to the top of recylerview and trigger pull to refresh, list is refreshed though, it results in endless scroll disabled. I am posting the code, any help appreciated

Layout XML :

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:tools="http://schemas.android.com/tools"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    xmlns:app="http://schemas.android.com/apk/res-auto"
                    android:orientation="vertical">

        <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_confirmedListRowtoday"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:listitem="@layout/adapter_item_confirmed"
                />
        </android.support.v4.widget.SwipeRefreshLayout>

        <com.victor.loading.rotate.RotateLoading
            android:id="@+id/rotateloading"
            app:loading_color="@color/primary_light"
            app:loading_width="5dp"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_centerInParent="true"
            />

        <TextView
            android:id="@+id/emptyTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="center"
            android:text="No Appointments"
            android:textColor="@color/black_semi_transparent"
            android:textSize="20sp"
            android:visibility="gone"
            tools:visibility="visible"/>


    </RelativeLayout>

ScrollListener implementation :

private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;

RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            visibleItemCount = mRecyclerView.getChildCount();
            totalItemCount = llm.getItemCount();
            firstVisibleItem = llm.findFirstVisibleItemPosition();

            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            if (!loading && (totalItemCount - visibleItemCount)
                    <= (firstVisibleItem + visibleThreshold)) {
                pageNumber++;
                callAppointmentApi();
                loading = true;
            }
        }
    };

RecyclerView code :



mRecyclerView.setHasFixedSize(true);
    llm = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(llm);
    adapter = new ConfirmedRecyclerAdapter(getActivity(), appointmentModelList);
    mRecyclerView.setAdapter(adapter);

     // Setup refresh listener which triggers new data loading
    swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    // Your code to refresh the list here.
                    // Make sure you call swipeContainer.setRefreshing(false)
                    // once the network request has completed successfully.
                    appointmentModelList.clear();
                    pageNumber = 0;
                    callAppointmentApi();
                    mRecyclerView.addOnScrollListener( onScrollListener );
                }
            });
    mRecyclerView.addOnScrollListener( onScrollListener );
1

There are 1 answers

0
Alok Omkar On

This is my solution :

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View rootView = inflater.inflate(R.layout.fragment_confirmed, container, false);
        ButterKnife.bind(this, rootView);
        appointmentModelList = new ArrayList<>();
        preferenceManager = new PreferenceManager(getActivity());
        setRecyclerView();
        appointmentsInterface = ParseApiGenerator.createService(AppointmentsApi.class);
        callAppointmentApi(appointmentModelList.size());
        return rootView;
    }

    private int mFirstVisibleItem, mVisibleItemCount, mTotalItemCount;
    private boolean mLoading;
    private int mPreviousTotal = 0;
    RecyclerView.OnScrollListener mRecylerViewScrollListener = new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            mVisibleItemCount = mRecyclerView.getChildCount();
            mTotalItemCount = llm.getItemCount();
            mFirstVisibleItem = llm.findFirstVisibleItemPosition();

            if (mLoading) {
                if (mTotalItemCount > mPreviousTotal) {
                    mLoading = false;
                    mPreviousTotal = mTotalItemCount;
                }
            }
            if (!mLoading && (mTotalItemCount - mVisibleItemCount) <= (mFirstVisibleItem)) {
                callAppointmentApi(appointmentModelList.size());
                mLoading = true;
            }
        }
    };

    private void setRecyclerView() {

        mRecyclerView.setHasFixedSize(true);
        llm = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(llm);
        adapter = new ConfirmedRecyclerAdapter(getActivity(), appointmentModelList);
        mRecyclerView.setAdapter(adapter);

        // Setup refresh listener which triggers new data loading
        swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // Your code to refresh the list here.
                // Make sure you call swipeContainer.setRefreshing(false)
                // once the network request has completed successfully.
                appointmentModelList.clear();
                mPreviousTotal = 0;
                mLoading = false;
                callAppointmentApi(appointmentModelList.size());
            }
        });
        mRecyclerView.addOnScrollListener( mRecylerViewScrollListener );
        checkAdapterIsEmpty();
        // Configure the refreshing colors
        swipeRefresh.setColorSchemeResources(android.R.color.holo_blue_bright,
                android.R.color.holo_green_light,
                android.R.color.holo_orange_light,
                android.R.color.holo_red_light);
        rotateloading.start();
        emptyTextView.setVisibility(View.GONE);
        mRecyclerView.addOnItemTouchListener(new RecyclerViewItemClickListener(getActivity(), new RecyclerViewItemClickListener.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {

                Intent i = new Intent(getActivity(), AppointmentOverviewActivity.class);
                AppointmentModel appointmentModel = appointmentModelList.get(position);
                formatter = new DecimalFormat("#.##");
                if (appointmentModel.getDistance() != null) {


                    if (formatter.format(Double.parseDouble(appointmentModel.getDistance())).equals("00.00")) {
                        distanceStr = "";
                    } else if (Double.parseDouble(appointmentModel.getDistance()) > 1) {
                        //  spDistance.setText(formatter.format(appointmentModel.getDistance())+"miles");
                        distanceStr = "" + formatter.format(Double.parseDouble(appointmentModel.getDistance())) + " miles";
                    } else {
                        distanceStr = "" + formatter.format(Double.parseDouble(appointmentModel.getDistance())) + " mile";
                    }
                }
                else
                {
                    distanceStr="";
                }
                // Parcelable input
                i.putExtra("distance", distanceStr);
                i.putExtra("data_appointment", appointmentModel);
                getActivity().startActivityForResult(i, Constants.APPOINTMENT_REQUEST);
            }
        }));
    }

    private void checkAdapterIsEmpty() {
        if (adapter != null & emptyTextView != null)
            if (adapter.getItemCount() == 0) {
                emptyTextView.setVisibility(View.VISIBLE);
            } else {
                emptyTextView.setVisibility(View.GONE);
            }
    }

    public void callAppointmentApi( int pageNumber ) {

        if( pageNumber == 0 ) {
            appointmentModelList.clear();
        }

        JSONObject jsonObject = new JSONObject();
        try {
            JSONObject customerId = new JSONObject();
            customerId.put("__type", "Pointer");
            customerId.put("className", "Customer");
            customerId.put("objectId", preferenceManager.getCustomerObjectId()); // "YXH1dBgj1i"
            jsonObject.put("customerId", customerId);
            jsonObject.put("state", "cancelled");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Logger.d("Page is " + pageNumber );

        appointmentsInterface.getAppointments(
                jsonObject.toString(),
                "serviceProviderId,customerId,rating",
                pageNumber, 10,
                preferenceManager.getUserSessionToken(),
                new Callback<Response>() {
            @Override
            public void success(Response response, Response response2) {

                try {
                    JSONObject object = new JSONObject(new String(((TypedByteArray) response.getBody()).getBytes()));
                    Gson gson = new Gson();
                    AppointmentResultModel results = gson.fromJson(object.toString(), AppointmentResultModel.class);
                    if( results != null && results.getResult() != null ) {

                        appointmentModelList.addAll(results.getResult());
                        checkAdapterIsEmpty();
                        dismissProgressDisplay();
                    }
                    else {
                        dismissProgressDisplay();
                    }
                    adapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                    dismissProgressDisplay();
                }

            }
            @Override
            public void failure(RetrofitError error) {
                Logger.d("CancelledData RespFail", error.toString());
                dismissProgressDisplay();
            }
        });
    }

    private void dismissProgressDisplay() {
        if (swipeRefresh != null)
            swipeRefresh.setRefreshing(false);
        if (rotateloading != null)
            rotateloading.stop();
        CommonUtils.dismissProgressDialog();
    }