传统的适配方案,是根据Android中的 pxdensity,dp和dpi的关系做换算。而很多设备却并没有按屏幕尺寸、分辨率和像素密度的关系这个固有的规则来实现,得到的dpi值混乱。按传统的适配方式并不能完全解决适配的问题。因此本文探讨了一个优雅的方式来实现,核心思想是自定义density,以宽或者高一个维度去适配,保持该维度上和设计图一致。

前言

一个月前看了今日头条新的屏幕适配方案,这是传送门,对此不禁拍案叫绝,为此我想把这种方案融入到我工具类中直接一行代码即可适配,如今最新 1.18.0 版 AndroidUtilCode 已有其适配方案,其相关函数在 ScreenUtils 中,相关 API 如下所示:

adaptScreen4VerticalSlide  : 适配垂直滑动的屏幕
adaptScreen4HorizontalSlide: 适配水平滑动的屏幕
cancelAdaptScreen          : 取消适配屏幕

效果

UtilApk 中的 ScreenAdaptActivity 以 360dp 来做适配,代码如下所示:

public class ScreenAdaptActivity extends BaseActivity {private TextView tvUp;private TextView tvDown;public static void start(Context context) {Intent starter = new Intent(context, ScreenAdaptActivity.class);context.startActivity(starter);}@Overridepublic void initData(@Nullable Bundle bundle) {if (ScreenUtils.isPortrait()) {ScreenUtils.adaptScreen4VerticalSlide(this, 360);} else {ScreenUtils.adaptScreen4HorizontalSlide(this, 360);}}@Overridepublic int bindLayout() {return R.layout.activity_screen_adapt;}@Overridepublic void initView(Bundle savedInstanceState, View contentView) {tvUp = findViewById(R.id.tv_up);tvDown = findViewById(R.id.tv_down);if (!ScreenUtils.isPortrait()) {updateLayout();}}@Overridepublic void doBusiness() {}@Overridepublic void onWidgetClick(View view) {}public void toggleFullScreen(View view) {ScreenUtils.toggleFullScreen(this);updateLayout();}private void updateLayout() {int statusBarHeight = BarUtils.getStatusBarHeight();int statusBarHeightInDp = SizeUtils.px2dp(this, statusBarHeight);ViewGroup.LayoutParams upLayoutParams = tvUp.getLayoutParams();ViewGroup.LayoutParams downLayoutParams = tvDown.getLayoutParams();if (ScreenUtils.isFullScreen(this)) {int height = 360 / 2;String s = height + "dp";upLayoutParams.height = SizeUtils.dp2px(this, height);tvUp.setLayoutParams(upLayoutParams);tvUp.setText(s);downLayoutParams.height = SizeUtils.dp2px(this, height);tvDown.setLayoutParams(downLayoutParams);tvDown.setText(s);} else {int height = 360 / 2 - statusBarHeightInDp / 2;String s = height + "dp";upLayoutParams.height = SizeUtils.dp2px(this, height);tvUp.setLayoutParams(upLayoutParams);tvUp.setText(s);downLayoutParams.height = SizeUtils.dp2px(this, height);tvDown.setLayoutParams(downLayoutParams);tvDown.setText(s);}}
}

其在 1080x1920 420dpi(xxhdpi) 下的效果如下所示:


其在 768x1280 320dpi(xhdpi) 下的效果如下所示:


其在 480x800 240dpi(hdpi) 下的效果如下所示:


其在 320x480 160dpi(mdpi) 下的效果如下所示:


如上就是竖屏以 360dp 为宽度和宽屏以 360dp 为高度的适配效果。

原理

如果看了上面今日头条的那篇适配文章,那么你可能已经知道其原理了,不明白的话可以继续看下我的解释:原理就是修改 Activity 的 Resources#getDisplayMetrics 中的 density、densityDpi、scaledDensity 这三个变量,使其表现为设计图下的 dp 尺寸,因为 Activity 视图中所有尺寸的转化都会围绕上面三个变量,举个例子,我们以水平固定、垂直可滑动,设计图水平宽度为 360dp 为例子,那么我们强行把设备的 density 修改为 360,然后等比修改 densityDpi、scaledDensity 的值,那么这个 Activity 在任何设备下就强行转化为了水平宽度为 360dp 的尺寸,由于垂直可滑动,只要垂直方向是自适应高度来等比缩放那便在任何设备上效果都一致,这就达到了屏幕适配。

