IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 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

@Override
protected 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

@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进行设置的,集合里面包含了若干个一起动画,其中包含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);
        }
    }
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-08 19:10:08  更:2022-06-08 19:10:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 7:44:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码