Andorid分辨率适配sw

一、前语

我遇到的问题首要便是在自研 PAD 横屏时,微信使用的登录界面中,“登录” 和 “注册” 按钮堆叠在一起了。这个明显便是微信使用没有适配对应 sw 的资源布局导致的,我看了一下咱们自研 PAD 的 sw (最小宽度密度)是 800dp, 一般使用中会根据不同的 sw 值去寻觅对应的资源布局。

通过修改sw来适配应用界面——源码修改

搜了好多材料,可是都没有能够解决问题的,但有很多基础知识能够帮我慢慢地摸索到答案。这么多条的搜索记录中只有这条给我提供了思路,可是非常惋惜,我依照博主的办法试了一下,没有成功。但不得不承认这位博主收拾得不错,惋惜文章是 Android10.0 的版别了,google 这两年应该做了一些修正。

这位博主的修正办法:在 ResourcesManager 中修正 density 的值

//frameworks/base/core/java/android/app/ResourcesManager.java
private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
    Configuration config;
    final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
    final boolean hasOverrideConfig = key.hasOverrideConfiguration();
    if (!isDefaultDisplay || hasOverrideConfig) {
        config = new Configuration(getConfiguration());
        if (!isDefaultDisplay) {
            applyNonDefaultDisplayMetricsToConfiguration(dm, config);
        }
        if (hasOverrideConfig) {
            config.updateFrom(key.mOverrideConfiguration);
            if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
        }
    } else {
        config = getConfiguration();
    }
    //add {
    String apkName = key.mResDir;
    int setDensity = Resources.getSystem().getInteger(R.integer.config_desity_switch_value);
    String needChangedensityApk = Resources.getSystem().getString(R.string.density_change_pacagename);
    //Slog.d(TAG, "generateConfig---->" + apkName + "--setDensity-->" + setDensity + "--needChangedensityApk---->" + needChangedensityApk);
    if (apkName != null && needChangedensityApk != null && needChangedensityApk.contains(apkName)) {
       config.densityDpi = setDensity;
    }
    //add }
    return config;
}

我试了这个计划发现并没有收效,这儿的参数修正后边应该又被从头掩盖掉了,所以只能寻觅别的的计划。

二、解决计划

这儿咱们想让微信去改基本不可能,用户拿到你的 PAD 肯定会觉得是你 PAD 的问题。所以我拿来了联想、oppo、vivi 和 三星的 PAD 来比照,发现只有联想在横屏的时分微信布局是正常的,其他厂商都是一样的问题,这说明联想肯定做了一些适配。

我拿着联想 PAD 反编译它的 framework.jar , 惋惜的是并没有找到对应的修正计划。所以我拿着联想 PAD 持续研讨。总算我发现它的 density 值并没产生改动,我用 wm density 命令查看,density 始终未变。然后我切换微信的界面发现 sw 的值产生了改动,所以有了经过改动 sw 的值来改动分辨率的思路。

所以我去找能够改动 sw 值的当地,最终在 ActivityRecord#updateCompatDisplayInsets() 和 CompatibilityInfo# applyToConfiguration()处发现能够修正 smallestScreenWidthDp 的值来完成。

1. ActivityRecord#updateCompatDisplayInsets()

// frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
8102      // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
8103      private void updateCompatDisplayInsets() {
8104          if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
8105              // The override configuration is set only once in size compatibility mode.
8106              return;
8107          }
8108  
8109          Configuration overrideConfig = getRequestedOverrideConfiguration();
8110          final Configuration fullConfig = getConfiguration();
8111  
8112          // Ensure the screen related fields are set. It is used to prevent activity relaunch
8113          // when moving between displays. For screenWidthDp and screenWidthDp, because they
8114          // are relative to bounds and density, they will be calculated in
8115          // {@link Task#computeConfigResourceOverrides} and the result will also be
8116          // relatively fixed.
8117          overrideConfig.colorMode = fullConfig.colorMode;
8118          overrideConfig.densityDpi = fullConfig.densityDpi;
8119          // The smallest screen width is the short side of screen bounds. Because the bounds
8120          // and density won't be changed, smallestScreenWidthDp is also fixed.
              // 这儿修正这个 smllestScreenWidthDp 就能够了
