超久没写过博客了,来一篇小干货。几行代码完成屏幕适配方案。
发现问题
开发过程中,UI一般是以1080P的分辨率给与标注图的,宽度1080px,也就是360dp。当我们按照标注开发时,发现交付UI时,总是出现这样或者那样的问题,比如icon大小总是不对一样。
比如横排两个图片宽高540px,我们布局很容易就标注两个ImageView 180dp,可是我发现部分手机显示效果异常。
发现无法铺满屏幕,一般来说我们都会采用代码中动态布局的方式,计算出屏幕的一半宽度,最后修改布局的宽高解决,但是这样太损耗性能,和不方便了。
在代码中计算一波180dp->495px,并不是我们认为的540px,在小米的手机的开发者选项中有个可以调整的最小宽度
我们将其调整为360dp,显示效果则是我们预期的占满整个屏幕宽度。
问题分析
先摆出几个公式:
dpi = √(宽度²+高度²) / 屏幕尺寸(如5寸)
density = dpi/160f
dp = px / density
目前市面手机dpi都不同,所以导致density的值也就不同,特别是部分手机如小米可以调整density的值,导致我们根据UI标注图计算的dp放到代码中显示效果各有不同。
所以我们的解决思路就是修改density的值,使其满足我们的dp/px/sp直接的转换计算。
因为现在1080P的手机分辨率也不同于1920*1080,所以我们需要以宽度或者高度的其中一方作为标准去修改转换比率,我们不再根据dpi计算出density,而是根据屏幕宽度倒推出density。
有人说可以根据宽度和高度分别修改不同的比例,问题是如果180dp的值在宽高的px则不同,所以只能取宽或者高为基准
代码实现
package com.monke.screenfitdemo;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
public class ScreenFitManager {
private static ScreenFitManager instance;
private ScreenFitManager() {
}
public static ScreenFitManager getInstance() {
if (instance == null) {
synchronized (ScreenFitManager.class) {
if (instance == null) {
instance = new ScreenFitManager();
}
}
}
return instance;
}
private float temp = 0;
private Fit fit;
private float whole;
/**
* application onCreate第一行调用
*/
public void init(@NonNull Application application, Fit fit, float whole) {
this.fit = fit;
this.whole = whole;
if (whole < 0) {
throw new RuntimeException("whole数据必须大于0");
} else {
DisplayMetrics displayMetrics = application.getResources().getDisplayMetrics();
float defaultDensity = displayMetrics.density;
float defaultScaledDensity = displayMetrics.scaledDensity; //字体sp计算用
temp = defaultScaledDensity / defaultDensity;
float density;
int dpi;
float scaledDensity;
if (fit == Fit.WIDTH) {
density = displayMetrics.widthPixels / whole;
dpi = (int) (density * 160f);
scaledDensity = temp * density; //重新计算出字体缩放比例
} else {
density = displayMetrics.heightPixels / whole;
dpi = (int) (density * 160f);
scaledDensity = temp * density; //重新计算出字体缩放比例
}
displayMetrics.density = density;
displayMetrics.densityDpi = dpi;
displayMetrics.scaledDensity = scaledDensity;
}
}
/**
* activity onCreate中第一行调用
*/
public void fitInit(@NonNull Activity activity) {
DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
float density;
int dpi;
float scaledDensity;
if (fit == Fit.WIDTH) {
density = displayMetrics.widthPixels / whole;
dpi = (int) (density * 160f);
scaledDensity = temp * density; //重新计算出字体缩放比例
} else {
density = displayMetrics.heightPixels / whole;
dpi = (int) (density * 160f);
scaledDensity = temp * density; //重新计算出字体缩放比例
}
displayMetrics.density = density;
displayMetrics.densityDpi = dpi;
displayMetrics.scaledDensity = scaledDensity;
}
public enum Fit {
WIDTH,
HEIGHT
}
}
在Application中onCreate中添加
例如设计师给的是1080P的设计图
ScreenFitManager.getInstance().init(this, ScreenFitManager.Fit.WIDTH, 360f);
使用Activity中onCreate中添加即可
ScreenFitManager.getInstance().fitInit(this);;
满足我们的要求,并且字体缩放也做了适配,可以根据UI标注图肆无忌怛的填值了。
如果需要针对用户修改系统字体大小即时优化,请开发者自行添加监听器。
本方法基本没什么缺点,都是使用系统API,基本不用考虑后续android版本升级问题,且代码入侵性几乎为0。