android P/Q/R/S 9/10/11/12多任務(wù)手勢動畫OverviewInputConsumer情況
android P/Q/R/S 9/10/11/12多任務(wù)手勢動畫OverviewInputConsumer情況
hi,多任務(wù)手勢分析了OtherActivity的情況,這一節(jié)來分析一下在桌面本身就是前臺情況下,進(jìn)入多任務(wù)的源碼及情況分析。 首先來看看原生aosp上多任務(wù)的2個過程:
重點現(xiàn)象部分:
ps需要學(xué)習(xí)深入framework課程和課程優(yōu)惠新課程優(yōu)惠獲取請加入qq群:422901085(獲取demo源碼)
1、手指慢慢滑動,workspace整體也跟著慢慢滑動
這個過程就是我們還處于手指底部上劃過程,這個還是用個自己繪制的圖好展示一些:
即手指底部上劃過程中會有workspace上整體也會慢慢上劃
2、上劃到一定臨界值時候,直接有個進(jìn)入多任務(wù)的動畫過程
這個過程相對比較簡單好理解,就一個多任務(wù)界面不需要額外補(bǔ)充圖片解釋了
重點源碼分析部分:
1、workspace慢慢上劃過程
首先滑動其實全局的觸摸監(jiān)聽器監(jiān)聽了觸摸事件,這個情況下是OverviewInputConsumer來進(jìn)行觸摸監(jiān)聽的 具體路徑在Launcher代碼的如下類中: com/android/quickstep/inputconsumers/OverviewInputConsumer.java
?public?OverviewInputConsumer(T?activity,??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是關(guān)鍵的處理點
????????????mTarget.getLocationOnScreen(mLocationOnScreen);
????????????mProxyTouch?=?mTarget.prepareProxyEventStarting();
????????}
????}
????
????public?int?getType()?{
????????return?TYPE_OVERVIEW;
????}
????
????public?boolean?allowInterceptByParent()?{
????????return?!mTargetHandledTouch;
????}
????
????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);//這里最后會觸發(fā)調(diào)用到這里proxyTouchEvent
????????ev.offsetLocation(mLocationOnScreen[0],?mLocationOnScreen[1]);
????????ev.setEdgeFlags(flags);
???//省略
????}
//省略
}
這里其實onMotionEvent方法被調(diào)用后,其實調(diào)用的是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,所以考慮進(jìn)入它的處理,但是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);//這里處理是關(guān)鍵
????????return?mDetector.isDraggingOrSettling();//返回是否已經(jīng)處于DRAGGING狀態(tài)了
????}
????
???????@Override
????public?final?boolean?onControllerTouchEvent(MotionEvent?ev)?{
????????return?mDetector.onTouchEvent(ev);//調(diào)用了mDetector的onTouchEvent
????}
最后處理會調(diào)用到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只是做一些初始化相關(guān)及記錄坐標(biāo)工作
????????????????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))?{//開始看看是否已經(jīng)達(dá)到可以觸發(fā)讓處于DRAGGING
????????????????????setState(ScrollState.DRAGGING);
????????????????}
????????????????if?(mState?==?ScrollState.DRAGGING)?{
????????????????????reportDragging(ev);//如果已經(jīng)處于DRAGGING,開始滑動,那就要調(diào)用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);//抬起就變成完成狀態(tài)
????????????????}
????????????????mVelocityTracker.recycle();
????????????????mVelocityTracker?=?null;
????????????????break;
????????????default:
????????????????break;
????????}
????????return?true;
????}
這里來看看重點方法setState:
??private?void?setState(ScrollState?newState)?{
????????if?(newState?==?ScrollState.DRAGGING)?{//設(shè)置為DRAGGING
????????????initializeDragging();
????????????if?(mState?==?ScrollState.IDLE)?{
????????????????reportDragStart(false?/*?recatch?*/);//調(diào)用reportDragStart
????????????}?else?if?(mState?==?ScrollState.SETTLING)?{
????????????????reportDragStart(true?/*?recatch?*/);
????????????}
????????}
????????if?(newState?==?ScrollState.SETTLING)?{
????????????reportDragEnd();//Drag結(jié)束
????????}
????????mState?=?newState;
????}
????private?void?reportDragStart(boolean?recatch)?{
????????reportDragStartInternal(recatch);
????}
reportDragStartInternal實際是一個子類SingleAxisSwipeDetector實現(xiàn)的方法: com/android/launcher3/touch/SingleAxisSwipeDetector.java
@Override
protected?void?reportDragStartInternal(boolean?recatch)?{
????mListener.onDragStart(!recatch);
}
這里有調(diào)到了 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());//開始初始化動畫,這里面比較復(fù)雜,主要就是把Workspace的動畫設(shè)置好
????????????mDisplacementShift?=?0;
????????}?else?{
????????????mCurrentAnimation.pause();
????????????mStartProgress?=?mCurrentAnimation.getProgressFraction();
????????????mAtomicAnimAutoPlayInfo?=?null;
????????????if?(mAtomicComponentsController?!=?null)?{
????????????????mAtomicComponentsController.pause();
????????????}
????????}
????????mCanBlockFling?=?mFromState?==?NORMAL;
????????mFlingBlockCheck.unblockFling();
????}
上面已經(jīng)分析完成了DragStart情況,那么手指滑動過程中呢? 那就又要回到BaseSwipeDetector的onTouchEvent中的reportDragging
?if?(mState?==?ScrollState.DRAGGING)?{
????????????????????reportDragging(ev);
????????????????????//如果已經(jīng)處于DRAGGING,開始滑動,那就要調(diào)用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);//傳遞具體已經(jīng)滑動的距離,最后會轉(zhuǎn)換成progress
????????reportDraggingInternal(sTempPoint,?event);
????}
}
這里有回到了 reportDraggingInternal(sTempPoint, event)方法又是調(diào)用SingleAxisSwipeDetector的 reportDraggingInternal:
?@Override
????protected?void?reportDraggingInternal(PointF?displacement,?MotionEvent?event)?{
????????mListener.onDrag(mDir.extractDirection(displacement),?event);
????}
這里又調(diào)用到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);//最后又會調(diào)用對應(yīng)的重載方法onDrag
????}
?????@Override
????public?boolean?onDrag(float?displacement)?{
????????float?deltaProgress?=?mProgressMultiplier?*?(displacement?-?mDisplacementShift);
????????float?progress?=?deltaProgress?+?mStartProgress;
????????updateProgress(progress);//這里就是來更新進(jìn)度的
????????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進(jìn)行了設(shè)置
????????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最后會調(diào)用到AnimatorPlaybackController的setPlayFraction方法 com/android/launcher3/anim/AnimatorPlaybackController.java
@Override
public?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進(jìn)行設(shè)置的,集合里面包含了若干個一起動畫,其中包含Workspace的移動動畫,這里會進(jìn)行遍歷,然后設(shè)置動畫時間就可以完成對WorkSpace。。 是不是感覺動很妙,如果我們寫代碼是不是肯定這個時候直接調(diào)用ui來設(shè)置View的一些屬性
2、progress到的閾值后啟動進(jìn)入多任務(wù)的動畫 之前分析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是否已經(jīng)到了閾值進(jìn)度了要求
????????if?(passedThreshold?!=?mPassedOverviewAtomicThreshold)?{//到達(dá)進(jìn)度
????????????LauncherState?atomicFromState?=?passedThreshold???fromState:?toState;
????????????LauncherState?atomicToState?=?passedThreshold???toState?:?fromState;
????????????mPassedOverviewAtomicThreshold?=?passedThreshold;
????????????if?(mAtomicAnim?!=?null)?{
????????????????mAtomicAnim.cancel();
????????????}
????????????mAtomicAnim?=?createAtomicAnimForState(atomicFromState,?atomicToState,?ATOMIC_DURATION);//創(chuàng)建原子動畫,從NORMAL?--》OVERVIEW
???????????//省略部分
????????????mAtomicAnim.start();//開啟原子動畫
????????????mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
????????}
????}