Android屏幕适配之图片适配
2015-08-05 14:51
483 查看
参考:http://www.2cto.com/kf/201403/285698.html
一个好的App必须要支持绝大多数市面上的设备,适配繁多的分辨率一度让我们陷入了分辨率的海洋。无论如何,这个工作是逃不掉的。
我们可以用多个layout目录对不同分辨率进行单独布局,如下:
或者者直接使用下面这样:
与layout对应的,有不同的drawable:
用不同的layout毕竟工作量巨大,我们的实践是用不同的values来对应同layout中的值,目录如下:
有时必须要考虑到密度,如下:
二、主流手机分辨率情况
Android中view在setBackground加载图片时,通常会先去设备对应的像素密度的文件夹中去找对应的图片,如果没有找到就会去紧挨着的像素密度较高的文件夹中去找,然后再往上找,如果最高像素密度的文件夹中还没有找到该图片才会向像素密度较低的文件夹中去找。这是加载对应图片的一个查找过程。
比如一个设备的像素密度为240dpi,应用程序有drawable、drawable-ldpi、drawable-mdp、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi六个文件夹,则在一个view设置背景图片时,查找图片的顺序为drawable-hdpi ===== > drawable-xhdpi ====> drawable-xxhdpi ====> drawable-mdpi ====> drawable ====> drawable-ldpi。
这个顺序可以通过一个小的demo自己验证,是android查找图片资源的规则,不是通过几句话就能说明的。
设置view背景图片的过程
源码跟踪:
使用这里是设置一个view的背景图片,setBackgroundResource方法的源码为:
图-2
这个方法里面第14179行代码,红线标注部分,是通过Resources对象根据图片资源resid去获取图片对应的drawable对象,getDrawable方法源码如下:
图-3
这个方法里面通过loadDrawable方法返回一个Drawable对象,loadDrawable方法里面传入了一个TypedValue对象,而TypedValue对象是通过getValue方法获得的,这里可以通过代码查看一下TypedValue对象中存放了哪些对应资源图片的信息。
图-4
通过demo代码中,对代码进行debug,发现,根据图片资源resid获取的TypeValued对象中保存的信息主要有density=240和string=“res/drawable-hdpi/about-logo.png”,density是指找到的图片资源所在drawable-hdpi文件夹对应的像素密度,string是图片资源的路径。
实际上loadDrawable方法就是根据这个图片资源的路径去获取到相应的Drawable对象的。此时我是将图片放置到drawable-hdpi文件夹中的,那么如果我将图片移动到drawable-mdpi文件夹中,TypeValued值会一样吗?通过测试,发现如下结果:
图-5
通过图-4和图-5得出的typedValue信息可知,其获得的相应变量值是不一样的,此时的density=160、string=”res/drawable-mdpi/about-logo.png”,density是drawable-mdpi对应的像素密度,那么同样的图片放置在不同的资源文件夹中,得到的Drawable对象一样吗,通过证明,它们是不一样的。
about_logo.png原始图片大小为138*64像素,在同样的480*800像素240dpi的模拟器上运行,其得到的Drawable对象信息如下:
图片放置在drawable-mdpi文件夹下:
图-6
图片放置在drawable-hdpi文件夹下:
图-7
通过代码测试得出如下数据:
由此看出,放置在不同文件夹下面的相同的图片,在相同像素密度下所取得的图片Bitmap大小是不同的,如上表格中,drawable-mdpi下的图片实际上是进行了缩放的。
程序得到的图片宽度 = 实际图片宽度 * 设备像素密度 / 图片资源文件夹对应的像素密度
程序得到的图片高度 = 实际图片高度 * 设备像素密度 / 图片资源文件夹对应的像素密度
由此可以看出如果图片放置在低密度文件夹中,而要在高像素密度设备上显示时,其会先进行放大,然后再显示,这样就会导致高像素密度设备上显示模糊。
注:图片Bitmap放大的过程可以在源码中找到,源码在BitmapFactory.decodeStream方法中。详情请自己查看跟踪源码。
点9图的处理过程和上面的普通png图片是一样的,会根据所放置的资源文件夹和屏幕的像素密度先进行缩放,在显示的时候点9图会再进行局部拉伸,所以如果将带圆角的点9图片放置在低像素密度资源文件夹下,当使用高像素密度设备显示时,图片会先进行放大在进行局部拉伸,这样会导致在放大过程中图片圆角和边缘被拉伸,显示时会变的模糊。
解决方案:
1、尽量将点9图片放置在高像素密度资源文件夹中,这样即使在低像素密度手机上显示时会先对图片进行缩小再进行局部拉伸,但是在低像素密度手机上运行应用时,所有使用点9图片的地方都会对图片进行一次计算缩放,影响性能;
2、针对不同像素密度手机做多套点9图片。
补充:点9图片在缩放过后,如何进行局部拉伸渲染到屏幕上的?
源码跟踪,在View的draw方法中根据Drawable对象将图片作为背景绘制到指定区域中,点9图的实际绘制过程在NinePatch的draw方法中,通过canvas对象调用了本地方法nativeDraw对图片进行了绘制。至于如何绘制局部暂时看不到JNI方法的源码。
图-8
一、多分辨率适配常用目录:
一个好的App必须要支持绝大多数市面上的设备,适配繁多的分辨率一度让我们陷入了分辨率的海洋。无论如何,这个工作是逃不掉的。我们可以用多个layout目录对不同分辨率进行单独布局,如下:
layout-large-mdpi (1024x600) layout-large-tvdpi (800x1280) layout-large-xhdpi (1200x1920) layout-xlarge-mdpi (1280x800) layout-xlarge-xhdpi (2560x1600)
或者者直接使用下面这样:
layout-640x360 layout-800x480
与layout对应的,有不同的drawable:
res/drawable (default) res/drawable-ldpi/ (240x320 and nearer resolution) res/drawable-mdpi/ (320x480 and nearer resolution) res/drawable-hdpi/ (480x800, 540x960 and nearer resolution) res/drawable-xhdpi/ (720x1280 - Samsung S3, Micromax Canvas HD etc) res/drawable-xxhdpi/ (1080x1920 - Samsung S4, HTC one, Nexus 5, etc)
用不同的layout毕竟工作量巨大,我们的实践是用不同的values来对应同layout中的值,目录如下:
res/values/dimens.xml(default) res/values-ldpi/dimens.xml (240x320 and nearer resolution) res/values-mdpi/dimens.xml (320x480 and nearer resolution) res/values-hdpi/dimens.xml (480x800, 540x960 and nearer resolution) res/values-xhdpi/dimens.xml (720x1280 - Samsung S3, Micromax Canvas HD, etc) res/values-xxhdpi/dimens.xml (1080x1920 - Samsung S4, HTC one, etc) res/values-large/dimens.xml (480x800) res/values-large-mdpi/dimens.xml (600x1024) res/values-sw600dp/dimens.xml (600x1024) res/values-sw720dp/dimens.xml (800x1280) res/values-xlarge-xhdpi/dimens.xml (2560x1600 - Nexus 10") res/values-large-xhdpi/dimens.xml (1200x1920 - Nexus 7"(latest))
有时必须要考虑到密度,如下:
ldpi 120dpi 0.75 mdpi 160dpi 1 hdpi 240dpi 1.5 xhdpi 320dpi 2
二、主流手机分辨率情况
三、Android中view设置背景图片时查找图片资源的顺序
Android中view在setBackground加载图片时,通常会先去设备对应的像素密度的文件夹中去找对应的图片,如果没有找到就会去紧挨着的像素密度较高的文件夹中去找,然后再往上找,如果最高像素密度的文件夹中还没有找到该图片才会向像素密度较低的文件夹中去找。这是加载对应图片的一个查找过程。比如一个设备的像素密度为240dpi,应用程序有drawable、drawable-ldpi、drawable-mdp、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi六个文件夹,则在一个view设置背景图片时,查找图片的顺序为drawable-hdpi ===== > drawable-xhdpi ====> drawable-xxhdpi ====> drawable-mdpi ====> drawable ====> drawable-ldpi。
这个顺序可以通过一个小的demo自己验证,是android查找图片资源的规则,不是通过几句话就能说明的。
设置view背景图片的过程
源码跟踪:
View view = new View(this); view.setBackgroundResource(R.drawable.about_logo);图-1
使用这里是设置一个view的背景图片,setBackgroundResource方法的源码为:
图-2
这个方法里面第14179行代码,红线标注部分,是通过Resources对象根据图片资源resid去获取图片对应的drawable对象,getDrawable方法源码如下:
图-3
这个方法里面通过loadDrawable方法返回一个Drawable对象,loadDrawable方法里面传入了一个TypedValue对象,而TypedValue对象是通过getValue方法获得的,这里可以通过代码查看一下TypedValue对象中存放了哪些对应资源图片的信息。
图-4
通过demo代码中,对代码进行debug,发现,根据图片资源resid获取的TypeValued对象中保存的信息主要有density=240和string=“res/drawable-hdpi/about-logo.png”,density是指找到的图片资源所在drawable-hdpi文件夹对应的像素密度,string是图片资源的路径。
实际上loadDrawable方法就是根据这个图片资源的路径去获取到相应的Drawable对象的。此时我是将图片放置到drawable-hdpi文件夹中的,那么如果我将图片移动到drawable-mdpi文件夹中,TypeValued值会一样吗?通过测试,发现如下结果:
图-5
通过图-4和图-5得出的typedValue信息可知,其获得的相应变量值是不一样的,此时的density=160、string=”res/drawable-mdpi/about-logo.png”,density是drawable-mdpi对应的像素密度,那么同样的图片放置在不同的资源文件夹中,得到的Drawable对象一样吗,通过证明,它们是不一样的。
about_logo.png原始图片大小为138*64像素,在同样的480*800像素240dpi的模拟器上运行,其得到的Drawable对象信息如下:
图片放置在drawable-mdpi文件夹下:
图-6
图片放置在drawable-hdpi文件夹下:
图-7
通过代码测试得出如下数据:
图片放置的文件夹 | 对应像素密度 | 设备像素密度 | 得到的图片对应的Bitmap的宽高值 |
drawable-mdpi | 160dpi | 240dpi | 207*96 |
drawable-hdpi | 240dpi | 240dpi | 138*64 |
程序得到的图片宽度 = 实际图片宽度 * 设备像素密度 / 图片资源文件夹对应的像素密度
程序得到的图片高度 = 实际图片高度 * 设备像素密度 / 图片资源文件夹对应的像素密度
由此可以看出如果图片放置在低密度文件夹中,而要在高像素密度设备上显示时,其会先进行放大,然后再显示,这样就会导致高像素密度设备上显示模糊。
注:图片Bitmap放大的过程可以在源码中找到,源码在BitmapFactory.decodeStream方法中。详情请自己查看跟踪源码。
四、点9图片的缩放
点9图片其实也要进行缩放然后局部拉伸的。点9图的处理过程和上面的普通png图片是一样的,会根据所放置的资源文件夹和屏幕的像素密度先进行缩放,在显示的时候点9图会再进行局部拉伸,所以如果将带圆角的点9图片放置在低像素密度资源文件夹下,当使用高像素密度设备显示时,图片会先进行放大在进行局部拉伸,这样会导致在放大过程中图片圆角和边缘被拉伸,显示时会变的模糊。
解决方案:
1、尽量将点9图片放置在高像素密度资源文件夹中,这样即使在低像素密度手机上显示时会先对图片进行缩小再进行局部拉伸,但是在低像素密度手机上运行应用时,所有使用点9图片的地方都会对图片进行一次计算缩放,影响性能;
2、针对不同像素密度手机做多套点9图片。
补充:点9图片在缩放过后,如何进行局部拉伸渲染到屏幕上的?
源码跟踪,在View的draw方法中根据Drawable对象将图片作为背景绘制到指定区域中,点9图的实际绘制过程在NinePatch的draw方法中,通过canvas对象调用了本地方法nativeDraw对图片进行了绘制。至于如何绘制局部暂时看不到JNI方法的源码。
图-8
相关文章推荐
- Android动画之translate(位移动画)
- Android编程权威指南-第十七章挑战练习
- android应用生命周期
- Android 谷歌 开源 通信框架 VOLLEY(六)——应用实例
- Android图片下载缓存库picasso解析
- Android 自定义ViewGroup 实现流式布局
- 深入解析Android的自定义布局
- Android L 5.0 上紧急电话EmergencyCall与普通电话在MO流程上的区别
- Android学习笔记——声明
- Android init进程——属性服务
- android 问题集锦
- Android编译过程详解(三)
- Android 图形:绘制渐变色奥运五环图形,游戏文字,验证码,Matrix旋转,缩放,倾斜,平移等
- Android编译过程详解(二)
- Android编译过程详解(一)
- Android优化之代码优化
- android studio sdk更新 (2015-8-5)
- Android学习笔记——Android生命周期
- Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
- 终结android开发关于R文件的报错