android 分屏显示左右或者上下

Android N 支持多窗口模式,或者叫分屏模式,即在屏幕上可以同时显示多个窗口。

在手机模式下,两个应用可以并排或者上下同时显示,如图 1 所示,屏幕上半部分的窗口是系统的 CLOCK 应用,下半部分是系统设置功能。用户可以拖动两个应用之间的分界线改变两个窗口的大小,放大其中一个应用,同时缩小另一个应用。
 

分屏模式
图 1  分屏模式


在分屏模式下,各个窗口的应用都可以正常运行,但是只能有一个窗口获得焦点,而另外的窗口则属于暂停状态。

在某个应用界面进入分屏,这个应用会显示在上半部分或者左半部分,在android9之后会显示在下半部分或者右半部分。公司的车机项目从android8升级到android9,客户想要显示的和之前的一样,显示在左半部分。

分屏模式相关代码在SystemUI中,进入分屏的代码如下

private boolean onLongPressRecents() {
    if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
            || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()) {
        return false;
    }

    return mStatusBar.toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
            MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
}

走到src/com/android/systemui/statusbar/phone/StatusBar.java中

protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
    if (mRecents == null) {
        return false;
    }    
    int dockSide = WindowManagerProxy.getInstance().getDockSide();
    if (dockSide == WindowManager.DOCKED_INVALID) {
        final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
        if (navbarPos == NAV_BAR_POS_INVALID) {
            return false;
        }    
        int createMode = navbarPos == NAV_BAR_POS_LEFT
                ? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
                : ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
        return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode, null, metricsDockAction);
    } else {
        Divider divider = getComponent(Divider.class);
        if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {
            // Undocking from the minimized state is not supported
            return false;
        } else {
            EventBus.getDefault().send(new UndockingTaskEvent());
            if (metricsUndockAction != -1) {
                mMetricsLogger.action(metricsUndockAction);
            }    
        }    
    }    
    return true;
}

进入分屏时,dockSide = WindowManager.DOCKED_INVALID,所以执行mRecents.splitPrimaryTask()

在src/com/android/systemui/recents/Recents.java的方法splitPrimaryTask中,最终执行的是

if (sSystemServicesProxy.isSystemUser(currentUser)) {
    mImpl.splitPrimaryTask(runningTask.id, dragMode, stackCreateMode, initialBounds);
}

在src/com/android/systemui/recents/RecentsImpl.java

    public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,
            Rect initialBounds) {
        SystemServicesProxy ssp = Recents.getSystemServices();

        // Make sure we inform DividerView before we actually start the activity so we can change
        // the resize mode already.
        if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {
            EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
        }    
    }

EventBus是一种用于Android的事件发布-订阅框架,通过解耦发布者和订阅者简化Android事件传递,因此接收DockedTopTaskEvent事件是具体执行方法。

在src/com/android/systemui/stackdivider/DividerView.java接收EventBus发送的事件

    public final void onBusEvent(DockedTopTaskEvent event) {
        if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
            mState.growAfterRecentsDrawn = false;
            mState.animateAfterRecentsDrawn = true;
            startDragging(false /* animate */, false /* touching */);
        }    
        updateDockSide();
        mEntranceAnimationRunning = true;

        resizeStack(calculatePositionForInsetBounds(), mSnapAlgorithm.getMiddleTarget().position,
                mSnapAlgorithm.getMiddleTarget());
    }

mState.animateAfterRecentsDrawn置为true,执行分屏动画

    public void onRecentsDrawn() {
        updateDockSide();
        final int position = calculatePositionForInsetBounds();
        if (mState.animateAfterRecentsDrawn) {
            mState.animateAfterRecentsDrawn = false;

            mHandler.post(() -> { 
                // Delay switching resizing mode because this might cause jank in recents animation
                // that's longer than this animation.
                stopDragging(position, getSnapAlgorithm().getMiddleTarget(),
                        mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
                        200 /* endDelay */);
            });  
        }
        if (mState.growAfterRecentsDrawn) {
            mState.growAfterRecentsDrawn = false;
            updateDockSide();
            EventBus.getDefault().send(new RecentsGrowingEvent());
            stopDragging(position, getSnapAlgorithm().getMiddleTarget(), 336,
                    Interpolators.FAST_OUT_SLOW_IN);
        }
    }

此方法根据animateAfterRecentsDrawn是否为true而执行stopDragging()

    public void stopDragging(int position, SnapTarget target, long duration, long startDelay,
            long endDelay, Interpolator interpolator) {
        mHandle.setTouching(false, true /* animate */);
        flingTo(position, target, duration, startDelay, endDelay, interpolator);
        mWindowManager.setSlippery(true);
        releaseBackground();
    }

    private void flingTo(int position, SnapTarget target, long duration, long startDelay,
            long endDelay, Interpolator interpolator) {
        ValueAnimator anim = getFlingAnimator(position, target, endDelay);
        anim.setDuration(duration);
        anim.setStartDelay(startDelay);
        anim.setInterpolator(interpolator);
        anim.start();
    }

通过getFlingAnimator获取动画,在getFlingAnimator中

    ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
    anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
            taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
                    ? TASK_POSITION_SAME
                    : snapTarget.taskPosition,
            snapTarget));

resizeStackDelayed是动画执行的步骤

    public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
        Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
                taskSnapTarget);
        message.setAsynchronous(true);
        mSfChoreographer.scheduleAtSfVsync(mHandler, message);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RESIZE_STACK:
                    resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
        if (mRemoved) {
            // This divider view has been removed so shouldn't have any additional influence.
            return;
        }
        calculateBoundsForPosition(position, mDockSide, mDockedRect);

        if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
            return;
        }

        // Make sure shadows are updated
        if (mBackground.getZ() > 0f) {
            mBackground.invalidate();
        }

        mLastResizeRect.set(mDockedRect);
        if (mHomeStackResizable && mIsInMinimizeInteraction) {
            calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
                    mDockedTaskRect);
            calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
                    DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);

            // Move a right-docked-app to line up with the divider while dragging it
            if (mDockSide == DOCKED_RIGHT) {
                mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
                        - mDockedTaskRect.left + mDividerSize, 0);
            }
            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
                    mOtherTaskRect, null);
            return;
        }

                ... ...
                ... ...
    }

mDockSide是影响显示左右的参数,

    private void updateDockSide() {
        mDockSide = mWindowManagerProxy.getDockSide();
        mMinimizedShadow.setDockSide(mDockSide);
    }

mDockSide是通过mWindowManagerProxy.getDockSide(),最终是通过WindowManagerService获取,我最终没找到WMS在哪里设置的,只能在updateDockSide方法中将mDockSide置为WindowManager.DOCKED_LEFT。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值