DisplayMetrics获取宽高不对

DisplayMetrics获取宽高不对

一、引言

该车机项目为宽屏显示,使用Android 9.0系统开发,分辨率为1920x720,配置navigation bar为120px,显示在左侧。

二、问题

通过如下方法拿到的屏幕高度值是576,正常应该是720。

DisplayMetrics dm = getResources().getDisplayMetrics();
int screenHeight = dm.heightPixels;
三、分析
1、dumpsys display

dumpsys display信息如下:

Logical Displays: size=2
Display 0:
mDisplayId=0
mLayerStack=0
mHasContent=true
mRequestedMode=0
mRequestedColorMode=0
mDisplayOffset=(0, 0)
mPrimaryDisplayDevice=内置屏幕
mBaseDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1920 x 720, real 1920 x 720, largest app 1920 x 720, smallest app 1920 x 720, mode 1, defaultMode 1, modes [{id=1, width=1920, height=720, fps=60.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities android.view.Display$HdrCapabilities@40f16308, rotation 0, density 240 (160.0 x 160.0) dpi, layerStack 0, appVsyncOff 8300000, presDeadline 9366666, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, removeMode 0}
mOverrideDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1920 x 576, real 1920 x 720, largest app 1920 x 1808, smallest app 720 x 536, mode 1, defaultMode 1, modes [{id=1, width=1920, height=720, fps=60.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities android.view.Display$HdrCapabilities@40f16308, rotation 0, density 240 (160.0 x 160.0) dpi, layerStack 0, appVsyncOff 8300000, presDeadline 9366666, type BUILT_IN, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, removeMode 0}
Display 1:
mDisplayId=1
mLayerStack=1
mHasContent=false
mRequestedMode=0
mRequestedColorMode=0
mDisplayOffset=(0, 0)
mPrimaryDisplayDevice=HDMI 屏幕
mBaseDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1920 x 720, real 1920 x 720, largest app 1920 x 720, smallest app 1920 x 720, mode 2, defaultMode 2, modes [{id=2, width=1920, height=720, fps=60.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities android.view.Display$HdrCapabilities@40f16308, rotation 0, density 213 (213.0 x 213.0) dpi, layerStack 1, appVsyncOff 8300000, presDeadline 9366666, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION, removeMode 0}
mOverrideDisplayInfo=DisplayInfo{"HDMI 屏幕", uniqueId "local:1", app 1920 x 720, real 1920 x 720, largest app 1920 x 1920, smallest app 720 x 720, mode 2, defaultMode 2, modes [{id=2, width=1920, height=720, fps=60.000004}], colorMode 0, supportedColorModes [0], hdrCapabilities android.view.Display$HdrCapabilities@40f16308, rotation 0, density 213 (213.0 x 213.0) dpi, layerStack 1, appVsyncOff 8300000, presDeadline 9366666, type HDMI, state ON, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS, FLAG_PRESENTATION, removeMode 0}

看到mOverrideDisplayInfo中打印为1920 x 576;

2、DisplayContent

跟踪代码发现DisplayContent中会更新mOverrideDisplayInfo

frameworks\base\services\core\java\com\android\server\wm\DisplayContent.java
    /**
     * Update {@link #mDisplayInfo} and other internal variables when display is rotated or config
     * changed.
     * Do not call if {@link WindowManagerService#mDisplayReady} == false.
     */
    private DisplayInfo updateDisplayAndOrientation(int uiMode) {
        ...
        final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
                mDisplayId, displayCutout);
        final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
                mDisplayId, displayCutout);
         ...
                // We usually set the override info in DisplayManager so that we get consistent display
        // metrics values when displays are changing and don't send out new values until WM is aware
        // of them. However, we don't do this for displays that serve as containers for ActivityView
        // because we don't want letter-/pillar-boxing during resize.
        final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
                ? mDisplayInfo : null;
        mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
                overrideDisplayInfo);

继续跟进,发现调用的mPolicy获取的appWidth和appHeight,分别在PhoneWindowManager中的getNonDecorDisplayWidth与getNonDecorDisplayHeight实现。

3、PhoneWindowManager
frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
    private int getNavigationBarWidth(int rotation, int uiMode) {
        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
            return mNavigationBarWidthForRotationInCarMode[rotation];
        } else {
            return mNavigationBarWidthForRotationDefault[rotation];
        }
    }

    @Override
    public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation, int uiMode,
            int displayId, DisplayCutout displayCutout) {
        int width = fullWidth;
        // TODO(multi-display): Support navigation bar on secondary displays.
        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
            // For a basic navigation bar, when we are in landscape mode we place
            // the navigation bar to the side.
            if (mNavigationBarCanMove && fullWidth > fullHeight) {
                width -= getNavigationBarWidth(rotation, uiMode);
            }
        }
        if (displayCutout != null) {
            width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();
        }
        return width;
    }

    private int getNavigationBarHeight(int rotation, int uiMode) {
        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
            return mNavigationBarHeightForRotationInCarMode[rotation];
        } else {
            return mNavigationBarHeightForRotationDefault[rotation];
        }
    }

    @Override
    public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation, int uiMode,
            int displayId, DisplayCutout displayCutout) {
        int height = fullHeight;
        // TODO(multi-display): Support navigation bar on secondary displays.
        if (displayId == DEFAULT_DISPLAY && mHasNavigationBar) {
            // For a basic navigation bar, when we are in portrait mode we place
            // the navigation bar to the bottom.
            if (!mNavigationBarCanMove || fullWidth < fullHeight) {
                height -= getNavigationBarHeight(rotation, uiMode);
            }
        }
        if (displayCutout != null) {
            height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();
        }
        return height;
    }

