Android 12系统源码_SystemUI(十)窗口焦点发生变化导航栏闪烁问题分析

前言

在使用Android12为车机系统载体进行系统SystemUI开发的过程中发现一个很奇特的问题,当不同页面发生切换的时候,导航栏总是会闪一下,其实就是窗口焦点发生变化的时候,导航栏总是会消失一下再出现,虽然问题不是很严重,但这对于用户体验来说是极差的,本篇文章我们就来梳理一下为什么会出现这种现象。

一、窗口焦点发生变化

1、当窗口焦点发生变化的时候,首先会触发WindowManagerService的updateFocusedWindowLocked方法。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    RootWindowContainer mRoot;
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }
}

WindowManagerService的updateFocusedWindowLocked方法会进一步调用RootWindowContainer的updateFocusedWindowLocked方法。

2、RootWindowContainer的updateFocusedWindowLocked方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

class RootWindowContainer extends WindowContainer<DisplayContent>
        implements DisplayManager.DisplayListener {

    // List of children for this window container. List is in z-order as the children appear on
    // screen with the top-most window container at the tail of the list.
    protected final WindowList<E> mChildren = new WindowList<E>();//注意,此属性是父类WindowContainer的属性

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        mTopFocusedAppByProcess.clear();
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayContent dc = mChildren.get(i);
            //循环调用集合子条目DisplayContent的updateFocusedWindowLocked方法
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
            final WindowState newFocus = dc.mCurrentFocus;
            if (newFocus != null) {
                final int pidOfNewFocus = newFocus.mSession.mPid;
                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
                }
                if (topFocusedDisplayId == INVALID_DISPLAY) {
                    topFocusedDisplayId = dc.getDisplayId();
                }
            } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
                // The top-most display that has a focused app should still be the top focused
                // display even when the app window is not ready yet (process not attached or
                // window not added yet).
                topFocusedDisplayId = dc.getDisplayId();
            }
        }
        if (topFocusedDisplayId == INVALID_DISPLAY) {
            topFocusedDisplayId = DEFAULT_DISPLAY;
        }
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
            mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
            ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
        }
        return changed;
    }
 }

RootWindowContainer 是屏幕设备的根容器管理者,子容器是 DisplayContent,每个DisplayContent代表一个屏幕设备的显示区域,
RootWindowContainer的updateFocusedWindowLocked方法会循环调用子容器DisplayContent的updateFocusedWindowLocked方法,通知各个屏幕设备窗口焦点发生了变化。

3、DisplayContent的updateFocusedWindowLocked方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

    /**
     * 如果窗口焦点发生了变化,会调用此方法对焦点窗口做一些参数调整*
     * @param mode Indicates the situation we are in. Possible modes are:
     *             {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
     *             {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
     *             {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
     *             {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
     * @param updateInputWindows Whether to sync the window information to the input module.
     * @param topFocusedDisplayId Display id of current top focused display.
     * @return {@code true} if the focused window has changed.
     */
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, int topFocusedDisplayId) {
        //获取顶部焦点设备的焦点窗口
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        if (mCurrentFocus == newFocus) {//如果窗口焦点没有变化直接返回false
            return false;
        }
        boolean imWindowChanged = false;
        final WindowState imWindow = mInputMethodWindow;
        if (imWindow != null) {
            final WindowState prevTarget = mImeLayeringTarget;
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            imWindowChanged = prevTarget != newTarget;

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }

            if (imWindowChanged) {
                mWmService.mWindowsChanged = true;
                setLayoutNeeded();
                newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
            }
        }

        ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
        final WindowState oldFocus = mCurrentFocus;
        mCurrentFocus = newFocus;

        if (newFocus != null) {
            mWinAddedSinceNullFocus.clear();
            mWinRemovedSinceNullFocus.clear();

            if (newFocus.canReceiveKeys()) {
                // Displaying a window implicitly causes dispatching to be unpaused.
                // This is to protect against bugs if someone pauses dispatching but
                // forgets to resume.
                newFocus.mToken.paused = false;
            }
        }

        //通知屏幕窗口策略对象设备焦点发生了变化
        getDisplayPolicy().focusChangedLw(oldFocus, newFocus);

        if (imWindowChanged && oldFocus != mInputMethodWindow) {
            // Focus of the input method window changed. Perform layout if needed.
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                //准备重新布局
                performLayout(true /*initial*/,  updateInputWindows);
            } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                // Client will do the layout, but we need to assign layers
                // for handleNewWindowLocked() below.
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }

        if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
            // If we defer assigning layers, then the caller is responsible for doing this part.
            getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
        }
        //如果需要的话会对输入法窗口参数做调整
        adjustForImeIfNeeded();

        // We may need to schedule some toast windows to be removed. The toasts for an app that
        // does not have input focus are removed within a timeout to prevent apps to redress
        // other apps' UI.
        scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);

        if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
            pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
        }

        // Notify the accessibility manager for the change so it has the windows before the newly
        // focused one starts firing events.
        // TODO(b/151179149) investigate what info accessibility service needs before input can
        // dispatch focus to clients.
        if (mWmService.mAccessibilityController.hasCallbacks()) {
            mWmService.mH.sendMessage(PooledLambda.obtainMessage(
                    this::updateAccessibilityOnWindowFocusChanged,
                    mWmService.mAccessibilityController));
        }

        return true;
    }
 }

