Androidandroid 屏幕适配方案问题

青岛Android培训中心
美国上市公司 · 亿元级外企Android培训企业
课程咨询 : QQ:
&&&&&&Android屏幕适配全攻略
Android屏幕适配全攻略
为什么需要屏幕适配
Android是一个开放的系统,全球各种用户、手机企业、OEM厂商都可以对Android系统进行定制,这就导致了Android系统的碎片化问题。其中对于开发者来讲工作中最常碰到的就是屏幕碎片化,那么如何解决屏幕碎片化问题,实现最优的屏幕适配,是每个Android开发者所要面临的问题。
常见的定义
● 屏幕尺寸
屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米
比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等
● 屏幕分辨率
屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如。
● 屏幕像素密度
屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per
inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。
● dp、dip、dpi、sp、px
px:我们应该是比较熟悉的,前面的分辨率就是用的像素为单位,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,像是获取屏幕宽高等。
dip和dp:是一个意思,都是Density Independent
Pixels的缩写,即密度无关像素,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
sp:即scale-independent pixels,与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
● dip与px之间的换算公式
a. 2N + 2N/2 = PX
b.(2N-1)+ 2N/2 = PX
注:偶数值dip 的1.5倍等于相对应的px值,偶数值的间距与奇数元素设置居中对齐的时候会有1px的误差。
屏幕适配方案
1.充分利用”wrap_content” 、”match_parent”以及“weight”
通常我们会在布局视图中使用”wrap_content”和”match_parent”来确定它的宽和高。如果你使用了”wrap_content”,相应视图的宽和高就会被设定成刚好能够包含视图中内容的最小值。而如果你使用了”match_parent”,就会让视图的宽和高延伸至充满整个父布局。
2.多使用相对布局RelativeLayout,少使用绝对布局
如果你需要让子视图能够有更多的排列方式,而不是简单地排成一行或一列,使用RelativeLayout将会是更好的解决方案。RelativeLayout允许布局的子控件之间使用相对定位的方式控制控件的位置,比如你可以让一个子视图居屏幕左侧对齐,让另一个子视图居屏幕右侧对齐。
3.使用.9图
“点九”是andriod平台的应用软件开发里的一种特殊的图片形式,文件扩展名为:.9.png
智能手机中有自动横屏的功能,同一幅界面会在随着手机(或平板电脑)中的方向传感器的参数不同而改变显示的方向,在界面改变方向后,界面上的图形会因为长宽的变化而产生拉伸,造成图形的失真变形。
我们都知道android平台有多种不同的分辨率,很多控件的切图文件在被放大拉伸后,边角会模糊失真。OK,在android平台下使用点九PNG技术,可以将图片横向和纵向同时进行拉伸,以实现在多分辨率下的完美显示效果。
4.支持各种屏幕密度
使用非密度制约像素
由于各种屏幕的像素密度都有所不同,因此相同数量的像素在不同设备上的实际大小也有所差异,这样使用像素定义布局尺寸就会产生问题。因此,请务必使用 dp 或
sp 单位指定尺寸。dp 是一种非密度制约像素,其尺寸与 160 dpi 像素的实际尺寸相同。sp
也是一种基本单位,但它可根据用户的偏好文字大小进行调整(即尺度独立性像素),因此我们应将该测量单位用于定义文字大小。
5.提供备用位图
由于 Android
可在具有各种屏幕密度的设备上运行,因此我们提供的位图资源应始终可以满足各类普遍密度范围的要求:低密度、中等密度、高密度以及超高密度。这将有助于我们的图片在所有屏幕密度上都能得到出色的质量和效果。
要生成这些图片,我们应先提取矢量格式的原始资源,然后根据以下尺寸范围针对各密度生成相应的图片。
xhdpi:2.0
ldpi:0.75
也就是说,如果我们为 xhdpi 设备生成了 200x200 px尺寸的图片,就应该使用同一资源为 hdpi、mdpi 和 ldpi 设备分别生成
150x150、100x100 和 75x75 尺寸的图片。
然后,将生成的图片文件放在 res/
下的相应子目录中(mdpi、hdpi、xhdpi、xxhdpi),系统就会根据运行您应用的设备的屏幕密度自动选择合适的图片。
这样一来,只要我们引用 @drawable/id,系统都能根据相应屏幕的 dpi 选取合适的位图。
但是还有个问题需要注意下,如果是.9图或者是不需要多个分辨率的图片,就放在drawable文件夹即可,对应分辨率的图片要正确的放在合适的文件夹,否则会造成图片拉伸等问题
推荐文章(06-20)(06-16)(06-10)(06-10)(06-10)(07-30)(07-30)(07-29)(07-29)(07-29)
最新开班日期 &|
Android--零基础全日制
开班日期:1月20
Android--零基础周末班
开班日期:1月20
Android--高端全日制班
开班日期:1月20
Android--高端周末班
开班日期:1月20
达内新闻 &|
达内时代科技集团有限公司 版权所有 京ICP证8000853号-56android 屏幕适配问题_android开发_ThinkSAAS
android 屏幕适配问题
android 屏幕适配问题
转自.cn/s/blog_74c22bo.html
如何将一个应用程序适配在不同的手机上,虽然这不算是一个技术问题,但是对于刚刚做屏幕的开发人员来说,还真不是一件多么简单的事情。
首先:你需要在AndroidManifest.xml文件的&manifest&元素如下添加子元素
&supports-screens android:largescreens="true"android:normalscreens="true"android:anydensity="true"android:smallscreens="true"&&/supports-screens&
名如其意,以上是为我们的屏幕设置多分辨率支持(更准确的说是适配大、中、小三种密度)。android:anyDensity="true",这一句对整个的屏幕都起着十分重要的作用,值为true,我们的应用程序当安装在不同密度的手机上时,程序会分别加载hdpi,mdpi,ldpi文件夹中的资源。
相反,如果值设置为false,即使我们在hdpi,mdpi,ldpi文件夹下拥有同一种资源,那么应用也不会自动地去相应文件夹下寻找资源,这种情况都是出现在高密度,以及低密度的手机上,比如说一部240×320像素的手机,如果设置android:anyDensity="false",Android系统会将240 x 320(低密度)转换为 320×480(中密度),这样的话,应用就会在小密度手机上加载mdpi文件中的资源。
2.细心的人会发现自android2.0开始之后drawable文件被三个文件夹drawable-hdpi,drawable-mdpi,drawable-ldpi三个文件夹所取代,有些编程人员为了让应用程序默认地加载某些图片,他们会特意地去在android2.0之后的应用程序中重新创建drawable文件夹,其实这样做完全没有必要,通过第一段的分析我们得知,android:anyDensity="false",则应用会将大小密度转变成中密度,从而去加载mdpi中的资源。这里同样,当android:anyDensity="false",则应用会去加载mdpi中的资源。
总结一下:
第一:android:anyDensity="true",系统会依据屏幕密度,自动去找对应的文件夹
第二:android:anyDensity="false",
如果drawable-hdpi,drawable-mdpi,drawable-ldpi三个文件夹中有同一张图片资源的不同密度表示,那么系统会去加载drawable_mdpi文件夹中的资源
如果drawable-hpdi中有高密度图片,其它两个文件夹中没有对应图片资源,那么系统会去加载drawable-hdpi中的资源。
如果drawable-hdpi,drawable-mdpi中有图片资源,drawable-ldpi中没有对应的图片资源,那么系统会加载drawable-mdpi文件夹中的资源
3. 注意上图各种文件夹的不同表示。
drawable-hdpi 该图片即适用于横屏,也适用于竖屏
drawable-land-hdpi,当屏幕为横屏,且为高密度时,加载此文件夹中的资源
drawable-port-hdpi,当屏幕为竖屏,且为高密度时,加载此文件夹中的资源
3. 有时候会根据需要在代码中动态地设置某个值,比如地图,地图的pin和地图的地址提示框的相对偏移量在不同密度的手机上是不同的。这时候可以通过以下方法求出屏幕密度:
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
int densityDpi = metric.densityD
// 屏幕密度DPI(120 / 160 / 240)
然后可以在代码中为这几种密度分别设置便宜量
但是这种方法最好不要使用,最好的方式是在xml文件中不同密度的手机进行分别设置。
这里地图的偏移量可以在values-hpdi,values-mdpi,values-ldpi三种文件夹中的dimens.xml文件进行设置
值得一提的是:
&dimen name="bitmap_common_topoffset"&40dp&/dimen&&dimen name="bitmap_common_bottomoffset"&-14dp&/dimen&
这里的负数是完全起作用的,系统会认为它是一个负值
4. 各大手机厂商对于Android操作系统都有或多或少的改动,当然这些改动会对我们应用程序产生某些影响
(1)系统源代码中连接music服务的aidl文件所在包名:com.android.music
(2)LG则可能将该aidl文件修改所在的包(例如修改为 com.android.music.player),并且修改其中的文件内容(增加一个方法,或者减少几个方法,或者修改方法名称)那么我们的应用要想在LG的手机上发布,那么我们就必须改变所要连接的aidl文件,必须跟LG厂商修改的完全一致。
5. 国际化问题.
有时候在xml中设置了相应的语言,但是为什么当我们更改语言之后,UI显示仍然不起作用?
不要怀疑是系统出了问题,这与我们在代码中引用values/string.xml中字符串的方式有关。
错误的方式:
1. 声明全局变量 private static String tempS
2. 在onCreate方法中对该变量赋值 tempStr = context.getString(R.string.test);
3. 在更新UI的方法(非onCreate方法)中引用该变量。 textView.setText(tempStr);
原因是由于,当修改本地语言时,onCreate不会再被执行一遍. 变量tempStr 依然会使用页面刚启动时加载的默认英语。
正确的方式:
直接进行第三步:textView.setText(context.getString(R.string.test));&/manifest&
PHP开发框架
开发工具/编程工具
服务器环境
ThinkSAAS商业授权:
ThinkSAAS为用户提供有偿个性定制开发服务
ThinkSAAS将为商业授权用户提供二次开发指导和技术支持
让ThinkSAAS更好,把建议拿来。
开发客服微信android手机屏幕适配问题怎么解决?_手机问题_土巴兔装修问答
android手机屏幕适配问题怎么解决?
报价结果查看方式:
微信人工报价
报价结果将发送到您的手机
深圳装修顾问-馨馨
4年行业经验,24h可咨询
10秒闪电通过好友
您的装修预算约
*装修管家将回电您,免费提供装修咨询服务
*因材料品牌及工程量不同,具体报价以量房实测为准
深圳装修顾问 -馨馨
(四年装修行业经验)
微信扫一扫
android手机屏幕适配问题怎么解决?
提问者:严琳怡|
时间: 11:08:09
已有3条答案
回答数:115661|被采纳数:197
四川省柯演建筑装饰工程有限公司
所有回答:&115661
你好!很高兴为你解答,适配只能说是半可以了,只是觉得 &&采用官方文档说的 &&swdp &&或 &&values-mdpi-分辨率 &&之类的方法只能解决部分屏幕,android &&手机 &&屏幕大小,分辨率等太复杂,就像我现在适配好 &&问题里面说的这个手机,我又建了一个 &&屏幕更大的分辨率的屏适配并没有像android &&官网所说的会自动适配最接近的 &&而是只根据密度来的
回答数:2957|被采纳数:0
所有回答:&2957
&& &&a.在manifest里定义你的程序支持的屏幕类型,相应代码如下:
&& &&b.对不同大小的屏幕提供不同的layout。
&&比如,如果需要对大小为large的屏幕提供支持,需要在res目录下新建一个文件夹layout-large/并提供layout。当然,也可以在res目录下建立layout-port和layout-land两个目录,里面分别放置竖屏和横屏两种布局文件,以适应对横屏竖屏自动切换。
&& &&c.对不同密度的屏幕提供不同的图片。
  应尽量使用点9格式的图片,如需对密度为low的屏幕提供合适的图片,需新建文件夹drawable-ldpi/,并放入合适大小的图片。相应的,medium对应drawable-mdpi && && &&/,high对应drawable-hdpi/,extra && && &&high对应drawable-xhdpi/。
  图片大小的确定:low:medium:high:extra && && &&high比例为3:4:6:8。举例来说,对于中等密度(medium)的屏幕你的图片像素大小为48×48,那么低密度(low)屏幕的图片大小应为36×36,高(high)的为72×72,extra && && &&high为96×96。
