在向右滑动时结束活动?

26

当用户在屏幕的任何位置向右滑动时,我必须完成Activity。我尝试使用GestureDetector,如果Activity中没有ScrollViewRescyclerView存在,并且具有onClickListener的视图也不允许检测它们上面的滑动,则可以正常工作。因此,我采用了另一种方法,在程序中将一个视图覆盖到所有布局的顶部,然后尝试在其上检测滑动事件。

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;

    }
}

逻辑是如果在OverlayLayout上执行滑动操作,则拦截触摸事件到其他视图,然后结束Activity。然而,现在我能够检测到在OverlayLayout上的滑动事件,但即使我在onTouchEvent的else条件中使用了return super.onTouchEvent(event);,其他视图也无法响应,就像你在我的代码中看到的那样。请有人帮我解决这个问题。我被卡住了,非常想学习这个技巧 :)

尝试在您的活动中添加以下内容: @Override public boolean dispatchTouchEvent(MotionEvent ev){ super.dispatchTouchEvent(ev);
return productGestureDetector.onTouchEvent(ev); }
- Prashanth Debbadwar
@Stella,你尝试过在Activity中使用dispatchTouchEvent吗? - Nisarg
尝试搜索任何类型布局中控制手势的touchIntercept方法。 - Mohammed Atif
我在Activity中尝试了dispatchTouchEvent,但似乎没有帮助我解决问题。我尝试了所有的搜索,但都无法解决问题,所以最终来到这里向@Atif求助。如果您知道发生了什么,请给予帮助之手。谢谢。 - Stella
@Stella可能会帮忙 - Nisarg
5个回答

18
你试图做的基本上是在 Android Wear 中的默认行为,这在 Android 手表 中被视为标准实践来退出应用程序。在 Android Wear 中,DismissOverlayView 会为您处理所有繁重的工作。
智能手机有返回按钮,而手表则依赖长按或滑动手势以退出屏幕。您应该在后退按下时关闭活动(Activity),将 Wear 模式与 Android 智能手机混合使用会使用户感到困惑。至少显示一个警告对话框以避免意外退出。 解决方法 由于我看到这个问题标记了 Android Activity,我建议您创建一个 Base Activity 来处理滑动手势,并在从左到右滑动时 finish() 它本身。
基础活动类应该像这样:
   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);
    }
}

现在您可以让其他活动扩展这个基本活动,它们将继承并自动采用滑动取消行为。

public class SomeActivity extends SwipeDismissBaseActivity {

这种方法的优点

  • 纯面向对象编程(OOPS)方式
  • 代码简洁 - 无需在项目中使用每种布局类型(Relative, Linear等)中编写滑动监听器(swipe listener)
  • 在ScrollView中完美工作

4
可以完美地与“fling”一起使用。有没有想法如何随手指拖动活动并将其解除? - Seshu Vinay
1
{btsdaf} - Farooq Khan
@FarooqKhan,你是怎么做到的? - Arnold Brown

2

尝试这个,我正在使用函数实现滑动:

要滑动的视图...

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

SimpleGestureFilter 类

  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;
        }
    }

如果我将点击事件设置为任何子元素,这个功能就无法正常工作。 - Stella
你应该在滑动时检查子区域......检查区域是否与父级匹配并且高度匹配。 - Arjun saini

1
我认为您在使用RecyclerView和ScrollView时遇到的问题与子元素先于父元素获得焦点有关。您可以尝试为Recycler/Scroll视图设置android:descendantFocusability="beforeDescendants"

1
我曾经遇到过与Activity覆盖层相关的问题。这个方法对我有用:
1)定义您的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)使用以下提到的接口。
    public interface SwipeListenerInterface {

    void onRightToLeftSwipe(View v);

    void onLeftToRightSwipe(View v);

    void onTopToBottomSwipe(View v);

    void onBottomToTopSwipe(View v);
}

3) 创建对象并将其绑定到您的overlayView(请确保调整接口以使其能够接收回调)

    sl = new SwipeListener(this);
    overlayView.setOnTouchListener(sl);

0

SwipeBack 是一个 Android 库,用于让 Activities 像 Android 的“返回按钮”一样工作,但是通过使用滑动手势以非常直观的方式完成。

从 Maven 中央获取它

compile 'com.hannesdorfmann:swipeback:1.0.4'

创建一个基本的活动并编写方法

 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();
                    }
                });
    }

然后将您的布局ID传递给放置在基本活动中的方法

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

在你的查询中提到的所有场景下,这都能很好地工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接