一、前言

我遇到的问题主要就是在自研 PAD 横屏时,微信应用的登录界面中,“登录” 和 “注册” 按钮堆叠在一起了。这个明显就是微信应用没有适配对应 sw 的资源布局导致的,我看了一下我们自研 PAD 的 sw (最小宽度密度)是 800dp, 一般应用中会根据不同的 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. 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
    倍图对应关系:

  4. 屏幕适配方案
    Android 中常见的 UI 适配方案

  5. 多 layout 适配

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

  7. smallestWidth 限定符适配:创建多个 values 文件夹,系统根据限定符去寻找对应的 dimens.xml 文件,以确定在不同设备上的大小展示。

sw 适配的优势

  1. Android 适配屏幕尺寸较多
    绝大多数设备的最小宽度都大于 360dp, 这样 sw 就不需要大量适配。
  2. 适配单位方便
    屏幕分辨率适配的单位是 px(像素), sw 单位是 dp
  3. 适配宽松
    sw 适配从大往小找,不需要匹配的十分准确。

参考文献

  1. 基于Android10.0适配应用界面–修改系统源码:https://juejin.cn/post/6930949098090528775
  2. Android屏幕尺寸适配常见方案smallestWidth:https://blog.csdn.net/cat_is_so_cute/article/details/124458433

通过修改sw来适配应用界面——源码修改相关推荐

  1. Android6.0 源码修改之Settings音量调节界面增加通话音量调节

    Android6.0 源码修改之Settings音量调节界面增加通话音量调节 前言 今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话 ...

  2. android打开volte代码,Android8.1 源码修改之插入SIM卡默认启用Volte功能

    前言 公用电话产品,插入SIM卡后要求自动打开Volte功能,即插即用,用完拔卡就走 实现 第一步 开关对应的代码 通过打印日志和全局查找,源码位置 vendor/mediatek/proprieta ...

  3. Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮...

    Android6.0 源码修改之 仿IOS添加全屏可拖拽浮窗返回按钮 前言 之前写过屏蔽系统导航栏功能的文章,具体可看Android6.0 源码修改之屏蔽导航栏虚拟按键(Home和RecentAPP) ...

  4. Android Dialer,Mms,Contacts源码修改笔记,移动端混合开发经验

    ②在AndroidManifest.xml中修改相应Activity的theme <activity android:name=".HomeActivity" android ...

  5. Deep Compression阅读理解及Caffe源码修改

    Deep Compression阅读理解及Caffe源码修改 作者:may0324 更新:  没想到这篇文章写出后有这么多人关注和索要源码,有点受宠若惊.说来惭愧,这个工作当时做的很粗糙,源码修改的比 ...

  6. 最近在修改statusBar,添加几张图片.编译源码包时,一直提示无法找到R.drawable.xxxx必须手动编译下指定的图片文件生成R.

    最近在修改statusBar,添加几张图片.编译源码包时,一直提示无法找到R.drawable.xxxx  必须手动编译下指定的图片文件生成R.  $touch frameworks/base/pac ...

  7. 写一个PE的壳_Part 5:PE格式修复+lief源码修改

    系列汇总 写一个PE的壳_Part 1:加载PE文件到内存 写一个PE的壳_Part 2:ASLR+修复输入表(IAT)+重定位表支持(.reloc) 写一个PE的壳_Part 3:Section里实 ...

  8. adb 工具源码修改

    adb 工具源码修改 1.修改客户端输入后的判断逻辑,使带密码的命令 可以通过命令检查 修改 system/core/adb/client/commandline.cpp 在最前面 新增一个函数 用来 ...

  9. 微信小程序:装B神器P图修改微信流量主小程序源码下载趣味恶搞图制作免服务器域名

    今天给大家带来的这一款小程序是装逼生成,趣味制图工具 该小程序免服务器和域名,低成本运营 内容丰富,搭建简单,而且更逼真哟 内涵N种模板制作,另外还可以设置推荐小程序更好的互引 简单说几个模板吧 红包 ...

最新文章

  1. 【C++】【四】企业链表
  2. Linux下遍历指定目录的C++实现
  3. CSS类名称/选择器中哪些字符有效?
  4. vscode配置C/C++ windows编译环境。
  5. Android Studio——[Missing essential plugin: org.jetbrains.android ...]解决方案
  6. 宝塔命令号操作全-最实用的莫过于修改密码啦
  7. java web scala_spring boot+scala编写web接口
  8. ElementUI+Java实现搜索提示列表
  9. STM32 HAL库 UART 串口读写功能笔记
  10. TaskBarProgress(任务栏进度条)
  11. 异步处理函数async_Spring @Async异步处理注释
  12. 【整理】牛客网编程题前端篇(较难难度)
  13. 使用腾讯AI开放平台api进行车牌OCR识别和其他比如图片文字识别等通用
  14. Big Sur MacOS高清动态壁纸
  15. 微计算机控制器功能是什么意思,微型计算机中,控制器的基本功能是().
  16. Linux下的hostid
  17. Unity 游戏实例开发集合 之 FlappyBird (像素鸟) 休闲小游戏快速实现
  18. 基于入侵杂草和花授粉混合算法的WSN节点部署优化
  19. Java工程师 Java基础面试题JVM(Day12)
  20. ios13全选手势_iOS13操作新手势:使用iPhone编辑文本更方便

热门文章

  1. matlalb与python编程进行动力总成悬置模态计算对比——困惑待解
  2. CCRC资质认证实施规则最新版
  3. 详解linux虚拟内存原理
  4. Zabbix控制系统添加被监控的主机(步骤详细)
  5. 【阅读笔记】联邦学习实战——联邦学习医疗健康应用案例
  6. instagram akp_创建RSS源以跟踪没有帐户的Instagram和Twitter用户
  7. 读书笔记:《机器人SLAM导航核心技术与实战》目录
  8. DaDianNao: A Machine-Learning Supercomputer
  9. 5G加油站,需要中频段
  10. SQL SERVER 的压缩功能