分析整个分屏的流程-2

分析整个分屏的流程-2

在这里插入图片描述

分屏总览

1.分屏模式是通过长按最近任务列表(RecetsActivity)的任一个历史应用(TaskTiew)进入的,如果该应用不支持分屏就提示用户,如果可以分屏就显示可以分屏的区域,之后拖拽想要分屏的TaskView,在拖拽的过程中判断touch事件移动的位置是否进入了分屏区域,如果没有继续处理touch事件,如果进入了分屏区域,就会更新屏幕区域分屏,此时结束拖拽。
2.调用AMS为分屏区域创建stack,根据屏幕尺寸计算stack的尺寸,然后对stack中task再重新计算尺寸,最后启动分屏应用。

请求分屏

最近任务列表是由一个个的TaskView组成,当我们选择要分屏的TaskVIew,长按就进入了TaskVIew.java的onLongClick函数中,请求对其分屏。
SystemUI/legacy/recents/src/com/android/systemui/recents/views/TaskView.java

/**** View.OnLongClickListener Implementation ****/

@Override
public boolean onLongClick(View v) {
    SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
    boolean inBounds = false;
    Rect clipBounds = new Rect(mViewBounds.getClipBounds());
    if (!clipBounds.isEmpty()) {
        // If we are clipping the view to the bounds, manually do the hit test.
        clipBounds.scale(getScaleX());
        inBounds = clipBounds.contains(mDownTouchPos.x, mDownTouchPos.y);
    } else {
        // Otherwise just make sure we're within the view's bounds.
        inBounds = mDownTouchPos.x <= getWidth() && mDownTouchPos.y <= getHeight();
    }
    if (v == this && inBounds && !ssp.hasDockedTask()) {
        // Start listening for drag events
        setClipViewInStack(false);

        mDownTouchPos.x += ((1f - getScaleX()) * getWidth()) / 2;
        mDownTouchPos.y += ((1f - getScaleY()) * getHeight()) / 2;

        EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
        EventBus.getDefault().send(new DragStartEvent(mTask, this, mDownTouchPos));
        return true;
    }
    return false;
}

TaskView发生长按事件后,获取TaskView的边界值,进行判断mDownTouchPos的x,y值是否在TaskView的范围之内,该值就是触摸事件上报的值,也就是手指按下的位置,在onInterceptTouchEvent函数中获取。

 @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
	if (ev.getAction() == MotionEvent.ACTION_DOWN) {
		mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
	}
	return super.onInterceptTouchEvent(ev);
}

只有点击的View与当前的对象一致,并且手指按下的位置在TaskView的范围之内,并且此时并不在分屏模式下才会监听拖拽事件,注册EventBus事件,并且发送DragStartEvent事件,将当前的Task,TaskView,以及手指按下的点封装进DragStartEvent对象中。

显示可分屏区域

在TaskView中发送DragStartEvent事件,首先在RecentsViewTouchHandler.java的onEventBus函数接收到该事件。
/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java

