WindowInsets 分发 & WindowInsets 相关类

WindowInsets 分发

ViewRootImpl 在 performTraversals 时会调用 dispatchApplyInsets 方法

private void performTraversals() {
    // cache mView since it is used so much below...
    final View host = mView;
    ...
    Rect frame = mWinFrame;
    if (mFirst) {
        ...
        dispatchApplyInsets(host);
    } else {
        ...
    }
    ...
}

ViewRootImpl::dispatchApplyInsets 定义如下

public void dispatchApplyInsets(View host) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchApplyInsets");
    mApplyInsetsRequested = false;
    WindowInsets insets = getWindowInsets(true /* forceConstruct */);
    if (!shouldDispatchCutout()) {
        // Window is either not laid out in cutout or the status bar inset takes care of
        // clearing the cutout, so we don't need to dispatch the cutout to the hierarchy.
        insets = insets.consumeDisplayCutout();
    }
    host.dispatchApplyWindowInsets(insets);//1
    mAttachInfo.delayNotifyContentCaptureInsetsEvent(insets.getInsets(Type.all()));
    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}

注释 1 处的 host,其实就是在调用 ViewRootImpl::setView 时传递进来的 View 对象,通常来说,是一个 ViewGroup 对象
ViewGroup::dispatchApplyWindowInsets

@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
    insets = super.dispatchApplyWindowInsets(insets);//1
    if (insets.isConsumed()) {
        return insets;
    }
    if (View.sBrokenInsetsDispatch) {
        return brokenDispatchApplyWindowInsets(insets);
    } else {
        return newDispatchApplyWindowInsets(insets);
    }
}

注释 1 处先执行 View 的 dispatchApplyWindowInsets 方法
View::dispatchApplyWindowInsets

public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
    try {
        mPrivateFlags3 |= PFLAG3_APPLYING_INSETS;
        if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) {//1
            return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets);//2
        } else {
            return onApplyWindowInsets(insets);//3
        }
    } finally {
        mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS;
    }
}

在注释 1 处判断,是否存在 mOnApplyWindowInsetsListener,该 Listener 可以通过 View:: setOnApplyWindowInsetsListener 来设置,如果设置过 mOnApplyWindowInsetsListener 则走注释 2 的代码,即调用 mOnApplyWindowInsetsListener 的 onApplyWindowInsets 方法;若未设置 mOnApplyWindowInsetsListener,则走注释 3 的代码,即继续执行 View::onApplyWindowInsets 的逻辑。
注意注释 2 和注释 3 是互斥的,如果调用 View::setOnApplyWindowInsetsListener 设置了 listener,则不会走注释 3 的逻辑。
先来看不设置 listener 的情况,即注释 2 处的代码

onApplyWindowInsets 分支

View::onApplyWindowInsets

public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    if ((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
            && (mViewFlags & FITS_SYSTEM_WINDOWS) != 0) {
        return onApplyFrameworkOptionalFitSystemWindows(insets);//1
    }
    if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
        // We weren't called from within a direct call to fitSystemWindows,
        // call into it as a fallback in case we're in a class that overrides it
        // and has logic to perform.
        if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) {
            return insets.consumeSystemWindowInsets();//2
        }
    } else {
        // We were called from within a direct call to fitSystemWindows.
        if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) {
            return insets.consumeSystemWindowInsets();//3
        }
    }
    return insets;
}

View::onApplyWindowInsets 根据两种情况来讨论

分支 1

注释 1 所在分支是第一种情况,DecorView 满足 (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0,参考下面代码
PhoneWindow::installDecor

private void installDecor() {
     if (mDecor == null) {
        mDecor = generateDecor(-1);
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    } else {
        mDecor.setWindow(this);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);

        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
        mDecor.makeFrameworkOptionalFitsSystemWindows();
        ...
    }
}

View::makeFrameworkOptionalFitsSystemWindows

public void makeFrameworkOptionalFitsSystemWindows() {
    mPrivateFlags4 |= PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS;
}

(mViewFlags & FITS_SYSTEM_WINDOWS) != 0 可以通过View::setFitsSystemWindows 控制
View::setFitsSystemWindows

public void setFitsSystemWindows(boolean fitSystemWindows) {
    setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS : 0, FITS_SYSTEM_WINDOWS);
}

Android S 上 DecorView 默认不具备 FITS_SYSTEM_WINDOWS Flag

分支 2

分支 1 不满足以后,第二个 if 语句用于判断是使用 fitSystemWindows 还是使用 fitSystemWindowsInt
View