回答数:1272|被采纳数:1
黄琦木木三
所有回答:&1272
方法如下:
方法一:对每一中不同的ppi使用不同的图片,这需要我们先判断屏幕的ppi,再对图片进行处理,然后显示。
方法二:需要为每个drawable目录都生成对应的图片版本,如果图片较多的话,生成不同版本的图片倒是小事更主要的缺点是会增大应用的大小,那么可不可以只在一个目录下存放图片,例如只在drawable-mdpi目录下存放图片呢答案是可以的。
资料来源网络,仅供参考!希望对你有所帮助。
已有 3 个回答
已有 3 个回答
已有 3 个回答
已有 3 个回答
已有 1 个回答
位业主已在问吧找到答案
一万套装修案例
下载土巴兔APP
中国装修网Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因、重要概念、解决方案及最佳实践,我相信如果你能认真的学习本文,对于Android的屏幕适配,你将有所收获!
Android屏幕适配出现的原因
屏幕分辨率
屏幕像素密度
dpdipdpisppx
mdpihdpixdpixxdpi
支持各种屏幕尺寸
使用wrap_contentmatch_parentweight
使用相对布局禁用绝对布局
使用限定符
使用尺寸限定符
使用最小宽度限定符
使用布局别名
使用屏幕方向限定符
使用自动拉伸位图
支持各种屏幕密度
使用非密度制约像素
提供备用位图
实施自适应用户界面流程
确定当前布局
根据当前布局做出响应
重复使用其他活动中的片段
处理屏幕配置变化
关于高清设计图尺寸
ImageView的ScaleType属性
更多参考资料
Android屏幕适配出现的原因
在我们学习如何进行屏幕适配之前,我们需要先了解下为什么Android需要进行屏幕适配。
由于Android系统的开放性,任何用户、开发者、OEM厂商、运营商都可以对Android进行定制,修改成他们想要的样子。
但是这种“碎片化”到底到达什么程度呢?
在2012年,OpenSignalMaps(以下简称OSM)发布了第一份Android碎片化报告,统计数据表明,
2012年,支持Android的设备共有3997种。
2013年,支持Android的设备共有11868种。
2014年,支持Android的设备共有18796种。
下面这张图片所显示的内容足以充分说明当今Android系统碎片化问题的严重性,因为该图片中的每一个矩形都代表着一种Android设备。
而随着支持Android系统的设备(手机、平板、电视、手表)的增多,设备碎片化、品牌碎片化、系统碎片化、传感器碎片化和屏幕碎片化的程度也在不断地加深。而我们今天要探讨的,则是对我们开发影响比较大的——屏幕的碎片化。
下面这张图是Android屏幕尺寸的示意图,在这张图里面,蓝色矩形的大小代表不同尺寸,颜色深浅则代表所占百分比的大小。
而与之相对应的,则是下面这张图。这张图显示了IOS设备所需要进行适配的屏幕尺寸和占比。
当然,这张图片只是4,4s,5,5c,5s和平板的尺寸,现在还应该加上新推出的iphone6和plus,但是和Android的屏幕碎片化程度相比而言,还是差的太远。
详细的统计数据请到 这里 查看
现在你应该很清楚为什么要对Android的屏幕进行适配了吧?屏幕尺寸这么多,为了让我们开发的程序能够比较美观的显示在不同尺寸、分辨率、像素密度(这些概念我会在下面详细讲解)的设备上,那就要在开发的过程中进行处理,至于如何去进行处理,这就是我们今天的主题了。
但是在开始进入主题之前,我们再来探讨一件事情,那就是Android设备的屏幕尺寸,从几寸的智能手机,到10寸的平板电脑,再到几十寸的数字电视,我们应该适配哪些设备呢?
其实这个问题不应该这么考虑,因为对于具有相同像素密度的设备来说,像素越高,尺寸就越大,所以我们可以换个思路,将问题从单纯的尺寸大小转换到像素大小和像素密度的角度来。
下图是2014年初,友盟统计的占比5%以上的6个主流分辨率,可以看出,占比最高的是480*800,320*480的设备竟然也占据了很大比例,但是和半年前的数据相比较,中低分辨率(320*480、480*800)的比例在减少,而中高分辨率的比例则在不断地增加。虽然每个分辨率所占的比例在变化,但是总的趋势没变,还是这六种,只是分辨率在不断地提高。
所以说,我们只要尽量适配这几种分辨率,就可以在大部分的手机上正常运行了。
当然了,这只是手机的适配,对于平板设备(电视也可以看做是平板),我们还需要一些其他的处理。
好了,到目前为止,我们已经弄清楚了Android开发为什么要进行适配,以及我们应该适配哪些对象,接下来,终于进入我们的正题了!
首先,我们先要学习几个重要的概念。
什么是屏幕尺寸、屏幕分辨率、屏幕像素密度? 什么是dp、dip、dpi、sp、px?他们之间的关系是什么? 什么是mdpi、hdpi、xdpi、xxdpi?如何计算和区分?
在下面的内容中我们将介绍这些概念。
屏幕尺寸指屏幕的对角线的长度,单位是英寸,1英寸=2.54厘米
比如常见的屏幕尺寸有2.4、2.8、3.5、3.7、4.2、5.0、5.5、6.0等
屏幕分辨率
屏幕分辨率是指在横纵向上的像素点数,单位是px,1px=1个像素点。一般以纵向像素*横向像素,如。
屏幕像素密度
屏幕像素密度是指每英寸上的像素点数,单位是dpi,即“dot per inch”的缩写。屏幕像素密度与屏幕尺寸和屏幕分辨率有关,在单一变化条件下,屏幕尺寸越小、分辨率越高,像素密度越大,反之越小。
dp、dip、dpi、sp、px
px我们应该是比较熟悉的,前面的分辨率就是用的像素为单位,大多数情况下,比如UI设计、Android原生API都会以px作为统一的计量单位,像是获取屏幕宽高等。
dip和dp是一个意思,都是Density Independent Pixels的缩写,即密度无关像素,上面我们说过,dpi是屏幕像素密度,假如一英寸里面有160个像素,这个屏幕的像素密度就是160dpi,那么在这种情况下,dp和px如何换算呢?在Android中,规定以160dpi为基准,1dip=1px,如果密度是320dpi,则1dip=2px,以此类推。
假如同样都是画一条320px的线,在480*800分辨率手机上显示为2/3屏幕宽度,在320*480的手机上则占满了全屏,如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一般的长度。这也是为什么在Android开发中,写布局的时候要尽量使用dp而不是px的原因。
而sp,即scale-independent pixels,与dp类似,但是可以根据文字大小首选项进行放缩,是设置字体大小的御用单位。
mdpi、hdpi、xdpi、xxdpi
其实之前还有个ldpi,但是随着移动设备配置的不断升级,这个像素密度的设备已经很罕见了,所在现在适配时不需考虑。
mdpi、hdpi、xdpi、xxdpi用来修饰Android中的drawable文件夹及values文件夹,用来区分不同像素密度下的图片和dimen值。
那么如何区分呢?Google官方指定按照下列标准进行区分:
像素密度范围
120dpi~160dpi
160dpi~240dpi
240dpi~320dpi
320dpi~480dpi
480dpi~640dpi
在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。下面以图标设计为例进行介绍。
在设计图标时,对于五种主流的像素密度(MDPI、HDPI、XHDPI、XXHDPI 和 XXXHDPI)应按照 2:3:4:6:8 的比例进行缩放。例如,一个启动图标的尺寸为48x48 dp,这表示在 MDPI 的屏幕上其实际尺寸应为 48x48 px,在 HDPI 的屏幕上其实际大小是 MDPI 的 1.5 倍 (72x72 px),在 XDPI 的屏幕上其实际大小是 MDPI 的 2 倍 (96x96 px),依此类推。
虽然 Android 也支持低像素密度 (LDPI) 的屏幕,但无需为此费神,系统会自动将 HDPI 尺寸的图标缩小到 1/2 进行匹配。
下图为图标的各个屏幕密度的对应尺寸
支持各种屏幕尺寸
使用wrap_content、match_parent、weight
要确保布局的灵活性并适应各种尺寸的屏幕,应使用 “wrap_content” 和 “match_parent” 控制某些视图组件的宽度和高度。
使用 “wrap_content”,系统就会将视图的宽度或高度设置成所需的最小尺寸以适应视图中的内容,而 “match_parent”(在低于 API 级别 8 的级别中称为 “fill_parent”)则会展开组件以匹配其父视图的尺寸。
如果使用 “wrap_content” 和 “match_parent” 尺寸值而不是硬编码的尺寸,视图就会相应地仅使用自身所需的空间或展开以填满可用空间。此方法可让布局正确适应各种屏幕尺寸和屏幕方向。
下面是一段示例代码
&code class=&hljs xml has-numbering& &&span class=&hljs-tag& &&&span class=&hljs-title& &LinearLayout&/span& &span class=&hljs-attribute& &xmlns:android&/span&=&span class=&hljs-value& &&/apk/res/android&&/span& &span class=&hljs-attribute& &android:orientation&/span&=&span class=&hljs-value& &&vertical&&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&match_parent&&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&match_parent&&/span&&&/span& &span class=&hljs-tag& &&&span class=&hljs-title& &LinearLayout&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&match_parent&&/span& &span class=&hljs-attribute& &android:id&/span&=&span class=&hljs-value& &&@+id/linearLayout1&&/span& &span class=&hljs-attribute& &android:gravity&/span&=&span class=&hljs-value& &&center&&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&50dp&&/span&&&/span& &span class=&hljs-tag& &&&span class=&hljs-title& &ImageView&/span& &span class=&hljs-attribute& &android:id&/span&=&span class=&hljs-value& &&@+id/imageView1&&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&wrap_content&&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&wrap_content&&/span& &span class=&hljs-attribute& &android:src&/span&=&span class=&hljs-value& &&@drawable/logo&&/span& &span class=&hljs-attribute& &android:paddingRight&/span&=&span class=&hljs-value& &&30dp&&/span& &span class=&hljs-attribute& &android:layout_gravity&/span&=&span class=&hljs-value& &&left&&/span& &span class=&hljs-attribute& &android:layout_weight&/span&=&span class=&hljs-value& &&0&&/span& /&&/span& &span class=&hljs-tag& &&&span class=&hljs-title& &View&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&wrap_content&&/span& &span class=&hljs-attribute& &android:id&/span&=&span class=&hljs-value& &&@+id/view1&&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&wrap_content&&/span& &span class=&hljs-attribute& &android:layout_weight&/span&=&span class=&hljs-value& &&1&&/span& /&&/span& &span class=&hljs-tag& &&&span class=&hljs-title& &Button&/span& &span class=&hljs-attribute& &android:id&/span&=&span class=&hljs-value& &&@+id/categorybutton&&/span& &span class=&hljs-attribute& &android:background&/span&=&span class=&hljs-value& &&@drawable/button_bg&&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&match_parent&&/span& &span class=&hljs-attribute& &android:layout_weight&/span&=&span class=&hljs-value& &&0&&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&120dp&&/span& &span class=&hljs-attribute& &style&/span&=&span class=&hljs-value& &&@style/CategoryButtonStyle&&/span&/&&/span& &span class=&hljs-tag& &&/&span class=&hljs-title& &LinearLayout&/span&&&/span& &span class=&hljs-tag& &&&span class=&hljs-title& &fragment&/span& &span class=&hljs-attribute& &android:id&/span&=&span class=&hljs-value& &&@+id/headlines&&/span& &span class=&hljs-attribute& &android:layout_height&/span&=&span class=&hljs-value& &&fill_parent&&/span& &span class=&hljs-attribute& &android:name&/span&=&span class=&hljs-value& &&com.example.android.newsreader.HeadlinesFragment&&/span& &span class=&hljs-attribute& &android:layout_width&/span&=&span class=&hljs-value& &&match_parent&&/span& /&&/span& &span class=&hljs-tag& &&/&span class=&hljs-title& &LinearLayout&/span&&&/span&&/code&&ul class=&pre-numbering& &&li &1&/li&&li &2&/li&&li &3&/li&&li &4&/li&&li &5&/li&&li &6&/li&&li &7&/li&&li &8&/li&&li &9&/li&&li &10&/li&&li &11&/li&&li &12&/li&&li &13&/li&&li &14&/li&&li &15&/li&&li &16&/li&&li &17&/li&&li &18&/li&&li &19&/li&&li &20&/li&&li &21&/li&&li &22&/li&&li &23&/li&&li &24&/li&&li &25&/li&&li &26&/li&&li &27&/li&&li &28&/li&&li &29&/li&&li &30&/li&&li &31&/li&&li &32&/li&&/ul&
下图是在横纵屏切换的时候的显示效果,我们可以看到这样可以很好的适配屏幕尺寸的变化。
weight是线性布局的一个独特的属性,我们可以使用这个属性来按照比例对界面进行分配,完成一些特殊的需求。
但是,我们对于这个属性的计算应该如何理解呢?
首先看下面的例子,我们在布局中这样设置我们的界面
我们在布局里面设置为线性布局,横向排列,然后放置两个宽度为0dp的按钮,分别设置weight为1和2,在效果图中,我们可以看到两个按钮按照1:2的宽度比例正常排列了,这也是我们经常使用到的场景,这是时候很好理解,Button1的宽度就是1/(1+2) = 1/3,Button2的宽度则是2/(1+2) = 2/3,我们可以很清楚的明白这种情景下的占比如何计算。
但是假如我们的宽度不是0dp(wrap_content和0dp的效果相同),则是match_parent呢?
下面是设置为match_parent的效果
我们可以看到,在这种情况下,占比和上面正好相反,这是怎么回事呢?说到这里,我们就不得不提一下weight的计算方法了。
android:layout_weight的真实含义是:如果View设置了该属性并且有效,那么该 View的宽度等于原有宽度(android:layout_width)加上剩余空间的占比。
从这个角度我们来解释一下上面的现象。在上面的代码中,我们设置每个Button的宽度都是match_parent,假设屏幕宽度为L,那么每个Button的宽度也应该都为L,剩余宽度就等于L-(L+L)= -L。
Button1的weight=1,剩余宽度占比为1/(1+2)= 1/3,所以最终宽度为L+1/3*(-L)=2/3L,Button2的计算类似,最终宽度为L+2/3(-L)=1/3L。
这是在水平方向上的,那么在垂直方向上也是这样吗?
下面是测试代码和效果
如果是垂直方向,那么我们应该改变的是layout_height的属性,下面是0dp的显示效果
下面是match_parent的显示效果,结论和水平是完全一样的
虽然说我们演示了match_parent的显示效果,并说明了原因,但是在真正用的时候,我们都是设置某一个属性为0dp,然后按照权重计算所占百分比。
使用相对布局,禁用绝对布局
在开发中,我们大部分时候使用的都是线性布局、相对布局和帧布局,绝对布局由于适配性极差,所以极少使用。
由于各种布局的特点不一样,所以不能说哪个布局好用,到底应该使用什么布局只能根据实际需求来确定。我们可以使用 LinearLayout 的嵌套实例并结合 “wrap_content” 和 “match_parent”,以便构建相当复杂的布局。不过,我们无法通过 LinearLayout 精确控制子视图的特殊关系;系统会将 LinearLayout 中的视图直接并排列出。
如果我们需要将子视图排列出各种效果而不是一条直线,通常更合适的解决方法是使用 RelativeLayout,这样就可以根据各组件之间的特殊关系指定布局了。例如,我们可以将某个子视图对齐到屏幕左侧,同时将另一个视图对齐到屏幕右侧。
下面的代码以官方Demo为例说明。
&?xmlversion=&1.0&encoding=&utf-8&?&&RelativeLayoutxmlns:android=&/apk/res/android&android:layout_width=&match_parent&android:layout_height=&match_parent&&&TextViewandroid:id=&@+id/label&android:layout_width=&match_parent&android:layout_height=&wrap_content&android:text=&Typehere:&/&&EditTextandroid:id=&@+id/entry&android:layout_width=&match_parent&android:layout_height=&wrap_content&android:layout_below=&@id/label&/&&Buttonandroid:id=&@+id/ok&android:layout_width=&wrap_content&android:layout_height=&wrap_content&android:layout_below=&@id/entry&android:layout_alignParentRight=&true&android:layout_marginLeft=&10dp&android:text=&OK&/&&Buttonandroid:layout_width=&wrap_content&android:layout_height=&wrap_content&android:layout_toLeftOf=&@id/ok&android:layout_alignTop=&@id/ok&android:text=&Cancel&/&&/RelativeLayout&
在上面的代码中我们使用了相对布局,并且使用alignXXX等属性指定了子控件的位置,下面是这种布局方式在应对屏幕变化时的表现
在小尺寸屏幕的显示
在平板的大尺寸上的显示效果
虽然控件的大小由于屏幕尺寸的增加而发生了改变,但是我们可以看到,由于使用了相对布局,所以控件之前的位置关系并没有发生什么变化,这说明我们的适配成功了。
使用限定符
使用尺寸限定符
上面所提到的灵活布局或者是相对布局,可以为我们带来的优势就只有这么多了。虽然这些布局可以拉伸组件内外的空间以适应各种屏幕,但它们不一定能为每种屏幕都提供最佳的用户体验。因此,我们的应用不仅仅只实施灵活布局,还应该应针对各种屏幕配置提供一些备用布局。
如何做到这一点呢?我们可以通过使用配置限定符,在运行时根据当前的设备配置自动选择合适的资源了,例如根据各种屏幕尺寸选择不同的布局。
很多应用会在较大的屏幕上实施“双面板”模式,即在一个面板上显示项目列表,而在另一面板上显示对应内容。平板电脑和电视的屏幕已经大到可以同时容纳这两个面板了,但手机屏幕就需要分别显示。因此,我们可以使用以下文件以便实施这些布局:
res/layout/main.xml,单面板(默认)布局:
&LinearLayoutxmlns:android=&/apk/res/android&android:orientation=&vertical&android:layout_width=&match_parent&android:layout_height=&match_parent&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&match_parent&/&&/LinearLayout&
res/layout-large/main.xml,双面板布局:
&LinearLayoutxmlns:android=&/apk/res/android&android:layout_width=&fill_parent&android:layout_height=&fill_parent&android:orientation=&horizontal&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&400dp&android:layout_marginRight=&10dp&/&&fragmentandroid:id=&@+id/article&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.ArticleFragment&android:layout_width=&fill_parent&/&&/LinearLayout&
请注意第二种布局名称目录中的 large 限定符。系统会在属于较大屏幕(例如 7 英寸或更大的平板电脑)的设备上选择此布局。系统会在较小的屏幕上选择其他布局(无限定符)。
使用最小宽度限定符
在版本低于 3.2 的 Android 设备上,开发人员遇到的问题之一是“较大”屏幕的尺寸范围,该问题会影响戴尔 Streak、早期的 Galaxy Tab 以及大部分 7 英寸平板电脑。即使这些设备的屏幕属于“较大”的尺寸,但很多应用可能会针对此类别中的各种设备(例如 5 英寸和 7 英寸的设备)显示不同的布局。这就是 Android 3.2 版在引入其他限定符的同时引入“最小宽度”限定符的原因。
最小宽度限定符可让您通过指定某个最小宽度(以 dp 为单位)来定位屏幕。例如,标准 7 英寸平板电脑的最小宽度为 600 dp,因此如果您要在此类屏幕上的用户界面中使用双面板(但在较小的屏幕上只显示列表),您可以使用上文中所述的单面板和双面板这两种布局,但您应使用 sw600dp 指明双面板布局仅适用于最小宽度为 600 dp 的屏幕,而不是使用 large 尺寸限定符。
res/layout/main.xml,单面板(默认)布局:
&LinearLayoutxmlns:android=&/apk/res/android&android:orientation=&vertical&android:layout_width=&match_parent&android:layout_height=&match_parent&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&match_parent&/&&/LinearLayout&
res/layout-sw600dp/main.xml,双面板布局:
&LinearLayoutxmlns:android=&/apk/res/android&android:layout_width=&fill_parent&android:layout_height=&fill_parent&android:orientation=&horizontal&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&400dp&android:layout_marginRight=&10dp&/&&fragmentandroid:id=&@+id/article&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.ArticleFragment&android:layout_width=&fill_parent&/&&/LinearLayout&
也就是说,对于最小宽度大于等于 600 dp 的设备,系统会选择 layout-sw600dp/main.xml(双面板)布局,否则系统就会选择 layout/main.xml(单面板)布局。
但 Android 版本低于 3.2 的设备不支持此技术,原因是这些设备无法将 sw600dp 识别为尺寸限定符,因此我们仍需使用 large 限定符。这样一来,就会有一个名称为 res/layout-large/main.xml 的文件(与 res/layout-sw600dp/main.xml 一样)。但是没有太大关系,我们将马上学习如何避免此类布局文件出现的重复。
使用布局别名
最小宽度限定符仅适用于 Android 3.2 及更高版本。因此,如果我们仍需使用与较低版本兼容的概括尺寸范围(小、正常、大和特大)。例如,如果要将用户界面设计成在手机上显示单面板,但在 7 英寸平板电脑、电视和其他较大的设备上显示多面板,那么我们就需要提供以下文件:
res/layout/main.xml: 单面板布局
res/layout-large: 多面板布局
res/layout-sw600dp: 多面板布局
后两个文件是相同的,因为其中一个用于和 Android 3.2 设备匹配,而另一个则是为使用较低版本 Android 的平板电脑和电视准备的。
要避免平板电脑和电视的文件出现重复(以及由此带来的维护问题),您可以使用别名文件。例如,您可以定义以下布局:
res/layout/main.xml,单面板布局
res/layout/main_twopanes.xml,双面板布局
然后添加这两个文件:
res/values-large/layout.xml:
&resources&&itemname=&main&type=&layout&&@layout/main_twopanes&/item&&/resources&
res/values-sw600dp/layout.xml:
&resources&&itemname=&main&type=&layout&&@layout/main_twopanes&/item&&/resources&
后两个文件的内容相同,但它们并未实际定义布局。它们只是将 main 设置成了 main_twopanes 的别名。由于这些文件包含 large 和 sw600dp 选择器,因此无论 Android 版本如何,系统都会将这些文件应用到平板电脑和电视上(版本低于 3.2 的平板电脑和电视会匹配 large,版本高于 3.2 的平板电脑和电视则会匹配 sw600dp)。
使用屏幕方向限定符
某些布局会同时支持横向模式和纵向模式,但我们可以通过调整优化其中大部分布局的效果。在新闻阅读器示例应用中,每种屏幕尺寸和屏幕方向下的布局行为方式如下所示:
小屏幕,纵向:单面板,带徽标
小屏幕,横向:单面板,带徽标
7 英寸平板电脑,纵向:单面板,带操作栏
7 英寸平板电脑,横向:双面板,宽,带操作栏
10 英寸平板电脑,纵向:双面板,窄,带操作栏
10 英寸平板电脑,横向:双面板,宽,带操作栏
电视,横向:双面板,宽,带操作栏
因此,这些布局中的每一种都定义在了 res/layout/ 目录下的某个 XML 文件中。为了继续将每个布局分配给各种屏幕配置,该应用会使用布局别名将两者相匹配:
res/layout/onepane.xml:(单面板)
&LinearLayoutxmlns:android=&/apk/res/android&android:orientation=&vertical&android:layout_width=&match_parent&android:layout_height=&match_parent&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&match_parent&/&&/LinearLayout&
res/layout/onepane_with_bar.xml:(单面板带操作栏)
&LinearLayoutxmlns:android=&/apk/res/android&android:orientation=&vertical&android:layout_width=&match_parent&android:layout_height=&match_parent&&&LinearLayoutandroid:layout_width=&match_parent&android:id=&@+id/linearLayout1&android:gravity=&center&android:layout_height=&50dp&&&ImageViewandroid:id=&@+id/imageView1&android:layout_height=&wrap_content&android:layout_width=&wrap_content&android:src=&@drawable/logo&android:paddingRight=&30dp&android:layout_gravity=&left&android:layout_weight=&0&/&&Viewandroid:layout_height=&wrap_content&android:id=&@+id/view1&android:layout_width=&wrap_content&android:layout_weight=&1&/&&Buttonandroid:id=&@+id/categorybutton&android:background=&@drawable/button_bg&android:layout_height=&match_parent&android:layout_weight=&0&android:layout_width=&120dp&style=&@style/CategoryButtonStyle&/&&/LinearLayout&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&match_parent&/&&/LinearLayout&
res/layout/twopanes.xml:(双面板,宽布局)
&LinearLayoutxmlns:android=&/apk/res/android&android:layout_width=&fill_parent&android:layout_height=&fill_parent&android:orientation=&horizontal&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&400dp&android:layout_marginRight=&10dp&/&&fragmentandroid:id=&@+id/article&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.ArticleFragment&android:layout_width=&fill_parent&/&&/LinearLayout&
res/layout/twopanes_narrow.xml:(双面板,窄布局)
&LinearLayoutxmlns:android=&/apk/res/android&android:layout_width=&fill_parent&android:layout_height=&fill_parent&android:orientation=&horizontal&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&200dp&android:layout_marginRight=&10dp&/&&fragmentandroid:id=&@+id/article&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.ArticleFragment&android:layout_width=&fill_parent&/&&/LinearLayout&
既然我们已定义了所有可能的布局,那就只需使用配置限定符将正确的布局映射到各种配置即可。
现在只需使用布局别名技术即可做到这一点:
res/values/layouts.xml:
&resources&&itemname=&main_layout&type=&layout&&@layout/onepane_with_bar&/item&&boolname=&has_two_panes&&false&/bool&&/resources&
res/values-sw600dp-land/layouts.xml:
&resources&&itemname=&main_layout&type=&layout&&@layout/twopanes&/item&&boolname=&has_two_panes&&true&/bool&&/resources&
res/values-sw600dp-port/layouts.xml:
&resources&&itemname=&main_layout&type=&layout&&@layout/onepane&/item&&boolname=&has_two_panes&&false&/bool&&/resources&
res/values-large-land/layouts.xml:
&resources&&itemname=&main_layout&type=&layout&&@layout/twopanes&/item&&boolname=&has_two_panes&&true&/bool&&/resources&
res/values-large-port/layouts.xml:
&resources&&itemname=&main_layout&type=&layout&&@layout/twopanes_narrow&/item&&boolname=&has_two_panes&&true&/bool&&/resources&
使用自动拉伸位图
支持各种屏幕尺寸通常意味着您的图片资源还必须能适应各种尺寸。例如,无论要应用到什么形状的按钮上,按钮背景都必须能适应。
如果在可以更改尺寸的组件上使用了简单的图片,您很快就会发现显示效果多少有些不太理想,因为系统会在运行时平均地拉伸或收缩您的图片。解决方法为使用自动拉伸位图,这是一种格式特殊的 PNG 文件,其中会指明可以拉伸以及不可以拉伸的区域。
.9的制作,实际上就是在原图片上添加1px的边界,然后按照我们的需求,把对应的位置设置成黑色线,系统就会根据我们的实际需求进行拉伸。
下图是对.9图的四边的含义的解释,左上边代表拉伸区域,右下边代表padding box,就是间隔区域,在下面,我们给出一个例子,方便大家理解。
先看下面两张图,我们理解一下这四条线的含义。
上图和下图的区别,就在于右下边的黑线不一样,具体的效果的区别,看右边的效果图。上图效果图中深蓝色的区域,代表内容区域,我们可以看到是在正中央的,这是因为我们在右下边的是两个点,这两个点距离上下左右四个方向的距离就是padding的距离,所以深蓝色内容区域在图片正中央,我们再看下图,由于右下边的黑线是图片长度,所以就没有padding,从效果图上的表现就是深蓝色区域和图片一样大,因此,我们可以利用右下边来控制内容与背景图边缘的padding。
如果你还不明白,那么我们看下面的效果图,我们分别以图一和图二作为背景图,下面是效果图。
我们可以看到,使用wrap_content属性设置长宽,图一比图二的效果大一圈,这是为什么呢?还记得我上面说的padding吗?
这就是padding的效果提现,怎么证明呢?我们再看下面一张图,给图一添加padding=0,这样背景图设置的padding效果就没了,是不是两个一样大了?
ok,我想你应该明白右下边的黑线的含义了,下面我们再看一下左上边的效果。
下面我们只设置了左上边线,效果图如下
上面的线没有包住图标,下面的线正好包住了图标,从右边的效果图应该可以看出差别,黑线所在的区域就是拉伸区域,上图黑线所在的全是纯色,所以图标不变形,下面的拉伸区域包裹了图标,所以在拉伸的时候就会对图标进行拉伸,但是这样就会导致图标变形。注意到下面红线区域了嘛?这是系统提示我们的,因为这样拉伸,不符合要求,所以会提示一下。
支持各种屏幕密度
使用非密度制约像素
由于各种屏幕的像素密度都有所不同,因此相同数量的像素在不同设备上的实际大小也有所差异,这样使用像素定义布局尺寸就会产生问题。因此,请务必使用 dp 或 sp 单位指定尺寸。dp 是一种非密度制约像素,其尺寸与 160 dpi 像素的实际尺寸相同。sp 也是一种基本单位,但它可根据用户的偏好文字大小进行调整(即尺度独立性像素),因此我们应将该测量单位用于定义文字大小。
例如,请使用 dp(而非 px)指定两个视图间的间距:
&Buttonandroid:layout_width=&wrap_content&android:layout_height=&wrap_content&android:text=&@string/clickme&android:layout_marginTop=&20dp&/&
请务必使用 sp 指定文字大小:
&TextViewandroid:layout_width=&match_parent&android:layout_height=&wrap_content&android:textSize=&20sp&/&
除了介绍这些最基础的知识之外,我们下面再来讨论一下另外一个问题。
经过上面的介绍,我们都清楚,为了能够规避不同像素密度的陷阱,Google推荐使用dp来代替px作为控件长度的度量单位,但是我们来看下面的一个场景。
假如我们以Nexus5作为书写代码时查看效果的测试机型,Nexus5的总宽度为360dp,我们现在需要在水平方向上放置两个按钮,一个是150dp左对齐,另外一个是200dp右对齐,中间留有10dp间隔,那么在Nexus5上面的显示效果就是下面这样
但是如果在Nexus S或者是Nexus One运行呢?下面是运行结果
可以看到,两个按钮发生了重叠。
我们都已经用了dp了,为什么会出现这种情况呢?
你听我慢慢道来。
虽然说dp可以去除不同像素密度的问题,使得1dp在不同像素密度上面的显示效果相同,但是还是由于Android屏幕设备的多样性,如果使用dp来作为度量单位,并不是所有的屏幕的宽度都是相同的dp长度,比如说,Nexus S和Nexus One属于hdpi,屏幕宽度是320dp,而Nexus 5属于xxhdpi,屏幕宽度是360dp,Galaxy Nexus属于xhdpi,屏幕宽度是384dp,Nexus 6 属于xxxhdpi,屏幕宽度是410dp。所以说,光Google自己一家的产品就已经有这么多的标准,而且屏幕宽度和像素密度没有任何关联关系,即使我们使用dp,在320dp宽度的设备和410dp的设备上,还是会有90dp的差别。当然,我们尽量使用match_parent和wrap_content,尽可能少的用dp来指定控件的具体长宽,再结合上权重,大部分的情况我们都是可以做到适配的。
但是除了这个方法,我们还有没有其他的更彻底的解决方案呢?
我们换另外一个思路来思考这个问题。
下面的方案来自Android Day Day Up 一群的【blue-深圳】,谢谢他的分享精神
因为分辨率不一样,所以不能用px;因为屏幕宽度不一样,所以要小心的用dp,那么我们可不可以用另外一种方法来统一单位,不管分辨率是多大,屏幕宽度用一个固定的值的单位来统计呢?
答案是:当然可以。
我们假设手机屏幕的宽度都是320某单位,那么我们将一个屏幕宽度的总像素数平均分成320份,每一份对应具体的像素就可以了。
具体如何来实现呢?我们看下面的代码
importjava.io.Fimportjava.io.FileNotFoundEimportjava.io.FileOutputSimportjava.io.PrintWpublicclassMakeXml{privatefinalstaticStringrootPath=&C://Users//Administrator//Desktop//layoutroot//values-{0}x{1}//&;privatefinalstaticfloatdw=320f;privatefinalstaticfloatdh=480f;privatefinalstaticStringWTemplate=&&dimenname=/&x{0}/&&{1}px&/dimen&/n&;privatefinalstaticStringHTemplate=&&dimenname=/&y{0}/&&{1}px&/dimen&/n&;publicstaticvoidmain(String[]args){makeString(320,480);makeString(480,800);makeString(480,854);makeString(540,960);makeString(600,1024);makeString(720,1184);makeString(720,1196);makeString(720,1280);makeString(768,1024);makeString(800,1280);makeString();makeString();makeString();}publicstaticvoidmakeString(intw,inth){StringBuffersb=newStringBuffer();sb.append(&&?xmlversion=/&1.0/&encoding=/&utf-8/&?&/n&);sb.append(&&resources&&);floatcellw=w/for(inti=1;i&320;i++){sb.append(WTemplate.replace(&{0}&,i+&&).replace(&{1}&,change(cellw*i)+&&));}sb.append(WTemplate.replace(&{0}&,&320&).replace(&{1}&,w+&&));sb.append(&&/resources&&);StringBuffersb2=newStringBuffer();sb2.append(&&?xmlversion=/&1.0/&encoding=/&utf-8/&?&/n&);sb2.append(&&resources&&);floatcellh=h/for(inti=1;i&480;i++){sb2.append(HTemplate.replace(&{0}&,i+&&).replace(&{1}&,change(cellh*i)+&&));}sb2.append(HTemplate.replace(&{0}&,&480&).replace(&{1}&,h+&&));sb2.append(&&/resources&&);Stringpath=rootPath.replace(&{0}&,h+&&).replace(&{1}&,w+&&);FilerootFile=newFile(path);if(!rootFile.exists()){rootFile.mkdirs();}FilelayxFile=newFile(path+&lay_x.xml&);FilelayyFile=newFile(path+&lay_y.xml&);try{PrintWriterpw=newPrintWriter(newFileOutputStream(layxFile));pw.print(sb.toString());pw.close();pw=newPrintWriter(newFileOutputStream(layyFile));pw.print(sb2.toString());pw.close();}catch(FileNotFoundExceptione){e.printStackTrace();}}publicstaticfloatchange(floata){inttemp=(int)(a*100);returntemp/100f;}}
代码应该很好懂,我们将一个屏幕宽度分为320份,高度480份,然后按照实际像素对每一个单位进行复制,放在对应values-widthxheight文件夹下面的lax.xml和lay.xml里面,这样就可以统一所有你想要的分辨率的单位了,下面是生成的一个320*480分辨率的文件,因为宽高分割之后总分数和像素数相同,所以x1就是1px,以此类推
&?xmlversion=&1.0&encoding=&utf-8&?&&resources&&dimenname=&x1&&1.0px&/dimen&&dimenname=&x2&&2.0px&/dimen&&dimenname=&x3&&3.0px&/dimen&&dimenname=&x4&&4.0px&/dimen&&dimenname=&x5&&5.0px&/dimen&&dimenname=&x6&&6.0px&/dimen&&dimenname=&x7&&7.0px&/dimen&&dimenname=&x8&&8.0px&/dimen&&dimenname=&x9&&9.0px&/dimen&&dimenname=&x10&&10.0px&/dimen&...省略好多行&dimenname=&x300&&300.0px&/dimen&&dimenname=&x301&&301.0px&/dimen&&dimenname=&x302&&302.0px&/dimen&&dimenname=&x303&&303.0px&/dimen&&dimenname=&x304&&304.0px&/dimen&&dimenname=&x305&&305.0px&/dimen&&dimenname=&x306&&306.0px&/dimen&&dimenname=&x307&&307.0px&/dimen&&dimenname=&x308&&308.0px&/dimen&&dimenname=&x309&&309.0px&/dimen&&dimenname=&x310&&310.0px&/dimen&&dimenname=&x311&&311.0px&/dimen&&dimenname=&x312&&312.0px&/dimen&&dimenname=&x313&&313.0px&/dimen&&dimenname=&x314&&314.0px&/dimen&&dimenname=&x315&&315.0px&/dimen&&dimenname=&x316&&316.0px&/dimen&&dimenname=&x317&&317.0px&/dimen&&dimenname=&x318&&318.0px&/dimen&&dimenname=&x319&&319.0px&/dimen&&dimenname=&x320&&320px&/dimen&&/resources&
那么分辨率下是什么样子呢?我们可以看下,由于是3.37倍的关系,所以x1=3.37px
&?xmlversion=&1.0&encoding=&utf-8&?&&resources&&dimenname=&x1&&3.37px&/dimen&&dimenname=&x2&&6.75px&/dimen&&dimenname=&x3&&10.12px&/dimen&&dimenname=&x4&&13.5px&/dimen&&dimenname=&x5&&16.87px&/dimen&&dimenname=&x6&&20.25px&/dimen&&dimenname=&x7&&23.62px&/dimen&&dimenname=&x8&&27.0px&/dimen&&dimenname=&x9&&30.37px&/dimen&&dimenname=&x10&&33.75px&/dimen&...省略好多行&dimenname=&x300&&1012.5px&/dimen&&dimenname=&x301&&1015.87px&/dimen&&dimenname=&x302&&1019.25px&/dimen&&dimenname=&x303&&1022.62px&/dimen&&dimenname=&x304&&1026.0px&/dimen&&dimenname=&x305&&1029.37px&/dimen&&dimenname=&x306&&1032.75px&/dimen&&dimenname=&x307&&1036.12px&/dimen&&dimenname=&x308&&1039.5px&/dimen&&dimenname=&x309&&1042.87px&/dimen&&dimenname=&x310&&1046.25px&/dimen&&dimenname=&x311&&1049.62px&/dimen&&dimenname=&x312&&1053.0px&/dimen&&dimenname=&x313&&1056.37px&/dimen&&dimenname=&x314&&1059.75px&/dimen&&dimenname=&x315&&1063.12px&/dimen&&dimenname=&x316&&1066.5px&/dimen&&dimenname=&x317&&1069.87px&/dimen&&dimenname=&x318&&1073.25px&/dimen&&dimenname=&x319&&1076.62px&/dimen&&dimenname=&x320&&1080px&/dimen&&/resources&
无论在什么分辨率下,x320都是代表屏幕宽度,y480都是代表屏幕高度。
那么,我们应该如何使用呢?
首先,我们要把生成的所有values文件夹放到res目录下,当设计师把UI高清设计图给你之后,你就可以根据设计图上的尺寸,以某一个分辨率的机型为基础,找到对应像素数的单位,然后设置给控件即可。
下图还是两个Button,不同的是,我们把单位换成了我们在values文件夹下dimen的值,这样在你指定的分辨率下,不管宽度是320dp、360dp,还是410dp,就都可以完全适配了。
但是,还是有个问题,为什么下面的三个没有适配呢?
这是因为由于在生成的values文件夹里,没有对应的分辨率,其实一开始是报错的,因为默认的values没有对应dimen,所以我只能在默认values里面也创建对应文件,但是里面的数据却不好处理,因为不知道分辨率,我只好默认为x1=1dp保证尽量兼容。这也是这个解决方案的几个弊端,对于没有生成对应分辨率文件的手机,会使用默认values文件夹,如果默认文件夹没有,就会出现问题。
所以说,这个方案虽然是一劳永逸,但是由于实际上还是使用的px作为长度的度量单位,所以多少和google的要求有所背离,不好说以后会不会出现什么不可预测的问题。其次,如果要使用这个方案,你必须尽可能多的包含所有的分辨率,因为这个是使用这个方案的基础,如果有分辨率缺少,会造成显示效果很差,甚至出错的风险,而这又势必会增加软件包的大小和维护的难度,所以大家自己斟酌,择优使用。
更多信息可参考鸿洋的新文章 Android 屏幕适配方案
提供备用位图
由于 Android 可在具有各种屏幕密度的设备上运行,因此我们提供的位图资源应始终可以满足各类普遍密度范围的要求:低密度、中等密度、高密度以及超高密度。这将有助于我们的图片在所有屏幕密度上都能得到出色的质量和效果。
要生成这些图片,我们应先提取矢量格式的原始资源,然后根据以下尺寸范围针对各密度生成相应的图片。
xhdpi:2.0
mdpi:1.0(最低要求)
ldpi:0.75
也就是说,如果我们为 xhdpi 设备生成了 200x200 px尺寸的图片,就应该使用同一资源为 hdpi、mdpi 和 ldpi 设备分别生成 150x150、100x100 和 75x75 尺寸的图片。
然后,将生成的图片文件放在 res/ 下的相应子目录中(mdpi、hdpi、xhdpi、xxhdpi),系统就会根据运行您应用的设备的屏幕密度自动选择合适的图片。
这样一来,只要我们引用 @drawable/id,系统都能根据相应屏幕的 dpi 选取合适的位图。
还记得我们上面提到的图标设计尺寸吗?和这个其实是一个意思。
但是还有个问题需要注意下,如果是.9图或者是不需要多个分辨率的图片,就放在drawable文件夹即可,对应分辨率的图片要正确的放在合适的文件夹,否则会造成图片拉伸等问题。
实施自适应用户界面流程
前面我们介绍过,如何根据设备特点显示恰当的布局,但是这样做,会使得用户界面流程可能会有所不同。例如,如果应用处于双面板模式下,点击左侧面板上的项即可直接在右侧面板上显示相关内容;而如果该应用处于单面板模式下,点击相关的内容应该跳转到另外一个Activity进行后续的处理。所以我们应该按照下面的流程,一步步的完成自适应界面的实现。
确定当前布局
由于每种布局的实施都会稍有不同,因此我们需要先确定当前向用户显示的布局。例如,我们可以先了解用户所处的是“单面板”模式还是“双面板”模式。要做到这一点,可以通过查询指定视图是否存在以及是否已显示出来。
publicclassNewsReaderActivityextendsFragmentActivity{booleanmIsDualP@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main_layout);ViewarticleView=findViewById(R.id.article);mIsDualPane=articleView!=null&&articleView.getVisibility()==View.VISIBLE;}}
请注意,这段代码用于查询“报道”面板是否可用,与针对具体布局的硬编码查询相比,这段代码的灵活性要大得多。
再举一个适应各种组件的存在情况的方法示例:在对这些组件执行操作前先查看它们是否可用。例如,新闻阅读器示例应用中有一个用于打开菜单的按钮,但只有在版本低于 3.0 的 Android 上运行该应用时,这个按钮才会存在,因为 API 级别 11 或更高级别中的 ActionBar 已取代了该按钮的功能。因此,您可以使用以下代码为此按钮添加事件侦听器:
ButtoncatButton=(Button)findViewById(R.id.categorybutton);OnClickListenerlistener=/*createyourlistenerhere*/;if(catButton!=null){catButton.setOnClickListener(listener);}
根据当前布局做出响应
有些操作可能会因当前的具体布局而产生不同的结果。例如,在新闻阅读器示例中,如果用户界面处于双面板模式下,那么点击标题列表中的标题就会在右侧面板中打开相应报道;但如果用户界面处于单面板模式下,那么上述操作就会启动一个独立活动:
@OverridepublicvoidonHeadlineSelected(intindex){mArtIndex=if(mIsDualPane){/*displayarticleontherightpane*/mArticleFragment.displayArticle(mCurrentCat.getArticle(index));}else{/*startaseparateactivity*/Intentintent=newIntent(this,ArticleActivity.class);intent.putExtra(&catIndex&,mCatIndex);intent.putExtra(&artIndex&,index);startActivity(intent);}}
同样,如果该应用处于双面板模式下,就应设置带导航标签的操作栏;但如果该应用处于单面板模式下,就应使用下拉菜单设置导航栏。因此我们的代码还应确定哪种情况比较合适:
finalStringCATEGORIES[]={&热门报道&,&政治&,&经济&,&Technology&};publicvoidonCreate(BundlesavedInstanceState){....if(mIsDualPane){/*usetabsfornavigation*/actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);for(i=0;i&CATEGORIES.i++){actionBar.addTab(actionBar.newTab().setText(CATEGORIES[i]).setTabListener(handler));}actionBar.setSelectedNavigationItem(selTab);}else{/*uselistnavigation(spinner)*/actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);SpinnerAdapteradap=newArrayAdapter(this,R.layout.headline_item,CATEGORIES);actionBar.setListNavigationCallbacks(adap,handler);}}
重复使用其他活动中的片段
多屏幕设计中的重复模式是指,对于某些屏幕配置,已实施界面的一部分会用作面板;但对于其他配置,这部分就会以独立活动的形式存在。例如,在新闻阅读器示例中,对于较大的屏幕,新闻报道文本会显示在右侧面板中;但对于较小的屏幕,这些文本就会以独立活动的形式存在。
在类似情况下,通常可以在多个活动中重复使用相同的 Fragment 子类以避免代码重复。例如,在双面板布局中使用了 ArticleFragment:
&LinearLayoutxmlns:android=&/apk/res/android&android:layout_width=&fill_parent&android:layout_height=&fill_parent&android:orientation=&horizontal&&&fragmentandroid:id=&@+id/headlines&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.HeadlinesFragment&android:layout_width=&400dp&android:layout_marginRight=&10dp&/&&fragmentandroid:id=&@+id/article&android:layout_height=&fill_parent&android:name=&com.example.android.newsreader.ArticleFragment&android:layout_width=&fill_parent&/&&/LinearLayout&
然后又在小屏幕的Activity布局中重复使用了它 :
ArticleFragmentfrag=newArticleFragment();getSupportFragmentManager().beginTransaction().add(android.R.id.content,frag).commit();
当然,这与在 XML 布局中声明片段的效果是一样的,但在这种情况下却没必要使用 XML 布局,因为报道片段是此活动中的唯一组件。
请务必在设计片段时注意,不要针对具体活动创建强耦合。要做到这一点,通常可以定义一个接口,该接口概括了相关片段与其主活动交互所需的全部方式,然后让主活动实施该界面:
例如,新闻阅读器应用的 HeadlinesFragment 会精确执行以下代码:
publicclassHeadlinesFragmentextendsListFragment{...OnHeadlineSelectedListenermHeadlineSelectedListener=/*Mustbeimplementedbyhostactivity*/publicinterfaceOnHeadlineSelectedListener{publicvoidonHeadlineSelected(intindex);}...publicvoidsetOnHeadlineSelectedListener(OnHeadlineSelectedListenerlistener){mHeadlineSelectedListener=}}
然后,如果用户选择某个标题,相关片段就会通知由主活动指定的侦听器(而不是通知某个硬编码的具体活动):
publicclassHeadlinesFragmentextendsListFragment{...@OverridepublicvoidonItemClick(AdapterView&?&parent,Viewview,intposition,longid){if(null!=mHeadlineSelectedListener){mHeadlineSelectedListener.onHeadlineSelected(position);}}...}
除此之外,我们还可以使用第三方框架,比如说使用“订阅-发布”模式的EventBus来更多的优化组件之间的通信,减少耦合。
处理屏幕配置变化
如果我们使用独立Activity实施界面的独立部分,那么请注意,我们可能需要对特定配置变化(例如屏幕方向的变化)做出响应,以便保持界面的一致性。
例如,在运行 Android 3.0 或更高版本的标准 7 英寸平板电脑上,如果新闻阅读器示例应用运行在纵向模式下,就会在使用独立活动显示新闻报道;但如果该应用运行在横向模式下,就会使用双面板布局。
也就是说,如果用户处于纵向模式下且屏幕上显示的是用于阅读报道的活动,那么就需要在检测到屏幕方向变化(变成横向模式)后执行相应操作,即停止上述活动并返回主活动,以便在双面板布局中显示相关内容:
publicclassArticleActivityextendsFragmentActivity{intmCatIndex,mArtI@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);mCatIndex=getIntent().getExtras().getInt(&catIndex&,0);mArtIndex=getIntent().getExtras().getInt(&artIndex&,0);//Ifshouldbeintwo-panemode,finishtoreturntomainactivityif(getResources().getBoolean(R.bool.has_two_panes)){finish();}...}
通过上面几个步骤,我们就完全可以建立一个可以根据用户界面配置进行自适应的App了。
关于高清设计图尺寸
Google官方给出的高清设计图尺寸有两种方案,一种是以mdpi设计,然后对应放大得到更高分辨率的图片,另外一种则是以高分辨率作为设计大小,然后按照倍数对应缩小到小分辨率的图片。
根据经验,我更推荐第二种方法,因为小分辨率在生成高分辨率图片的时候,会出现像素丢失,我不知道是不是有方法可以阻止这种情况发生。
而分辨率可以以或者是作为主要分辨率进行设计。
ImageView的ScaleType属性
设置不同的ScaleType会得到不同的显示效果,一般情况下,设置为centerCrop能获得较好的适配效果。
有一些情况下,我们需要动态的设置控件大小或者是位置,比如说popwindow的显示位置和偏移量等,这个时候我们可以动态的获取当前的屏幕属性,然后设置合适的数值
publicclassScreenSizeUtil{publicstaticintgetScreenWidth(Activityactivity){returnactivity.getWindowManager().getDefaultDisplay().getWidth();}publicstaticintgetScreenHeight(Activityactivity){returnactivity.getWindowManager().getDefaultDisplay().getHeight();}}
最新教程周点击榜
微信扫一扫

我要回帖

更多关于 android 字体适配屏幕 的文章

 

随机推荐