/**** Events ****/
//SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
public final void onBusEvent(DragStartEvent event) {
	SystemServicesProxy ssp = LegacyRecentsImpl.getSystemServices();
	mRv.getParent().requestDisallowInterceptTouchEvent(true);
	mDragRequested = true;
	// We defer starting the actual drag handling until the user moves past the drag slop
	mIsDragging = false;
	mDragTask = event.task;
	mTaskView = event.taskView;
	if (ActivityTaskManager.supportsMultiWindow(mRv.getContext()) && !ssp.hasDockedTask()
			&& mDividerSnapAlgorithm.isSplitScreenFeasible()) {
		if (!event.task.isDockable) { // 如果应用不支持分屏
			EventBus.getDefault().send(new ShowIncompatibleAppOverlayEvent());
		} else {
			// 根据当前屏幕方向获取分屏的状态值
			DockState[] dockStates = LegacyRecentsImpl.getConfiguration()
					.getDockStatesForCurrentOrientation();
			for (DockState dockState : dockStates) {
				// 将分屏状态记录在mDropTargets列表中
				registerDropTargetForCurrentDrag(dockState);
				mVisibleDockStates.add(dockState); //将dockState记录在mVisibleDockStates列表中		
		}
	}
	// Request other drop targets to register themselves
	EventBus.getDefault().send(new DragStartInitializeDropTargetsEvent(event.task,
			event.taskView, this));
}

在RecentsViewTouchHandler中首先清理之前的信息,如果系统支持多窗口,并且此时不处于分屏模式,就会判断当前的task是否支持分屏,如果不支持分屏,就会提示用户该应用不支持分屏,如果支持分屏,根据当前系统的方向来获取分屏的状态。最后将获取的分屏状态记录在mVisibleDockStates和mDropTargets列表中。
SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsConfiguration.java

/**
 * Returns the preferred dock states for the current orientation.
 * @return a list of dock states for device and its orientation
 */
public DockState[] getDockStatesForCurrentOrientation() {
	boolean isLandscape = mAppContext.getResources().getConfiguration().orientation ==
			Configuration.ORIENTATION_LANDSCAPE;
	RecentsConfiguration config = LegacyRecentsImpl.getConfiguration();
	if (config.isLargeScreen) {
		return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
	} else {
		return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
	}
}

在getDockStatesForCurrentOrientation函数中根据Configuration判断是平板电脑还是手机,还是横屏竖屏。
手机设备在横屏状态只允许左右分屏,在竖屏状态只允许上下分屏,由于设备太小了。

平板电脑在横屏状态可以往左边也可以往右边分屏,而在竖屏状态就和手机一样只能往上边分。

总之,接收到事件后从event中获取对应的Task以及TaskView,清理mDropTargets与mVisibleDockStates列表内容。只有系统支持多窗口模式,并且此时不处于分屏模式,并且分屏后的窗口大小要大于等于最小的窗口尺寸才可以进行分屏。如果应用不支持分屏就会发送ShowIncompatibleAppOverlayEvent事件,提示用户应用不支持分屏。如果支持分屏就调用getDockStatesForCurrentOrientation函数获取当前的设备是平板还是手机,是要上下分屏还是左右分屏。

最后将获得分屏状态保存在mDropTargets与mVisibleDockStates列表中。RecentsViewTouchHandler处理完DragStartEvent事件后,分发给RecentsView.java,由RecentsView进行处理。
/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java

//SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
public final void onBusEvent(DragStartEvent event) {
	updateVisibleDockRegions(LegacyRecentsImpl.getConfiguration().getDockStatesForCurrentOrientation(),
			true /* isDefaultDockState */, DockState.NONE.viewState.dockAreaAlpha,
			DockState.NONE.viewState.hintTextAlpha,
			true /* animateAlpha */, false /* animateBounds */);

	// Temporarily hide the stack action button without changing visibility
	if (mStackActionButton != null) {
		mStackActionButton.animate()
				.alpha(0f)
				.setDuration(HIDE_STACK_ACTION_BUTTON_DURATION)
				.setInterpolator(Interpolators.ALPHA_OUT)
				.start();
	}
}

首先调用updateVisibleDockRegions来更新可见的分屏区域,根据当前屏幕方向获取分屏状态(左右,上下分屏),此时为默认分屏状态,dockAreaAlpha为80背景区域为灰白色,hintTextAlpha为255字体不透明。
/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java

//SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
/**
 * Updates the dock region to match the specified dock state.
 */
private void updateVisibleDockRegions(DockState[] newDockStates,
		boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha,
		boolean animateAlpha, boolean animateBounds) {
	ArraySet<DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates,
			new ArraySet<DockState>());
	// 获取前面得到的mVisibleDockState列表
	ArrayList<DockState> visDockStates = mTouchHandler.getVisibleDockStates();
	for (int i = visDockStates.size() - 1; i >= 0; i--) {
		DockState dockState = visDockStates.get(i);
		DockState.ViewState viewState = dockState.viewState;
		if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
			// This is no longer visible, so hide it
			viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION,
					Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds);
		} else {
			// This state is now visible, update the bounds and show it
			int areaAlpha = overrideAreaAlpha != -1
					? overrideAreaAlpha
					: viewState.dockAreaAlpha;
			int hintAlpha = overrideHintAlpha != -1
					? overrideHintAlpha
					: viewState.hintTextAlpha;
			Rect bounds = isDefaultDockState // 获取提示用户拖拽的区域边界
					? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
							mSystemInsets)
					: dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
							mDividerSize, mSystemInsets, getResources());
			if (viewState.dockAreaOverlay.getCallback() != this) {
				viewState.dockAreaOverlay.setCallback(this);
				viewState.dockAreaOverlay.setBounds(bounds);
			}
			viewState.startAnimation(bounds, areaAlpha, hintAlpha,
					TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
					animateAlpha, animateBounds); // 启动动画显示分屏区域
		}
	}
}

将newDockStates转变为ArraySet,获取mVisibleDockStates对象,此时mVisibleDockStates有两个对象与newDockStates中的对象一致。遍历mVisibleDockStates中两个对象,获得areaAlpha为80,hintAlpha为255,下面主要计算bounds的值。

由于isDefaultDockState的值为true,所以调用getPreDockedBounds函数,获得准备分屏模式的边界区。

//SystemUI/legacy/recents/src/com/android/systemui/recents/views/DockState.java
/**
 * Returns the docked task bounds with the given {@param width} and {@param height}.
 */
public Rect getPreDockedBounds(int width, int height, Rect insets) {
	getMappedRect(dockArea, width, height, mTmpRect);
	return updateBoundsWithSystemInsets(mTmpRect, insets);
}

/**
 * Returns the expanded bounds in certain dock sides such that the bounds account for the
 * system insets (namely the vertical nav bar).  This call modifies and returns the given
 * {@param bounds}.
 */
private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
	if (dockSide == DOCKED_LEFT) {
		bounds.right += insets.left;
	} else if (dockSide == DOCKED_RIGHT) {
		bounds.left -= insets.right;
	}
	return bounds;
}

获取bounds后使用动画显示对应区域

拖拽分屏

当屏幕显示出来分屏区域后,抬起手指停止拖拽TaskView,在RecentsView的onTouchEvent函数中接收touch事件,之后调用RecentsViewTouchHandler的handleTouchEvent处理触摸事件。最后调用handleTouchEvent处理。

@Overrid
public boolean onTouchEvent(MotionEvent ev){
	return mTouchHandler.onTouchEvent(ev);
}

public boolean onTouchEvent(MotionEvent ev){
	handleTouchEvent(ev);
	return mDragRequested;
}

在手指移动过程中获取坐标点evX,evY,在进行分屏时mLastDropTarget的值为null,并且currentDropTarget也为null,所以遍历mDropTargets列表,根据前面DragStartEvent事件可以知道mDropTargets列表中是左右分屏或者上下分屏的DockState对象,就会调用DockState的acceptDrop函数来判断手指移动的位置是否可以进行分屏。

开始分屏

冷启动分屏

获取stacksize

Resize窗口

热启动分屏

Move Stack

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值