protected boolean fitSystemWindows(Rect insets) {
    if ((mPrivateFlags3 & PFLAG3_APPLYING_INSETS) == 0) {
        if (insets == null) {
            // Null insets by definition have already been consumed.
            // This call cannot apply insets since there are none to apply,
            // so return false.
            return false;
        }
        // If we're not in the process of dispatching the newer apply insets call,
        // that means we're not in the compatibility path. Dispatch into the newer
        // apply insets path and take things from there.
        try {
            mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS;
            return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed();
        } finally {
            mPrivateFlags3 &= ~PFLAG3_FITTING_SYSTEM_WINDOWS;
        }
    } else {
        // We're being called from the newer apply insets path.
        // Perform the standard fallback behavior.
        return fitSystemWindowsInt(insets);
    }
}

private boolean fitSystemWindowsInt(Rect insets) {
    if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {//1
        Rect localInsets = sThreadLocal.get();
        boolean res = computeFitSystemWindows(insets, localInsets);//2
        applyInsets(localInsets);//3
        return res;
    }
    return false;
}

fitSystemWindows 是老方法,在 SDK 20 以后就已经不推荐使用了,主要研究 fitSystemWindowsInt
在注释 1 处,判断当前 View 是否设置了 android:fitsSystemWindows=”true” 如果设置了,则在注释 2 处计算 Insets,并在注释 3 处应用该 Insets
View::computeFitSystemWindows

protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
    WindowInsets innerInsets = computeSystemWindowInsets(new WindowInsets(inoutInsets),
            outLocalInsets);//1
    inoutInsets.set(innerInsets.getSystemWindowInsetsAsRect());
    return innerInsets.isSystemWindowInsetsConsumed();
}

computeFitSystemWindows 调用 computeSystemWindowInsets 计算 insets

public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) {
    boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
            || (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;//1
    if (isOptionalFitSystemWindows && mAttachInfo != null) {
        OnContentApplyWindowInsetsListener listener =
                mAttachInfo.mContentOnApplyWindowInsetsListener;//2
        if (listener == null) {
            // The application wants to take care of fitting system window for
            // the content.
            outLocalInsets.setEmpty();
            return in;
        }
        Pair<Insets, WindowInsets> result = listener.onContentApplyWindowInsets(this, in);//3
        outLocalInsets.set(result.first.toRect());
        return result.second;
    } else {
        outLocalInsets.set(in.getSystemWindowInsetsAsRect());
        return in.consumeSystemWindowInsets().inset(outLocalInsets);
    }
}

注释 1 的判断是针对 DecorView 的,DecorView 带有 PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
注释 2 的 mContentOnApplyWindowInsetsListener,是 PhoneWindow 根据mDecorFitsSystemWindows 调用过判断而给 ViewRootImpl 设置的监听器,用户可以通过调用 PhoneWindow::setDecorFitsSystemWindows 手动设置该值。
PhoneWindow

@Override
public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
    mDecorFitsSystemWindows = decorFitsSystemWindows;
    applyDecorFitsSystemWindows();
}

private void applyDecorFitsSystemWindows() {
    ViewRootImpl impl = getViewRootImplOrNull();
    if (impl != null) {
        impl.setOnContentApplyWindowInsetsListener(mDecorFitsSystemWindows
                ? sDefaultContentInsetsApplier
                : null);
    }
}

注释 3 在 mContentOnApplyWindowInsetsListener 不为 null 的前提下,根据 ContentOnApplyWindowInsetsListener::onContentApplyWindowInsets 计算 insets
对于 DecorView 来说,这里调用的是下面的方法

/**
 * @see Window#setDecorFitsSystemWindows
 */
private static final OnContentApplyWindowInsetsListener sDefaultContentInsetsApplier =
        (view, insets) -> {
            if ((view.getWindowSystemUiVisibility() & SYSTEM_UI_LAYOUT_FLAGS) != 0) {
                return new Pair<>(Insets.NONE, insets);
            }
            Insets insetsToApply = insets.getSystemWindowInsets();
            return new Pair<>(insetsToApply,
                    insets.inset(insetsToApply).consumeSystemWindowInsets());
        };

对于 computeSystemWindowInsets 方法来说,它的 outLocalInsets 参数指应用的 insets 矩形,返回值用于标记该 insets 是否被消费过,如果 in.consumeSystemWindowInsets() 则说明该 insets 被消费了
回到 computeFitSystemWindows 方法

