时间:2021/01/21
之前公司不允许csdn,笔记写在其它地方。最近整理过来
Q和R差异上滑进入allapps和recentUI手势有了名下区别,本片文章主要分析recentUI界面进入退出时机。
AndroidQ | AndroidR |
---|---|
1、连续上滑进入allapps | 1、只有最下方上滑滑出recentUI |
2、上滑中间停顿,呼出recentUI peek,松手进入recentUI | 2、屏幕中间上滑不会再呼出recentUI peek界面 |
3、继续上滑到顶部,recentUI peek退出,松手进入allapps |
更加清晰的区分出了2个界面的启动时机
触摸事件中,根据时机调用
这个流程中所有的触摸处理从SwipeDetector.onTouchEvent分发下去,FlingAndHoldTouchController具体处理和做动画。
伪流程图
SwipeDetector.Java FlingAndHoldTouchController.Java
onTouchEvent–>setState–>reportDragStart/reportDragEnd
reportDragStart–>onDragStart:设置停顿监听,滑动停顿时,进入退出recentUI的peek动画(动画实现在FlingAndHoldTouchController.onDragStart)
onTouchEvent.move中–>reportDragging–>onDrag: 持续判断手势是否在可停顿范围内,是否可停顿,修改标志位
reportDragEnd–>onDragEnd:如果标志位是停顿状态,启动进入recentUI动画
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();
}
大体流程和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,系统提供了这样的方法。表示滑动的时候,手的移动要大于这个返回的距离值才开始移动控件