五行代码搞定安卓全屏幕适配——简单粗暴-低入侵,无继承,简单高效

我的github文档     ***转载源自 进行了提示和完善

### 话不多说,先上解决方案

```java

/**  将此文件直接复制到项目中,不要忘记清单文件配置Application,另 布局中使用pt

* (例如: android:layout_height="300pt" 用错可不适配哦!)

* feisher  @2017年8月11日14:52:27 二次整理,原稿 为新浪大牛 布隆

* 458079442@qq.com

*/

public class MyApplication extends Application{

public final static float DESIGN_WIDTH = 750; //绘制页面时参照的设计图宽度

@Override

public void onCreate() {

super.onCreate();

resetDensity();//注意不要漏掉

}

@Override

public void onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig);

resetDensity();//这个方法重写也是很有必要的

}

public void resetDensity(){

Point size = new Point();

((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

getResources().getDisplayMetrics().xdpi = size.x/DESIGN_WIDTH*72f;

}

}

```

## 为什么会有这么一篇文章呢?

**现状**

由于Android碎片化严重,屏幕适配一直是开发中较为头疼的问题。面对市面上五花八门的屏幕大小与分辨率,Android基于dp与res目录名称来适配的方案已无法满足一次编写全屏幕适配的需求,为了达到最优的视觉效果,开发过程中总是需要花费较多资源进行适配。也有开发者给出了一些自己的解决方案。首先来分析一下一些常见的解决方案的现状:

1. ### **官方适配方案**

- dp。dp是Android开发中特有的一个单位。与px不同,dp是基于屏幕像素密度的一种单位。在密度低的屏幕上或许1dp=1px,但在密度高的屏幕上可能1dp=4px。编写布局xml时,如果一个控件的长宽都使用dp来指定,那么能确保该控件在各种大小与分辨率的屏幕下的绝对大小都大致相当。也就是说无论在pad下还是大小屏手机下,我们实际看到的该控件的大小是差不多的:

![](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/dp.png)

- 资源目录名。上图可见虽然使用dp确保了控件在不同屏幕中的绝对大小一致。这样的好处在于,在大小相近的屏幕中,无论分辨率多大都不会对布局造成影响;但是当屏幕大小相差较大时,仅保证控件的绝对大小看起来就有些问题了。在res目录下可以给各资源目录都加上例如'-1920x1080'等后缀来适配不同的屏幕,具体规则可见官网文档。这样可以针对不同的屏幕提供不同的布局,甚至针对pad与手机提供两套完全不同的布局样式。但是通常情况下,设计师并不会对不同屏幕提供不同的设计图,他们的需求仅仅是不同屏幕下控件对屏幕的相对大小一致,所以dp并不能满足这一点,而对各种屏幕适配一遍又显得略为繁琐,并且修改也较为麻烦。通常我们需要的适配是这样的:

![image](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/hope.png)

- 百分比布局支持库。没有使用过,但是deprecated in API level 26.0.0-beta1。

- ConstraintLayout。百分比支持库deprecated之后推荐使用的布局,看起来似乎略复杂。

### **玩家适配方案**

广大玩家的适配目的很明确,目的就是要确保控件在不同屏幕的相对大小一致,看起来一毛一样的。以一位大神玩家的两种适配方案为例:

- 方案一。编写脚本将长度转换成各分辨率下的长度,缺点是难以覆盖市面上的所有分辨率。

- 方案二。AutoLayout支持库(安卓中**鸿洋**大神的开源)。该库的想法非常好:对照设计图,使用px编写布局,不影响预览;绘制阶段将对应设计图的px数值计算转换为当前屏幕下适配的大小;为简化接入,inflate时自动将各Layout转换为对应的AutoLayout,从而不需要在所有的xml中更改。但是同时该库也存在以下等问题:

- 扩展性较差。对于每一种ViewGroup都要对应编写对应的AutoLayout进行扩展,对于各View的每个需要适配的属性都要编写代码进行适配扩展;

- 在onMeasure阶段进行数值计算。这对于非LayoutParams中的属性存在较多不合理之处。比如在onMeasure时对TextView的textSize进行换算并setTextSize,那么玩家在代码中动态设置的textSize都会失效,因为在每次onMesasure时都会重新被AutoLayout重新设置覆盖。

- issue较多并且作者已**不再维护。**

2

### 进行一下探索和尝试如何?