protected boolean computeFitSystemWindows(Rect inoutInsets, Rect outLocalInsets) {
    WindowInsets innerInsets = computeSystemWindowInsets(new WindowInsets(inoutInsets),
            outLocalInsets);//1
    inoutInsets.set(innerInsets.getSystemWindowInsetsAsRect());
    return innerInsets.isSystemWindowInsetsConsumed();//2
}

在注释 2 处根据当前 insets 是否被消费过,返回 true/false
View::fitSystemWindowsInt

private boolean fitSystemWindowsInt(Rect insets) {
    if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {//1
        Rect localInsets = sThreadLocal.get();
        boolean res = computeFitSystemWindows(insets, localInsets);//2
        applyInsets(localInsets);//3
        return res;
    }
    return false;
}

回到 View::fitSystemWindowsInt 的注释 3,在这里将应用 Insets
View::applyInsets

private void applyInsets(Rect insets) {
    mUserPaddingStart = UNDEFINED_PADDING;
    mUserPaddingEnd = UNDEFINED_PADDING;
    mUserPaddingLeftInitial = insets.left;
    mUserPaddingRightInitial = insets.right;
    internalSetPadding(insets.left, insets.top, insets.right, insets.bottom);
}

在这里将通过 internalSetPadding 将 insets 以 padding 的形式给 View 设置上
后续,回到 View::onApplyWindowInsets

public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    if ((mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
            && (mViewFlags & FITS_SYSTEM_WINDOWS) != 0) {
        return onApplyFrameworkOptionalFitSystemWindows(insets);//1
    }
    if ((mPrivateFlags3 & PFLAG3_FITTING_SYSTEM_WINDOWS) == 0) {
        // We weren't called from within a direct call to fitSystemWindows,
        // call into it as a fallback in case we're in a class that overrides it
        // and has logic to perform.
        if (fitSystemWindows(insets.getSystemWindowInsetsAsRect())) {
            return insets.consumeSystemWindowInsets();//2
        }
    } else {
        // We were called from within a direct call to fitSystemWindows.
        if (fitSystemWindowsInt(insets.getSystemWindowInsetsAsRect())) {
            return insets.consumeSystemWindowInsets();//3
        }
    }
    return insets;
}

若 View::fitSystemWindowsInt 的注释 2 返回 true (即被消耗),则也会让 View::onApplyWindowInsets 的参数也变为被消耗的 Insets (注释 2、注释 3)
回到 ViewGroup::dispatchApplyWindowInsets

@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
    insets = super.dispatchApplyWindowInsets(insets);//1
    if (insets.isConsumed()) {//2
        return insets;
    }
    if (View.sBrokenInsetsDispatch) {//3
        return brokenDispatchApplyWindowInsets(insets);
    } else {
        return newDispatchApplyWindowInsets(insets);//4 
    }
}

若 insets 在注释 2 处判断为被消耗,则不会进行分发 WindowInsets,否则会继续执行注释 3 的逻辑判断是否中断分发, sBrokenInsetsDispatch 为 true 的条件如下

sBrokenInsetsDispatch = targetSdkVersion < Build.VERSION_CODES.R;

在 Android S 系统中,该值为 false,所以继续看注释 4 处的逻辑
ViewGroup::newDispatchApplyWindowInsets

private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) {
    final int count = getChildCount();
    for (int i = 0; i < count; i++) {
        getChildAt(i).dispatchApplyWindowInsets(insets);//1
    }
    return insets;
}

在注释 1 处,开始调用子 View 的 dispatchApplyWindowInsets 方法,让 WindowInsets 继续向子分发
对于低于 R 版本的系统来说,替代 newDispatchApplyWindowInsets 的是 brokenDispatchApplyWindowInsets
ViewGroup::brokenDispatchApplyWindowInsets

private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) {
    final int count = getChildCount();
    for (int i = 0; i < count; i++) {
        insets = getChildAt(i).dispatchApplyWindowInsets(insets);
        if (insets.isConsumed()) {
            break;
        }
    }
    return insets;
}

如果 app targetSdkVersion < 30 ,如果某个节点消费了 Insets,所有没遍历到的节点都不会收到 WindowInsets 的分发;
当 app 运行在 Android 11 以上版本的设备上且 targetSdkVersion >= 30,如果某个节点消费了 Insets,该节点的所有子节点不会收到 WindowInsets 分发。
区别在于一个节点消费了 WindowInsets 分发,其兄弟节点似乎否还能收到分发。

