> 文档中心 > android P/Q/R/S 9/10/11/12多任务手势动画OverviewInputConsumer情况-第二节

android P/Q/R/S 9/10/11/12多任务手势动画OverviewInputConsumer情况-第二节


android P/Q/R/S 9/10/11/12多任务手势动画OverviewInputConsumer情况-第二节

hi,多任务手势分析了OtherActivity的情况,这一节来分析一下在桌面本身就是前台情况下,进入多任务的源码及情况分析。
首先来看看原生aosp上多任务的2个过程:

重点现象部分:[

[入门课,实战课,跨进程专题,input专题](https://ke.qq.com/course/package/51285?tuin=7d4eb354)
ps需要学习深入framework课程和课程优惠
新课程优惠获取请加入qq群:422901085(获取demo源码)

1、手指慢慢滑动,workspace整体也跟着慢慢滑动

在这里插入图片描述
这个过程就是我们还处于手指底部上划过程,这个还是用个自己绘制的图好展示一些:
在这里插入图片描述
即手指底部上划过程中会有workspace上整体也会慢慢上划

2、上划到一定临界值时候,直接有个进入多任务的动画过程

在这里插入图片描述

这个过程相对比较简单好理解,就一个多任务界面不需要额外补充图片解释了

重点源码分析部分:

1、workspace慢慢上划过程

首先滑动其实全局的触摸监听器监听了触摸事件,这个情况下是OverviewInputConsumer来进行触摸监听的
具体路径在Launcher代码的如下类中:
com/android/quickstep/inputconsumers/OverviewInputConsumer.java

 public OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,     boolean startingInActivityBounds) { mActivity = activity; mInputMonitor = inputMonitor; mStartingInActivityBounds = startingInActivityBounds; mTarget = activity.getDragLayer(); if (startingInActivityBounds) {     mEventReceiver = mTarget::dispatchTouchEvent;     mProxyTouch = true; } else {     // Only proxy touches to controllers if we are starting touch from nav bar.     mEventReceiver = mTarget::proxyTouchEvent;//这里proxyTouchEvent是关键的处理点     mTarget.getLocationOnScreen(mLocationOnScreen);     mProxyTouch = mTarget.prepareProxyEventStarting(); }    }    @Override    public int getType() { return TYPE_OVERVIEW;    }    @Override    public boolean allowInterceptByParent() { return !mTargetHandledTouch;    }    @Override    public void onMotionEvent(MotionEvent ev) { if (!mProxyTouch) {     return; } int flags = ev.getEdgeFlags(); if (!mStartingInActivityBounds) {     ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR); } ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]); boolean handled = mEventReceiver.test(ev);//这里最后会触发调用到这里proxyTouchEvent ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]); ev.setEdgeFlags(flags);   //省略    }//省略}

这里其实onMotionEvent方法被调用后,其实调用的是proxyTouchEvent方法来处理
packages/apps/Trebuchet/src/com/android/launcher3/views/BaseDragLayer.java

    /**     * Proxies the touch events to the gesture handlers     */    public boolean proxyTouchEvent(MotionEvent ev) { boolean handled; if (mProxyTouchController != null) {//刚开始肯定为null     handled = mProxyTouchController.onControllerTouchEvent(ev); } else {     mProxyTouchController = findControllerToHandleTouch(ev);//需要遍历寻找合适TouchController     handled = mProxyTouchController != null; } int action = ev.getAction(); if (action == ACTION_UP || action == ACTION_CANCEL) {     mProxyTouchController = null;     mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY; } return handled;    }//这里来看看findControllerToHandleTouch方法 private TouchController findControllerToHandleTouch(MotionEvent ev) { AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {     return topView; } for (TouchController controller : mControllers) { //遍历看看哪个TouchController会对该事件感兴趣就返回谁     if (controller.onControllerInterceptTouchEvent(ev)) {  return controller;     } } return null;    }

这里我们mControllers实际是在
packages/apps/Trebuchet/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java

public static TouchController[] createTouchControllers(Launcher launcher) {    ArrayList<TouchController> list = new ArrayList<>();    list.add(launcher.getDragController());    if (launcher.getDeviceProfile().isVerticalBarLayout()) { list.add(new LandscapeStatesTouchController(launcher)); list.add(new LandscapeEdgeSwipeController(launcher));    } else { boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)  .getMode().hasGestures; list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));    }    if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE     && !launcher.getDeviceProfile().isMultiWindowMode     && !launcher.getDeviceProfile().isVerticalBarLayout()) { list.add(new StatusBarTouchController(launcher));    }    return list.toArray(new TouchController[list.size()]);}

这里可以看出我们其实会有PortraitStatesTouchController,所以考虑进入它的处理,但是PortraitStatesTouchController本身没有,但是AbstractStateChangeTouchController有处理:
packages/apps/Trebuchet/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java

    @Override    public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) {     mNoIntercept = !canInterceptTouch(ev);     if (mNoIntercept) {  return false;     }     // Now figure out which direction scroll events the controller will start     // calling the callbacks.     final int directionsToDetectScroll;     boolean ignoreSlopWhenSettling = false;     if (mCurrentAnimation != null) {  directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH;  ignoreSlopWhenSettling = true;     } else {  directionsToDetectScroll = getSwipeDirection();  if (directionsToDetectScroll == 0) {      mNoIntercept = true;      return false;  }     }     mDetector.setDetectableScrollConditions(      directionsToDetectScroll, ignoreSlopWhenSettling); } if (mNoIntercept) {     return false; } onControllerTouchEvent(ev);//这里处理是关键 return mDetector.isDraggingOrSettling();//返回是否已经处于DRAGGING状态了    }    @Override    public final boolean onControllerTouchEvent(MotionEvent ev) { return mDetector.onTouchEvent(ev);//调用了mDetector的onTouchEvent    }

最后处理会调用到mDetector.onTouchEvent,这里mDetector就是BaseSwipeDetector:
com/android/launcher3/touch/BaseSwipeDetector.java

这里面的BaseSwipeDetector就是真正触摸事件逻辑重点

   public boolean onTouchEvent(MotionEvent ev) { int actionMasked = ev.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {     mVelocityTracker.clear(); } if (mVelocityTracker == null) {     mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); switch (actionMasked) {     case MotionEvent.ACTION_DOWN://刚开始down只是做一些初始化相关及记录坐标工作  mActivePointerId = ev.getPointerId(0);  mDownPos.set(ev.getX(), ev.getY());  mLastPos.set(mDownPos);  mLastDisplacement.set(0, 0);  mDisplacement.set(0, 0);  if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {      setState(ScrollState.DRAGGING);  }  break;    //省略部分     case MotionEvent.ACTION_MOVE://开始移动  int pointerIndex = ev.findPointerIndex(mActivePointerId);  if (pointerIndex == INVALID_POINTER_ID) {      break;  }  mDisplacement.set(ev.getX(pointerIndex) - mDownPos.x,   ev.getY(pointerIndex) - mDownPos.y);//记录移动点  if (mIsRtl) {      mDisplacement.x = -mDisplacement.x;  }  // handle state and listener calls.  if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {//开始看看是否已经达到可以触发让处于DRAGGING      setState(ScrollState.DRAGGING);  }  if (mState == ScrollState.DRAGGING) {      reportDragging(ev);//如果已经处于DRAGGING,开始滑动,那就要调用reportDragging  }  mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));  break;     case MotionEvent.ACTION_CANCEL:     case MotionEvent.ACTION_UP:  // These are synthetic events and there is no need to update internal values.  if (mState == ScrollState.DRAGGING) {      setState(ScrollState.SETTLING);//抬起就变成完成状态  }  mVelocityTracker.recycle();  mVelocityTracker = null;  break;     default:  break; } return true;    }

这里来看看重点方法setState:

  private void setState(ScrollState newState) { if (newState == ScrollState.DRAGGING) {//设置为DRAGGING     initializeDragging();     if (mState == ScrollState.IDLE) {  reportDragStart(false /* recatch */);//调用reportDragStart     } else if (mState == ScrollState.SETTLING) {  reportDragStart(true /* recatch */);     } } if (newState == ScrollState.SETTLING) {     reportDragEnd();//Drag结束 } mState = newState;    }    private void reportDragStart(boolean recatch) { reportDragStartInternal(recatch);    }

reportDragStartInternal实际是一个子类SingleAxisSwipeDetector实现的方法:
com/android/launcher3/touch/SingleAxisSwipeDetector.java

@Overrideprotected void reportDragStartInternal(boolean recatch) {    mListener.onDragStart(!recatch);}

这里有调到了
com/android/launcher3/touch/AbstractStateChangeTouchController.java

@Override    public void onDragStart(boolean start) { mStartState = mLauncher.getStateManager().getState(); mIsLogContainerSet = false; if (mCurrentAnimation == null) {     mFromState = mStartState;     mToState = null;     cancelAnimationControllers();     reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());//开始初始化动画,这里面比较复杂,主要就是把Workspace的动画设置好     mDisplacementShift = 0; } else {     mCurrentAnimation.pause();     mStartProgress = mCurrentAnimation.getProgressFraction();     mAtomicAnimAutoPlayInfo = null;     if (mAtomicComponentsController != null) {  mAtomicComponentsController.pause();     } } mCanBlockFling = mFromState == NORMAL; mFlingBlockCheck.unblockFling();    }

上面已经分析完成了DragStart情况,那么手指滑动过程中呢?
那就又要回到BaseSwipeDetector的onTouchEvent中的reportDragging

 if (mState == ScrollState.DRAGGING) {      reportDragging(ev);      //如果已经处于DRAGGING,开始滑动,那就要调用reportDragging  }

这里来看

private void reportDragging(MotionEvent event) {    if (mDisplacement != mLastDisplacement) { if (DBG) {     Log.d(TAG, String.format("onDrag disp=%s", mDisplacement)); } mLastDisplacement.set(mDisplacement); sTempPoint.set(mDisplacement.x - mSubtractDisplacement.x,  mDisplacement.y - mSubtractDisplacement.y);//传递具体已经滑动的距离,最后会转换成progress reportDraggingInternal(sTempPoint, event);    }}

这里有回到了 reportDraggingInternal(sTempPoint, event)方法又是调用SingleAxisSwipeDetector的
reportDraggingInternal:

 @Override    protected void reportDraggingInternal(PointF displacement, MotionEvent event) { mListener.onDrag(mDir.extractDirection(displacement), event);    }

这里又调用到com/android/launcher3/touch/AbstractStateChangeTouchController.java

  @Override    public boolean onDrag(float displacement, MotionEvent ev) { if (!mIsLogContainerSet) {     if (mStartState == ALL_APPS) {  mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;     } else if (mStartState == NORMAL) {  mStartContainerType = getLogContainerTypeForNormalState(ev);     } else if (mStartState == OVERVIEW) {  mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;     }     mIsLogContainerSet = true; } return onDrag(displacement);//最后又会调用对应的重载方法onDrag    }     @Override    public boolean onDrag(float displacement) { float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift); float progress = deltaProgress + mStartProgress; updateProgress(progress);//这里就是来更新进度的 boolean isDragTowardPositive = mSwipeDirection.isPositive(  displacement - mDisplacementShift); if (progress <= 0) {     if (reinitCurrentAnimation(false, isDragTowardPositive)) {  mDisplacementShift = displacement;  if (mCanBlockFling) {      mFlingBlockCheck.blockFling();  }     } } else if (progress >= 1) {     if (reinitCurrentAnimation(true, isDragTowardPositive)) {  mDisplacementShift = displacement;  if (mCanBlockFling) {      mFlingBlockCheck.blockFling();  }     } } else {     mFlingBlockCheck.onEvent(); } return true;    }    protected void updateProgress(float fraction) { mCurrentAnimation.setPlayFraction(fraction);//这里其实只是把mCurrentAnimation动画的setPlayFraction进行了设置 if (mAtomicComponentsController != null) {     // Make sure we don't divide by 0, and have at least a small runway.     float start = Math.min(mAtomicComponentsStartProgress, 0.9f);     mAtomicComponentsController.setPlayFraction((fraction - start) / (1 - start)); } maybeUpdateAtomicAnim(mFromState, mToState, fraction);    }

这里的mCurrentAnimation.setPlayFraction最后会调用到AnimatorPlaybackController的setPlayFraction方法
com/android/launcher3/anim/AnimatorPlaybackController.java

@Overridepublic void setPlayFraction(float fraction) {    mCurrentFraction = fraction;    // Let the animator report the progress but don't apply the progress to child    // animations if it has been cancelled.    if (mTargetCancelled) { return;    }    long playPos = clampDuration(fraction);    for (ValueAnimator anim : mChildAnimations) { anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));    }}

这里面就会对这个mChildAnimations动画集合由前面onDragStart里面reinitCurrentAnimation进行设置的,集合里面包含了若干个一起动画,其中包含Workspace的移动动画,这里会进行遍历,然后设置动画时间就可以完成对WorkSpace。。
是不是感觉动很妙,如果我们写代码是不是肯定这个时候直接调用ui来设置View的一些属性

2、progress到的阈值后启动进入多任务的动画
之前分析updateProgress还有一个maybeUpdateAtomicAnim方法

private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,     float progress) { if (!goingBetweenNormalAndOverview(fromState, toState)) {     return; } float threshold = toState == OVERVIEW ? ATOMIC_OVERVIEW_ANIM_THRESHOLD  : 1f - ATOMIC_OVERVIEW_ANIM_THRESHOLD; boolean passedThreshold = progress >= threshold;//判断progress是否已经到了阈值进度了要求 if (passedThreshold != mPassedOverviewAtomicThreshold) {//到达进度     LauncherState atomicFromState = passedThreshold ? fromState: toState;     LauncherState atomicToState = passedThreshold ? toState : fromState;     mPassedOverviewAtomicThreshold = passedThreshold;     if (mAtomicAnim != null) {  mAtomicAnim.cancel();     }     mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);//创建原子动画,从NORMAL --》OVERVIEW    //省略部分     mAtomicAnim.start();//开启原子动画     mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); }    }