个人觉得AutoLayout的设计思想非常优秀,但是将LayoutParams与属性作为切入口在mesure过程中进行转换计算的方案存在效率与扩展性等方面的问题。那么Android计算长度的收口在哪里,能不能在Android计算长度时进行换算呢?如果能在Android计算长度时进行换算,那么就不需要一系列多余的计算以及适配,一切问题就都迎刃而解了。

经过一番寻觅,发现系统进行长度计算的收口为TypedValue中的applyDimension函数,传入单位与value将其计算为对应的px数值。

```java

public static float applyDimension(int unit, float value,DisplayMetrics metrics){

switch (unit) {

case COMPLEX_UNIT_PX:

return value;

case COMPLEX_UNIT_DIP:

return value * metrics.density;

case COMPLEX_UNIT_SP:

return value * metrics.scaledDensity;

case COMPLEX_UNIT_PT:

return value * metrics.xdpi * (1.0f/72);

case COMPLEX_UNIT_IN:

return value * metrics.xdpi;

case COMPLEX_UNIT_MM:

return value * metrics.xdpi * (1.0f/25.4f);

}

return 0;

}

```

- 可以看见换算方法非常简单,而DisplayMetrics的所有属性都是public的,不用反射就能修改;

- pt的原意是长度单位磅,根据当前屏幕与设计图尺寸将metrics.xdpi进行修改就可以实现将pt这个单位重定义成我们所需要的相对长度单位,使修改之后计算出的1pt实际对应的px/屏幕宽度px=1px/设计图宽度px。

- 而这个DisplayMetrics从哪来?从源码中可以看出一般为mContext.getResources().getDisplayMetrics(),这个mContext即为所在Activity;

- Activity中所拿到的DisplayMetrics与Application中拿到的DisplayMetrics虽然不是一个实例,但是所有数值都相同,在Application中进行更改也会影响到所有Activity中;

- Configuration的变化会导致DisplayMetrics的重新计算还原,需要handle;

- px,dp与sp都是平时常用的单位,而pt,in与mm几乎没有看见过,从这些不常见的单位下手正好可以不影响其他常用的单位。

基于以上几点,便有了以下方案。

3

## 方案 (**注意划重点了**)

适配的目标是:完全按照设计图上标注的尺寸来编写页面,所编写的页面在所有大小与分辨率的屏幕上都表现一致,即控件在所有屏幕上相对于整个屏幕的相对大小都一致(看起来只是将设计图缩放至屏幕大小)。

- 核心。使用冷门的pt作为长度单位。

- 绘制。编写xml时完全对照设计稿上的尺寸来编写,只不过单位换为pt。

- 需要在代码中动态转换成px时使用TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, value, metrics)。

*UI给我们提供的设计图是这样的*

![img](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/UI.jpg)

**创建什么样的预览使用的设备以设计图为准**

- 预览。实时预览时绘制页面是很重要的一个环节。以1334x750的设计图为例,为了实现于正常绘制时一样的预览功能,创建一个长为1334磅,宽为750磅的设备作为预览,经换算约为21.5英寸((sqrt(1334^2+750^2))/72)。**此处直接按照iphone 6尺寸设置4.7也没影响**。预览时选择这个设备即可。

![img](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/RomSetting.jpg)

- 怎么创建那个750设计稿分辨率的设备呐?看图,_知道的同学请跳过_

![img](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/creatRom.png)

- 代码处理。在Application的onCreate中与onConfigurationChanged中更改DisplayMetrics(其中DESIGN_WIDTH是绘制页面时参照的设计图宽度):

```java

/**  将此文件直接复制到项目中,不要忘记清单文件配置Application,另 布局中使用pt

* (例如: android:layout_height="300pt" 用错可不适配哦!)

* feisher  @2017年8月11日14:52:27 二次整理,

* 458079442@qq.com

*/

public class MyApplication extends Application{

public final static float DESIGN_WIDTH = 750; //绘制页面时参照的设计图宽度

@Override

public void onCreate() {

super.onCreate();

resetDensity();//注意不要漏掉

}

@Override

public void onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig);

resetDensity();//这个方法重写也是很有必要的

}

public void resetDensity(){

Point size = new Point();

((WindowManager)getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getSize(size);

getResources().getDisplayMetrics().xdpi = size.x/DESIGN_WIDTH*72f;

}

}

```

这样绘制出来的页面就跟设计图几乎完全一样,无论大小屏上看起来就只是将设计图缩放之后的结果。

适配前:

![img](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/old.jpg)

适配后:

![img](https://raw.githubusercontent.com/feisher/ScreenAdapter/master/result.jpg)

*ps:引用自新浪微博  布隆  博客,感谢辛勤的开创者*

再次感谢辛勤的各位开发者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容