手淘启动页全面屏和虚拟键适配

背景

华为对新发布的机器进行适配测试,发现手淘存在全面屏适配问题,随后还附了个3页的文档,文档比较粗泛的描述了一下不适配将会存在的问题,适配可以采取的措施,以及Google开发者文档。简单来说,因为全面屏长宽比大于16:9的标准屏,如果不做全面屏适配,会出现上下方黑边,对于全屏设置背景的图片,可能出现上下拉伸效果,体现到手淘上是这样的:



随后google了一下全面屏适配,果然发现其他厂商也有同样的问题,比如小米全面屏适配文档,就点名了今日头条的黑边:



和手机淘宝的拉伸:



随后贴出了完美适配过的王者荣耀:



打脸啪啪啪啊,再一看适配文档日期,居然这个问题存在了大半年,不禁为用户捏了把汗。。。 
再来看下某东的闪屏页,中规中矩,没这个毛病:



淘宝好歹也是大厂,首屏就输给了某东,这个我服~~~~

开始适配

既然问题存在,那就开干吧,按照华为文档的说法是:



也就是说,只要在application全局添加android.max_aspect属性即可,so eazy!但随后打开主工程的AndroidManifest.xml,看到android.max_aspect已经存在,时间是2017-06-01。确认了一下google 开发者文档,targetSdkVersion=23是Ok的,也就是说之前做过全面屏适配,只是启动页被忽视掉了。那对于启动页这种纯图片背景的怎么适配呢?在文档中看到这么一句话:



再看一下启动页的代码实现,本质上是定义了一个Theme.Welcome,然后将Theme.Welcome设置到Application,这样就实现了图片打底的特效,研究了一番Theme的适配属性,发现不存在类似于CENTER_CROP效果的(组合)属性。那既然使用ImageView承接可以实现,何不将启动页改造成ImageView来实现闪屏的效果呢?说干就干,写了一个很简单的Demo,设置好ImageView的属性,如下:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/aab"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:contextClickable="true">

    <ImageView
        android:id="@+id/image_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/taobao_launch_origin"/>
</FrameLayout>

显示效果:



整体看起来这个效果比拉伸和黑边好多了,但是仔细看下启动过程,点击icon之后会先白一下,随后才看到欢迎页,而这种启动白屏会带给用户启动的迟钝感:



研究了一下,发现启动的之所以会出现白屏和黑屏,原因是系统Launcher启动淘宝第一个Activity过程中会有个耗时,而此时设置的主题是纯色的系统主题,并没有有效可见的画面,而这个白屏和黑屏的时长,会随着应用Application的耗时变化而变化。市面上有不少的App依然采用的这种方案,比如我们熟知的某乎,启动也是白屏一段时间。

动画里的白屏时间是Demo展示的,实际上手淘由于Application较重,加上有外链回流等各种情况,用户感知的白屏时间会更长。到这里,基本上宣告ImageView填充的方式失败,也可以跟华为的适配文档说拜拜了。

重整方案

既然ImageView的方案不可行,那就重新探索一份方案吧,调研了一下适配全面屏和虚拟键的闪屏方案,发现可以有下面三种方式: 
(1)使用多套图适配不同尺寸手机。利用安卓自动资源匹配的优势,给不同屏幕提供不同比例图片,对应的三星S8应该提供一套drawable-long的启动图。 
(2)使用layer-list自定义drawable + Theme 来动态布局的形式。如果将淘宝启动页画面切割为上下两个区域,这种形式可以实现淘宝首屏素材的动态布局,一个居中,一个靠下,利用自定义drawable自动适配的特性,就能适配到大部分机型。 
(3)使用.9 drawable + Theme的形式来定义启动界面。.9 负责实现画面的填充。 
第一种方案首先毙掉,作为一个体量这么大的App,为了适配首屏加这么多图片,包大小管不住了要,架构组的KPI保不住了要,而且这样做无非是增大了UED同学的切图工作量,是一条最简单的路,但效果却不是最优。 
说说第二种方案,在网上看到这个方案的时候,作者用youtube app举例,中间一个小icon,底图是Google 的logo,灰色背景上面一个播放icon,清新而脱俗,视觉效果也不错(详细文章链接),见下图:



