赞
踩
最近做一个功能,由于我们设备上,没有功能键,所以需要实现一个功能,不管在设备上的哪个应用里,双指长按,就必须返回Launcher界面。
刚开始接这个需求,一脸懵逼,我去,这…之前都是在一个App里跳转来跳转去的,这可咋整,能咋整,不会写,那抄呗,网上一顿搜,搜到的资料很少,突然想到,全局手势返回桌面,那去安卓原生的全局手势代码那块瞅瞅呗
///WorkSpaces/LA.UM.9.15/LINUX/android/frameworks/base/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java //全局手势核心代码 @Override public void onPointerEvent(MotionEvent event) { if (mGestureDetector != null && event.isTouchEvent()) { mGestureDetector.onTouchEvent(event); } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mSwipeFireable = true; mDebugFireable = true; mDownPointers = 0; captureDown(event, 0); if (mMouseHoveringAtEdge) { mMouseHoveringAtEdge = false; mCallbacks.onMouseLeaveFromEdge(); } mCallbacks.onDown(); break; case MotionEvent.ACTION_POINTER_DOWN: captureDown(event, event.getActionIndex()); if (mDebugFireable) { mDebugFireable = event.getPointerCount() < 5; if (!mDebugFireable) { if (DEBUG) Slog.d(TAG, "Firing debug"); mCallbacks.onDebug(); } } break; case MotionEvent.ACTION_MOVE: if (mSwipeFireable) { final int swipe = detectSwipe(event); mSwipeFireable = swipe == SWIPE_NONE; if (swipe == SWIPE_FROM_TOP) { if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop"); mCallbacks.onSwipeFromTop(); } else if (swipe == SWIPE_FROM_BOTTOM) { if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom"); mCallbacks.onSwipeFromBottom(); } else if (swipe == SWIPE_FROM_RIGHT) { if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight"); mCallbacks.onSwipeFromRight(); } else if (swipe == SWIPE_FROM_LEFT) { if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft"); mCallbacks.onSwipeFromLeft(); } } break; case MotionEvent.ACTION_HOVER_MOVE: if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (!mMouseHoveringAtEdge && event.getY() == 0) { mCallbacks.onMouseHoverAtTop(); mMouseHoveringAtEdge = true; } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) { mCallbacks.onMouseHoverAtBottom(); mMouseHoveringAtEdge = true; } else if (mMouseHoveringAtEdge && (event.getY() > 0 && event.getY() < screenHeight - 1)) { mCallbacks.onMouseLeaveFromEdge(); mMouseHoveringAtEdge = false; } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mSwipeFireable = false; mDebugFireable = false; mCallbacks.onUpOrCancel(); break; default: if (DEBUG) Slog.d(TAG, "Ignoring " + event); } }
在这里,我们看到四个回调,就是我们常见的四个全局手势
interface Callbacks {
void onSwipeFromTop();
void onSwipeFromBottom();
void onSwipeFromRight();
void onSwipeFromLeft();
}
然后我们可以在这里看到他们的实现
//frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java DisplayPolicy(WindowManagerService service, DisplayContent displayContent) { //................. mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler, new SystemGesturesPointerEventListener.Callbacks() { @Override public void onSwipeFromTop() { synchronized (mLock) { if (mStatusBar != null) { requestTransientBars(mStatusBar); } } } @Override public void onSwipeFromBottom() { synchronized (mLock) { if (mNavigationBar != null && mNavigationBarPosition == NAV_BAR_BOTTOM) { requestTransientBars(mNavigationBar); } } } @Override public void onSwipeFromRight() { final Region excludedRegion = Region.obtain(); synchronized (mLock) { mDisplayContent.calculateSystemGestureExclusion( excludedRegion, null /* outUnrestricted */); final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture || mNavigationBarPosition == NAV_BAR_RIGHT; if (mNavigationBar != null && sideAllowed && !mSystemGestures.currentGestureStartedInRegion( excludedRegion)) { requestTransientBars(mNavigationBar); } } excludedRegion.recycle(); } @Override public void onSwipeFromLeft() { final Region excludedRegion = Region.obtain(); synchronized (mLock) { mDisplayContent.calculateSystemGestureExclusion( excludedRegion, null /* outUnrestricted */); final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture || mNavigationBarPosition == NAV_BAR_LEFT; if (mNavigationBar != null && sideAllowed && !mSystemGestures.currentGestureStartedInRegion( excludedRegion)) { requestTransientBars(mNavigationBar); } } excludedRegion.recycle(); } @Override public void onFling(int duration) { if (mService.mPowerManagerInternal != null) { mService.mPowerManagerInternal.powerHint( PowerHint.INTERACTION, duration); } } }); //................... }
这里是对他们的具体实现的地方,所以如果我们的需求是上划返回桌面,那么只需在onSwipeFromBottom做返回操作就可以了。不过我们的需求是双指长按…
不对,我们刚才好像看到啥了,回过头去看看
///WorkSpaces/LA.UM.9.15/LINUX/android/frameworks/base/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java @Override public void onPointerEvent(MotionEvent event) { if (mGestureDetector != null && event.isTouchEvent()) { mGestureDetector.onTouchEvent(event); } //....................... } //继续往下看 public void systemReady() { // GestureDetector records statistics about gesture classification events to inform gesture // usage trends. SystemGesturesPointerEventListener creates a lot of noise in these // statistics because it passes every touch event though a GestureDetector. By creating an // anonymous subclass of GestureDetector, these statistics will be recorded with a unique // source name that can be filtered. // GestureDetector would get a ViewConfiguration instance by context, that may also // create a new WindowManagerImpl for the new display, and lock WindowManagerGlobal // temporarily in the constructor that would make a deadlock. mHandler.post(() -> { final int displayId = mContext.getDisplayId(); final DisplayInfo info = DisplayManagerGlobal.getInstance().getDisplayInfo(displayId); if (info == null) { // Display already removed, stop here. Slog.w(TAG, "Cannot create GestureDetector, display removed:" + displayId); return; } mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), mHandler) { }; }); } private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener { private OverScroller mOverscroller; FlingGestureDetector() { mOverscroller = new OverScroller(mContext); } @Override public boolean onSingleTapUp(MotionEvent e) { if (!mOverscroller.isFinished()) { mOverscroller.forceFinished(true); } return true; } @Override public boolean onFling(MotionEvent down, MotionEvent up, float velocityX, float velocityY) { //............ return true; } } ///frameworks/base/core/java/android/view/GestureDetector.java public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener, OnContextClickListener { public boolean onSingleTapUp(MotionEvent e) { return false; } public void onLongPress(MotionEvent e) { } public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; } public void onShowPress(MotionEvent e) { } public boolean onDown(MotionEvent e) { return false; } public boolean onDoubleTap(MotionEvent e) { return false; } public boolean onDoubleTapEvent(MotionEvent e) { return false; } public boolean onSingleTapConfirmed(MotionEvent e) { return false; } public boolean onContextClick(MotionEvent e) { return false; } }
可以看到底层已经实现好的一些方法比如单机,双击等供我们使用。
灵机一动,那我们在GestureDetector里实现双指长按的逻辑,然后在上面FlingGestureDetector里调用双指长按,在它里面去实现回到桌面的逻辑,不就OK了。
给SimpleOnGestureListener添加双指长按事件,然后在GestureDetector的onTouchEvent方法里,做双指长按事件触发逻辑,大概如下
private void handlePointerDown(MotionEvent ev, Float focusX) { Log.d(TAG, "handlePointerDown"); mDoubleFingerPressStartMs = SystemClock.elapsedRealtime(); if (ev.getPointerCount() != 2 || !isDoubleFingerEvent()) { return; } mDownFocusX = focusX; if (mCurrentPointerDownEvent != null) { mCurrentPointerDownEvent.recycle(); } mCurrentPointerDownEvent = MotionEvent.obtain(ev); mHandler.removeMessages(DOUBLE_FINGER_PRESS); mHandler.sendEmptyMessageAtTime(DOUBLE_FINGER_PRESS, SystemClock.uptimeMillis() + 1000); } private boolean isDoubleFingerEvent() { return mDoubleFingerPressStartMs - mSingleFingerPressStartMs <= 100; } private void handlePointerUp(MotionEvent ev) { Log.d(TAG, "handlePointerUp"); if (ev.getPointerCount() != 2 || !isDoubleFingerEvent()) { return; } mHandler.removeMessages(DOUBLE_FINGER_PRESS); }
而在SystemGesturesPointerEventListener类的FlingGestureDetector这边实现添加的双指长按方法:
@Override
public boolean onDoubleFingerLongPress(MotionEvent e) {
if (DEBUG) Slog.d(TAG, "onDoubleFingerLongPress");
return true;
}
编译出包,刷进去之后,双指长按,发现日志顺利打出来,接下来,我们要考虑如何回到桌面
这个就容易多了,我们home键,或者back键都可以返回,但是back键的话,如果应用进入二级页面,那么就得执行两次双指长按操作,不符合需求。所以肯定考虑home事件。
参考
private void sendKey(int keyCode) { try { int inputSource = KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY | InputDevice.SOURCE_KEYBOARD; long now = SystemClock.uptimeMillis(); boolean istrue = InputManager.getInstance().injectInputEvent( new KeyEvent( now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource ), InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH ); boolean isok = InputManager.getInstance().injectInputEvent( new KeyEvent( now, now, KeyEvent.ACTION_UP, keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource ), InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH ); } catch (Exception e) { e.printStackTrace(); } } //所以上面我们只需要这样调用就可以返回桌面了。 @Override public boolean onDoubleFingerLongPress(MotionEvent e) { if (DEBUG) Slog.d(TAG, "onDoubleFingerLongPress"); sendKey(KeyEvent.KEYCODE_HOME); return true; }
总结: Framework代码,还是多看,看看优秀的人是咋写的
看完之后呢,照猫画虎哈,没事,慢慢就好了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。