WindowInsets 相关类

InsetsSource

表示为单个类型 insets 的具体信息,例如 Insets 大小、是否可见等信息

InsetsState

InsetsState 持有当前系统 insets 的状态信息,其内部存有一个 InsetsSource 类型的数组 mSources
android.view.InsetsState#mSources

private final InsetsSource[] mSources = new InsetsSource[SIZE];

该数组用于保存当前屏幕中所有类型的 insets
当一个 Window 在客户端被添加、更新的时候,ViewRootImpl 将会调用 relayoutWindow 方法,从 WMS 获取当前 Window 的 InsetsState 信息
android.view.ViewRootImpl#relayoutWindow

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
        boolean insetsPending) throws RemoteException {

    float appScale = mAttachInfo.mApplicationScale;
    boolean restore = false;
    if (params != null && mTranslator != null) {
        restore = true;
        params.backup();
        mTranslator.translateWindowLayout(params);
    }

    if (params != null) {
        if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);

        if (mOrigWindowType != params.type) {
            // For compatibility with old apps, don't crash here.
            if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                Slog.w(mTag, "Window type can not be changed after "
                        + "the window is added; ignoring change of " + mView);
                params.type = mOrigWindowType;
            }
        }
    }

    long frameNumber = -1;
    if (mSurface.isValid()) {
        frameNumber = mSurface.getNextFrameNumber();
    }

    int relayoutResult = mWindowSession.relayout(mWindow, params,
            (int) (mView.getMeasuredWidth() * appScale + 0.5f),
            (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
            insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
            mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
            mTempControls, mSurfaceSize);//1
    mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
    if (mSurfaceControl.isValid()) {
        if (!useBLAST()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            final Surface blastSurface = getOrCreateBLASTSurface();
            // If blastSurface == null that means it hasn't changed since the last time we
            // called. In this situation, avoid calling transferFrom as we would then
            // inc the generation ID and cause EGL resources to be recreated.
            if (blastSurface != null) {
                mSurface.transferFrom(blastSurface);
            }
        }
        if (mAttachInfo.mThreadedRenderer != null) {
            if (HardwareRenderer.isWebViewOverlaysEnabled()) {
                addPrepareSurfaceControlForWebviewCallback();
                addASurfaceTransactionCallback();
            }
            mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
        }
    } else {
        destroySurface();
    }

    mPendingAlwaysConsumeSystemBars =
            (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;

    if (restore) {
        params.restore();
    }

    if (mTranslator != null) {
        mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
        mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
        mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls);
    }
    setFrame(mTmpFrames.frame);//2
    mWillMove = false;
    mWillResize = false;
    mInsetsController.onStateChanged(mTempInsets);//3
    mInsetsController.onControlsChanged(mTempControls);//4
    return relayoutResult;
}

在注释 1 处,调用 IWindowSession::relayout 方法,IWindowSession 是 ViewRootImpl 与 WindowMangerService 的代理,从注释 1 处得到该当前 Window 的框架大小 mTmpFrames、以及 Insets 情况(mTempInsets、mTempControls),并在注释 2 、注释 3 、注释 4 将这些属性同步给 ViewRootImpl
注释 3、4 中的 mInsetsController 是 InsetsController 类型的实例,它实现了 WindowInsetsController 接口

WindowInsetsController

WindowInsetsController 是客户端用于控制 Window 的管理器,它在客户端的实现类是 InsetsController,每个 Window 会有一个 ViewRootImpl,每个 ViewRootImpl 都将持有一个 InsetsController 实例,客户端程序可以通过 View::getWindowInsetsController 得到该实例,对当前 Window 的 insets 进行控制
android.view.View#getWindowInsetsController

/**
 * Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
 *
 * @return The {@link WindowInsetsController} or {@code null} if the view is neither attached to
 *         a window nor a view tree with a decor.
 * @see Window#getInsetsController()
 */
public @Nullable WindowInsetsController getWindowInsetsController() {
    if (mAttachInfo != null) {
        return mAttachInfo.mViewRootImpl.getInsetsController();
    }
    ViewParent parent = getParent();
    if (parent instanceof View) {
        return ((View) parent).getWindowInsetsController();
    } else if (parent instanceof ViewRootImpl) {
        // Between WindowManager.addView() and the first traversal AttachInfo isn't set yet.
        return ((ViewRootImpl) parent).getInsetsController();
    }
    return null;
}

InsetsSourceControl

