How to adjust the swipe down distance in SwipeRefreshLayout?
Asked Answered
F

4

29

I implemented SwipeRefreshLayout in my app. I need to change the height which has to be scrolled down to call onRefresh() method. Which method should I use to add a custom height ?

Implementation of SwipeRefreshLayout

private SwipeRefreshLayout swipeLayout;

In oncreate

swipeLayout   = (SwipeRefreshLayout)view.findViewById(R.id.swipe_container);
swipeLayout.setOnRefreshListener(this);
        swipeLayout.setColorScheme(android.R.color.holo_blue_dark, 
                android.R.color.holo_green_dark, 
                android.R.color.holo_purple, 
                android.R.color.holo_orange_dark);   
        swipeLayout.setSoundEffectsEnabled(true);

Implementing an AsyncTask in onrefresh method

   @Override
public void onRefresh() 
{
    // Asynctask to perform some task  

}
Forme answered 4/4, 2014 at 7:52 Comment(3)
You should really share your codeBarmaid
Can someone comment why this question has been downvoted ?Forme
Check out here http://blog.xamarin.com/android-the-swipe-down-to-refresh-pattern/ it may be helpful to youCub
S
31

If you're using API 21 of the support library, refer to and please upvote Han He's answer here. A method exists to set the trigger distance called setDistanceToTriggerSync.


Prior to API 21, as adneal mentioned, there are no public or internal methods to modify the trigger distance.

However, if you don't want to keep a copy of the classes to modify the constants, you can use reflection to manually set a trigger distance.

swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);

// define a distance
Float mDistanceToTriggerSync = yourCalculation(); 

try {
    // Set the internal trigger distance using reflection.
    Field field = SwipeRefreshLayout.class.getDeclaredField("mDistanceToTriggerSync");
    field.setAccessible(true);
    field.setFloat(swipeLayout, mDistanceToTriggerSync);
} catch (Exception e) {
    e.printStackTrace();
}

If you're using the height of the layout to determine the distance, you may want to use something like a GlobalLayoutListener.

mDistanceToTriggerSync

In SwipeRefreshLayout, there is an internal variable called mDistanceToTriggerSync. This is used to determine at what distance to trigger the refresh.

In the source code, it is set by the code below in SwipeRefreshLayout:

final DisplayMetrics metrics = getResources().getDisplayMetrics();
mDistanceToTriggerSync = (int) Math.min(
        ((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
                REFRESH_TRIGGER_DISTANCE * metrics.density);

The above uses the parent view height and some constants to calculate the trigger distance. MAX_SWIPE_DISTANCE_FACTOR (0.6) and REFRESH_TRIGGER_DISTANCE (120) are private constants in the class that you cannot modify.

You can use the above to calculate your trigger distance and use your own constants for the swipe distance factors.

GlobalLayoutListener

Setting of the mDistanceToTriggerSync can be done inside a global layout listener so that the height of the layout can be retrieved properly for calculating the trigger distance. Calling getHeight on a view in onCreate will always return 0 because it has not been drawn yet. I got the code from here. You may or may not need to do this depending on your requirements.

ViewTreeObserver vto = swipeLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Calculate the trigger distance.
        final DisplayMetrics metrics = getResources().getDisplayMetrics();
        Float mDistanceToTriggerSync = Math.min(
                ((View) swipeLayout.getParent()).getHeight() * 0.6f,
                120 * metrics.density);

        try {
            // Set the internal trigger distance using reflection.
            Field field = SwipeRefreshLayout.class.getDeclaredField("mDistanceToTriggerSync");
            field.setAccessible(true);
            field.setFloat(swipeLayout, mDistanceToTriggerSync);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Only needs to be done once so remove listener.
        ViewTreeObserver obs = swipeLayout.getViewTreeObserver();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
        } else {
            obs.removeGlobalOnLayoutListener(this);
        }
    }
});

The setting of the variable only needs to be done once if I understand the underlying code properly as it only sets mDistanceToTriggerSync if it is equal to -1. Therefore, it should be safe to do it in the global layout listener once.

SwipeRefreshLayout

If you're interested in the source code of SwipeRefreshLayout you can find it here.

Samaniego answered 5/4, 2014 at 18:55 Comment(4)
Yeah, other than copying the classes over this is really the only other workaround aside from submitting a patch to the AOSP. Each of our answers have their drawbacks, but I think it's a good answer nonetheless.Gillies
@Gillies thanks and yeah both have drawbacks, hopefully it's more customizable in the future unless there's reason why it's like this.Samaniego
I have created gist on Github you can access it from here : gist.github.com/bhaumiknsoni/77850b4369a1bd4a67ab Thanks to singularhumMenchaca
If you are using the reflection method the field is now called "mTotalDragDistance" instead of "mDistanceToTriggerSync". Just in case someone comes across this.Clubhaul
H
42

The reversion 21 of v4 support library has provided a API to set the distance.

public void setDistanceToTriggerSync (int distance)

Check the document here.

One more thing, the rev. 21 changed the behaviour of progress bar, it's now a circle image. Have no idea how to get the old way back.

