Recycler view not scrolling properly after implementing swipe to refresh layout

Prior to the implementation of the Swipe to refresh view, the recycler view was working smoothly but not whenever I try to scroll the recycler view downwards the swipe to refresh interferes and hinders the scroll movement.

Here is the screenshot of the issue :

  • SSL certificate for REST web services (used by Android)?
  • How to simulate Android killing my process
  • Is Dalvik even more memory hungry than HotSpot in terms of object sizes?
  • Does Proguard remove unused code (on Android)
  • Graphical Layout tab does not appear for some layout files into Eclipse
  • Android ActionBar tabs set initially selected tab
  • (Notice the swipe to refresh layout comes while the recycler view is scrolled downwards)

    enter image description here

    Here is my layout fragment:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/activity_main_swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:wheel="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/availableTaskRecyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
        <RelativeLayout
            android:id="@+id/progress_wheel"
            android:visibility="gone"
            tools:visibility="visible"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_horizontal|center_vertical"
            android:background="@color/transparentLoaderBackground">
    
            <com.pnikosis.materialishprogress.ProgressWheel
                android:layout_width="50dp"
                android:layout_height="50dp"
                wheel:matProg_barColor="@color/white"
                wheel:matProg_barWidth="1dp"
                wheel:matProg_progressIndeterminate="true"/>
        </RelativeLayout>
    
    
        <RelativeLayout
            android:id="@+id/no_available_tasks"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            tools:visibility = "visible"
            >
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="No tasks currently available. Pull to refresh"
                android:layout_centerInParent="true"
                android:padding="16dp"
                android:gravity="center_horizontal"
                android:textSize="18sp"/>
    
        </RelativeLayout>
    
    </RelativeLayout>
      </android.support.v4.widget.SwipeRefreshLayout>
    

    Underneath is the code for my fragment :

    package com.packrboy.fragments;
    
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.RelativeLayout;
    
    import com.android.volley.AuthFailureError;
    import com.android.volley.NetworkError;
    import com.android.volley.NetworkResponse;
    import com.android.volley.NoConnectionError;
    import com.android.volley.ParseError;
    import com.android.volley.Request;
    import com.android.volley.RequestQueue;
    import com.android.volley.Response;
    import com.android.volley.ServerError;
    import com.android.volley.TimeoutError;
    import com.android.volley.VolleyError;
    import com.android.volley.toolbox.JsonObjectRequest;
    import com.avast.android.dialogs.fragment.SimpleDialogFragment;
    import com.packrboy.R;
    import com.packrboy.activities.TaskActivity;
    import com.packrboy.adapters.TaskAdapter;
    import com.packrboy.classes.SharedPreferenceClass;
    import com.packrboy.classes.Shipment;
    import com.packrboy.network.VolleySingleton;
    import com.pnikosis.materialishprogress.ProgressWheel;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    import static com.packrboy.extras.Keys.Shipment.KEY_CREATED_AT;
    import static com.packrboy.extras.Keys.Shipment.KEY_ID;
    import static com.packrboy.extras.Keys.Shipment.KEY_IN_TRANSIT_STATUS;
    import static com.packrboy.extras.Keys.Shipment.KEY_ITEM_IMAGE;
    import static com.packrboy.extras.Keys.Shipment.KEY_ITEM_QUANTITY;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_CITY;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_LATITUDE;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_LONGITUDE;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_POSTAL_CODE;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_ROUTE;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_STATE;
    import static com.packrboy.extras.Keys.Shipment.KEY_PICKUP_STREET_NO;
    import static com.packrboy.extras.Keys.Shipment.KEY_SHIPMENT_ARRAY;
    import static com.packrboy.extras.urlEndPoints.KEY_ASSIGN_DELIVERY;
    import static com.packrboy.extras.urlEndPoints.KEY_ASSIGN_PICKUP;
    import static com.packrboy.extras.urlEndPoints.KEY_AVAILABLE;
    import static com.packrboy.extras.urlEndPoints.KEY_SHIPMENT_URL;
    import static com.packrboy.extras.urlEndPoints.KEY_UAT_BASE_URL_API;
    import static com.packrboy.extras.Keys.Shipment.KEY_TYPE;
    import static com.packrboy.extras.Keys.Shipment.KEY_SHIPMENT;
    import static com.packrboy.extras.Keys.ServiceKeys.KEY_ERROR_CODE;
    
    /**
     * Created by arindam.paaltao on 29-Jul-15.
     */
    public class AvailableTaskFragment extends Fragment implements TaskAdapter.ClickListener{
        private RecyclerView mRecyclerView;
        private TaskAdapter mTaskAdapter;
        private JSONArray shipmentListArray;
        private TaskActivity activity;
        private SharedPreferenceClass preferenceClass;
        private ArrayList<Shipment> shipmentArrayList = new ArrayList<>();
        int shipmentId;
        String userId,transitStatus,requestType,streetNo,route,city,state,postalCode,imageURL,customerName,latitude,longitude,createdTime,updatedTime,itemQuantity;
        View layout;
        RelativeLayout progressWheel,noAvailableTasks;
        private SwipeRefreshLayout mSwipeRefreshLayout;
    
        public AvailableTaskFragment() {
    
        }
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            layout = inflater.inflate(R.layout.available_task_fragment, container, false);
    
            mSwipeRefreshLayout = (SwipeRefreshLayout)layout.findViewById(R.id.activity_main_swipe_refresh_layout);
            preferenceClass = new SharedPreferenceClass(getActivity());
            userId = preferenceClass.getCustomerId();
            progressWheel = (RelativeLayout)layout.findViewById(R.id.progress_wheel);
            noAvailableTasks = (RelativeLayout)layout.findViewById(R.id.no_available_tasks);
            sendJsonRequest();
            mRecyclerView = (RecyclerView) layout.findViewById(R.id.availableTaskRecyclerView);
            mTaskAdapter = new TaskAdapter(getActivity(), activity);
            mRecyclerView.setAdapter(mTaskAdapter);
            mTaskAdapter.setClickListener(this);
            mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
            mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    sendJsonRequest();
                }
            });
    
    
            return layout;
        }
    
        public static String getRequestUrl(){
            return KEY_UAT_BASE_URL_API + KEY_SHIPMENT_URL + KEY_AVAILABLE;
        }
    
        public void sendJsonRequest(){
            progressWheel.setVisibility(View.VISIBLE);
            final JSONObject testObject = new JSONObject();
            try {
                testObject.put("token", preferenceClass.getAccessToken());
    
            } catch (JSONException e) {
                e.printStackTrace();
            }
    
            RequestQueue requestQueue = VolleySingleton.getsInstance().getRequestQueue();
            JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, getRequestUrl(), testObject, new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject jsonObject) {
                    if (mSwipeRefreshLayout.isRefreshing()){
                        mSwipeRefreshLayout.setRefreshing(false);
                    }
                    progressWheel.setVisibility(View.GONE);
                    Log.i("error", jsonObject.toString());
                    Log.i("login", testObject.toString());
                    shipmentArrayList = parseJsonResponse(jsonObject);
                    mTaskAdapter.setShipmentArrayList(shipmentArrayList);
    
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                    if (volleyError instanceof TimeoutError || volleyError instanceof NoConnectionError) {
    
    
                    } else if (volleyError instanceof AuthFailureError) {
    
                        //TODO
                    } else if (volleyError instanceof ServerError) {
    
                        //TODO
                    } else if (volleyError instanceof NetworkError) {
    
                        //TODO
                    } else if (volleyError instanceof ParseError) {
    
                        //TODO
                    }
    
                }
            }) {
                @Override
                protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                    return super.parseNetworkResponse(response);
                }
    
                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    Map<String, String> headers = super.getHeaders();
    
                    if (headers == null
                            || headers.equals(Collections.emptyMap())) {
                        headers = new HashMap<String, String>();
                    }
    
                    headers.put("Cookie", preferenceClass.getCookie());
                    return headers;
                }
            };
            requestQueue.add(jsonObjectRequest);
        }
    
        public ArrayList<Shipment> parseJsonResponse(JSONObject response) {
    
            ArrayList<Shipment> shipmentArrayList = new ArrayList<>();
            if (response != null && response.length() > 0) {
    
                try {
                    shipmentListArray  = response.getJSONArray(KEY_SHIPMENT_ARRAY);
                    if (shipmentListArray.length() == 0){
                        if (noAvailableTasks.getVisibility() == View.GONE)
                            noAvailableTasks.setVisibility(View.VISIBLE);
                    }else {
                        if (noAvailableTasks.getVisibility() == View.VISIBLE)
                            noAvailableTasks.setVisibility(View.GONE);
    
                        for (int i = 0; i < shipmentListArray.length(); i++) {
                            JSONObject shipmentObject = shipmentListArray.getJSONObject(i);
                            if (shipmentObject.has(KEY_TYPE)) {
                                requestType = shipmentObject.getString(KEY_TYPE);
                                transitStatus = shipmentObject.getString(KEY_IN_TRANSIT_STATUS);
                            }
                            JSONObject shipmentDetails = new JSONObject();
                            shipmentDetails = shipmentObject.getJSONObject(KEY_SHIPMENT);
    
                            streetNo = shipmentDetails.getString(KEY_PICKUP_STREET_NO);
                            route = shipmentDetails.getString(KEY_PICKUP_ROUTE);
                            city = shipmentDetails.getString(KEY_PICKUP_CITY);
                            state = shipmentDetails.getString(KEY_PICKUP_STATE);
                            postalCode = shipmentDetails.getString(KEY_PICKUP_POSTAL_CODE);
                            imageURL = shipmentDetails.getString(KEY_ITEM_IMAGE);
                            latitude = shipmentDetails.getString(KEY_PICKUP_LATITUDE);
                            longitude = shipmentDetails.getString(KEY_PICKUP_LONGITUDE);
                            createdTime = shipmentDetails.getString(KEY_CREATED_AT);
                            itemQuantity = shipmentDetails.getString(KEY_ITEM_QUANTITY);
                            shipmentId = Integer.parseInt(shipmentDetails.getString(KEY_ID));
    
    
                            Shipment current = new Shipment();
                            current.setImageURL(imageURL);
                            current.setCity(city);
                            current.setCreatedTime(createdTime);
                            current.setLatitude(Double.parseDouble(latitude));
                            current.setLongitude(Double.parseDouble(longitude));
                            current.setPostalCode(postalCode);
                            current.setState(state);
                            current.setStreetNo(streetNo);
                            current.setRoute(route);
                            current.setRequestType(requestType);
                            current.setItemQuantity(itemQuantity);
                            current.setItemId(shipmentId);
                            current.setTransitStatus(transitStatus);
    
                            shipmentArrayList.add(current);
    
                        }
                    }
    
                } catch (JSONException e) {
                    e.printStackTrace();
                }
    
            }
            return shipmentArrayList;
        }
    
    
        public static String getAcceptRequestRequestUrl(){
            return KEY_UAT_BASE_URL_API + KEY_SHIPMENT_URL + KEY_ASSIGN_PICKUP;
        }
    
        public void sendAcceptRequestJsonRequest(){
            final JSONObject shipmentObject = new JSONObject();
            final JSONObject pendingTaskObject = new JSONObject();
            try {
                shipmentObject.put("packrboy_id", userId);
                shipmentObject.put("shipment_id", shipmentId);
                pendingTaskObject.put("payload", shipmentObject);
                pendingTaskObject.put("token", preferenceClass.getAccessToken());
    
    
            } catch (JSONException e) {
                e.printStackTrace();
            }
    
            RequestQueue requestQueue = VolleySingleton.getsInstance().getRequestQueue();
            JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, getAcceptRequestRequestUrl(), pendingTaskObject, new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject jsonObject) {
                    Log.i("error", jsonObject.toString());
                    Log.i("login", pendingTaskObject.toString());
                    if (jsonObject.has(KEY_ERROR_CODE)){
                        try {
                            String errorCode = jsonObject.getString(KEY_ERROR_CODE);
                            if (errorCode.contentEquals("200")){
                                SimpleDialogFragment.createBuilder(getActivity(), getFragmentManager()).setTitle("Request Accepted").setMessage(R.string.pickup_request_accepted).show();
                            }
                            else {
                                SimpleDialogFragment.createBuilder(getActivity(), getFragmentManager()).setTitle("Request cannot be accepted").setMessage(R.string.pickup_request_not_allowed).show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
    
                    }
    
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                    if (volleyError instanceof TimeoutError || volleyError instanceof NoConnectionError) {
    
    
                    } else if (volleyError instanceof AuthFailureError) {
    
                        //TODO
                    } else if (volleyError instanceof ServerError) {
    
                        //TODO
                    } else if (volleyError instanceof NetworkError) {
    
                        //TODO
                    } else if (volleyError instanceof ParseError) {
    
                        //TODO
                    }
    
                }
            }) {
                @Override
                protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                    return super.parseNetworkResponse(response);
                }
    
                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    Map<String, String> headers = super.getHeaders();
    
                    if (headers == null
                            || headers.equals(Collections.emptyMap())) {
                        headers = new HashMap<String, String>();
                    }
    
                    headers.put("Cookie", preferenceClass.getCookie());
                    return headers;
                }
            };
            requestQueue.add(jsonObjectRequest);
        }
    
    
        public static String getAcceptDeliveryRequestRequestUrl(){
            return KEY_UAT_BASE_URL_API + KEY_SHIPMENT_URL + KEY_ASSIGN_DELIVERY;
        }
    
        public void sendAcceptDeliveryRequestJsonRequest(){
            final JSONObject shipmentObject = new JSONObject();
            final JSONObject pendingTaskObject = new JSONObject();
            try {
                shipmentObject.put("packrboy_id", userId);
                shipmentObject.put("shipment_id", shipmentId);
                pendingTaskObject.put("payload", shipmentObject);
                pendingTaskObject.put("token", preferenceClass.getAccessToken());
    
    
            } catch (JSONException e) {
                e.printStackTrace();
            }
    
            RequestQueue requestQueue = VolleySingleton.getsInstance().getRequestQueue();
            JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, getAcceptDeliveryRequestRequestUrl(), pendingTaskObject, new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject jsonObject) {
                    Log.i("error", jsonObject.toString());
                    Log.i("login", pendingTaskObject.toString());
                    if (jsonObject.has(KEY_ERROR_CODE)){
                        try {
                            String errorCode = jsonObject.getString(KEY_ERROR_CODE);
                            if (errorCode.contentEquals("200")){
                                SimpleDialogFragment.createBuilder(getActivity(), getFragmentManager()).setTitle("Request Accepted").setMessage("Delivery request has been accepted").show();
                            }
                            else {
                                SimpleDialogFragment.createBuilder(getActivity(), getFragmentManager()).setTitle("Request cannot be accepted").setMessage("The request cannot be accepted").show();
                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
    
                    }
    
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                    if (volleyError instanceof TimeoutError || volleyError instanceof NoConnectionError) {
    
    
                    } else if (volleyError instanceof AuthFailureError) {
    
                        //TODO
                    } else if (volleyError instanceof ServerError) {
    
                        //TODO
                    } else if (volleyError instanceof NetworkError) {
    
                        //TODO
                    } else if (volleyError instanceof ParseError) {
    
                        //TODO
                    }
    
                }
            }) {
                @Override
                protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                    return super.parseNetworkResponse(response);
                }
    
                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    Map<String, String> headers = super.getHeaders();
    
                    if (headers == null
                            || headers.equals(Collections.emptyMap())) {
                        headers = new HashMap<String, String>();
                    }
    
                    headers.put("Cookie", preferenceClass.getCookie());
                    return headers;
                }
            };
            requestQueue.add(jsonObjectRequest);
        }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
        @Override
        public void itemClicked(View view, int position) {
            shipmentId = shipmentArrayList.get(position).getItemId();
            if (shipmentArrayList.get(position).getRequestType().contentEquals("pickup")){
            sendAcceptRequestJsonRequest();}
    
            else{
                sendAcceptDeliveryRequestJsonRequest();
            }
    
    
    
        }
    }
    

    I have also implemented the coordinator layout in the activity layout. Here is the code:

    <android.support.design.widget.CoordinatorLayout 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">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
    
            <include
                layout="@layout/toolbar"
                android:id="@+id/toolbar"/>
    
            <android.support.design.widget.TabLayout
                android:id="@+id/sliding_tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabMode="scrollable"
                app:tabGravity="fill" />
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    </android.support.design.widget.CoordinatorLayout>
    

    Can you help me resolve this issue. It will be of great help. Thanks in advance.

    Related posts:

    Screen orientation lock
    Google Play: We found Ad SDKs in your application
    MediaPlayer TimeoutException
    RelativeLayout is taking fullscreen for wrap_content
    How to get the build variant at runtime in Android Studio?
    Handling Push Notification scenarios on iOS and Android
  • Disable toolbar scrolling
  • Android: Native process provoke ReferenceTable overflow after a while (android-ndk)
  • How to check device compatibility for finger print authentication in android
  • The method showDialog(int) from the type Activity is deprecated in android?
  • How to change Action Mode icon of SherlockActionBar library?
  • android - overridden volume button has affected back button?
  • 3 Solutions collect form web for “Recycler view not scrolling properly after implementing swipe to refresh layout”

    I troubleshooted it myself finally.

    The problem lies in the xml layout itself. Recycler view should be a single child element inside a SwipeToReferesh layout.

    The layout file should be as follows:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:wheel="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/activity_main_swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/completedTaskRecyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:scrollbars="vertical" />
    
        </android.support.v4.widget.SwipeRefreshLayout>
    
        <com.pnikosis.materialishprogress.ProgressWheel
            android:id="@+id/progress_wheel"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:visibility="gone"
            tools:visibility="visible"
            wheel:matProg_barColor="@color/colorAccent"
            wheel:matProg_barWidth="1dp"
            wheel:matProg_progressIndeterminate="true" />
    
    
        <TextView
            android:id="@+id/no_available_tasks"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:gravity="center_horizontal|center_vertical"
            android:padding="16dp"
            android:text="No tasks currently available. Pull to refresh"
            android:textSize="18sp"
            android:visibility="gone"
            tools:visibility="visible" />
    </RelativeLayout>
    

    No need to add some other utility classes or override SwipeToRefesh’s canChildScrollUp() method.

    This is a known issue and I wonder why this has been not fixed already. The problem here is that SwipeRefreshLayout always assumes that the list cannot scroll upwards any further.

    One solution is to extend SwipeRefreshLayout and override canChildScrollUp(). This method gets called when you start scrolling and returning false here when the list is not at its top position should do the trick.

    Next, use this utility method to determine whether or not your RecyclerView can scroll further upwards:

    /**
     * Returns whether or not a View can scroll vertically any further. 
     * @param downwardScroll The direction to check for. Pass true for downwards and
     *                       false for upward. Note that downward scroll == upward swipe
     * */
    public static boolean canScrollVerticallyAnyFurther(View view, boolean downwardScroll){
        return view.canScrollVertically(downwardScroll ? +1 : -1);
    }
    

    I managed to implement Swipe down to refresh and infinite scrolling. If anyone is interested check out.You can see implementation of this at

    https://github.com/snijsure/TwitterSample

    Although from UX perspective I am not sure if supporting SwipeDown to refresh as well as refresh on end of scroll, is a good idea, it might be to confusing to the end user.

    Cheers!

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.