End up activity on swipe right?
Asked Answered
S

6

26

I have to finish Activity when user offer a right swipe anywhere in the screen. I have tried with GestureDetector and that is works fine if there is neither ScrollView nor RescyclerView exists in the Activity and in addition views that have onClickListener also doesn't allow to detect swipe over them. So I had tried a different way by overlaying a view into the layout at the top of all them programmatically then tried to detect the swipe event over it.

private void swipeOverToExit(ViewGroup rootView) {

        OverlayLayout child = new OverlayLayout(this);

        ViewGroup.LayoutParams layoutParams =
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

        child.setLayoutParams(layoutParams);

        rootView.addView(child);

}

OverlayLayout

public class OverlayLayout extends RelativeLayout {

    private float x1, x2;
    private final int MIN_DISTANCE = 150;

    public OverlayLayout(Context context) {
        super(context);
    }

    public OverlayLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public OverlayLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public OverlayLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        /*
         * This method JUST determines whether we want to intercept the motion.
         * If we return true, onTouchEvent will be called and we do the actual
         * logic there.
         */

        final int action = MotionEventCompat.getActionMasked(event);

        Logger.logD("Intercept===", action + "");


        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_DOWN) {
            return true; // Intercept touch event, let the parent handle swipe
        }

        Logger.logD("===", "Out side" + action + "");


        // In general, we don't want to intercept touch events. They should be
        // handled by the child view.
        return false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                x1 = event.getX();
                break;
            case MotionEvent.ACTION_UP:

                x2 = event.getX();
                float deltaX = x2 - x1;

                if (Math.abs(deltaX) > MIN_DISTANCE) {

                    Logger.logD("Swipe Right===", MIN_DISTANCE + "");
                    return true;

                } else {

                    Logger.logD("Tap===", "Tap===");
                    return super.onTouchEvent(event);
                }
        }

        return true;

    }
}

The logic is to intercept touch event to other view if swipe action performs over the OverlayLayout then further end up the Activity. However, now I can detect the swipe event on OverlayLayout but other views couldn't respond even though I had return return super.onTouchEvent(event); in else condition of onTouchEvent as u can figure out there in my code. Any one please help me to make it . I'm pinned here and super excited to learn the trick :)

Smote answered 15/7, 2016 at 5:6 Comment(5)
try adding @Override public boolean dispatchTouchEvent(MotionEvent ev){ super.dispatchTouchEvent(ev); return productGestureDetector.onTouchEvent(ev); } to your activityEdytheee
@Smote Have you tried dispatchTouchEvent in activity?Herald
Try searching for touchIntercept method of controlling the gestures in any kind of layout.Mcniel
I had tried dispatchTouchEvent in activity it seems doesn't help me out. I tried all searching and I couldn't make it so tat's why finally I'm here @Atif. Give a hand of help if ur aware of what's going on . ThanksSmote
@Smote might helpHerald
M
18

What you are trying to do is basically default behavior in Android Wear and its is consider as standard practices in Android Watches to exit an app. In Android wear DismissOverlayView does all heavy lifting for you.

Smartphones have back button while Wear rely on long press or swipe dismiss pattern for to exit screen. You should dismiss Activity on back press, mixing wear pattern in Android Smartphones will make user confused. At Least show a warning dialog to avoid accidental exit.

Solution

As I see this question is tagged with Android Activity I would suggest you to make an Base Activity which will take care of swipe gesture and finish() itself on left to right swipe.

The base activity class should look like this :-

   public abstract class SwipeDismissBaseActivity extends AppCompatActivity {
    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private GestureDetector gestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        gestureDetector = new GestureDetector(new SwipeDetector());
    }

    private class SwipeDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            // Check movement along the Y-axis. If it exceeds SWIPE_MAX_OFF_PATH,
            // then dismiss the swipe.
            if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                return false;

            // Swipe from left to right.
            // The swipe needs to exceed a certain distance (SWIPE_MIN_DISTANCE)
            // and a certain velocity (SWIPE_THRESHOLD_VELOCITY).
            if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                finish();
                return true;
            }

            return false;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TouchEvent dispatcher.
        if (gestureDetector != null) {
            if (gestureDetector.onTouchEvent(ev))
                // If the gestureDetector handles the event, a swipe has been
                // executed and no more needs to be done.
                return true;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
}