8121          overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp;
8122          if (info.isFixedOrientation()) {
8123              // lock rotation too. When in size-compat, onConfigurationChanged will watch for and
8124              // apply runtime rotation changes.
8125              overrideConfig.windowConfiguration.setRotation(
8126                      fullConfig.windowConfiguration.getRotation());
8127          }
8128  
8129          // The role of CompatDisplayInsets is like the override bounds.
8130          mCompatDisplayInsets =
8131                  new CompatDisplayInsets(
8132                          mDisplayContent, this, mLetterboxBoundsForFixedOrientationAndAspectRatio);
8133      }

这个办法形似行不通,完成的作用缺一次改写,第一次进入使用时分辨率不改动,息屏或许把使用退到后台,再进入前台,这个时分分辨率能够更新成功。原因是这个办法在开机后会调用一次,mCompatDisplayInsets 会被赋值,后边再次进入时就直接 return 了,对应参数不会产生改动。而当咱们点击微信使用强制改动参数时,mCompatDisplayInsets 里是之前初始化的初始值(所以第一次进入时分辨率不会改动),第2次进入后分辨率才有作用。

咱们没有办法做到在第一次调用这个办法的时分针对微信使用做参数适配。除非大局都适配,这样改波及太大。这儿需求完成的作用是首次进入时分辨率就得及时更新,所以这个计划就不行了。咱们看看别的一个计划。

2. CompatibilityInfo# applyToConfiguration()

这两处其实都是针对兼容形式来做处理的,具体流程逻辑后边再收拾,先说处理计划。这个办法会被重复调用,不会存在缺一次改写的问题。

// frameworks/base/core/java/android/content/res/CompatibilityInfo.java
550      public void applyToConfiguration(int displayDensity, Configuration inoutConfig) {
551          if (!supportsScreen()) {
552              // This is a larger screen device and the app is not
553              // compatible with large screens, so we are forcing it to
554              // run as if the screen is normal size.
555              inoutConfig.screenLayout =
556                      (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK)
557                      | Configuration.SCREENLAYOUT_SIZE_NORMAL;
558              inoutConfig.screenWidthDp = inoutConfig.compatScreenWidthDp;
559              inoutConfig.screenHeightDp = inoutConfig.compatScreenHeightDp;
                  // 这儿修正这个 smllestScreenWidthDp 就能够了
560              inoutConfig.smallestScreenWidthDp = inoutConfig.compatSmallestScreenWidthDp;
561          }
             // add 
             ...
562          inoutConfig.densityDpi = displayDensity;
563          if (isScalingRequired()) {
564              float invertedRatio = applicationInvertedScale;
565              inoutConfig.densityDpi = (int)((inoutConfig.densityDpi * invertedRatio) + .5f);
566              inoutConfig.windowConfiguration.getMaxBounds().scale(invertedRatio);
567              inoutConfig.windowConfiguration.getBounds().scale(invertedRatio);
568              final Rect appBounds = inoutConfig.windowConfiguration.getAppBounds();
569              if (appBounds != null) {
570                  appBounds.scale(invertedRatio);
571              }
572          }
573      }
574

三、基础知识弥补

屏幕尺度、屏幕分辨率、屏幕像素密度

1. 屏幕尺度

屏幕尺度是指设备对角线的物理尺度,常用单位为英寸

2. 屏幕分辨率

屏幕分辨率是指设备在横向、纵向上的像素总和,常用 宽 * 高 来描绘。

如480*600,则表示在横向上有480个像素点,在纵向上有600个像素点。

3. 屏幕像素密度

屏幕像素密度指的是设备每英寸的像素点数, 单位 dpi

倍图对应关系:

通过修改sw来适配应用界面——源码修改

4. 屏幕适配计划

Android 中常见的 UI 适配计划

  1. 多 layout 适配

  2. 屏幕分辨率限定符适配

  3. smallestWidth 限定符适配:创建多个 values 文件夹,体系根据限定符去寻觅对应的 dimens.xml 文件,以确定在不同设备上的巨细展现。

sw 适配的优势

  1. Android 适配屏幕尺度较多
    绝大多数设备的最小宽度都大于 360dp, 这样 sw 就不需求大量适配。

  2. 适配单位方便
    屏幕分辨率适配的单位是 px(像素), sw 单位是 dp

  3. 适配宽松
    sw 适配从大往小找,不需求匹配的十分精确。

参考文献

  1. 基于Android10.0适配使用界面–修正体系源码:/post/693094…
  2. Android屏幕尺度适配常见计划smallestWidth:blog.csdn.net/cat_is_so_c…