Android launcher 上滑进入allapps和recentUI

时间:2021/01/21
之前公司不允许csdn,笔记写在其它地方。最近整理过来

背景描述

Q和R差异上滑进入allapps和recentUI手势有了名下区别,本片文章主要分析recentUI界面进入退出时机。

现象对比

AndroidQAndroidR
1、连续上滑进入allapps1、只有最下方上滑滑出recentUI
2、上滑中间停顿,呼出recentUI peek,松手进入recentUI2、屏幕中间上滑不会再呼出recentUI peek界面
3、继续上滑到顶部,recentUI peek退出,松手进入allapps

更加清晰的区分出了2个界面的启动时机

AndroidQ流程分析

触摸事件中,根据时机调用
这个流程中所有的触摸处理从SwipeDetector.onTouchEvent分发下去,FlingAndHoldTouchController具体处理和做动画。

伪流程图
SwipeDetector.Java FlingAndHoldTouchController.Java
onTouchEvent–>setState–>reportDragStart/reportDragEnd

reportDragStart–>onDragStart:设置停顿监听,滑动停顿时,进入退出recentUI的peek动画(动画实现在FlingAndHoldTouchController.onDragStart)
onTouchEvent.move中–>reportDragging–>onDrag: 持续判断手势是否在可停顿范围内,是否可停顿,修改标志位
reportDragEnd–>onDragEnd:如果标志位是停顿状态,启动进入recentUI动画

Q的代码逻辑
com/android/launcher3/touch/SwipeDetector.java
onTouchEvent中
对手指触摸进行处理,
case MotionEvent.ACTION_MOVE:
    int pointerIndex = ev.findPointerIndex(mActivePointerId);
    if (pointerIndex == INVALID_POINTER_ID) {
        break;
    }
    //获取手在Y轴上的位移
    //具体实现在com/android/launcher3/touch/SwipeDetector.java中,分别针对横屏和竖屏计算
    mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
    if (TestProtocol.sDebugTracing) {
        Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 1");
    }

    // handle state and listener calls.
    if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
        if (TestProtocol.sDebugTracing) {
            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 2");
        }
        setState(ScrollState.DRAGGING);
    }
    if (mState == ScrollState.DRAGGING) {
        reportDragging(ev);
    }
    mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
    break;

com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java

//设置运动暂停监听,呼入呼出peek动画
@Override
public void onDragStart(boolean start) {
    mMotionPauseDetector.clear();

    super.onDragStart(start);

    if (handlingOverviewAnim()) {
        mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
            RecentsView recentsView = mLauncher.getOverviewPanel();
            recentsView.setOverviewStateEnabled(isPaused);
            if (mPeekAnim != null) {
                mPeekAnim.cancel();
            }
            LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
            LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
            long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
            mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
                    new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration);
            mPeekAnim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mPeekAnim = null;
                }
            });
            mPeekAnim.start();
            recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
        });
    }
}
//动态判断手势
@Override
public boolean onDrag(float displacement, MotionEvent event) {
    float upDisplacement = -displacement;

    //判断当前点的位置是否允许暂停,只有在合理范围内暂停才会进入recentUI界面
    //这2个坐标点,判断上滑的位置 进入和退出recentUI界面前提动画--动画1
    //如果在这2个数值之间停顿、松手,就会进入recentUI界面--动画2
    mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement
            || upDisplacement > mMotionPauseMaxDisplacement);
    //判断是否暂停
    mMotionPauseDetector.addPosition(displacement, event.getEventTime());
    return super.onDrag(displacement, event);
}

//运动判断相关
com/android/quickstep/util/MotionPauseDetector.java
addPosition
checkMotionPaused
updatePaused
//回调监听呼出peek动画
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);// listener实现在FlingAndHoldTouchController中

//松手后判断是否进入recentUI
@Override
public void onDragEnd(float velocity, boolean fling) {
    if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
        if (mPeekAnim != null) {
            mPeekAnim.cancel();
        }

        AnimatorSetBuilder builder = new AnimatorSetBuilder();
        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
        if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
            builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
            builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
        }
        //呼出recentUI之后松手进入recentUI界面动画
        AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
                NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
        overviewAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
            }
        });
        overviewAnim.start();
    } else {
        super.onDragEnd(velocity, fling);
    }
    mMotionPauseDetector.clear();
}

AndroidR流程分析

大体流程和Q相同,但是R上多了下面一个类:
NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController
继承并重写关键方法,实现进入overview动画
packages/apps/Launcher3/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java

@Override
protected boolean canInterceptTouch(MotionEvent ev) {
    //判断是否从边缘触发Touch事件
    mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
    Log.d(TestProtocol.PAUSE_NOT_DETECTED,"ev.getEdgeFlags(): "+ev.getEdgeFlags()+" EDGE_NAV_BAR: "+EDGE_NAV_BAR);
    return super.canInterceptTouch(ev);
}

@Override
protected boolean handlingOverviewAnim() {
    return mDidTouchStartInNavBar && super.handlingOverviewAnim();
}

@Override
protected void onMotionPauseChanged(boolean isPaused) {
    if (mCurrentAnimation == null) {
        return;
    }
    mNormalToHintOverviewScrimAnimator = null;
    mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
        mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
            mReachedOverview = true;
            maybeSwipeInteractionToOverviewComplete();
        });
    });
    VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
...未完待续

剩余部分逻辑参考一下https://blog.csdn.net/a396604593/article/details/127965396

ViewConfiguration.getScaledTouchSlop//;触发移动事件的最小距离,自定义View处理touch事件的时候,有的时候需要判断用户是否真的存在movie,系统提供了这样的方法。表示滑动的时候,手的移动要大于这个返回的距离值才开始移动控件
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值