不过我们仔细看下淘宝的启动页,最下面是“阿里云提供云计算”,中间是淘宝的Logo抽象出来的小盒子,主体部分是从小盒子里面“腾飞”出来的五彩斑斓的物品,一张图解释了什么叫万能的淘宝。回到手淘的情况,我把启动图片分成两段,上部分居中,下部分靠下,结果整体去看“万能的淘宝”,发现整个上部分是空白,而下部分在小屏手机上会交错到一块,总之两部分画面的协调不是很好。如果要继续做的话,恐怕是需要UED同学重新设计首屏画风了。 
第三种方案基本思想是使用一张图适配所有的机型。最后的效果实现,其实是利用了安卓Bitmap的预缩放 + .9的填充,具体做法是,提供一张合适大小的.9 图片,放置到合适density的目录,然后在.9图片标注好合适的安全区域跟可填充区域。下面来探索下上面提到的三个“合适”。

适配探索

Android的“碎片化”是业界皆知的,所谓的碎片化就是指因为Android开放的特性,导致各个厂商定制了不同版本的Rom,定制了不同版本的屏幕尺寸。所以相对于iOS应用开发者,Android应用开发者在适配问题上耗费的精力会更多一些。不过Android在设计之初就考虑到了这个问题,并且方案在不断完善中,我们需要做的就是了解其中原理,并选择合适的方案和充分的测试去保证适配的成功。

(1)选择合适尺寸的图片

首先我这边拿到的原图是一张720px * 1280px的png图片,我们在建立项目的时候,一般会有下面几组drawable文件夹:drawable, drawable-hdpi, drawable-xhdpi, drawable-xxhdpi, drawable-xxxhdpi,有时候我们为了适配特殊情况,可能还会加入drawable-long, drawable-nodpi,那这么多文件夹,图片应该放到哪个呢?我们来看看Google的全屏幕适配标准



PS:这里Google开发者文档里面的dpi的概念实际上是ppi的误用,可以参考:ppi-vs-dpi-whats-the-difference 。关于 dpi, ppi, px, dp, dip, sp 的概念,建议大家参考这篇文章:http://www.jianshu.com/p/913943d25829 
这里我们看到Google会建议不同dpi/ppi区间的资源文件放置到对应的目录,但图片只有px属性,需要换算。这里我们假设目标机器也是1280 * 720,以HTC One X 为例,斜对角尺寸是4.7 inch,那么PPI就是:



312ppi再对应上面的屏幕适配标准,应该放到xhdpi里面。这里为什么要以真机举例呢,这是因为纯图片尺寸只有像素的概念,单纯给定一张图片,说他的dpi是多少,该放哪个文件夹是没办法决断的,所以给定一张图我们决定要放置到哪个目录,一般会取市面上同分辨率的有代表性的机器,计算出对应的dpi再决定。或者做的更好的是,我们可以有一份市面上主流机型的分辨率,PPI参数汇总,然后决定出什么样的图,放置到哪个目录。这样的话,我们在所有机型上面图片的整体缩放性能开销表现最佳。 为什么这么说,这是因为Android手机在使用drawable创建bitmap的时候,会有个“选择合适图片”的逻辑,首先它会获取设备本身固有的PPI参数,比如HTC One X是312 ppi,那么首先会从xhdpi的文件夹中寻找,如果找到这张图片并且发现分辨率跟设备一致,就不会对图片进行放缩,直接用这张图片覆盖屏幕,而如果没有找到,就会接着从高dpi的文件夹寻找(xxhdpi, xxxhdpi),再找不到就会从nodpi寻找,其次是hdpi -> mdpi -> ldpi。如果寻找的不是对应dpi目录的图片,会对图片进行一次放缩,放缩的scale = 设备自身density / 资源目录density,这样高分辨率的图放到低dpi的目录,会导致bitmap内存占用的增加,参考:你的Bitmap到底占用多大内存。而从高dpi的目录找到一张低dpi的图片,又会导致图片被压缩,在不带其他参数的情况下,会导致图片填不满目标区域。

