一、现状
由于Android碎片化严峻,屏幕适配一直是开发中较为头疼的问题。面临市面上形形色色的屏幕巨细与分辨率, Android依据dp与res目录称号来适配的计划已无法满意一次编写全屏幕适配的需求,为了 到达最优的视觉效果,开发过程中总是需求花费较多资源进行适配。也有开发者给出了一些自己的解决计划。首先来剖析一下一些常见的解决计划的现状:
官方适配计划
dp。dp是Android开发中特有的一个单位。与px不同,dp是依据屏幕像素密度的一种单位。在 密度低的屏幕上或许1dp=1px,但在密度高的屏幕上或许1dp=4px。编写布局xml时,假设一个控件的长宽都运用dp来指定,那么能保证该控件在各种巨细与分辨率的屏幕下的肯定巨细都大致适当。也便是说不管在pad下仍是巨细屏手机下,咱们实践看到的该控件的巨细是差不多的:
资源目录名。上图可见尽管运用dp保证了控件在不同屏幕中的肯定巨细共同。这样的优点在于,在巨细附近的屏幕中, 不管分辨率多大都不会对布局形成影响;可是当屏幕巨细相差较大时,仅保证控件的 肯定巨细看起来就有些问题了。在res目录下能够给 各资源目录都加上例如‘-1920×1080’等后缀来适配不同的 屏幕,详细规矩可见官网文档。这样能够针对不同的屏幕供给不同的布局,乃至针对pad与手机供给两套彻底不同的布局款式。可是一般情况下,规划师并不会对不同屏幕供给不同的规划图,他们的需求仅仅是不同屏幕下控件对屏幕的相对巨细共同,所以dp并不能满意这一点,而对各种屏幕适配一遍又显得略为繁琐,而且修正也较为费事。一般咱们需求的适配是这样的:
百分比布局支撑库。没有运用过,可是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较多而且作者已不再保护。
二、主意
关于巨细差异较大的屏幕,本不应运用同一套规划计划,不然大屏的优势没有彻底体现出来,从官方的适配计划也好像是表达了这个意思。可是在实践规划与开发中,关于一个一般的App,很少有项目有志愿有精力来对各屏幕来别离规划与开发一套规划计划来适配。
一般的一个简略的适配需求是:假设规划图宽度为200,一个控件在规划图上标示的长度为3,那么该控件长度适当于总宽度的3/200,那么咱们期望在任何巨细的屏幕上该控件所体现的长度都为屏幕宽度的3/200。
个人觉得AutoLayout的规划思维十分优异,可是将LayoutParams与 特点作为切入口在mesure过程中进行 转化核算的计划存在功率与扩展性等方面的问题。那么Android核算长度的收口在哪里,能不能在Android核算长度时进行换算呢?假设能在Android核算长度时进行换算,那么就不需求一系列剩余的核算以及适配,一切问题就都方便的解决了。
通过一番寻找,发现体系进行长度核算的收口为 TypedValue中的applyDimension函数,传入单位与value将其核算为对应的px数值。
能够看见换算办法十分简略,而DisplayMetrics的 一切特点都是public的,不必反射就能修正;
pt的本意是长度单位磅,依据当时屏幕与规划图尺度将metrics.xdpi 进行修正就能够完成将pt这个单位重界说成咱们所需求的相对长度单位,使修正之后核算出的1pt实践对应的px/屏幕宽度px=1px/规划图宽度px。
而这个DisplayMetrics从哪来?从源码中能够看出一般为mContext.getResources().getDisplayMetrics(),这个mContext即为地点AcTIvity;
反正屏切换等ConfiguraTIon的改变会导致DisplayMetrics 的 从头核算复原;
px,dp与sp都是平常常用的单位,而pt,in与mm简直没有看见过,从这些不常见的单位下手正好能够不影响其他常用的单位。
依据以上几点,便有了以下计划。
三、计划
本适配计划的方针是:彻底依照规划图上标示的尺度来编写页面,所编写的页面在一切巨细与分辨率的屏幕上都体现共同,即控件在一切屏幕上相关于整个屏幕的相对巨细都共同(看起来仅仅将规划图等比缩放至屏幕宽度巨细)。
中心。运用冷门的pt作为长度单位,依照上述主意将其重界说为与屏幕巨细相关的相对单位,不会对dp等常用单位的运用形成影响。
制作。编写xml时 彻底对照规划稿上的尺度来编写,只不过单位换为pt。假设规划图宽度为200,一个控件在规划图上标示的长度为3,只需求在初始化时界说宽度为200,制作该控件时长度写为3pt,那么在任何巨细的屏幕上该控件所体现的长度都为屏幕宽度的3/200。假设需求在代码中动态转化成px的话,运用 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PT, value, metrics)。
预览。实时预览时制作页面是很重要的一个环节。以1334×750的规划图为例,为了完成于正常制作时相同的预览功用,创立一个长为1334磅,宽为750磅的设备作为预览,经换算约为21.5英寸((sqrt(1334^2+750^2))/72)。 预览时挑选这个设备即可。
代码处理。在acTIvityonCreate时修正DisplayMetrics即可,引荐写在基类或AcTIvityLifecycleCallbacks中,参阅github demo。
Point size = new Point(); activity.getWindowManager().getDefaultDisplay().getSize(size); context.getResources().getDisplayMetrics().xdpi = size.x / designWidth * 72f;
这样制作出来的页面就跟规划图简直彻底相同,不管巨细屏上看起来就仅仅将规划图缩放之后的成果。
适配前(左图API19 400×800, 右图API24 1440×2560):
适配后(左图API19 400×800, 右图API24 1440×2560):
尽管计划比较简略,可是为了方便运用也收拾成了一个library,代码及demo见github