探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制
Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南
自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理
PS:Android多分辨率适配框架视频教程同步更新啦
前言
在上一篇文章中我们先讨论了Andoid中常见的与度量有关的知识和技术;然后对于drawable的加载原理也做了一个完整分析。
在完成这些准备之后,就要直面我们的目标:一套UI图实现多分辨率的适配
思路
国庆的时候出去玩了一圈,回来之后我洗了一张女友的照片放到相框里面
昨天,我又买了一个同款的小号的相框,它的宽和高只有原来的一半。
我现在把原来的照片等比例缩小二分之一是不是就可以将其放入新的相框了呢?
嗯哼,与此类似:为了实现多分辨率的适配,我们需要将UI进行等比例的缩放。
比如:可以把一个在高分辨率(例如1920*1080)手机上的布局等比例缩小后展示在其他分辨率(例如800*480)的手机上。
在知晓了总体思路后,我们再来拆解Android多分辨率适配框架的具体实现。
UI切图
在多数情况下UI设计师会切几套对应的图,我们再将其放入drawable-ldpi、drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi、drawable-xxxhdpi文件夹即可。但是,现在,我们只需要一套图,那么到底采用哪一套切图呢?目前,Android手机中主流的分辨率是xxhdpi,所以我们可以采用drawable-xxhdpi这套UI切图,在xxhdpi的手机上调好布局后,当其他较低dpi手机加载该UI时再将其等比例缩小即可。
切图是有了,但是该把它放到res下哪个文件夹中呢?
有人说:这不是明摆着的么,当然放到drawable-xxhdpi中呗。
真的是这样么?
嗯哼,结合我们上一篇的讲解可知:如果把切图放到了drawable-xxhdpi中,那么当这些切图显示在hdpi的手机上时图片会被缩小并且可能导致失真。同理,如果这些切图显示在xxxhpi的手机上时图片会被放大并且可能导致失真。图片的放大和缩小,这个好理解,但是为什么会导致图片失真呢?或者说为什么不是等比例的放大或者缩小呢?现在通过一个实例来分析原因。
在此,准备两部测试手机:
华为P7,分辨率为1920*1080,dpi为480
HTC T392,分辨率为800*480,dpi为240
现在有一套专门为dpi等于480的手机准备的切图放在drawable-xxhdi中。当P7加载该套切图时图片显示正常,当T392加载该套图片时图片被缩小,且缩小比为240/480=0.5。但是请注意两部手机分辨率的比值。其中,宽的比:480/1080=0.44;高的比800/1920=0.42;也就是说两个屏幕的分辨率的比大概是0.4,但是图片的缩放比是0.5,所以这两者的不一致导致了图片缩放后的失真。如果要使图片在HTC T392上不失真,那么需要按照0.4缩放图片,而非0.5.
为了摆脱这个桎梏,避免图片的缩放导致图片失真:
- 将切图放入drawable-nodpi中。
该文件夹中的图片不会被缩放,在不同分辨率的手机上都只显示原图的大小。如此以来,摒弃了系统对于图片的缩放,为我们以后自己处理图片的缩放做好了铺垫。 - 计算出缩放比。
依据不同的分辨率计算出缩放比。
总之,我们不再采用系统提供的对于图片的适配和缩放,而是自己确定一个准确的缩放比例将高分辨率的UI按照该比例缩放从而实现多分辨率的适配
布局的细节
嗯哼,在知道了怎么做之后,我们就首先要在一个高分辨率(如:1920*1080)手机上完成布局。
在布局的过程中,请注意一个问题:不再使用dp、sp作为大小单位,而是统一使用px。
为什么要这么做呢?
- 缩放比例的确定是基于屏幕的分辨率而确定的。
屏幕的分辨率均是采用px作为单位的,所以在布局时亦采用px从而保证缩放比例的一致和准确 - dp和sp均与设备的dpi有关。
不同设备的dpi值不一样,所以在不同的设备上同一个dp和sp所对应的px值是不尽相同的。如果采用dp和sp作为尺寸的单位,那么在缩放时会产生较大的偏差
代码实现
好了,现在图片有了,布局也写好了,现在就要开始对UI缩放了。
第一步:
计算缩放比
int widthPixels = displayMetrics.widthPixels;
scale = (float)widthPixels / BASE_SCREEN_WIDTH_FLOAT;
通过设备的宽与BASE_SCREEN_WIDTH_FLOAT的比值计算出缩放比。
此处的BASE_SCREEN_WIDTH_FLOAT就是一个缩放的基准,比如高分辨率手机(如:1920*1080)中的1080。
第二步:
等比例缩放UI
/**
* 原创作者:
* 谷哥的小弟
*
* 博客地址:
* http://blog.csdn.net/lfdfhl
*/
public static void scaleViewSize(View view) {
if (null != view) {
int paddingLeft = getScaleValue(view.getPaddingLeft());
int paddingTop = getScaleValue(view.getPaddingTop());
int paddingRight = getScaleValue(view.getPaddingRight());
int paddingBottom = getScaleValue(view.getPaddingBottom());
view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
LayoutParams layoutParams = view.getLayoutParams();
if (null != layoutParams) {
if (layoutParams.width > 0) {
layoutParams.width = getScaleValue(layoutParams.width);
}
if (layoutParams.height > 0) {
layoutParams.height = getScaleValue(layoutParams.height);
}
if (layoutParams instanceof MarginLayoutParams) {
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) layoutParams;
int topMargin = getScaleValue(marginLayoutParams.topMargin);
int leftMargin = getScaleValue(marginLayoutParams.leftMargin);
int bottomMargin = getScaleValue(marginLayoutParams.bottomMargin);
int rightMargin = getScaleValue(marginLayoutParams.rightMargin);
marginLayoutParams.topMargin = topMargin;
marginLayoutParams.leftMargin = leftMargin;
marginLayoutParams.bottomMargin = bottomMargin;
marginLayoutParams.rightMargin = rightMargin;
}
}
view.setLayoutParams(layoutParams);
}
}
利用该方法对布局中的每个View进行缩放操作。
在该方法中对每个View的宽高,padding,margin值都按比例缩放,并且在缩放后重新设置其布局参数。
第三步:
关于TextView的特殊处理。
对于TextView,不但要缩放其尺寸,还需要对其字体进行缩放:
/**
* 原创作者:
* 谷哥的小弟
*
* 博客地址:
* http://blog.csdn.net/lfdfhl
*/
Object isScale = textView.getTag(R.id.is_scale_font_tag);
if (!(isScale instanceof Boolean) || !((Boolean) isScale).booleanValue()) {
float size = textView.getTextSize();
size *= scale;
textView.setTextSize(0, size);
}
除此以外,还要考虑到对TextView的CompoundDrawable进行缩放
/**
* 原创作者:
* 谷哥的小弟
*
* 博客地址:
* http://blog.csdn.net/lfdfhl
*/
public static Drawable scaleDrawableBounds(Drawable drawable) {
int right=getScaleValue(drawable.getIntrinsicWidth());
int bottom=getScaleValue(drawable.getIntrinsicHeight());
drawable.setBounds(0, 0, right, bottom);
return drawable;
}
总结
嗯哼,关于Android多分辨率适配框架的原理就分析完了。再回过头看,可以发现:其实它没有我们想象的那么难。
对于该框架的理解和使用,有以下几个要点:
- 切图存放于drawable-nodpi
- 抛开系统的dpi并且摒弃dp和sp,统一使用px作为尺寸单位
- 按照高分辨率(如1920*1080)切图和布局
- 在代码中等比例缩放每个View
目前,xxhdpi分辨率的手机占了主流,所以在该框架中采用了drawable-xxhdpi的切图。倘若以后xxxhdpi分辨率的手机占了主导地位,那么就请UI设计师按照该分辨率切图,我们将其放在drawable-nohdpi中,再修改BASE_SCREEN_WIDTH_FLOAT即可。
who is the next one? ——> Demo
PS:Android多分辨率适配框架视频教程同步更新啦