Now you can make other activities extend this base Activity and they will Inheritance will automatically make them adopt swipe to dismiss behavior .

public class SomeActivity extends SwipeDismissBaseActivity {

Advantages of this way

  • Purely OOPS approach
  • Clean code - no need to write swipe listener in each type of layout used in project (Relative,Linear etc)
  • Work perfectly in ScrollView
Madra answered 18/7, 2016 at 16:47 Comment(3)
Works perfectly fine with fling. Any idea how to drag activity along with the finger and dismiss?Maritime
@hitesh-sahu can you please answer the question asked by Seshu, I need that too.Finalize
@FarooqKhan how did you achieve this?Mexicali
K
2

Try this I am using Function for swipe like that

View to swipe...

 yourview.setOnTouchListener(new SimpleGestureFilter(this)); // yourview is layout or container to swipe

SimpleGestureFilter class

  public class SimpleGestureFilter implements View.OnTouchListener {

        static final String logTag = "ActivitySwipeDetector";
        private Context activity;
        static final int MIN_DISTANCE = 100;// TODO change this runtime based on screen resolution. for 1920x1080 is to small the 100 distance
        private float downX, downY, upX, upY;

        // private NDAAgreementActivity mMainActivity;

        public SimpleGestureFilter(Context mainActivity) {
            activity = mainActivity;
        }

        public void onRightToLeftSwipe() {


          //do your code to right to left



        }

        public void onLeftToRightSwipe() {
            //do your code to left to  right
        }

        public void onTopToBottomSwipe() {

        }

        public void onBottomToTopSwipe() {

        }

        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    downX = event.getX();
                    downY = event.getY();
                    return true;
                }
                case MotionEvent.ACTION_UP: {
                    upX = event.getX();
                    upY = event.getY();

                    float deltaX = downX - upX;
                    float deltaY = downY - upY;

                    // swipe horizontal?
                    if (Math.abs(deltaX) > MIN_DISTANCE) {
                        // left or right
                        if (deltaX < 0) {
                            this.onLeftToRightSwipe();
                            return true;
                        }
                        if (deltaX > 0) {
                            this.onRightToLeftSwipe();
                            return true;
                        }
                    } else {
                        Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long horizontally, need at least " + MIN_DISTANCE);
                        // return false; // We don't consume the event
                    }

                    // swipe vertical?
                    if (Math.abs(deltaY) > MIN_DISTANCE) {
                        // top or down
                        if (deltaY < 0) {
                            this.onTopToBottomSwipe();
                            return true;
                        }
                        if (deltaY > 0) {
                            this.onBottomToTopSwipe();
                            return true;
                        }
                    } else {
                        Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long vertically, need at least " + MIN_DISTANCE);
                        // return false; // We don't consume the event
                    }

                    return false; // no swipe horizontally and no swipe vertically
                }// case MotionEvent.ACTION_UP:
            }
            return false;
        }
    }
Kirby answered 15/7, 2016 at 5:58 Comment(2)
This isn't working well if I had set click event to any of the child.Smote
you should check the child area within you swipe..... check the area match parent and match height than check it....Kirby
A
1

I have faced the same problem regarding the Activity Overlay. This worked for me

1) Define you SwipeListener

