欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android 分屏显示左右或者上下

程序员文章站 2022-04-14 11:28:43
AndroidN 支持多窗口模式,或者叫分屏模式,即在屏幕上可以同时显示多个窗口。在手机模式下,两个应用可以并排或者上下同时显示,如图 1 所示,屏幕上半部分的窗口是系统的 CLOCK 应用,下半部分是系统设置功能。用户可以拖动两个应用之间的分界线改变两个窗口的大小,放大其中一个应用,同时缩小另一个应用。图 1 分屏模式在分屏模式下,各个窗口的应用都可以正常运行,但是只能有一个窗口获得焦点,而另外的窗口则属于暂停状态。在某个应用界面进入分屏,这个应用会显示在上半部分或者左半部分,......

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

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

android 分屏显示左右或者上下
图 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。

本文地址:https://blog.csdn.net/qq_34694875/article/details/107934844