8

I'm trying to make a left-right swipeable card system (like Tinder), where the card has a NestedScrollView on it. The goal would be that if the user swiped up and down only, the NestedScrollView would scroll, but if the user swiped left or right, the Card would use that swipe.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="4dp"
    card_view:cardUseCompatPadding="true">

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/nested_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="4dp">

            <TextView
                android:id="@+id/text_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

        </LinearLayout>

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

</android.support.v7.widget.CardView>

(The card library I am using is https://github.com/wenchaojiang/AndroidSwipeableCardStack )

compile 'com.github.wenchaojiang:AndroidSwipeableCardStack:0.1.5'

When I put a margin_top on the NestedScrollView and touch within that margin, the CardStack correctly grabs my input and the card moves left or right, but the NestedScrollView grabs my input regardless of direction if I touch and drag anywhere else.

Which class/onTouchEvent should I be extending/overriding to have this effect, or might there be a simpler approach?

Thanks!

ogoldbart3
  • 253
  • 5
  • 14
  • I too am trying to figure this out, considering and trying... 1) onInterceptTouch in parent class (recording steps maybe and dispatching?) 2) modifying the library to implemented NestedChildHelper 2) Recyclerview with ItemTouchHelper... Pretty tough one so far. . Not sure if matters, but so far the libraries I'm trying are meetic shuffle and swipestack. https://github.com/Meetic/Shuffle and https://github.com/flschweiger/SwipeStack – nAndroid May 02 '17 at 14:28
  • Using one of those libraries is preferable than recyclerview b/c they do a lot. – nAndroid May 02 '17 at 14:37
  • I believe onInterceptTouchEvent() is the method you're looking for. See https://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent) – varsha-venkatesh May 05 '17 at 14:48
  • @varsha-venkatesh Oops. I just realized this question is backwards; I have a few where the up and down scroller is OUTSIDE my swipestack – nAndroid May 08 '17 at 22:36
  • Refere this link.Give upvote if u found useful. [Click this link.Added gesture listener.](http://stackoverflow.com/questions/41033279/moving-a-recyclerview-via-touch-or-gesture-recognition/41122132#41122132) – Ronak Gadhia May 09 '17 at 07:00

2 Answers2

0

Yes, you should do this by implement on Java code the CardStack.CardEventListener, and set it as listener mCardStack.setListener(yourListener); It's on readme inside the project. Try this:

Class YourListener extends CardStack.CardEventListener{
    //implement card event interface
    @Override
    public boolean swipeEnd(int direction, float distance) {
        //if "return true" the dismiss animation will be triggered 
        //if false, the card will move back to stack
        //distance is finger swipe distance in dp


        return (distance>300)? true : false;
    }

    @Override
    public boolean swipeStart(int direction, float distance) {

        return true;
    }

    @Override
    public boolean swipeContinue(int direction, float distanceX, float distanceY) {

        return true;
    }

    @Override
    public void discarded(int id, int direction) {
       //this callback invoked when dismiss animation is finished. 
    }

    @Override
    public void topCardTapped() {
         //this callback invoked when a top card is tapped by user. 
    }
}
mmelotti
  • 277
  • 3
  • 11
  • mmeloti - this did not work because the swipestack did not work properly with this libray – nAndroid May 08 '17 at 05:26
  • The problem is the swipestack? I don't know, seems like a issue in your xml, but without the code java. Impossible to track down the problem... – mmelotti May 08 '17 at 12:41
  • mmeloti - Any situation at all where you have a swipestack in a scrollview where they both work. see https://github.com/neiljaywarner/TestSwipeStack – nAndroid May 12 '17 at 20:10
0

I do this for 2 interfaces for my app. Use a GestureDetector and on onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) check horizontal speed. After a certain threshold pass or intercept touch event for views. You can check how to intercept or pass events from Touch api.

This is a general solution independent of which ViewGroups or Views you use, it works. You consume event before it's passed to your layout or custom view.

Touch Events propagates as

  1. Activity.dispatchTouchEvent()
  2. ViewGroup.dispatchTouchEvent()
  3. View.dispatchTouchEvent()
  4. View.onTouchEvent()
  5. ViewGroup.onTouchEvent()
  6. Activity.onTouchEvent()

If you use GestureDetector inside your Activity's dispatchTouchEvent() method you will be able to consume or propagate event to ViewGroups or Views correctly.

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    gestureDetector.onTouchEvent(event);
    // Be sure to call the superclass implementation
    return super.dispatchTouchEvent(event);
}
Thracian
  • 6,939
  • 2
  • 33
  • 64