Alexandre Nussbaumer
Alexandre Nussbaumer

Reputation: 331

Disable vertical swipe (SwipeRefreshLayout) when swiping in a horizontal Listview

My app is currently composed by a ViewPager that have several Fragments. In a specific Fragment I have a SwipeRefreshLayout with a ScrollView and into this container, a HorizotalListView of type TwoWayView.

The problem is when i swipe horizontally in the ListView the vertical scroll for the SwipeRefreshLayoutisn't disabled and sometimes it breaks the horizontal swipe during the action.

This is what I tried to do to resolve it:

horizListView.setOnTouchListener(new ListView.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                // Disallow ScrollView to intercept touch events.
                mSwipeRefreshLayout.requestDisallowInterceptTouchEvent(true);
                v.getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
                // Allow ScrollView to intercept touch events.
                mSwipeRefreshLayout.requestDisallowInterceptTouchEvent(false);
                v.getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }

        // Handle HorizontalScrollView touch events.
        v.onTouchEvent(event);
        return true;
    }
});

And here is my layout:

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
            <LinearLayout
                android:layout_width="match_parent"
                android:orientation="vertical"
                android:layout_height="match_parent">

                <TextView
                    android:id="@+id/tv_info1"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:padding="10dp"
                    android:textStyle="bold"
                    android:textSize="@dimen/text_medium"
                    android:textColor="@color/light_gray"
                    android:text="@string/place_proposal" />

                <org.lucasr.twowayview.TwoWayView
                    android:orientation="horizontal"
                    android:id="@+id/horizlistview"
                    android:layout_height="230dp"
                    android:scaleType="centerCrop"
                    android:drawSelectorOnTop="false"
                    android:layout_width="fill_parent" />

                <TextView
                    android:id="@+id/tv_info2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="@color/light_gray"
                    android:textSize="@dimen/text_medium"
                    android:textStyle="bold"
                    android:padding="10dp"
                    android:text="@string/actual_position"
                    android:paddingTop="5dp" />

                <ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
                    android:id="@+id/vf"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content">

                    <include
                        android:id="@+id/include_w"
                        layout="@layout/include_wicard_homescreen" />

                    <include
                        android:id="@+id/include_no_w"
                        layout="@layout/include_no_wicard" />

                </ViewFlipper>

            </LinearLayout>
    </ScrollView>

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

If anyone can help me it would be great.

Upvotes: 1

Views: 3544

Answers (2)

I had a similar problem with SwipeRefreshLayout intercepting touch events. In latest support library (i am using 25.3.1 atm) this can be dealt with without extending SwipeRefreshLayout. Here's how i did it:

  1. Your view that should receive touch event must return true in isNestedScrollingEnabled().
  2. Your touch listener must return true in onTouchEvent() for ACTION_DOWN events. Otherwise the events with this pointer id will be handled by SwipeRefreshLayout.
  3. Your touch listener must return true in onTouchEvent() for all the actions you want to handle with your view.
  4. Use requestDisallowInterceptTouchEvent(bool) to turn off/on touch sensivity of SwipeRefreshLayout and its parents if any.

In OP's case setting RecyclerView.setNestedScrollingEnabled(true); should solve the issue.

Here's a sample of my custom view's code. When Using this view, i can change tabs by swiping horizontally and enjoy the functionality of SwipeRefreshLayout when the view is scrolled all the way up. All other vertical scrolling is consumed by this view. I hope it helps.

public void CustomScrollableView extends View {
private final GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        // Check if view is zoomed.
        if (mIsZooming)
            return true;

        if (Math.abs(distanceY) < Math.abs(distanceX)) return false; // horizontal scrolling

        // Calculate the new origin after scroll.
        mYOffset -= distanceY;
        ViewCompat.postInvalidateOnAnimation(CustomScrollableView.this);
        return mYOffset < 0;
    }
}

private void blockParentScrolling(final boolean block) {
    final ViewParent parent = getParent();
    if (parent != null) {
        parent.requestDisallowInterceptTouchEvent(block);
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    boolean consumedByGestureDetector = mGestureDetector.onTouchEvent(event);
    blockParentScrolling(consumedByGestureDetector);
    return consumedByGestureDetector;
}

@Override
public boolean isNestedScrollingEnabled() {
    return true;
}
}

Upvotes: 0

Yong.Hu
Yong.Hu

Reputation: 11

Override SwipeRefreshLayout. And here is my code, it works fine:

public class RefreshLayoutExIntercepter extends SwipeRefreshLayout {

public RefreshLayoutExIntercepter(Context ctx) {
    super(ctx);
    mTouchSlop = ViewConfiguration.get(ctx).getScaledTouchSlop();
}

public RefreshLayoutExIntercepter(Context ctx, AttributeSet attrs) {
    super(ctx, attrs);
    mTouchSlop = ViewConfiguration.get(ctx).getScaledTouchSlop();
}

private int mTouchSlop;
private float mPrevX;

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        mPrevX = event.getX();
        break;

    case MotionEvent.ACTION_MOVE:
        final float eventX = event.getX();
        float xDiff = Math.abs(eventX - mPrevX);

        if (xDiff > mTouchSlop) {
            return false;
        }
    }

    return super.onInterceptTouchEvent(event);
}
}

Upvotes: 1

Related Questions