分析这两个方法,发现:

  • mNavigationBarCanMove为true且fullWidth > fullHeight的情况,认为navigation bar显示在左侧或右侧,getNonDecorDisplayWidth获取时需减掉navigation bar的宽度;
  • 而fullWidth < fullHeight时,认为navigation bar始终显示在底部,则getNonDecorDisplayHeight需减掉navigation bar的高度。

所以,我们保证上述条件满足时即可正确计算应用显示的宽高。

四、实现

1、新增属性ro.vendor.navi_bar_pos配置navigation bar显示位置

device/[project]/system.prop
+#navigation bar position, 1-left 2-right
+ro.vendor.navi_bar_pos=1

2、配置显示navigation bar及宽高

a、device/[project]/overlay/frameworks/base/core/res/res/values/config.xml
    <bool name="config_showNavigationBar">true</bool>

b、device/[project]/system.prop
-qemu.hw.mainkeys=1

c、device/[project]/overlay/frameworks/base/core/res/res/values/dimens.xml
    <!-- Height of the bottom navigation / system bar. -->
    <dimen name="navigation_bar_height">120px</dimen>
        <!-- Width of the navigation bar when it is placed vertically on the screen -->
    <dimen name="navigation_bar_width">120px</dimen>

3、PhoneWindowManager适配

--- a/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2514,6 +2514,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         }
         //Log.d(TAG, "wlf shortSizeDp = "+shortSizeDp);
+        // add start
+        if(SystemProperties.getInt("ro.vendor.navi_bar_pos", 0) > 0) {
+            mNavigationBarCanMove = true;
+        }
+        // add end

         mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);

@@ -5168,10 +5173,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {

     @NavigationBarPosition
     private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+        // add start
+        int position= SystemProperties.getInt("ro.bw.navi_bar_pos", 0);
+        if(displayWidth > displayHeight && position > 0) {
+            return position;
+        }
+        // add end

         if (mNavigationBarCanMove && displayWidth > displayHeight) {
             if (displayRotation == Surface.ROTATION_270) {

该修改主要是保证mNavigationBarCanMove为true,还有就是navigationBarPosition根据显示宽高调整位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shusuanly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值