Android 通过DecorView计算statusBar、navigationBar的高度

2020-06-16 09:46:56 浏览数 (2)

背景

近期在做项目的时候碰到了底部虚拟按键在各个厂商适配的问题,闷逼了一圈,后面搜索一圈,发现即使各大厂商有变动,还是离不开原生本质

正题

我们都知道activity >> window >> decorView,适配的问题,闷逼了一圈,后面搜索一圈,发现即使各大厂商有变动,还是离不开原生本质

activity 的 decorview

我们都知道activity >> window >> decorView,Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。

  • Activity相关代码:
代码语言:javascript复制
public class Activity extends ContextThemeWrappe{
  private Window mWindow;

  mWindow = new PhoneWindow(this);
}
  • PhoneWindow 相关代码:
代码语言:javascript复制
public class PhoneWindow extends Window{
  // This is the top-level view of the window, containing the window decor.
  private DecorView mDecor; 
}

DocorView包含了一个状态栏,一个navigationBar,一个LinearLayout我们通常的内容展示区,如下:

代码语言:javascript复制
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <!-- Popout bar for action modes -->
    <ViewStub
        android:id="@ id/action_mode_bar_stub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inflatedId="@ id/action_mode_bar"
        android:layout="@layout/action_mode_bar"
        android:theme="?attr/actionBarTheme" />

    <FrameLayout
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/windowTitleSize">

        <TextView
            android:id="@android:id/title"
            style="?android:attr/windowTitleStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical" />
    </FrameLayout>

    <FrameLayout
        android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foreground="?android:attr/windowContentOverlay"
        android:foregroundGravity="fill_horizontal|top" />
</LinearLayout>

在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。相关源码扯犊子到这边差不多,可以知道statusbar和navigationBar两者和decorView的关系了,就是他的两个儿子。

计算statusBar和NavigationBar的高度
代码语言:javascript复制
public class DecorUtil {

    /**
     * 请勿在dialog中使用
     * <p>
     * 主题的 android:windowTranslucentStatus 属性, 会影响 contentView 的 padding top.
     * <p>
     * 如果设置了 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN , 那么 contentView 的 padding top 都是 0
     */
    public static void demo(@NonNull final Window window) {
        final View decorView = window.getDecorView();
        int measuredHeight = decorView.getMeasuredHeight();
        if (measuredHeight <= 0) {
            decorView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    decorView.getViewTreeObserver().removeOnPreDrawListener(this);

                    demo(window);
                    return true;
                }
            });
        } else {
            Rect outRect = new Rect();
            decorView.getWindowVisibleDisplayFrame(outRect);
            L.w("可视区域:"   outRect);

            L.w("屏幕高度:"   measuredHeight);
            if (decorView instanceof ViewGroup) {
                int childCount = ((ViewGroup) decorView).getChildCount();
                if (childCount > 0) {
                    View contentView = ((ViewGroup) decorView).getChildAt(0);
                    L.w("内容高度:"   contentView.getMeasuredHeight()   " p:"   contentView.getPaddingTop());
                }
                if (childCount > 1) {
                    View childView = ((ViewGroup) decorView).getChildAt(1);
                    if (isStatusBar(decorView, childView)) {
                        L.w("状态栏高度:"   childView.getMeasuredHeight());
                    } else if (isNavigationBar(decorView, childView)) {
                        L.w("导航栏高度:"   childView.getMeasuredHeight());
                    } else {
                        L.w("未知:"   childView);
                    }
                }
                if (childCount > 2) {
                    View childView = ((ViewGroup) decorView).getChildAt(2);
                    if (isStatusBar(decorView, childView)) {
                        L.w("状态栏高度:"   childView.getMeasuredHeight());
                    } else if (isNavigationBar(decorView, childView)) {
                        L.w("导航栏高度:"   childView.getMeasuredHeight());
                    } else {
                        L.w("未知:"   childView);
                    }
                }
            }
        }
    }

    private static boolean isStatusBar(@NonNull View decorView, @NonNull View childView) {
        if (childView.getTop() == 0 &&
                childView.getMeasuredWidth() == decorView.getMeasuredWidth() &&
                childView.getBottom() < decorView.getBottom()
                ) {
            return true;
        }
        return false;
    }

    private static boolean isNavigationBar(@NonNull View decorView, @NonNull View childView) {
        if (childView.getTop() > decorView.getTop() &&
                childView.getMeasuredWidth() == decorView.getMeasuredWidth() &&
                childView.getBottom() == decorView.getBottom()
                ) {
            return true;
        }
        return false;
    }
}
参考文献

https://blog.csdn.net/angcyo/article/details/53240763

0 人点赞