问题背景:之前项目里UI是通过JSON模版来进行控制的,为了数据通用性强(与字体大小对应,匹配xml方便)采用dip作为单位。但遇到的问题是在不同的分辨率或屏幕密度的屏幕上不能够正常显示。虽然采用相对布局的模板设计既能解决屏幕适配问题又能解决手机在横竖屏翻转UI切换问题,但这样做有可能因为屏幕不同而造成UI显示变形(目前的需求不考虑屏幕翻转)。所以在不改变我之前就定好的UI模版规则的前提下,使一个模版适配所有屏幕就显得尤为重要了。
自己总结一般有如下方法:
1. fragment,它就是为屏幕尺寸的“碎片”而生的,但并不全能。
2. 百分比,目前新出来有开源库 compile ‘com.zhy:percent-support-extends:1.1.1’,很好用。
3. 缩放,缩放又有多种缩放方式:
a.对整个屏幕的横竖两个方向按照“屏幕宽/模板宽”和“屏幕高/模板高”的比例分别缩放,即将所有的位置、宽高、字体大小等数据全部按两个方向分别缩放。优点是:模板效果和屏幕效果接近。缺点:可能会因为屏幕不同造成轻度显示差异。
b.显示区域按照“屏幕宽/模板宽”与“屏幕高/模板高”的最小值进行横竖缩放。优点:不会造成显示差异。缺点:可能造成留白。
本次采用方法2中的a,原因有三点:
1. 界面效果与界面模板绑定。
2.考虑屏幕翻转,只要不是横屏的模版数据显示在竖屏上,就不会产生明显的显示差异。
3.不考虑屏幕翻转,在不同屏幕上的显示差异较小。
当然这是在我在画满了两大张纸然后解决了匹配所有屏幕这个问题的前提下,写这些的。(纸张就不传了,放在这也没多少人看得懂,不是难看懂而是难看)。而屏幕的翻转也只不过是在onConfigurationChanged()中切换模板而已。
dip,dp,px中与屏幕上显示效果直接相关的是px,其他概念自己查,网上一大堆。如果没有理解这些概念,下面的东西也是可有可无。
px与dip之间转换如下:
public float dip2px(float dipValue, float density) {
final float scale = density;
return dipValue * scale + 0.5f;
}
public float px2dip(float pxValue, float density) {
final float scale = density;
return pxValue / scale + 0.5f;
}
下来先分享一下我的思路:
首先,要解决的问题是匹配不同分辨率的屏幕,最常见的是1920×1080,还有640×480等等很多不同屏幕,如果要保证UI的比例不变就必须对不同分辨率的屏幕进行比例换算。如果有人问我为什么是px单位而不是dip,还是那张动图。
所谓换算,其实是按长宽比例相比于模板数据(定的标准)到当前设备进行换算。所得值便是当前屏幕要显示的数据了。
代码如下:
public float exchangeUnit(int basicUnit, int toUnit, float nowpx){
float topx = nowpx;
try {
topx = (nowpx * toUnit)/basicUnit;
}catch (ArithmeticException e){
e.printStackTrace();
}
return topx;
}
接下来,考虑屏幕密度,这个时候就需要用到dip/dp了。将上面计算得到的当前屏幕中的数据值(px)转换成dip,当然转换的时候就要按照当前density(屏幕密度值)来计算了,换算如下:
/**
*
* @param basicUnit 标准模板px
* @param toUnit 当前屏幕px
* @param nowpx 要转换的值
* @param density 当前屏幕密度
* @return
*/
public float exchangeUnit(int basicUnit, int toUnit, float nowpx, float density){
float topx = nowpx;
try {
topx = (nowpx * toUnit)/basicUnit;
}catch (ArithmeticException e){
e.printStackTrace();
}
return px2dip(topx, density);
}
最后,使用以上函数就可以进行转换了。如下例子,模板数据屏幕密度为1.5:
public void exchangeAllHorizontalValue(int basicUnit, int toUnit, float density){
x = (int)exchangeUnit(basicUnit, toUnit, dip2px(x, 1.5f), density);
width = (int)exchangeUnit(basicUnit, toUnit, dip2px(width, 1.5f), density);
}
public void exchangeAllVerticalValue(int basicUnit, int toUnit, float density){
y = (int)exchangeUnit(basicUnit, toUnit, dip2px(y, 1.5f), density);
height = (int)exchangeUnit(basicUnit, toUnit, dip2px(height, 1.5f), density);
}
到这里,得到的值就是从标准模板中转换成当前屏幕中dip值了(因为我的自定义控件初始化函数所用数据都是dip格式)。其实还是按照标准模板与当前屏幕分辨率比例进行转换了而已,能够达到适配各种屏幕而不至于使UI严重变形,又简单。至于字体大小,大家可以尝试一下SpannableString这个类
当然,这些是已经在大量屏幕上测过的,类似于百分比但又比百分比更加稳定。经项目实际使用,百分比库不支持个别控件所以可能出现明显变形。但这种参照标准屏幕进行数据转换的方法只局限于动态加载界面的方式(最好将转换过程放到后端进行,这样前端就不需要对每个数据进行判断或转换了)