DisplayContent的updateFocusedWindowLocked方法会进一步调用屏幕窗口策略对象DisplayPolicy的focusChangedLw方法。

4、DisplayPolicy的focusChangedLw方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

public class DisplayPolicy {
    /**
     * A new window has been focused.
     */
    public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
      	//焦点发生了变化
        mFocusedWindow = newFocus;
        mLastFocusedWindow = lastFocus;
        if (mDisplayContent.isDefaultDisplay) {
            mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
        }
        //更新SystemBar的属性
        updateSystemBarAttributes();
    }
}

DisplayPolicy的focusChangedLw方法最终会调用updateSystemBarAttributes方法来刷新系统栏属性。

二、更新系统栏属性

1、DisplayPolicy的updateSystemBarAttributes的方法如下所示。

public class DisplayPolicy {

    //更新SystemBar的属性
    void updateSystemBarAttributes() {
        WindowState winCandidate = mFocusedWindow;//焦点窗口
        if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
                && (mTopFullscreenOpaqueWindowState.mAttrs.flags
                & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
            //只有焦点窗口才能控制系统栏
            winCandidate = mTopFullscreenOpaqueWindowState;
        }
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        if (winCandidate == null) {
            return;
        }

        // The immersive mode confirmation should never affect the system bar visibility, otherwise
        // it will unhide the navigation bar and hide itself.
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {

            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
            // keys, we let it keep controlling the visibility.
            final boolean lastFocusCanReceiveKeys =
                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
            winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
                            : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return;
            }
        }
        final WindowState win = winCandidate;
        mSystemUiControllingWindow = win;

        final int displayId = getDisplayId();//获取屏幕设备id
        final int disableFlags = win.getDisableFlags();//获取窗口禁止的属性标记
        //结合参数disableFlags重新更新系统栏窗口参数
        final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);

 		final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
        mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
        final boolean isNavbarColorManagedByIme =
                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
                navColorWin) | opaqueAppearance;
        final int behavior = win.mAttrs.insetsFlags.behavior;
        final String focusedApp = win.mAttrs.packageName;
        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
        final AppearanceRegion[] appearanceRegions =
                new AppearanceRegion[mStatusBarColorWindows.size()];
        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
            final WindowState windowState = mStatusBarColorWindows.get(i);
            appearanceRegions[i] = new AppearanceRegion(
                    getStatusBarAppearance(windowState, windowState),
                    new Rect(windowState.getFrame()));
        }
        if (mLastDisableFlags != disableFlags) {
            mLastDisableFlags = disableFlags;
            final String cause = win.toString();
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        if (mLastAppearance == appearance
                && mLastBehavior == behavior
                && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                && Objects.equals(mFocusedApp, focusedApp)
                && mLastFocusIsFullscreen == isFullscreen
                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
            return;
        }
        if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
                && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
            mService.mInputManager.setSystemUiLightsOut(
                    isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
        }
        final InsetsVisibilities requestedVisibilities =
                new InsetsVisibilities(win.getRequestedVisibilities());
        mLastAppearance = appearance;
        mLastBehavior = behavior;
        mRequestedVisibilities = requestedVisibilities;
        mFocusedApp = focusedApp;
        mLastFocusIsFullscreen = isFullscreen;
        mLastStatusBarAppearanceRegions = appearanceRegions;
        //触发状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged
        callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
                requestedVisibilities, focusedApp));
    }
 }

