android手势返回操作检测

近期项目由于没有预留硬件返回按键,所以只能做成跟全面屏一样的左滑返回的手势逻辑。
实现方案:
1.参考android官方文档,https://developer.android.google.cn/training/gestures,针对apk层面的监听可以用,这样子做就需要所有的apk页面都需要监听,如果是自己设计的apk还可以,但是第三方apk则不能监听,无法实现返回。

framework/base\core\java\android\view\GestureDetector.java中接口监听
 public class MainActivity extends Activity implements
            GestureDetector.OnGestureListener,
            GestureDetector.OnDoubleTapListener{

        private static final String DEBUG_TAG = "Gestures";
        private GestureDetectorCompat mDetector;

        // Called when the activity is first created.
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // Instantiate the gesture detector with the
            // application context and an implementation of
            // GestureDetector.OnGestureListener
            mDetector = new GestureDetectorCompat(this,this);
            // Set the gesture detector as the double tap
            // listener.
            mDetector.setOnDoubleTapListener(this);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event){
            if (this.mDetector.onTouchEvent(event)) {
                return true;
            }
            return super.onTouchEvent(event);
        }

        @Override
        public boolean onDown(MotionEvent event) {
            Log.d(DEBUG_TAG,"onDown: " + event.toString());
            return true;
        }

        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2,
                float velocityX, float velocityY) {
            Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
            return true;
        }

        @Override
        public void onLongPress(MotionEvent event) {
            Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
        }

        @Override
        public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
                float distanceY) {
            Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
            return true;
        }

        @Override
        public void onShowPress(MotionEvent event) {
            Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
        }

        @Override
        public boolean onSingleTapUp(MotionEvent event) {
            Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
            return true;
        }

        @Override
        public boolean onDoubleTap(MotionEvent event) {
            Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
            return true;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent event) {
            Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent event) {
            Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
            return true;
        }
    }

2.想要第三方也能监听按键的back按键以及退出,则必须要把手势的逻辑做在系统层。
想到系统层的导航栏上下滑隐藏的逻辑,在framework\base\services\core\java\com\android\server\policy\PhoneWindowManager.java中ini有手势检测的逻辑。

  mSystemGestures = new SystemGesturesPointerEventListener(context,
            new SystemGesturesPointerEventListener.Callbacks() {
                @Override
				/*上滑*/
                public void onSwipeFromTop() {
                    if (mStatusBar != null) {
                        requestTransientBars(mStatusBar);
                    }
                }
				
				/*下滑*/
                @Override
                public void onSwipeFromBottom() {
                    if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) {
                        requestTransientBars(mNavigationBar);
                    }
                }
                
                /*右滑*/
                @Override
                public void onSwipeFromRight() {
                    if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_RIGHT) {
                        requestTransientBars(mNavigationBar);
                    }
                }
                
				/*左滑*/
                @Override
                public void onSwipeFromLeft() {
                	/*导航栏的相关逻辑**/
                    if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_LEFT) {
                        requestTransientBars(mNavigationBar);
                    }
                    //左滑发送返回按键
                    sendKeyCode(KeyEvent.KEYCODE_BACK);
                }
                @Override
                public void onFling(int duration) {
                    if (mPowerManagerInternal != null) {
                        mPowerManagerInternal.powerHint(
                                PowerHint.INTERACTION, duration);
                    }
                }
                @Override
                public void onDebug() {
                    // no-op
                }
                @Override
                public void onDown() {
                    mOrientationListener.onTouchStart();
                }
                @Override
                public void onUpOrCancel() {
                    mOrientationListener.onTouchEnd();
                }
                @Override
                public void onMouseHoverAtTop() {
                    mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                    Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
                    msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
                    mHandler.sendMessageDelayed(msg, 500);
                }
                @Override
                public void onMouseHoverAtBottom() {
                    mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                    Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
                    msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
                    mHandler.sendMessageDelayed(msg, 500);
                }
                @Override
                public void onMouseLeaveFromEdge() {
                    mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
                }
            });
    mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
	//手势的回调监听注册
    mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);

发送按键的相关逻辑实现如下,主要是参考网上模拟按键的按下,Instrumentation的sendKeyDownUpSync最终会调用input相关接口进行模拟按键的按下与松开。

/**
 * Simulate key down and up 
 */
private void sendKeyCode(final int keyCode) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
            Slog.d(TAG,"sendKeyCode by Instrumentation,keyCode="+keyCode);
            Instrumentation inst = new Instrumentation();
            inst.sendKeyDownUpSync(keyCode);//模拟按键的按下与松开
        } catch (Exception e) {
            e.printStackTrace();
        }
      }
    }).start();
  }

最后发现左滑无法触发onSwipeFromLeft的回调,查阅代码发现要满足一定左滑位置以及距离才能触发


/*检测手势的滑动距离*/
  private int detectSwipe(int i, long time, float x, float y) {
        final float fromX = mDownX[i];
        final float fromY = mDownY[i];
        final long elapsed = time - mDownTime[i];
        if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
                + " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
        if (fromY <= mSwipeStartThreshold
                && y > fromY + mSwipeDistanceThreshold
                && elapsed < SWIPE_TIMEOUT_MS) {
            return SWIPE_FROM_TOP;
        }
        if (fromY >= screenHeight - mSwipeStartThreshold
                && y < fromY - mSwipeDistanceThreshold
                && elapsed < SWIPE_TIMEOUT_MS) {
            return SWIPE_FROM_BOTTOM;
        }
        if (fromX >= screenWidth - mSwipeStartThreshold
                && x < fromX - mSwipeDistanceThreshold
                && elapsed < SWIPE_TIMEOUT_MS) {
            return SWIPE_FROM_RIGHT;
        }
        /*左滑的逻辑*/
        if (fromX <= mSwipeStartThreshold
                && x > fromX + mSwipeDistanceThreshold
                && elapsed < SWIPE_TIMEOUT_MS) {
            return SWIPE_FROM_LEFT;
        }
        return SWIPE_NONE;
    }

左滑成立要满足一下条件:
(1)起点x坐标<mSwipeStartThreshold
(2)终点的x坐标>(起点x坐标+mSwipeStartThreshold)
(3)elapsed < SWIPE_TIMEOUT_MS表示滑动速度要在SWIPE_TIMEOUT_MS内

其中mSwipeStartThreshold是初始化的时候com.android.internal.R.dimen.status_bar_height获取的配置项

  public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) {
        mContext = context;
        mCallbacks = checkNull("callbacks", callbacks);
        mSwipeStartThreshold = checkNull("context", context).getResources()
                .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        mSwipeDistanceThreshold = mSwipeStartThreshold;
        if (DEBUG) Slog.d(TAG,  "mSwipeStartThreshold=" + mSwipeStartThreshold
                + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
    }

./frameworks/base/core/res/res/values/dimens.xml

<dimen name="status_bar_height">0dp</dimen>

理论上每次从左侧边界滑动的时候x起点坐标应该是0,但是由于触摸板上报的坐标实际上根本无法上报x轴为0的点,基本上都是坐标点为1才可以上报,而且一般边界的坐标点都会为1-10以内的随机点,结合手的大小以及触摸板的精度,因为触摸板很少能做到这么精准的点触的。所以修改代码如下,再次编译就ok了

<dimen name="status_bar_height">12dp</dimen>
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值