用于控制单独 InsetsSource 的控制器,被 InsetsController 所持有,同样也是在 ViewRootImpl:: relayoutWindow 时从 WMS 中获得

InsetsSourceConsumer

对单一 InsetsSource 的消费者,其内部持有 InsetsSourceControl,可以控制其leash的可见性和动画。
InsetsSourceControl 持有 InsetsSourceConsumer 的创造器,会在 InsetsController::getSourceConsumer 时创建实例
android.view.InsetsController#getSourceConsumer

public InsetsController(Host host) {
    this(host, (controller, type) -> {
        if (type == ITYPE_IME) {
            return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
        } else {
            return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
                    controller);
        }
    }, host.getHandler());//1
}

@VisibleForTesting
public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetsType int type) {
    InsetsSourceConsumer controller = mSourceConsumers.get(type);
    if (controller != null) {
        return controller;
    }
    controller = mConsumerCreator.apply(this, type);//2
    mSourceConsumers.put(type, controller);
    return controller;
}

注释 2 处实际调用的是注释 1 的箭头函数

InsetsStateController

负责在服务端管理全局 insets 状态信息,InsetsStateController 中的 mState 代表当前屏幕的所以类型的 Insets 状态信息
InsetsStateController 还持有不同类型的 InsetsSourceProvider,通常 DisplayContent、InsetsPolicy 等模块会通过 InsetsStateController 访问某一类型的 InsetsSourceProvider 实例

InsetsSourceProvider

负责在服务端根据 WindowState 配置、生成特定类型的 InsetsSource
com.android.server.wm.InsetsSourceProvider#getSource

/**
 * Updates the window that currently backs this source.
 *
 * @param win The window that links to this source.
 * @param frameProvider Based on display frame state and the window, calculates the resulting
 *                      frame that should be reported to clients.
 * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
 *                         frame that should be reported to IME.
 */
void setWindow(@Nullable WindowState win,
        @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
        @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
    if (mWin != null) {
        if (mControllable) {
            mWin.setControllableInsetProvider(null);
        }
        // The window may be animating such that we can hand out the leash to the control
        // target. Revoke the leash by cancelling the animation to correct the state.
        // TODO: Ideally, we should wait for the animation to finish so previous window can
        // animate-out as new one animates-in.
        mWin.cancelAnimation();
        mWin.mProvidedInsetsSources.remove(mSource.getType());
    }
    ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
    mWin = win;
    mFrameProvider = frameProvider;
    mImeFrameProvider = imeFrameProvider;
    if (win == null) {
        setServerVisible(false);
        mSource.setFrame(new Rect());
        mSource.setVisibleFrame(null);
    } else {
        mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
        if (mControllable) {
            mWin.setControllableInsetProvider(this);
            if (mPendingControlTarget != null) {
                updateControlForTarget(mPendingControlTarget, true /* force */);
                mPendingControlTarget = null;
            }
        }
    }
}
InsetsSource getSource() {
    return mSource;
}

InsetsPolicy

被 DisplayContent 创建并持有,InsetsPolicy 负责管理不同类型 Inests 的 InsetsControlTarget 实例,同时 InsetsPolicy 还持有 InsetsStateController 类型实例,也提供了一些 Inests 相关行为策略
DisplayContent 对应一块屏幕的内容,一块屏幕对应一个 DisplayContent,一个 DisplayContent 下持有且仅一个 InsetsStateController 实例与 InsetsPolicy 实例,

InsetsControlTarget

InsetsControlTarget 是一个接口,代表可以控制 Insets 状态的对象,WindowState 实现了该接口,由于 WindowState 是每个 Window 在 WMS 中的状态描述,所以每个 Window 都具有控制 Insets 的能力,不同类型的 Insets 可能被不同的 Window 控制,可以通过 dump InsetsStateController 的信息查看当前 Insets 被谁控制
com.android.server.wm.InsetsStateController#dump

void dump(String prefix, PrintWriter pw) {
    pw.println(prefix + "WindowInsetsStateController");
    prefix = prefix + "  ";
    mState.dump(prefix, pw);
    pw.println(prefix + "Control map:");
    for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) {
        pw.print(prefix + "  ");
        pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> "
                + mTypeControlTargetMap.valueAt(i));
    }
    pw.println(prefix + "InsetsSourceProviders:");
    for (int i = mProviders.size() - 1; i >= 0; i--) {
        mProviders.valueAt(i).dump(pw, prefix + "  ");
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值