updateSystemBarAttributes方法首先获取当前的焦点窗口mFocusedWindow,将该窗口赋值给winCandidate,并判断该窗口是否可以操控系统栏,如果不允许会直接返回;如果允许,则会结合winCandidate的属性调用updateSystemBarsLw方法,来更新系统栏参数。

2、DisplayPolicy的updateSystemBarsLw方法如下所示。

public class DisplayPolicy {

    private int updateSystemBarsLw(WindowState win, int disableFlags) {
        final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
        final boolean multiWindowTaskVisible =
                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                        || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
        final boolean freeformRootTaskVisible =
                defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);

        //判断当前是否存在多任务窗口或者悬浮窗,如果存在则需要强制显示系统栏
        mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
        //经测试发现,窗口焦点变化的时候,这行代码会触发导航栏闪一下
        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
		//导航栏或状态栏不透明属性
        int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
        appearance = configureStatusBarOpacity(appearance);
        appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
                freeformRootTaskVisible);
		//是否需要隐藏导航栏
        final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
        final long now = SystemClock.uptimeMillis();
        final boolean pendingPanic = mPendingPanicGestureUptime != 0
                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
        final DisplayPolicy defaultDisplayPolicy =
                mService.getDefaultDisplayContentLocked().getDisplayPolicy();
        if (pendingPanic && requestHideNavBar && win != mNotificationShade
                && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)
                // TODO (b/111955725): Show keyguard presentation on all external displays
                && defaultDisplayPolicy.isKeyguardDrawComplete()) {
            // The user performed the panic gesture recently, we're about to hide the bars,
            // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
            mPendingPanicGestureUptime = 0;
            if (!isNavBarEmpty(disableFlags)) {
                mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC,
                        true /* isGestureOnSystemBar */);
            }
        }

        // update navigation bar
        boolean oldImmersiveMode = mLastImmersiveMode;
        boolean newImmersiveMode = isImmersiveMode(win);
        if (oldImmersiveMode != newImmersiveMode) {
            mLastImmersiveMode = newImmersiveMode;
            // The immersive confirmation window should be attached to the immersive window root.
            final RootDisplayArea root = win.getRootDisplayArea();
            final int rootDisplayAreaId = root == null ? FEATURE_UNDEFINED : root.mFeatureId;
            mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode,
                    mService.mPolicy.isUserSetupComplete(),
                    isNavBarEmpty(disableFlags));
        }

        return appearance;
    }
    
 }

以上代码经过测试发现,是mDisplayContent.getInsetsPolicy().updateBarControlTarget(win)这段代码导致窗口焦点发生变化的时候,导航栏会闪一下。

3、InsetsPolicy调用updateBarControlTarget更新当前可以控制系统栏的窗口对象。

frameworks/base/services/core/java/com/android/server/wm/InsetsPolicy.java

class InsetsPolicy {
    /** Updates the target which can control system bars. */
    void updateBarControlTarget(@Nullable WindowState focusedWin) {
        if (mFocusedWin != focusedWin){
            abortTransient();
        }
        mFocusedWin = focusedWin;
        final InsetsControlTarget statusControlTarget =
                getStatusControlTarget(focusedWin, false /* fake */);
        final InsetsControlTarget navControlTarget =
                getNavControlTarget(focusedWin, false /* fake */);
        mStateController.onBarControlTargetChanged(
                statusControlTarget,
                statusControlTarget == mDummyControlTarget
                        ? getStatusControlTarget(focusedWin, true /* fake */)
                        : null,
                navControlTarget,
                navControlTarget == mDummyControlTarget
                        ? getNavControlTarget(focusedWin, true /* fake */)
                        : null);
        mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
        mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
    }
 }

InsetsPolicy的updateBarControlTarget方法的主要目的是更新当前可以控制系统栏的窗口对象,此方法会进一步调用InsetsStateController的onBarControlTargetChanged方法。

4、InsetsStateController的onBarControlTargetChanged方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/InsetsStateController.java

class InsetsStateController {