public class SwipeListener implements View.OnTouchListener {

private SwipeListenerInterface activity;
private float downX, downY, upX, upY;

public SwipeListener(SwipeListenerInterface activity) {
    this.activity = activity;
}

public void onRightToLeftSwipe(View v) {
    Log.i(logTag, "RightToLeftSwipe!");
    activity.onRightToLeftSwipe(v);
}

public void onLeftToRightSwipe(View v) {
    Log.i(logTag, "LeftToRightSwipe!");
    activity.onLeftToRightSwipe(v);
}

public void onTopToBottomSwipe(View v) {
    Log.i(logTag, "TopToBottomSwipe!");
    activity.onTopToBottomSwipe(v);
}

public void onBottomToTopSwipe(View v) {
    Log.i(logTag, "BottomToTopSwipe!");
    activity.onBottomToTopSwipe(v);
}

public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            downX = event.getX();
            downY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_UP: {
            upX = event.getX();
            upY = event.getY();
            float deltaX = downX - upX;
            float deltaY = downY - upY;

                if (deltaX < 0 ) {
                    this.onLeftToRightSwipe(v);
                    return true;
                }
                if (deltaX > 0 ) {
                    this.onRightToLeftSwipe(v);
                    return true;
                }

                if (deltaY < 0) {
                    this.onTopToBottomSwipe(v);
                    return true;
                }
                if (deltaY > 0) {
                    this.onBottomToTopSwipe(v);
                    return true;
                }

        }
    }
    return false;
}

public void setSwipeRestrictions(int swipeRestrictionX, int swipeRestrictionY) {
    this.swipeRestrictionX = swipeRestrictionX;
    this.swipeRestrictionY = swipeRestrictionY;
}

2) With the following referred interface

    public interface SwipeListenerInterface {

    void onRightToLeftSwipe(View v);

    void onLeftToRightSwipe(View v);

    void onTopToBottomSwipe(View v);

    void onBottomToTopSwipe(View v);
}

3) Create the object and bind it to your overlayView (make sure to adjust the interface to the overLay view so it can receive the callbacks)

    sl = new SwipeListener(this);
    overlayView.setOnTouchListener(sl);
Ahmadahmar answered 21/7, 2016 at 16:26 Comment(0)
P
1

I believe the issue you have with the RecyclerView and ScrollView has to do with the child elements obtaining the focus before the parents. you can try to set the android:descendantFocusability="beforeDescendants" for the Recycler/Scroll views.

Paymaster answered 24/7, 2016 at 10:11 Comment(0)
D
0

SwipeBack is an android library is for Activities to do pretty the same as the android "back-button" will do, but in a really intuitive way by using a swipe gesture.

Get it from maven central

compile 'com.hannesdorfmann:swipeback:1.0.4'

Create a base activity and write down the method

 public void initDrawerSwipe(int layoutId) {
        SwipeBack.attach(this, Position.LEFT)
                .setContentView(layoutId)
                .setSwipeBackView(R.layout.layout_swipe_back)
                .setSwipeBackTransformer(new SlideSwipeBackTransformer() {
                    @Override
                    public void onSwipeBackCompleted(SwipeBack swipeBack, Activity activity) {
                        supportFinishAfterTransition();
                    }
                });
    }

Thereafter pass your layout id to the method that placed in your base activity

   @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initDrawerSwipe(R.layout.activity_stylists);
     }

This works well in all scenario that you had point out in your query.

Decomposer answered 28/7, 2016 at 8:38 Comment(0)
P
0

Kotlin with SwipeDismissBehavior. Refresh and swipe back to close.

Using androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01 for refresh. Enjoy!

class SwipeActivity : AppCompatActivity() {

private lateinit var binding: ActivitySilentSalesmenBinding
private lateinit var swipe: SwipeDismissBehavior<View>

@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    swipe = SwipeDismissBehavior()
    swipe.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END)

    swipe.listener = object : SwipeDismissBehavior.OnDismissListener {
        override fun onDismiss(view: View) {
            //......
        }

        override fun onDragStateChanged(state: Int) {
            finish()
        }
    }

    binding = ActivitySwipeBinding.inflate(layoutInflater)
    setContentView(binding.root)

    supportActionBar?.setDisplayHomeAsUpEnabled(true)

    binding.swiperefresh.setOnRefreshListener {
        Log.v(TAG, "Refreshing...")
        Timer().schedule(1000) {
            binding.swiperefresh.isRefreshing = false
        }
    }
}

override fun onPostCreate(savedInstanceState: Bundle?) {
    super.onPostCreate(savedInstanceState)
}

companion object {
    const val TAG = "SwipeActivity"
}
Plumbaginaceous answered 16/1 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.