(2)9-Patch的适配

那现在回到适配三星S8的事情上来,三星S8的分辨率是2960x1440(18.5:9),斜对角尺寸是5.8 inch,那么对应的ppi应该是567.5 ~= 568ppi,跟官方计算出来的参数一致,假设现在市面上80%都是三星S8手机,我们应该设计一张2960x1440的图片,放置到xxxhdpi目录下,这样子可以减少额外的图片放缩。但目前市面上大部分手机还都是16:9的屏,所以现在比较好的做法是采用16:9的图去适配三星18.5:9的屏幕。 好了,到这里我们文件夹目录是放对了,如下定义一个主题:

<style name="AppTheme.Splash" parent="@style/Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowBackground">@drawable/taobao_launch</item>
</style>

将定义好的Theme在Application或者Welcome页面应用,我们会发现图片被放大,且上下拉伸:



好了,我们分析下图片的拉伸过程。图片本身是1280 x 720,放到xhdpi,对应资源density是320(density的定义可以查看Android系统源码:BitmapFactory),目标设备是2960 x 1440,设备是568ppi,对应的density是640,当三星S8找到这张图的时候,会进行scale = 640 / 320 = 2 的放缩,这样放缩后的bitmap是2560 x 1440,也就是说宽刚好覆盖,但高小于2960。这个时候当系统加载这张图片作为windowBackground的时候,发现纵向无法满足会进行自适应拉伸,也就是出现了纵向拉伸的效果。所以为了避免高度不够的情况下出现纵向拉伸,需要定义.9来纵向填充,而不是简单地使用match_parent。

(3)探索合适的可拉伸区域

9-patch是Android支持的一种可伸缩的图片格式,格式跟png是兼容的,基本做法是在上下左右各增加1px的边框,左边和上边定义了可伸缩区域,右边下边定义了内容可覆盖区域(更多可以查看:draw-nine-patch)。为了支持最基本的适配,我们可以在背景图空白区域的上方和左方各打几个点,允许图片拉伸适配,如下图的箭头:



写个demo拿到三星S8上面适配运行一下,内容区域已经不再拉伸了,整体效果也不错:



到这里三星S8 为代表的全面屏适配应该没问题了,为了保险起见,从组内搜刮了其他同学的几台设备一个个测试,但是到Nexus 5的时候,发现下面奇怪的一幕:



哥们,你这脚踩三个透明的虚拟按键,用户就看不出来你被遮挡了吗? 
查了一下Nexus 5的配置参数,这款手机分辨率1920x1080,445ppi,那么对应的targetDensity应该是480,而图片的density是320,也就是1280x720的图片放缩480/320=1.5倍,刚好是1920x1080,此时会铺满屏幕,这样虚拟键就刚好盖住了文字部分。 那有没有其他的办法呢?继续琢磨了一下,发现Android在推出虚拟按键的时候,也提供了一个Api允许我们避开虚拟键区域,只需要在Theme里面定义属性android:windowDrawsSystemBarBackgrounds 为false。这样系统虚拟键弹出来的时候,我们可以只绘制虚拟键上方部分,而虚拟键收起来的时候,我们可以绘制全屏幕。真应了那什么什么门什么什么窗!又因为这是api level 21才引入的属性,所以我们需要建立一个values-v21的文件夹,同时在里面定义如下的Theme:

<style name="AppTheme.Splash" parent="@style/Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:windowBackground">@drawable/taobao_launch</item>
</style>

效果如下:



乍一看好像没啥问题,可是我们再认真看下这张图,下面的阿里云文字貌似显示不全,图片也怀疑被压缩,我们再回忆一下刚才的Nexus参数,包含虚拟键的部分是16:9,但是如果不包含呢?嗯,所以这肯定是被纵向压缩和截断了。Ok,到这里我们解决了纵向横向拉伸,解决了虚拟键遮挡问题,那目前这个问题有没有办法?答案是有,现在是纵向区域过长,那如果我们有一个办法,在纵向区域过长压缩的时候,只压缩空白区域,问题是不是解决了?让我们在仔细的看一眼9-patch的文档:https://developer.android.com/studio/write/draw9patch.html



上面整句话的含义其实我们不用care,但是我们注意一个字眼“scale down”,也就是说Android不但支持小图适配大屏幕,还支持在图片超出之后,对指定的区域进行压缩。这样的话,我们把纵向的空白区域选多一些,那么当纵向高度超出的时候,空白区域会等比例压缩。说干就干,试一下下面这样(为了适配宽屏把横向区域空白也选上,注意看左和上的条状):



再把做出来的这张素材,放到Nexus上面运行(虚拟键弹出),效果完美:



到这里,首页的全面屏和虚拟键适配自测都已经成功了,为了能够覆盖到Android各个尺寸,制定了这次适配测试的标准: 
1)三星,小米,华为的长宽比大于16:9的全面屏手机 
2)屏幕比小于16:9的安卓手机,比如三星的pad 
3)用户机型占比Top 10 的手机 
4)自定义Rom比较深的厂商,比如魅族,vivio,yunos 
测试同学也是非常给力,创建了一个50款机型的适配测试任务,为了用户体验的极致,我们也算是尽心尽力了。

参考文档

Supporting Multiple Screens

https://developer.android.com/guide/practices/screens_support.html

Create Resizable Bitmaps (9-Patch files)

https://developer.android.com/studio/write/draw9patch.html

小米开发者文档:

https://dev.mi.com/doc/p=10083/index.html

splash-screens-the-right-way

https://www.bignerdranch.com/blog/splash-screens-the-right-way/

你的Bitmap究竟占多大内存?

http://dev.qq.com/topic/591d61f56793d26660901b4e

详解Android开发中常用的 DPI / DP / SP

http://www.jianshu.com/p/913943d25829

时间: 2024-09-29 06:26:27

手淘启动页全面屏和虚拟键适配的相关文章

虚拟键 适配-android 有虚拟件的适配

问题描述 android 有虚拟件的适配 android 适配我用的百分比布局 进行的适配,其他手机都可以正常适配,但是遇到带有虚拟键(home menu back) 的手机时,项目的底部导航栏就被遮住了,请教各位大神解决办法,怎解决这个问题,跪谢!!!!!! 解决方案 这个问题已经解决.用的百分比,加上权重适配, 解决方案二: 你可以上传个图片看一眼,有一种解决思路,可以在进行布局时做个判断,有虚拟键的单独布局下方可以设置一段margin

手淘双十一系列(一) | 521 性能优化项目揭秘

该文章来自阿里巴巴技术协会(ATA)精选集  亿万用户都会在双十一这一天打开手机淘宝,高兴地在会场页面不断浏览,面对琳琅满目的商品图片,抢着添加购物车,下单付款.为了让用户更顺畅更方便地实现这一切,做到"如丝般顺滑",双十一前夕手机淘宝成立了"521"(我爱你)性能优化项目,在日常优化基础之上进行三个方面的专项优化攻关,分别是: 1)H5页面的一秒法则: 2)启动时间和页面帧率提升20%: 3)Android内存占用降低50%. 优化过程中遇到的困难,思考后找寻的方

小身材大屏幕 全面屏手机或成为游戏手机标配

今年以来,随着<阴阳师>.<王者荣耀>等现象级游戏的流行,手游市场掀起了发展狂潮,增速惊人.尤其是<王者荣耀>这款国民级手游,借助腾讯QQ.微信强大的社交网络,以星火燎原之势火遍全国,上至五六十岁的老年人,下到七八岁的小学生,在闲暇时间杀一局<王者荣耀>已经成为常态,这也使得手游成为了全民关注的话题. 纵观整个手游市场的发展史,从早期简单的点线单机游戏,到现如今画面绚烂.堪比端游的网络竞技类游戏,手游完全跟随着手机的发展,享受着手机屏幕变革带来的红利,以进一