    /**
     * Called when the focused window that is able to control the system bars changes.
     *
     * @param statusControlling The target that is now able to control the status bar appearance
     *                          and visibility.
     * @param navControlling The target that is now able to control the nav bar appearance
     *                       and visibility.
     */
    void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling,
            @Nullable InsetsControlTarget fakeStatusControlling,
            @Nullable InsetsControlTarget navControlling,
            @Nullable InsetsControlTarget fakeNavControlling) {
        onControlChanged(ITYPE_STATUS_BAR, statusControlling);//状态栏
        onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);//导航栏
        onControlChanged(ITYPE_CLIMATE_BAR, statusControlling);
        onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling);
        onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
        onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
        onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling);
        onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling);
        notifyPendingInsetsControlChanged();
    }
}

InsetsStateController的onBarControlTargetChanged方法会触发onControlChanged方法。

5、InsetsStateController的onControlChanged方法如下所示。

class InsetsStateController {
    private void onControlChanged(@InternalInsetsType int type,
            @Nullable InsetsControlTarget target) {
        //获取原来的系统栏控制对象
        final InsetsControlTarget previous = mTypeControlTargetMap.get(type);
        //如果新的系统栏控制对象和原来的相同直接返回
        if (target == previous) {
            return;
        }
        //获取系统栏提供者
        final InsetsSourceProvider provider = mProviders.get(type);
        //如果为空直接返回
        if (provider == null) {
            return;
        }
        //如果系统栏提供者不可以控制对应的系统栏也直接返回
        if (!provider.isControllable()) {
            return;
        }
        //系统栏提供者更新系统栏控制者对象
        provider.updateControlForTarget(target, false /* force */);
        target = provider.getControlTarget();
        if (previous != null) {//如果原来的系统栏控制者对象不为空
        	//将其从
            removeFromControlMaps(previous, type, false /* fake */);
            mPendingControlChanged.add(previous);
        }
        if (target != null) {
            addToControlMaps(target, type, false /* fake */);
            mPendingControlChanged.add(target);
        }
    }
 }

InsetsStateController的onControlChanged方法中会调用InsetsControlTarget的updateControlForTarget方法。

6、InsetsStateController的updateControlForTarget方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/InsetsStateController.java

class InsetsSourceProvider {

    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
        if (mSeamlessRotating) {
            // We are un-rotating the window against the display rotation. We don't want the target
            // to control the window for now.
            return;
        }

        if (mWin != null && mWin.getSurfaceControl() == null) {
            // if window doesn't have a surface, set it null and return.
            setWindow(null, null, null);
        }
        if (mWin == null) {
            mPendingControlTarget = target;
            return;
        }
        if (target == mControlTarget && !force) {
            return;
        }
        if (target == null) {
            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
            mWin.cancelAnimation();
            setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
            return;
        }
        final Point surfacePosition = getWindowFrameSurfacePosition();
        mAdapter = new ControlAdapter(surfacePosition);
        if (getSource().getType() == ITYPE_IME) {
            //设置客户端的可见性
            setClientVisible(target.getRequestedVisibility(mSource.getType()));
        }
        final Transaction t = mDisplayContent.getSyncTransaction();
        //开启动画,就是这个动画触发闪烁的
        mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
                ANIMATION_TYPE_INSETS_CONTROL);

        // The leash was just created. We cannot dispatch it until its surface transaction is
        // applied. Otherwise, the client's operation to the leash might be overwritten by us.
        mIsLeashReadyForDispatching = false;

        final SurfaceControl leash = mAdapter.mCapturedLeash;
        mControlTarget = target;
        updateVisibility();
        mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition,
                mSource.calculateInsets(mWin.getBounds(), true /* ignoreVisibility */));
        ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                "InsetsSource Control %s for target %s", mControl, mControlTarget);
    }

}

经过测试最终发现,就是mWin.startAnimation这段代码触发底部栏闪烁的。高版本的系统在窗口焦点发生变化的时候,会首先清除上一个窗口设置给状态栏和导航栏的相关属性,将导航栏变为不可见,然后再可见。

二、解决方案

解决方案有两种:
1、找到这个动画的源头,修改这个动画的逻辑。
2、在触发mWin.startAnimation这段代码的时候做一下过滤,过滤掉导航栏。

class InsetsSourceProvider {

    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
    	...代码省略...
        if (getSource().getType() != ITYPE_NAVIGATION_BAR) {
        	//开启动画,就是这个动画触发闪烁的,所以这里我们过滤掉导航栏
            mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
                    ANIMATION_TYPE_INSETS_CONTROL);
        }
       ...代码省略...
    }
    
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值