Honorific answered 27/10, 2014 at 2:7 Comment(0)
S
31

If you're using API 21 of the support library, refer to and please upvote Han He's answer here. A method exists to set the trigger distance called setDistanceToTriggerSync.


Prior to API 21, as adneal mentioned, there are no public or internal methods to modify the trigger distance.

However, if you don't want to keep a copy of the classes to modify the constants, you can use reflection to manually set a trigger distance.

swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);

// define a distance
Float mDistanceToTriggerSync = yourCalculation(); 

try {
    // Set the internal trigger distance using reflection.
    Field field = SwipeRefreshLayout.class.getDeclaredField("mDistanceToTriggerSync");
    field.setAccessible(true);
    field.setFloat(swipeLayout, mDistanceToTriggerSync);
} catch (Exception e) {
    e.printStackTrace();
}

If you're using the height of the layout to determine the distance, you may want to use something like a GlobalLayoutListener.

mDistanceToTriggerSync

In SwipeRefreshLayout, there is an internal variable called mDistanceToTriggerSync. This is used to determine at what distance to trigger the refresh.

In the source code, it is set by the code below in SwipeRefreshLayout:

final DisplayMetrics metrics = getResources().getDisplayMetrics();
mDistanceToTriggerSync = (int) Math.min(
        ((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
                REFRESH_TRIGGER_DISTANCE * metrics.density);

The above uses the parent view height and some constants to calculate the trigger distance. MAX_SWIPE_DISTANCE_FACTOR (0.6) and REFRESH_TRIGGER_DISTANCE (120) are private constants in the class that you cannot modify.

You can use the above to calculate your trigger distance and use your own constants for the swipe distance factors.

GlobalLayoutListener

Setting of the mDistanceToTriggerSync can be done inside a global layout listener so that the height of the layout can be retrieved properly for calculating the trigger distance. Calling getHeight on a view in onCreate will always return 0 because it has not been drawn yet. I got the code from here. You may or may not need to do this depending on your requirements.

ViewTreeObserver vto = swipeLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // Calculate the trigger distance.
        final DisplayMetrics metrics = getResources().getDisplayMetrics();
        Float mDistanceToTriggerSync = Math.min(
                ((View) swipeLayout.getParent()).getHeight() * 0.6f,
                120 * metrics.density);

        try {
            // Set the internal trigger distance using reflection.
            Field field = SwipeRefreshLayout.class.getDeclaredField("mDistanceToTriggerSync");
            field.setAccessible(true);
            field.setFloat(swipeLayout, mDistanceToTriggerSync);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Only needs to be done once so remove listener.
        ViewTreeObserver obs = swipeLayout.getViewTreeObserver();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            obs.removeOnGlobalLayoutListener(this);
        } else {
            obs.removeGlobalOnLayoutListener(this);
        }
    }
});

The setting of the variable only needs to be done once if I understand the underlying code properly as it only sets mDistanceToTriggerSync if it is equal to -1. Therefore, it should be safe to do it in the global layout listener once.

SwipeRefreshLayout

If you're interested in the source code of SwipeRefreshLayout you can find it here.

Samaniego answered 5/4, 2014 at 18:55 Comment(4)
Yeah, other than copying the classes over this is really the only other workaround aside from submitting a patch to the AOSP. Each of our answers have their drawbacks, but I think it's a good answer nonetheless.Gillies
@Gillies thanks and yeah both have drawbacks, hopefully it's more customizable in the future unless there's reason why it's like this.Samaniego
I have created gist on Github you can access it from here : gist.github.com/bhaumiknsoni/77850b4369a1bd4a67ab Thanks to singularhumMenchaca
If you are using the reflection method the field is now called "mTotalDragDistance" instead of "mDistanceToTriggerSync". Just in case someone comes across this.Clubhaul
G
3

Currently there isn't a public method, or even an internal one for that matter, that can be used to adjust the trigger distance. But you can copy over the three classes from the support library into your project, then modify SwipeRefreshLayout to your liking.

Here are the classes you'll need to copy over:

The tigger distance is calculated using the constants:

 private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
 private static final int REFRESH_TRIGGER_DISTANCE = 120;

In your layout change <android.support.v4.widget.SwipeRefreshLayout.../> to <your_path_to_SwipeRefreshLayout.../> and make sure you change your imports, if you need to.

Gillies answered 4/4, 2014 at 8:56 Comment(0)
D
-2

This one just did a trick and no more extra code Just wrap your recyclerview to NestedScrollView then this NestedScrollView is wrapped in SwipeRefreshLayout

Check code below

<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
     android:id="@+id/swipe_refresh_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent">

     <androidx.core.widget.NestedScrollView
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           
           <androidx.recyclerview.widget.RecyclerView
                  android:id="@+id/recycler_view"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent" />

     </androidx.core.widget.NestedScrollView>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
Depose answered 20/8, 2020 at 3:39 Comment(1)
It is bad practise. I would not recommend thatObliquity

© 2022 - 2024 — McMap. All rights reserved.