手淘天施:我眼中的Weex和Weex开源那些事

版权声明 作者:吴志华(花名:天施),阿里资深无线技术专家,淘宝移动平台基础平台部负责人,Weex项目负责人 本文为手淘技术团队投稿. 今天手淘宣布将Weex开源项目捐赠给Apache基金会开始孵化,这是阿里在JStorm.RocketMQ之后的第三个阿里Apache基金会开源项目. Apache基金会有着非常严格的准入机制,这次Weex加入表明它得到了国际上的认可,我们也非常希望看到中国的移动开源项目能够在国际上获得成功. 在今年6月份的GMTC大会的时候我和手淘同学聊到,Weex的开发完全基

jQuery实现类似淘宝购物车全选状态示例

今天写了个类似淘宝购物车全选状态,看下截图,效果还不错吧,具体的实现html及jQuery代码如下,感兴趣的朋友可以参考下哈   复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www

删除掉您的启动页吧,让用户爽一点!

关于应用程序启动页(Splash Screen),大家说的一般都是如何设计的更打动人心,有创意的启动页往往还能成为人们讨论的焦点.而关于"为什么要有启动页?",回答的人不多. 心理学上有个"七秒钟理论",就是说人与人在见面的时候,产生的好恶决定于见面的头七秒钟.看似挺有道理,但大部分人都没意识到,该理论说的是 "头"七秒钟,不是每次七秒钟,如果你希望你的app是满足用户真实需求.希望用户时不时就打开用一下的那种,就不要在每次启动时都弄个几秒钟的美

vivo X20全面屏手机推出黑金旗舰版,这个十月神秘无限

昨天娱乐圈迎来了一桩喜事,vivo X20代言人鹿晗在微博上公布恋情,让众多的粉丝们激动不已.今天上午,手机圈也带来一则大新闻,vivo官方正式宣布vivo X20全面屏手机黑金旗舰版的面世,将于10月10日正式与我们见面.黑金美学的独特前所未见,广大热衷时尚的V粉将迎来最新潮尊贵的手机产品. vivo X20作为目前全面屏手机的代表,人气方面自然不言而喻.首销日当天,线下和电商平台更是创下了多项记录,这次全新黑金旗舰版,更高贵的质感必将引起新一波的购机热潮.黑色代表着神秘和内敛,金色代表着光芒

【Parallax Animation】实现知乎 Android 客户端启动页视差滚动效果

欢迎转载,但请务必注明出处!http://ryanhoo.github.io/blog/2014/07/16/step-by-step-implement-parallax-animation-for-splash-screen-of-zhihu/ 前言 Parallax Scrolling(视差滚动),是一种常见的动画效果.视差一词来源于天文学,但在日常生活中也有它的身影.在疾驰的动车上看风景时,会发现越是离得近的,相对运动速度越快,而远处的山川河流只是缓慢的移动着,这就是最常见的视差效果.视

全面屏手机的高颜值代表:vivo X20真机实拍图曝光

今年下半年将会是全面屏手机集中爆发的时间点,国内众多手机厂商纷纷亮大招,不少都将要推出的全面屏手机,而在这其中,当属vivo X20全面屏手机最值的期待,自从vivo官微发布了渲染图之后就引发了网友一阵欢呼.而就在刚才,vivo X20全面屏手机的真机实拍图被曝光了,相比渲染图,真机实拍图显得的更为惊艳,可以说是全面屏手机的中的颜值顶峰了. 从曝光的真机图中,我们可以看到整机给人的感觉是非常的惊艳,采用了黑色的机身配色,让手机看起来浑然一体.最让人惊讶的就是它的屏占比,这超高的屏占比和左右几乎看