转载请标明出处https://www.jianshu.com/p/71a68addb357
前言:现在的Android开发环境下,手机适配是一直没有消失,长存的问题。就连IOS环境下的屏幕也都变得多样。反观Android环境下的各个厂家的各种尺寸屏幕手机的适配,是个难办的问题。在这环境之下,其实Android屏幕适配的解决,还是有几套方案可以实行的。包括常用的针对你所需要适配的手机屏幕的分辨率各自建立一个文件夹。hongyang大神文章里还有自动生成文件夹的工具,这里不赘述,这里说一种更低成本的,头条团队提供的方案。在此之下先说一下分辨率相关的几个基础知识点。
- 分辨率
分辨率就是手机屏幕的像素点数,一般描述成屏幕的“宽×高”,720×1280表示此屏幕在宽度方向有720个像素,在高度方向有1280个像素。在进行屏幕适配时,不要直接通过分辨率适配,应该通过屏幕尺寸和屏幕密度来适配
- 屏幕大小
屏幕尺寸是指屏幕的物理尺寸,是通过测量屏幕的对角线测量出来的。以英寸(inch)为单位。指对角线的尺寸,比如6寸×2.54厘米/寸=15.24厘米。
- 屏幕密度(dpi)
屏幕物理区域中的像素量,通常称为dpi/ppi(每英寸的像素点数,ios为ppi,anroid为dpi)。密度越高,成像的效果越好,越细腻。有些厂家会推出2k,3k,4k的屏幕,用户体验的确是很好的。
- px (像素)
px 即 pixel,像素点,电子屏幕上组成图像的最基本单位,在上面描述屏幕分辨率时使用到该单位。
- dp (也称 dip,与dpi区别)
Density-independent pixel (dp)独立像素密度。标准是160dip.即1dp对应1个pixel,计算公式如:px = dp * (dpi / 160),屏幕密度越大,1dp对应 的像素点越多。
- sp (可缩放独立像素)
在安卓系统里,sp 与 dp 类似,不同的是 sp 可以根据用户的字体大小首选项进行缩放,而 dp 则不会。 尽量使用 dp 作为空间大小单位,sp 作为文字相关大小单位,例如:新闻类和短信类等大篇幅文本,推荐使用 sp 为单位。
android中的dp在渲染前会将dp转为px。根据这个公式。
px = density * dp;
density = dpi / 160;
px = dp * (dpi / 160);
而dpi是这么计算出来的。
image即勾股定理出对角线,除以屏幕尺寸,就是dpi大小了。
这样会出现一个问题,就是当手机的dpi越大的时候,使用dp计算出来的px值是会越来越大的。假设你UI设计图是按360dp设计。
若实际手机是大于360dp的,那么显示效果,px值就会偏大。
所以为了解决这个问题,头条团队提供了一个方案,就是在设置系统density的值,通过获得UI图dpi和手机真实宽px计算,最终设置系统的density。
这个核心处理工具类
public class MyDensityUtil {
//屏幕密度
private static float sComponentDensity;
//屏幕显示的字体的密度
private static float sComponentScaledDensity;
/**
* 设置自定义的屏幕密度
* 在BaseActivity中的onCreate()方法中调用
*/
static void setCustomDensity(Activity activity, final Application application) {
//显示器的一般信息类
final DisplayMetrics appDisplayMetrics = application.getResources().getDisplayMetrics();
if (sComponentDensity ==0) {
//屏幕密度
sComponentDensity = appDisplayMetrics.density;
//屏幕显示的字体的密度
sComponentScaledDensity = appDisplayMetrics.scaledDensity;
//字体切换监听,防止切换字体再返回应用字体并没有变化
application.registerComponentCallbacks(new ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig !=null && newConfig.fontScale >0) {
sComponentScaledDensity =application.getResources().getDisplayMetrics().scaledDensity;
}
}
@Override
public void onLowMemory() {
}
});
}
//实际显示器的绝对宽度与UI宽度比例
final float targetDensity = appDisplayMetrics.widthPixels /1080F;//以1080dp和宽维度来适配的
//通过原本的ScaledDensity和density获取比例从而得出现在的实际显示器字体密度
final float targetScaledDensity = targetDensity * (sComponentScaledDensity /sComponentDensity);
//DENSITY_DEFAULT,默认参考密度
final int targetDensityDpi = (int) (DENSITY_DEFAULT * targetDensity);
//赋值到application中的density中
appDisplayMetrics.density = targetDensity;
appDisplayMetrics.scaledDensity = targetScaledDensity;
appDisplayMetrics.densityDpi = targetDensityDpi;
//下面赋值到activity中的density中
final DisplayMetrics activityDisplayMetrics = activity.getResources().getDisplayMetrics();
activityDisplayMetrics.density = targetDensity;
activityDisplayMetrics.scaledDensity = targetScaledDensity;
activityDisplayMetrics.densityDpi = targetDensityDpi;
}
使用如下:
public class BaseDensityActivity extends Activity {
/**
* px = density * dp;
* density = dpi / 160;
* px = dp * (dpi / 160);
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDensityUtil.setCustomDensity(this,getApplication());
setContentView(R.layout.activity_main);
}
}
代码比较简单,而且最重要的,这些都是公开的可操作的api,而并非是通过反射,hook去操作了系统非公开的私有api,是稳定,低入侵的方案。
网友评论