如果想要取消该屏幕适配,只需将 Application 的 Resources#getDisplayMetrics 中的 density、densityDpi、scaledDensity 这三个变量值赋予 Activity 对应的即可。

代码寥寥几行,如下所示:

/*** Adapt the screen for vertical slide.** @param designWidthInDp The size of design diagram's width, in dp,*                        e.g. the design diagram width is 720px, in XHDPI device,*                        the designWidthInDp = 720 / 2.*/
public static void adaptScreen4VerticalSlide(final Activity activity,final int designWidthInDp) {adaptScreen(activity, designWidthInDp, true);
}/*** Adapt the screen for horizontal slide.** @param designHeightInDp The size of design diagram's height, in dp,*                         e.g. the design diagram height is 1080px, in XXHDPI device,*                         the designHeightInDp = 1080 / 3.*/
public static void adaptScreen4HorizontalSlide(final Activity activity,final int designHeightInDp) {adaptScreen(activity, designHeightInDp, false);
}/*** Cancel adapt the screen.** @param activity The activity.*/
public static void cancelAdaptScreen(final Activity activity) {final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics();final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();activityDm.density = appDm.density;activityDm.scaledDensity = appDm.scaledDensity;activityDm.densityDpi = appDm.densityDpi;
}/*** Reference from: https://mp.weixin.qq.com/s/d9QCoBP6kV9VSWvVldVVwA*/
private static void adaptScreen(final Activity activity,final float sizeInDp,final boolean isVerticalSlide) {final DisplayMetrics appDm = Utils.getApp().getResources().getDisplayMetrics();final DisplayMetrics activityDm = activity.getResources().getDisplayMetrics();if (isVerticalSlide) {activityDm.density = activityDm.widthPixels / sizeInDp;} else {activityDm.density = activityDm.heightPixels / sizeInDp;}activityDm.scaledDensity = activityDm.density * (appDm.scaledDensity / appDm.density);activityDm.densityDpi = (int) (160 * activityDm.density);
}

坑点

之前写的 SizeUtils.px2dp 工具类都是以 Application 的 context 来使用的,所以在写 Demo 横屏的时候适配状态栏发现有点问题,尺寸总是不对,最后恍然大悟 Activity 的 Resources#getDisplayMetrics 和 Application 的 Resources#getDisplayMetrics 是两个不同的引用,所以我工具类对 SizeUtils.px2dp 拓展了一个 context 参数来适配 Activity 的尺寸转换,也就是 Demo 中的代码 SizeUtils.dp2px(this, height),发现了 Application 和 Activity Resources#getDisplayMetrics 区别这点也就方便了我做取消适配和优化今日头条的实现,其实代码根本就不需要他想的那么复杂,很多事情走到头来一般都会有优雅的解决方式,而我工具类中的实现便是如此。

建议

老项目那就不要大动干戈改动适配代码了,新项目我建议采用我工具类中的使用,可以让你爽到极致,在 BaseActivitysetContentView(xx) 之前调用适配代码即可,再啰嗦一次,传入第二个参数就是设计图转换为 dp 尺寸的大小,比如要做水平固定,可垂直滑动的屏幕适配,该设计图宽度为 720px-XHDPI,那么它换算为 dp 就是 720 /2 = 360dp,这个 2 怎么来的那我就不道破了,这是 Android 基础,不懂的话去补补基础,如果代码中涉及到了 px 和 dp、px 和 sp 互转,一定要用我工具类中 SizeUtils.dp2pxSizeUtils.px2dpSizeUtils.sp2pxSizeUtils.px2sp 传入 context 的重载,切莫省去 context 从而导致使用 Application 的 context

有了固定的 dp 尺寸,那么我们百分比是不是就很好实现了,计算后直接写 xxdp 即可,这样在所有设备上也都是一定的比例,哪里还需要什么百分比布局什么的来做?是不是 so easy,更多风骚的操作可待你解锁。

结语

如果我的工具类对你的适配造成了影响,欢迎到 AndroidUtilCode 提 issue,感谢今日头条的方案,让我可以站在巨人的肩膀上装一次 13。

本文转自:https://juejin.im/post/5b6250bee51d451918537021?utm_source=gold_browser_extension

