简介
其实Android 适配方案已经是老生常谈的事了,博主之前也深受其扰,常常羡慕做iOS的同学们不用在乎适配问题,直接使用绝对布局。随手查了一下umeng给出的分辨率分部情况,详细地地址点我,有兴趣的同学可以去看看,我也为懒得过去的同学贴一个截图吧。
有这么多的屏幕分辨率,适配也是一大难题,想必各位也想过很多相关的适配方案了吧,那么我也在这分享一套个人目前写出来觉得效果比较好的方案吧。
优点:
1.便于使用绝对布局
2.针对当前屏幕,自动适配
3.在使用绝对布局同时,可以减少onMeasure调用次数,优化UI
4.方便易用
缺点:
1.需要注入到View内部
那么列举一下我们需要了解的方面吧
dp 、 px 、 density
View 绘制流程
我们常常拿到的UI图是怎么样的
我们从什么地方着手去解决这个问题
1. dp、px、density
首先简单介绍下这些的概念
dp :
为了进一步简化适配工作,Android为我们提供了一个虚拟的像素单位 DP 或者 DIP (Density-Independent pixel),当然也可以理解为 Device-Independent Pixel。为什么说是虚拟呢,因为它的大小不是一个物理(Phisical)值,而是由操作系统根据屏幕大小和密度动态渲染出来的。
px :
pixels(像素) 屏幕上的点,不同设备不同的显示屏显示效果相同,这是绝对像素,是多少就永远是多少不会改变。
density :
简单的描述就是一个点 有几个像素。
其实这些这里就是要了解一个关键公式
px=dp∗density+0.5f
2.为什么要做这个方案
因为我们一般拿到的设计图是这样的
咋们的设计师一般就给个 这是1920 * 1080的,就这样了。
是不是觉得有些无助,这是什么鬼,我要布局可不是这样的,Android 那么多分辨率,会变形的呢。当然,有大神会考虑自己去绘制,但是我这里也就说一个点,团队中不是每个人都有那种能力,考虑到时间成本,又去看了看文档,写了这么一个小工具。
3.如何全适配
其实如何全适配,网上一找一大堆,但是要么出现了自定义属性,要不就反馈不是太好,当然还有另一种简单粗暴的方案,就是针对基本上所有的分辨率,生成一个对应的dimen放在对应的文件夹下面。
多么美好的方案,直到有一天用户反馈说他的设备适配有问题!!我觉得这么完美的方案,不支持的已定是奇怪的手机,直到有一天,我买了一个nexus 6 来做测试设备的时候
就是这个设备,我尝试了各种dimen 他也找不到对应的值 O.O,那么问题来了,是不是只有这一个设备是这样的?还有其他什么设备么?我没法一个个去尝试并且要求用户反馈。
那么进入正题
原理:
在应用启动前,我们要拿到当前屏幕的分辨率,以及density,这样就可以把需要显示的屏幕转换为一个假想的UI(即设计师给出的UI图),这样就可以跟设计给我们的UI图做等比转换。
那么我们将焦点关注到
protected void onFinishInflate() {}
这个函数是在讲View从XML加载完成后调用,在
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
之前,并且已经可以获得其中子View的参数(注意这里是子View,包含在其内部的View)
因此,我们可以在这段时间对之前在xml中设置的单位进行换算了。
4.如何使用
在Application 中初始化工具,调用函数:
//初始化,UI设计图模板为1280 * 720 的设计图 density 最好为 1,便于在XML中写适配以及测试
AutoUtils.init(context, 1280, 720, 1);
那我们就来看看这个有什么好用的地方吧
假如我们拿到了这么一个设计图(请忽略掉我只是简单的画了下框,实际设计图比这复杂得多)
首先这个Util 中包含
AutoScaleFrameLayout —> FrameLayout
AutoScaleLinearLayout —> LinearLayout
AutoScaleRelativeLayout —> RelativeLayout
在布局文件中,我们可以先这样写:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="640px"
android:layout_height="360px">
<FrameLayout
android:layout_width="320px"
android:background="#aa0040"
android:layout_height="360px">
</FrameLayout>
<View
android:layout_gravity="end"
android:layout_width="160px"
android:background="#af80f1"
android:layout_height="180px"/>
<View
android:layout_gravity="end"
android:layout_marginTop="180px"
android:layout_width="160px"
android:background="#f1a061"
android:layout_height="180px"/>
<View
android:layout_marginLeft="320px"
android:layout_width="160px"
android:background="#0f80f1"
android:layout_height="180px"/>
<View
android:layout_marginLeft="320px"
android:layout_width="160px"
android:layout_marginTop="180px"
android:background="#4ff0f1"
android:layout_height="180px"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="360px">
<TextView
android:layout_width="880px"
android:layout_height="120px"
android:text="@string/t1"
android:textSize="30px" />
<TextView
android:layout_width="match_parent"
android:layout_height="120px"
android:layout_marginTop="120px"
android:gravity="center"
android:text="@string/t2"
android:textSize="33px" />
</FrameLayout>
</LinearLayout>
接下来我们就可以使用Preview来预览了,用一个图,直接来说明吧
这就是我所需要的界面啦,并且所有设备上,都会这么显示,当然这仅仅是预览。接下来做件事:
1.把上面所有我xxxxLayout换成工具中的AutoScaleXXXXXLayout。
2.把所有的 px 换成 dp
改完后是这样的:
<?xml version="1.0" encoding="utf-8"?>
<com.ly2251.autoscaleviewui.autoviewutils.AutoScaleLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout
android:layout_width="640dp"
android:layout_height="360dp">
<com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout
android:layout_width="320dp"
android:background="#aa0040"
android:layout_height="360dp">
</com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout>
<View
android:layout_gravity="end"
android:layout_width="160dp"
android:background="#af80f1"
android:layout_height="180dp"/>
<View
android:layout_gravity="end"
android:layout_marginTop="180dp"
android:layout_width="160dp"
android:background="#f1a061"
android:layout_height="180dp"/>
<View
android:layout_marginLeft="320dp"
android:layout_width="160dp"
android:background="#0f80f1"
android:layout_height="180dp"/>
<View
android:layout_marginLeft="320dp"
android:layout_width="160dp"
android:layout_marginTop="180dp"
android:background="#4ff0f1"
android:layout_height="180dp"/>
</com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout>
<com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout
android:layout_width="match_parent"
android:layout_height="360dp">
<TextView
android:layout_width="880dp"
android:layout_height="120dp"
android:text="@string/t1"
android:textSize="30dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="120dp"
android:gravity="center"
android:text="@string/t2"
android:textSize="33dp" />
</com.ly2251.autoscaleviewui.autoviewutils.AutoScaleFrameLayout>
</com.ly2251.autoscaleviewui.autoviewutils.AutoScaleLinearLayout>
然后编译,完事大吉,是不是不用再去纠结这怎么就少了几个像素,完美的处女座杀手。
最后附上Demo地址:https://github.com/ly2251/AutoScaleView
此工具还有不是很完善的地方,也欢迎大家提出遇到的问题。