今日头条的屏幕适配

今日头条的屏幕适配

在Android 开发中,由于Android 碎片化严重,屏幕分辨率千奇百怪,而想要在各种分辨率的设备上显示基本一致的效果,适配成本越来越高。虽然Android官方提供了dp单位来适配,但其在各种奇怪分辨率下表现却不尽人意,因此下面探索一种简单地侵入烦人适配方式。

探索新的适配模式

1.支持以宽或者高一个维度去适配,保持该维度上和设计图一致;
2.支持dp和sp单位,控制迁移成本到最小

找兼容突破口

从dp和px的转换公式:px = dp*density

可以看出,如果设计图宽为360dp,想要保证在所有设备计算得出的px值都正好是屏幕宽度的话,我们只能修改density的值。

通过阅读源码,我们可以得知,density是DisplayMetrics中的成员变量,而DisplayMetrics实例通过Resources#getDisplayMetrics可以获得,而Resouces通过Activity或者Application的Context获得。

先来熟悉下 DisplayMetrics 中和适配相关的几个变量:
DisplayMetrics#density 就是上述的density
DisplayMetrics#densityDpi 就是上述的dpi
DisplayMetrics#scaledDensity字体的缩放因子,正常情况下和density相等,但是调节系统
字体大小后会改变这个值。

那么是不是所有的dp和px的转换都是通过DisplayMetrics中相关的值来计算的呢?

首先来看看布局文件中dp的转换,最终都是调用TypedValue#applyDimension(int unit,float value,DisplayMetrics metrics)来进行转换;

这里写图片描述

这里用到的DisplayMetrics正是从Resources中获得的。
再看看图片的decode,BitmapFactory#decodeResourceStream方法:

这里写图片描述

可见也是通过DisplayMetrics中的值来计算的。

当然还有些其他dp转换的场景,基本都是通过DisplayMetrics来计算的,这里不再详述。因此,想要满足上述需求,我们只需要修改DisplayMetrics中和dp转换相关的变量即可。

下面假设设计图宽度是360dp,以宽纬度来适配。
那么适配后的density = 设备真实宽(单位px)/360,接下来只需要我们计算好的density在系统中修改下即可,代码实现如下;

这里写图片描述

同时在Activity#onCreate方法中调用下。代码比较简单,也没有涉及到系统非公开api的调用
,因此理论上不会影响app稳定性。

在修改之后上线灰度测试了一版,稳定性符合预期,没有收到由此带来的crash,但是收到
了很多字体过小的反馈:

原因是在上面的适配中,我们忽略了DisplayMetrics#scaledDensity的特殊性,将DisplayMetrics#scaledDensity和DisplayMetrics#density设置为同样的值,从而某些用户在系统中修改了字体大小失效了,但是我们还不能直接用原始的scaledDensity,直接用的话可能导致某些文字超过显示区域,因此我们可以通过计算之前scaledDensity和density的比获得现在的scaledDensity,方式如下:

这里写图片描述

但是测试后发现另外一个问题,就是如果在系统设置中切换字体,再返回应用,字体并没有变化。于是还得监听下字体切换,调用Application#registerComponentCallbacks注册下onConfiggurationChanged监听即可。

因此最终方案如下:
这里写图片描述

当然以上代码只是以设计图宽360dp去适配的,如果要以高维度适配,可以再扩展下代码即可。

https://developer.android.com/guide/practices/screens_support

//实现代码

这里写代码片

//获取到的是系统的屏幕信息
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int widthPx = displayMetrics.widthPixels;//得到屏幕宽的分辨率
int heightPx = displayMetrics.heightPixels;//得到屏幕高的分辨率
float density = displayMetrics.density;//密度
float scaledDensity = displayMetrics.scaledDensity;//与字体大小相关的密度
//原始的scaledDensity 比上 原始的density(缩放比例)
float a = scaledDensity / density; //计算变化后的比例 原始系统的比例
scaledDensity = a * density;
int dpi = displayMetrics.densityDpi;//屏幕密度
float newDensity = widthPx / 360f;//真实的密度  360是dp的值
float newscaledDensity = newDensity * a;
displayMetrics.density = newDensity;//决定布局尺寸
displayMetrics.scaledDensity = newscaledDensity;//决定字体大小
setContentView(R.layout.activity_main);
// 360dp 设计图宽度
TextView textView = findViewById(R.id.showResult);

textView.setText("widthPx = "+widthPx+",heightPx = "+heightPx+",density = "+density+",newDensity="+newDensity);
Log.i("abc","widthPx = "+widthPx+",heightPx = "+heightPx+",density = "+density);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值