如何优雅的实现Android 屏幕适配方案相关推荐

  1. android屏幕适配教程,Android屏幕适配方案,android屏幕适配

    Android屏幕适配方案,android屏幕适配 文章转载禁止用于商业用途,且不能带有虚拟货币.积分.注册等附加条件.转载须注明出处莫高雷草原以及作者@JiongBull. Android屏幕适配方 ...

  2. 2021年最详细的Android屏幕适配方案汇总

    1 Android屏幕适配的度量单位和相关概念 建议在阅读本文章之前,可以先阅读快乐李同学写的文章<Android屏幕适配的度量单位和相关概念>,这篇文章包含了阅读本文的一些基础知识,推荐 ...

  3. Android屏幕适配方案

    一. 手机适配的应用和使用场景 使android应用程序适用于不同的国家语言.型号.尺寸和SDK版本等手机环境中,其主要功能和界面风格保持不变. 手机适配主要包括三个方面:语言适配.屏幕适配.SDK平 ...

  4. Android 屏幕适配方案(七)

    原文地址为: Android 屏幕适配方案(七) 一. 手机适配的应用和使用场景 使android应用程序适用于不同的国家语言.型号.尺寸和SDK版本等手机环境中,其主要功能和界面风格保持不变. 手机 ...

  5. 实用Android 屏幕适配方案分享

    转载地址:http://blog.csdn.net/gao_chun/article/details/45645051 真正可用,并且简单易行,可以在多个屏幕大小和屏幕密度上有良好表现的Android ...

  6. 开源,原创,实用Android 屏幕适配方案分享

    原理介绍,可以看看这里. http://www.dqqdo.com/?/article/2 已经出新的版本,源码也已经上传至csdn git,地址: http://blog.csdn.net/i778 ...

  7. android开发根据分辨率设置高度,最详细的Android屏幕适配方案分析

    为什么要屏幕适配 Android开发过程中我们常用的尺寸单位有px.dp,还有一种sp一般是用于字体的大小.但是因为px是像素单位,比方我们通常说的手机分辨例如1920*1080都是px的单位.现在A ...

  8. Android 屏幕适配方案(多分辨率适配)

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  9. 2022金九银十Android大厂面试题来袭,面试字节跳动被问Android屏幕适配方案

    前言 我最近在筹备Android面试的相关文章,我发现,Framework是一个十分重要的点,被不少大厂所看重. 如图,这是我们的一个技术交流群里面的对话↓↓↓ 前阵子我写的Android技术文里面, ...

最新文章

  1. 如果你铁了心要好好搞科研,我强烈建议你看一下这个帖子
  2. 【Cocos2d-X(2.x) 游戏开发系列之三】最新版本cocos2d-2.0-x-2.0.2使用资源加载策略
  3. 【技术综述】深度学习中的数据增强(下)
  4. windows右键没有显示“文本文档”的解决办法(建议用方法二)
  5. 如何使用 SAP CDS view 中的 currency conversion 功能
  6. C语言存储空间布局以及static解析
  7. erlang lists模块函数使用大全
  8. TensorFlow tf.keras.backend.ctc_batch_cost
  9. 关于程序员前途的看法和我系列文章的想法
  10. TensorFlow——如何查看当前版本TF编译使用的CUDA和cuDNN的编译版本
  11. 我的前端面试日记(一)
  12. linux下安装杰奇2.4,实现关关采集器远程采集详细教程
  13. 实时云渲染+虚拟仿真实验解决方案
  14. 【计算机组成原理】:计算机系统的组成和层次
  15. 拉格朗日、牛顿、拟合的应用
  16. 网站被qq拦截应该怎么处理
  17. android sim卡槽,识别Android平板电脑是否具有SIM卡插槽
  18. 桌面上的文件夹存储路径是什么?桌面文件夹误删了怎么找回
  19. 公安部:“净网2018”侦破网络犯罪案件57519起
  20. 小颖用计算机探索方程,数字信号处理(邓小颖)-中国大学mooc-题库零氪

热门文章

  1. Java HashMap的底层实现原理
  2. 基于结构光的立体视觉
  3. JAVA 数组的输出的三种方式
  4. 计算机一级msoffice讲义,XX全国一级计算机基础及MS Office应用讲义
  5. 编写一个MFC程序的主要步骤
  6. 【FCOS】FCOS理论知识讲解
  7. Nginx静态资源优化、压缩、缓存
  8. sqlserver创建视图索引
  9. SDUT 2138 图结构练习——BFSDFS——判断可达性
  10. 分布式Session与单点登录