当前位置:  编程技术>移动开发
本页文章导读:
    ▪多荧屏多分辨率的支持和一些概念        多屏幕多分辨率的支持和一些概念 多屏幕多分辨率的支持和一些概念快速预览Android 在不同的屏幕大小和密度上执行 屏幕的显示会影响用户界面的效果 系统处理大多数的自适应屏幕 你应.........
    ▪ 容易计算器 Basic Calculator (实例)        简单计算器 Basic Calculator (实例) Basic Calculator     In this tutorial i will be showing you how to create a basic calculator  Features:   1 Label 17 Round Rect Buttons This tutorial is a full featured app allowing you to add, minus,.........
    ▪ [转]Animation自定义卡通片效果的实现       [转]Animation自定义动画效果的实现 提供了三种动画效果:逐帧动画(frame-by-frame animation),这种动画和GIF一样,一帧一帧的显示来组成动画效果;布局动画(layout animation),这种动画用来设置lay.........

[1]多荧屏多分辨率的支持和一些概念
    来源: 互联网  发布时间: 2014-02-18
多屏幕多分辨率的支持和一些概念
多屏幕多分辨率的支持和一些概念
快速预览

Android 在不同的屏幕大小和密度上执行
屏幕的显示会影响用户界面的效果
系统处理大多数的自适应屏幕
你应该针对屏幕创建资源以更好的控制你的UI


Android 是一个系统,运行在这个系统上的很多设备它们的屏幕大小和密度都不一样。但我们需要解决这个问题。本节为详细的为你解释一些基础概念和教你怎么处理关于多屏幕多分辨率的支持。 虽然系统会根据不同的屏幕来执行缩放和调整你的App,但你还是应该自己在加点外力来最大化用户体验的效果。

多屏幕支持概述
以下是一些术语和概念,大家要稍微留心看看:

屏幕大小

根据屏幕对角线实际测量的物理大小(android已经根据屏幕大小分为四类small, normal, large, extra large)

屏幕密度

屏幕物理区域一个单位(一般用英寸)的像素数量,一般简称dpi(每英寸上的点数,记住DPI这个概念很重要)

这里android也分为四类 low, medium, high, extra high.

方向

从用户的角度看设备只有2个方向,那就是横屏和竖屏。关于横竖屏最好也要出一套相对应的UI,因为长宽比不一样,放心难度不是很大。

分辨率

一个物理屏幕上像素点的总数。一般我们的App不会直接使用分辨率,而是使用密度和屏幕大小

独立像素单位(Density-independent pixel (dp))

一个虚拟的单位,用来定于UI布局的。用来表述布局的尺寸或者位置。

dip(注 意dip是独立像素单位,dpi是屏幕密度)等价于一个160dpi(mdpi)的屏幕,运行时系统会根据你实际屏幕的密度来自动处理缩放。像素和dip 的转换公式为dp =px*(dpi/160) 。例如在240密度(dpi)的屏幕上一个dp等于1.5个像素(dp=px*1.5)。以后我们应该尽量使用dip单位布局,不要使用像素单位。这样会使你的app屏幕兼容性更好

多屏幕可以看成是许多Boss,要战胜这些boss有5点我们需要攻破它,分别是屏幕大小,屏幕密度,方向,分辨率,独立像素单位。

屏幕大小-Boss的外在体型:熟悉每个boss的体型,会让我们采取不同的战术,大的笨重,小的轻巧

屏幕密度-Boss的密度:别看有个boss体型很小,他密度高,一个俄罗斯梅花坐直接压死你,我们需要知道每个boss的密度。

方向-Boss的体型比:有的boss矮胖,看上去就像一个游戏手柄,但有些高瘦,看上去就像一根棍子。但记住我们是站着看的,但如果躺着看呢,所以角度不同看出来的结果就可能不一样

分辨率-boss的可识别度:隔boss近的话分辨率低,只能看到boss的某个部位而且很大,我不知道这是哪个boss;远的话分辨率高,能看到boss的所有部位,哦!原来这个boss就是伊利丹怒风

独立像素单位-boss的攻击力:每个boss的攻击技能都不太一样,所以我们需要吧所有boss的技能转换为攻击力来方便我们计算对付boss的策略

多屏幕支持的范围android1.6开始就支持多屏幕和密度了,这都是为了更好的用户体验,为了简化我们多屏幕的开发。android提供一组范围让我们使用:

关于屏幕大小: small, normal, large, xlarge(看来目前boss的体型就这四种)
注意: 关于android3.2会有些不同,以后我们在讲平板和手机支持时会说明

屏幕密度: ldpi (low), mdpi (medium), hdpi (high), xhdpi (extra high) (so 密度也是四种)
这些广义的大小和密度都是基于normal大小和mdpi来配置的或者说相对论。一般来说我们手机800*480或者854*480 密度都是240(hdpi),屏幕大小为large。

每一个大小和密度都有一个跨越幅度。但这些幅度中具体的微调我们是不用关心的,android会帮我们处理,下面我们来看图







我们设计UI都会要考虑一个最小化的空间,android中都是使用dp(dip)单位的。

xlarge 屏幕至少960dp x 720dp
large 屏幕至少 640dp x 480dp
normal 屏幕至少 470dp x 320dp
small 屏幕至少 426dp x 320dp
屏幕大小和密度是可以独立的,不是说一个5寸屏的密度就一定是hdpi。有一个很重要的例子就是例如一个WVGA (800×480)屏幕可能在不同的设备上会有mdpi和hdpid两种密度,正常的屏幕密度应该是hdpi。但不用担心我们是有办法解决这种问题的。

就是说boss体型大不一定密度高,万一它里面真空呢。但也不要小看体型小的boss,有可能它里面装的都是金属,你会很纠结怎么打败它。还有一些特殊的boss他体型一样,但密度可以变化,真牛B的boss啊!先来个真空上阵,再直接用铁砸死你。


独立密度维持独立密度是很重要的,因为没有它,一个UI元素(比如一个按钮)会出现在一个物理屏幕很大,但密度很少的显示效果(看起来就是像是被强行放大的,失真很严重)。这样相对位置的改变会出问题



图1是你的App没有支持不同密度产生的效果



图2是具有良好的支持程序为例产生的效果

android 系统帮助你的App实现独立密度的方法有两种:

系统缩放dp单位来适用当前屏幕密度The system scales dp units as appropriate for the current screen density
系统基于当前屏幕密度缩放drawable资源到适用的大小
图 1是用像素单位来定义的大小。 你发现他们的布局完全变了,有的大有的小。这是因为他们的实际屏幕大小可能是一样的,高密度屏幕每英寸的像素更多(所以你会发现像素一样,但高密度的设备显示出来的效果却很小)。图2使用的是dp单位,基于mdpi密度的设备它不会变化,ldpi它会自动缩小,hdpi它会自动放大。所以效果非常好。图片 显示出来的样子和大小完全一样。

使用密度系统会缩放图片使之看上去完全一样,但细心的你会发现有些图片变模糊了。所以我们应该提供可选择的图片资源让系统来选择以适应不同的密度。所以以后工程项目中hdpi,mdpi,ldpi三个文件夹就是用在这里的。这样就不会有模糊的感觉了,perfect!!

我们一定要换算boss的攻击力,好躲避技能,它的技能实在太多了,搞不好一口水就能淹死你(强力的攻击啊)。

怎样支持多屏幕(怎么搞定这么多boss)
以上我们学习了这么多的理论基础。我们应该优雅的处理它们了。

1.明确的在manifest文件中指出你的App支持的屏幕大小(哈哈,抓到你的小辫子了吧)
我们可以有选择性的过滤掉一些老掉牙的设备,以减轻我们的工作量。但这并不是一个好的方法仅供参考。在manifest 中使用节点<supports-screens>

2.根据不同的屏幕大小提供不同的布局(我们根据不同的boss体型来换不同的装备应对,暗影系boss需要多穿点暗抗装啊)
默认的android会调整你App的布局来使用当前设备的屏幕。大多数情况下可以满足,但某些特殊情况,你的UI可能需要一个更精确的调整。例如,一个larger 屏幕,你可能想要利用额外的屏幕控件调整位置和一些元素的大小,或者一个小屏幕中,你可能需要调整到刚好合适。比如你需要适应一个extra large 屏幕。你可以在文件夹layout-xlarge下放置布局(大家记住一般layut下放的都是xml布局文件)

Android 3.2开始,有一种新的放置方式。例如你的平板需求至少600dp的屏幕宽度,你可以使用layout-sw600dp 这种文件夹放置。以后我们在平板一节会详细描述

3.根据不同的屏幕密度提供不同的位图(根据不同的boss密度来学技能,你敢压我?我有闪避 谢谢!)
默认android会缩放你的位图(.png.jpg.gif),还有一种android专用的Nine-Patch(.9.png)。例如,你的App提供的位图仅仅为标准密度(mdpi),那么在hdpi的屏幕上这些位置会被等比放大。所以我们应该有选择性的对于不同的屏幕密度包含不同的分辨率。

所以android针对图片资源配置后缀,前面我们知道的4种密度(ldpi.mdpi,hdpi,xhpi),如果是ldpi 我们应该使用drawable- ldpi这种文件夹。以上关于屏幕大小和密度的后缀都符合屏幕支持的那些范围。

注意: 如果你没有熟悉的配置后缀, 不知道如何使用它们,没关系。我们以后会在App资源中讲解!

在运行时, 对于给定资源系统使用以下这些步骤保证你最佳的显示:

系统会使用合适的替代资源
基于当前屏幕的大小和密度,系统会针对屏幕的密度和大小使用资源。例如,如果设备有一个 hdpi的屏幕那么当App请求一个drawable 资源时,系统会在最匹配的的设备配置下的drawable 资源目录寻找。可以取决于其他可替代的资源,使用drawable-hdpi的资源目录最匹配的,所以系统会使用drawable-hdpi目录下的资源。

如果没有可用的匹配资源,系统会使用默认资源等比放大或缩小以匹配当前屏幕的大小和密度。
这里的默认资源是没有后缀的。例如,“drawable”就是默认的drawable 资源。系统会认为默认的资源都是基于normal 屏幕大小和medium 密度设计的。系统会等比缩放默认的密度资源来匹配hdpi密度或者ldpi密度。当系统没有找到针对密度资源的目录时,它就一直会使用默认的资源。例如,如果系统想找一个ldpi(低密度)的资源,但找不到。那么系统会等比缩小hdpi的资源,为什么不寻找mdpi呢?因为系统对于hdpi更容易缩放,它的系数为0.5,相比mdpi的0.75来说。0.5的的性价比更高(方便计算,正确率也高一点)。关于这一点最好的证明就是android版本的QQ浏览器。你解压后发现它关于drawable就只有drawable和drawable-hdpi两个文件夹,看来腾讯里android的开发者应该很熟悉android系统规律了,因为默认的drawable 就是mdpi,只需要定义一套hdpi的drawable资源就行了。



使用配置后缀

Android支持几种配置后缀,它允许你控制系统有选择性的筛选基于当前屏幕设备的资源文件。一个配置后缀就是一个String你可以在资源目录文件夹下追加指定的后缀即可 。



使用一个配置后缀的方法步骤:



在你的工程res目录下创建一个新的目录,格式为<资源种类名>-<后缀>: 
<资源种类名>是标准的资源名字(例如 drawable或者layout)
<后缀> 是指定屏幕配置使用哪一种资源(例如hdpi或者xlarge)
我们可以使用的后缀不仅仅只有一个哦,在一个文件夹下我们可以同时使用多种后缀来限定资源,使用"-"即可。

在新的目录中保存合适资源,资源文件的命名必须和默认资源文件一样。


例如 xlarge是一个后缀用于extra large 屏幕。当你追加后缀时(例如layout-xlarge) ,它说明系统会在extra large屏幕上使用这些资源



表格1.

装备,技能齐全啊。求秒杀boss!!!

屏幕特征 后缀 描述
大小 small 资源用于small大小的屏幕.
normal 资源用于normal 大小的屏幕。(这是默认的基准大小)
large 资源用于large 大小的屏幕
xlarge 资源用于extra large 大小的屏幕
密度 ldpi 资源用于 low-density (ldpi) 密度的屏幕 (~120dpi).
mdpi 资源用于medium-density (mdpi) 密度的屏幕 (~160dpi). (这是默认的基准密度.)
hdpi 资源用于high-density (hdpi) 密度的屏幕 (~240dpi).
xhdpi 资源用于extra high-density (xhdpi) 密度的屏幕 (~320dpi).
nodpi 资源用于所有密度. 系统不会根据当前屏幕密度去缩放资源
tvdpi 资源用于mdpi 和hdpi两者之间的某的密度;大约是213dpi。 这个很少用到。它主要用于电视,大多数App不需要用到它。如果你需要tvdpi资源,它的大小大概是1.33*mdpi(160)例如,一个在mdpi下100px*100px的图片,那么在 tvdpi中它会变成133px*133px。
方向 land 资源用于横屏
port 资源用于竖屏
长宽比 long 资源用于长宽比相差很远的配置(相对于4寸(normal)屏幕左右基准屏幕)
notlong 资源用于长宽比差不多的配置(相对论,同上)



注意: 如果对于android3.2或者更高的版本,有一些新的后缀,等下我们会讲解

下面我们就来举一些详细的例子

res/layout/my_layout.xml             // 用于normal 屏幕大小的布局 ("默认")res/layout-small/my_layout.xml       // 用于small 屏幕大小的布局res/layout-large/my_layout.xml       // 用于large屏幕大小的布局res/layout-xlarge/my_layout.xml      // 用于extra large屏幕大小的布局res/layout-xlarge-land/my_layout.xml // 于extra large屏幕大小并且是横屏的布局res/drawable-mdpi/my_icon.png        // 用于中等密度的位图res/drawable-hdpi/my_icon.png        // 用于高密度的位图res/drawable-xhdpi/my_icon.png       // 用于超高密度的位图
当android系统在运行时选择资源时,它会以一定的逻辑判断来匹配最适合的资源。前提是你没有加后缀。当基于使用大小的后缀时,如果当前没有资源更好的匹配你的后缀,那么系统会使用

比当前屏幕更小的资源来计算(例如,如果你没有添加large后缀,那么一个large大小的屏幕将会使用在normal(默认的)大小的屏幕资源),如果可用的资源后缀比当前屏幕更大(其实还是没匹配

上),那么系统将不使用它们(例如你吧所有layout资源放入xlarge后缀中,但设备是一个normal屏幕,系统就不会使用这些资源)。

是不是有点头晕了,简要的概述下就是如果没有匹配的系统会使用默认的资源,如果连默认下都没有放资源并且没有匹配到你的后缀,那么系统就没有资源可用啦。

注意:某些情况下你可能需要用到nodpi后缀,因为你可能不想要它被缩放,或者你想要在运行时通过代码手动缩放它。



设计可供选择的layouts(装备)和drawables(技能)

你应该根据于你App的需求来创建可供选择的资源类型。通常,你应该使用大小和方向后缀来提供可选择的Layout资源,使用密度后缀来提供可选择的位图drawable资源



可供选择的布局(layouts)


你应该知道你的App是否有这个需求,是否需要根据不同的屏幕来提供可选择的布局。例如:



当在一个small屏幕上测试时,你发现你的布局没有完全符合你的要求或者是没有匹配好屏幕。例如一排按钮没有很好的在small屏幕上显示。这种情况你应该为small屏幕提供一个可供选择的布局用来调整按钮的大小或者位置。
当在一个extra large屏幕测试时, 如果没有有效利用大屏幕或者明显的被拉伸了,这种情况你也应该提供一种根据extra large屏幕的布局,并且最好再优化它以适应将来的平板设备
虽然你的App没有提供大屏幕的布局运行起来也没问题,但UI被明显拉伸的感觉会让用户觉得这个APP不太精致,用户体验会大打折扣

当在横屏和竖屏对比测试时, 你应该注意到UI元素在竖屏下是在底部,横屏下却是右边。如果你一直需要都在底部的话,你也应该配置后缀
总的来说,你应该确保你的App布局如下:

适用于小屏幕
优化App使之在大屏幕上利用额外的空间
优化横竖屏
如果你的UI使用位图,那么你应该使用nine-patch (超牛B特殊技能,目前游戏版本没更新,以后更新了马上给学了)位图文件。nine-patch 这是android提供的一种格式并且它还提供一种根据让你吧美工出的图片稍微修饰。这种格式能允许你设置可以缩放的2D范围。当系统需要缩放时,系统会在你指定区域缩放 Nine-Patch位图。这样在不同的屏幕大小下你就不需要提供不同的位图资源了(可以节省APK的大小)。因为Nine-Patch会自动调整它的。但在不同的屏幕密度下你应该还是提供可供选择的Nine-Patch 文件。这样你的APP才会更健壮。关于nine-patch的使用我们以后会在第二大篇的图形章详细讲述。



可供选择的drawables




图3. 根据每一个密度下drawable下位图的相对大小(目前4种技能应该够用了)



几乎每一个App都会根据不同的密度提供可选择的drawable资源,因为这对于用户体验和UI真的很重要。

注意: 根据密度你仅需要提供drawable下的位图文件,如果你使用Xml来定义shape,colors或者其他drawable的资源,你就应该放到"drawable/"默认目录下

根据不同的密度提供可选择的位图drawable资源时,你应该在四种密度下使用3:4:6:8的缩放比。你可以参考工程下每个不同的drawable里的icon尺寸。以下是数据:

36x36 ldpi
48x48 mdpi
72x72 hdpi
96x96 xhdpi


关于Android 3.2平板上的布局
第一代平板运行于android3.0上,一般是使用xlarge配置后缀(res/layout-xlarge/)来声明平板布局。为了提供其他类型的平板和屏幕大小----尤其是7寸平板!android3.2引入了一种新的方法来为更多离散的屏幕大小来指定资源。你的布局需要一种新的基于控件容量的技术(例如指定600dp的宽度),而不是试图使你的布局去适应那些android概括性的分组(例如large或xlarge)

当android去概括大小分组时,对于7寸平板他们也很棘手,因为5寸手机和7寸平板使用同样的large组。这两个设备在size上表面上看起来更贴近彼此,但其实UI在相当大程度上还是不同的,用户体验的效果也不太好。因此一个7寸和5寸的屏幕将不使用同样的layout。根据这两种屏幕你应该尽可能的提供不同的布局,android现在允许我们指定基于宽度或高度layout资源,记住请使用dp单位。

例如, 在你根据平板风格设计布局以后,你可能需要当屏幕小于600dp的宽度时候停止工作。这个界限会因为你的平板布局而变成最小的尺寸。这样你能立刻指定当至少宽度在600dp时候,layout中的资源文件才被用到。

注意: 记住!dip是一个非常重要的单位,你的布局大小也应该用dp单位。因为它可以看做一种比例单位而非像素那样的绝对单位。

使用新的大小后缀(非密度)你能为你的布局指定不同的资源配置。在下方的表格2中这些新的后缀提供给你更多的控制,相对于传统的(small, normal, large, and xlarge)这些已经超过它们了

注意: 你指定使用的这些后缀并非实际的屏幕大小。当然,这些大小是根据宽度(dp单位)和高度(dp单位)被用于activity的window中。window的特殊性在于它方法是有个动作条,用来显示电池和通知信息的。所以你在设计UI的时候需要考虑 你的App是否需要显示动作条。

表格2. 以下是android3.2进入的新后缀(注意是基于屏幕大小的而不是密度)



屏幕 配置 后缀值 描述
最小宽度 sw<N>dp

例如:
sw600dp
sw720dp 用这个后缀可以确保不管当前屏幕是否横竖屏。你的App有一个至少<N>dp的可用宽度


例如, 如果你的layout一直需求最小屏幕的一边为600dp,那么你能使用这个后缀创建layout资源(res/layout-sw600dp/)。对于用户来讲,不管600dp是宽还是高,仅当屏幕可用的最小尺寸至少是600dp时,系统会使用这些资源(就是说你的设备不用以什么角度看,长和宽的某一边的最小值大于或等于600dp时,系统就会使用。)。当屏幕水平方向改变时,设备的最小宽度不会改变

设备的最小宽度要考虑屏幕的装饰和系统UI。例如,如果设备有一些持续不变的沿着你最小宽度的轴方向的UI元素(动作条等),那么系统会宣布最小宽度比实际屏幕宽度要小,因为那些UI元素对于你的UI来说是不可用的

因为宽度是经常影响布局的一个重要因素,所以使用最小宽度来控制一般的屏幕大小(针对平板)还是有用的。 可用宽度也是决定是否使用单屏幕布局(手机)和多屏幕布局(平板)关键因素,因此你很可能关心每一个设备上的最小方面。

屏幕可用宽度 w<N>dp

例如:
w720dp
w1024dp 用dp单位指定一个最小化的在可用宽度下可以使用的资源。系统会根据宽度改变(横竖屏切换时)来匹配这个值,并反映到当前实际可用的宽度上

当你决定是否使用多屏幕布局时它很有用,因为即使在平板设备上,你也经常不想要多屏幕布局会根据横竖屏来变化。因此你可以用这个来指定最小化的宽度需求,它可以用来代替方向后缀(land,port)和大小后缀(small,normal,large,xlarge)使之整合到一起。

屏幕可用高度 h<N>dp

例如:
h720dp
h1024dp
etc. 用dp单位指定一个最小化的屏幕高度。和"屏幕可用宽度"类似




当使用这些后缀时你可能觉得比传统的那四种后缀复杂些,实际上你用过后就发现它很简单,它能一次简化你的UI需求。当你设计UI时,主要的事情就是需要考虑我们App中的实际大小,并且对于手机和平板风格的切换。它取决于你某些特定切换点的设计,可能对于平板布局你需要720dp的宽度,可能600dp就足够了,或者480,或者这两个之间。使用表格2的后缀,你可以在布局改变时精确的控制大小。以后我们会根据实际开发来讲述的



配置例子以下是一些屏幕数据:

320dp: 一种手机屏幕(240x320 ldpi, 320x480 mdpi, 480x800 hdpi, 等).
480dp: 一种平板 (480x800 mdpi).
600dp:  7寸平板(600x1024 mdpi).
720dp: 10寸平板(720x1280 mdpi, 800x1280 mdpi, 等).
我们使用表格2中的后缀来为我们的App定义不同的切换风格(包括手机和平板),例如, 如果我们的平板布局600dp是最小可用宽度,我们能提供两套布局方式:

res/layout/main_activity.xml           # 手机res/layout-sw600dp/main_activity.xml   # 平板
这种情况下,屏幕可用的最小宽度为600dp,这是伪了支持平板布局被应用。

或者你可能想要区分7寸和10寸平板,你可以这么做:

res/layout/main_activity.xml           # 手机(比600dp更小的可用宽度)res/layout-sw600dp/main_activity.xml   # 7寸平板 (600dp的宽度或者更大)res/layout-sw720dp/main_activity.xml   # 10寸平板(720dp的宽度或者更大)
注意上面的两套例子的使用使用 sw(最小宽度)后缀,它指定屏幕的两边中的最小一边,不管屏幕的水平方向。它忽视横竖屏。

然而某些情况下我们需要精确布局。例如如果你有一个 2个面板合并在一起的显示效果。是否屏幕提供至少600dp的宽度,是否横竖屏你都要使用它。就应该这:

res/layout/main_activity.xml         # 手机(小于600dp的可用宽度)res/layout-w600dp/main_activity.xml  #多面板 (任意一个面板都是600dp或者更高的宽度)
注意上面这里用的是w<n>dp。实际设备可能需要两套布局,它依赖于屏幕的水平方向(一边至少是600dp的宽度,另一边小于600dp,你会发现不管横竖屏都满足这个条件。所以你需要准备两套关于横竖屏的布局)

关于什么是多屏手机或者叫多面板手机。我发个图给大家看看就明白了(下面这个手机其实有3屏)





关于声明屏幕支持的大小

在android3.2的manifest 中引入了<supports-screens> 节点,以后再讲manifest 文件的时候我们会详细描述

实践中应该注意哪些
在多样的屏幕中我们使用传统的四种配置还是能很好的获得支持的。上面我们提供了多种定义的方法。添加这些后缀能确保你的App能适应不同的屏幕设备。

下面是一些方法,告诉你如何确保你的应用程序可以正确地显示在不同的屏幕上:

在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位
在你的程序代码中最好不要使用像素(px)这种硬编码
不要使用AbsoluteLayout (绝对布局)
提供不同的位图drawable资源来适应不同的屏幕密度.
1. 使用在XML布局文件中请使用wrap_content, fill_parent, 或者 dp 单位(装备附魔要搞好)当在XML中为你的Views定义android:layout_width 和android:layout_height 时, 使用"wrap_content", "fill_parent" 或者dp 单位来保证View在当前屏幕上获得一个合适的大小。

例如,一个view的宽为layout_width="100dp"   在mdpi的屏幕下它是100px,在hdpi的屏幕下它就是150px,但显示出来的效果在物理屏幕上是一样的大小

同样, 对于定义文本的大小,我们应该用sp(scale-independent pixel)。 因为sp和dp的概念是一样的,它们不是绝对的像素值

2.在你的程序代码中最好不要使用像素(px)这种硬编码由于执行原因并使代码更简单,android系统使用像素作为标准单位用来描述尺寸或坐标值。就是说在代码中android还是使用的像素用来表述尺寸,但它是基于当前屏幕密度的,所以是变化的。如果代码中 View.getWidth()返回的值是10,这么这个10的单位为像素,但这仅仅是在某一个密度的屏幕上而已,其他不同密度的屏幕上它的结果就会不一样了。所以android是不建议我们在代码中用像素来设置布局的,因为它会加重我们的工作量,并且处理的也不一定很好。想象一下这么多屏幕设备你如果用代码来适配的话,你就得考虑的非常严谨了。不过如果你只针对某一种屏幕的话就另当别论了,但这种情况很少见。

3.不要使用AbsoluteLayout (绝对布局)(这种装备out了,赶紧摧毁)请不要使用AbsoluteLayout(绝对布局)这种布局是早期android的版本,在android1.5版本的时候就已经废弃了。虽然为了兼容以前很老的设备这种布局还存在,但目前来说,我们已经完全没有必要再使用它了。

4.提供不同的位图drawable资源来适应不同的屏幕密度.(根据不同的boss我们应该选相应的技能哦)虽然在当前屏幕配置上系统会自动缩放你的layout和drawable资源,但为了优化某些特定密度的设备,可能我们并不想让它缩放,我们想给这种密度指定一套资源也是可以而且这样的效果也

很不错,能更好的根据不同的屏幕来调整我们的UI。这个我们在本文中已经反复提到了,目的就是为了加深你的印象。

一个很好的例子就是关于你用eclipse生成android工程的时候,三种不同的drawable(res/drawable-ldpi/icon.png,res/drawable-mdpi/icon.png,res/drawable-hdpi/icon.png)中都

会自动生成不同尺寸的icon.png。

注意: 如果我们没有定义某个后缀,但屏幕密度又是需要那个后缀的话,那么系统会假定你的资源都是基于mdpi(默认)的。并会放缩你的资源



关于密度额外的注意事项


这一段主要描述android在不同屏幕密度上是怎么缩放位图drawable的,我们需要更进一步的掌握系统控制位图资源的原理。



当在运行时操作图形,我们能更好的理解它是怎样支持多屏幕的,我们应该了解下系统是如何保证适应屏幕并适当的缩放位图的:



载入时自动缩放 (比如位图drawables)--根据boss密度自动选择技能?这真太智能了
基于当前屏幕的密度,系统使用任意大小或者密度的资源来显示它们的时候是没有缩放过的。如果在当前密度下没有可用的资源,那么系统会载入默认资源并等比缩放它们来匹配当前屏幕的密度。除非有有针对密度的后缀出现,不然系统都是认为默认资源(没有后缀的drawable)是为mdpi的密度来设计的。因此系统这个时候会调整位图的大小来适应屏幕。

如果你需求资源缩放之前的大小,那系统实际上返回的是缩放后的大小。例如:如果你没有指定hdpi后缀,一个在mdpi下50px*50px的位图在一个hdpi下会缩放为75px*75px。在hdpi屏幕下系统会返回这个75px这个大小。

如果你不希望系统根据不同的密度来缩放资源,那么请记住使用“res/drawable-nodpi/”

运行时自动缩放(比如像素的大小和坐标)--打boss的时候在自动选?这也挺牛B
一个App能关闭载入时自动缩放的这个功能,只要你在manifest中设置android:anyDensity ="false",或者在程序中使用BitmapFactory.Options.inScaled返回的值为false。在这种情况下。系统会在绘制的时候自动等比缩放任何绝对坐标和像素大小。这么做确保用针对像素的屏幕元素能一直显示。系统会处理缩放,然后转化并报告缩放的像素大小,而不是物理像素大小。下面我们来举个例子:

例如,假设一个设备有一个WVGA(800*480)下是hdpi的屏幕,并且它和在传统的HVGA(480*320)屏幕一样的物理大小,但执行App的时候关闭了载入时自动缩放这个功能。这样的话,当系统查询屏幕大小时,它会认为谁TMD的这么坑爹。然后他会报告一个320*533的大小,为什么会是320*533呢,我们开的模拟器是800*480的啊!!因为系统在绘制的时候缩放了。系统报告的是缩放后的像素大小,而不是我们模拟器上的物理大小。然后App需要绘制操作时,本来想在(10,10)上的位置绘制,但会变成(15,15)。如果你的App直接操作缩放位图,那这种差异可能导致意外的行为。以前很多刚开始接触android的朋友经常会遇到系统报告320*533的这种问题。原因就是由于我们关闭了载入时自动缩放这个功能。你关闭了它的话,它就会在运行时缩放并返回结果。所以我们要检查manifest中是否设置android:anyDensity ="false"。如果有赶快去掉。也许还有朋友发现没有设置怎么也会出现这种问题,这是由于以前比较老的SDK版本系统默认设置关闭了载入时自动缩放这个功能导致的,不过目前这种情况很少发生了。



运行时创建缩放的位图对象我们的App如果需要在内存中创建一个位图(bitmap)对象,那么系统会认为你是基于mdpi密度的屏幕来创建的。默认的,系统会根据屏幕密度在绘制的时候自动缩放这个位图。当位图没有指

定密度属性时候,系统会自动缩放。为了控制位图在运行时创建后是否被缩放,我们可以指定位图的密度属性,使用Bitmap.setDensity()。具体的值可以传DisplayMetrics.DENSITY_HIGH或

其他。我们还可以从文件或者一个流中使用BitmapFactory来创建位图,使用BitmapFactory.Options来定义位图的属性,那么系统会根据你的属性来缩放它。我们还可以使用

BitmapFactory.Options.inDensity来指定这个位图是否需要匹配当前的屏幕密度。如果我们设置BitmapFactory.Options.inDensity=false;那么系统在载入位图时将不会自动缩放它,只会在

运行时缩放它。使用运行时缩放CPU占用率高,内存占用低。使用载入时缩放CPU占用率低,内存占用高。如何取舍就看你的需求了。



dp 和 像素的转换(攻击力有时候还需要转成技能的)

有些情况下,dp和px只需要相互转换的。例如一个App在用手指滑动屏幕的时候会感应用户的手指移动了多少个像素。在一个normal和mdpi的屏幕下,用户必须移动16px/160dpi,等价于十分之一英寸(大约2.5mm(毫米))。那么在一个hdpi(240dpi)的屏幕上用户必须移动16px/20dpi,等价十五分之一英寸(1.7mm)。距离是很短的,因此这对用来说会很敏感。为了修复这个问题,需要用代码吧dp转换成px单位。例如:



// 手势的响应的范围(dp)privatestaticfinalfloat GESTURE_THRESHOLD_DP =16.0f;// 获取屏幕的密度来缩放finalfloat scale =getResources().getDisplayMetrics().density;//根据密度吧dp转换成pxmGestureThreshold =(int)(GESTURE_THRESHOLD_DP * scale +0.5f);// 可以使用这个响应的范围了
复制代码


这里DisplayMetrics.density的值为(0.75[ldpi],1[mdpi],1.5[hdpi],2[xhdpi]) ,其实我们还可以使用ViewConfiguration类来处理,但前提是打开了载入时缩放这个功能(目前来说,默认都

是开的不用担心)并且我们可以使用ViewConfiguration. getScaledTouchSlop()来直接获得换算距离。具体使用如下:

privatestaticfinalint GESTURE_THRESHOLD_DP =ViewConfiguration.get(myContext).getScaledTouchSlop();
复制代码
在多种屏幕下怎样测试我们的App

图5. 这是android SDK中的AVD工具,它提供一套虚拟机设备。上面这些都是通过你点击"new"建立的,不是默认就有的哦,以后会详细讲述使用方法的

在发布我们的应用之前,我应该在不同的屏幕大小和密度上测试我们的App。android SDK中有很多模拟器,我们可以修改模拟器上的默认的大小,密度,分辨率来整合测试。例如使用android2.1版本的模拟器,我们可以同时新建多个2.1的版本,但每个的密度不同。这么多模拟器可以为我们省下很多钱来买不同的手机。如果你想修改某个模拟器的密度,你只需要选中它点击右边的edit就可以修改了。如下图所示:



为了测试App是否支持多种不同的屏幕,我们应该创建一系列的AVD(android virtual devices)。如果不用eclipse的话,我们可以在sdk目录下执行SDK Manager.exe这样就显示出图5的界面

关于android虚拟机的管理和使用我们以后会详细描述。



表格3. 以下是模拟器中多种可用的配置(加粗的是模拟器中自带的,如果需要其他就需要手动设置下了)

  Low density (120), ldpi Medium density (160), mdpi High density (240), hdpi Extra high density (320), xhdpi
Small screen QVGA (240x320)   480x640  
Normal screen WQVGA400 (240x400)
WQVGA432 (240x432) HVGA (320x480) WVGA800 (480x800)
WVGA854 (480x854)
600x1024 640x960
Large screen WVGA800(480x800)
WVGA854 (480x854) WVGA800(480x800)
WVGA854 (480x854)
600x1024    
Extra Large screen 1024x600 WXGA (1280x800)†
1024x768
1280x768 1536x1152
1920x1152
1920x1200 2048x1536
2560x1536
2560x1600

† 表示用于android3.0的平台




图6. 这是设置我们模拟器开启后的大小,一般默认的模拟在电脑上打开显然很大,如果你需要缩小的话就条市政以下scale即可

建议大家启动模拟器的时候最好不要缩放,这样看起来的结果是物理屏幕上区别就不会很大了。当你从AVD Manager启动一个模拟器时,我们指定的dpi的时候,可以和自己显示器匹配下。

如果我们需要使用命令启动的话,可以这样启动:

emulator -avd <avd_name>-scale 96dpi


总结:

关于android的屏幕和分辨率,这是非常重要的一个知识点,希望大家认真学好它,在以后的开发过程中绝对是非常有帮助。

现在我们稍微回顾一下关键的内容:

一,Boss的5个关键点

1.体型(屏幕大小)

2.体型的密度(屏幕密度)

3.体型比(屏幕的方向)

4.可识别度(屏幕的分辨率)

5.攻击力(dp单位)

二,我们也有相应的对策对付他们

drawable(我们游戏角色的技能,目前4种(ldpi,mdpi,hdpi,xhdpi))

layout(我们游戏角色的装备,目前4种(small,normal,large,extra large))

三,怎么样适应各个boss,让我们轻松搞定它们

manifest文件中指出你的App支持的屏幕大小

根据不同的屏幕大小提供不同的布局

根据不同的屏幕密度提供不同的位图

四,最后我们需要熟悉后缀和一些公式

目前那么多后缀不需要完全记住(以后用的多自然就记住了),但需要有个印象(后缀可以为drawable服务,也可以为layout服务,你可以想象成后缀既是宝石--附魔嵌在装备(layout)上的,又是铭文--提高技能(drawable)伤害或其他技能功能的)

还记得3:4:6:8吗? 还记得 0.75x, x ,1.5x, 2x吗?这些都很重要哦

下一节我们继续还是讲多屏幕的适配,但都比较具体的实例,大家应该很喜欢的,但前提是吧这一节的理论巩固好!


如果谁有好的写作工具麻烦推荐给我,谢谢!!

转载请注明出处:http://www.cnblogs.com/tianjian/category/354587.html

    
[2] 容易计算器 Basic Calculator (实例)
    来源: 互联网  发布时间: 2014-02-18
简单计算器 Basic Calculator (实例)

Basic Calculator 

 

 In this tutorial i will be showing you how to create a basic calculator 

Features: 

  •  1 Label
  • 17 Round Rect Buttons
This tutorial is a full featured app allowing you to add, minus, divide and multiply once created it can be ready to be submitted to the app store or you could expand on it and give it you own personal touch

 

The Code

calcViewController.h 


#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    float result;
    int currentOperation;
    float currentNumber;
    __weak IBOutlet UILabel *calulatorScreen;
    
}

- (IBAction)buttonDigitPressed:(id)sender;
- (IBAction)buttonOperationPressed:(id)sender;
- (IBAction)cancelInput:(id)sender;
- (IBAction)cancelOperation:(id)sender;

@end
 

calcViewController.m

 

- (IBAction)buttonDigitPressed:(id)sender 
{
    currentNumber = currentNumber*10 + (float)[sender tag];
    calulatorScreen.text = [NSString stringWithFormat:@"%2f",currentNumber];
}

- (IBAction)buttonOperationPressed:(id)sender 
{
    if(currentOperation == 0) result = currentNumber;
    else
    {
        switch (currentOperation) {
            case 1:
                result = result + currentNumber;
            break;
                
            case 2:
                result = result - currentNumber;
            break;
                
            case 3:
                result = result * currentNumber;
            break;
                
            case 4:
                result = result / currentNumber;
            break;
               
            default:
                currentNumber= 0;
            break;
        }
    }
    
    currentNumber = 0;
    calulatorScreen.text = [NSString stringWithFormat:@"%2f",result];
    if([sender tag] == 0) result = 0;
    currentOperation = [sender tag];
}

- (IBAction)cancelInput:(id)sender 
{
    currentNumber = 0;
    calulatorScreen.text = @"0";
}

- (IBAction)cancelOperation:(id)sender 
{
    currentNumber = 0;
    currentOperation = 0;
    calulatorScreen.text = @"0";
}
 

 

- (void)viewDidUnload
{
    calulatorScreen = nil;
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}
 

 

来源:http://www.youtube.com/watch?v=Ihw0cfNOrr4

 

 

 

 

 


    
[3] [转]Animation自定义卡通片效果的实现
    来源: 互联网  发布时间: 2014-02-18
[转]Animation自定义动画效果的实现

提供了三种动画效果:逐帧动画(frame-by-frame animation),这种动画和GIF一样,一帧一帧的显示来组成动画效果;布局动画(layout animation),这种动画用来设置layout内的所有UI控件;控件动画(view animation),这种是应用到具体某个view上的动画。

 
在这三种动画实现中逐帧动画是最简单的,而控件动画是有点复杂的,要涉及到线性代数中的矩阵运算,下面就由易到难逐个介绍,先来看看逐帧动画如何实现。
 
逐帧动画
逐帧动画是通过OPhone中的android.graphics.drawable.AnimationDrawable类来实现的,在该类中保存了帧序列以及显示的时间,为了简化动画的创建OPhone提供了一种通过XML来创建逐帧动画的方式,这样把动画的创建和代码分来以后如果需要修改动画内容,只需要修改资源文件就可以了不用修改代码,简化开发维护工作。在res/drawable/文件夹下创建一个XML文件,下面是一个示例文件(res\drawable\qq_animation.xml):
view plaincopy to clipboardprint?
  • <animation-list    
  •     xmlns:android="http://schemas.android.com/apk/res/android"  
  •     android:oneshot="false">   
  • <item android:drawable="@drawable/qq001" android:duration="80"/>   
  • <item android:drawable="@drawable/qq002" android:duration="80"/>   
  • <item android:drawable="@drawable/qq003" android:duration="80"/>   
  • <item android:drawable="@drawable/qq004" android:duration="80"/>   
  • <item android:drawable="@drawable/qq005" android:duration="80"/>   
  • <item android:drawable="@drawable/qq006" android:duration="80"/>   
  • <item android:drawable="@drawable/qq007" android:duration="80"/>   
  • <item android:drawable="@drawable/qq008" android:duration="80"/>   
  • </animation-list>  
  • Java代码  
  • <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/qq001" android:duration="80"/> <item android:drawable="@drawable/qq002" android:duration="80"/> <item android:drawable="@drawable/qq003" android:duration="80"/> <item android:drawable="@drawable/qq004" android:duration="80"/> <item android:drawable="@drawable/qq005" android:duration="80"/> <item android:drawable="@drawable/qq006" android:duration="80"/> <item android:drawable="@drawable/qq007" android:duration="80"/> <item android:drawable="@drawable/qq008" android:duration="80"/> </animation-list>  
  •  
     
    在上面的定义中通过animation-list来指定这是个AnimationDrawable动画定义,里面的item来指定每帧图片和显示时间(单位为毫秒),帧显示的顺序就是item定义的顺序。如果android:oneshot设置为true表明该动画只播放一次,否则该动画会循环播放。这些设置也可以通过AnimationDrawable提供的函数来设置。动画中引用的文件为QQ表情文件,包含在示例项目代码中。
    然后在layout中定义一个ImageView来显示上面定义的AnimationDrawable,layout代码如下(res\layout\main.xml):
    view plaincopy to clipboardprint?
  • <?xml version="1.0" encoding="utf-8"?>   
  • <LinearLayout    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  •     android:orientation="vertical"    
  •     android:layout_width="fill_parent"  
  •     android:layout_height="fill_parent">   
  •     <ImageView    
  •        android:id="@+id/animation_view"  
  •        android:layout_width="fill_parent"    
  •        android:layout_height="wrap_content"  
  •        android:src="/blog_article/@drawable/qq_animation/index.html" />   
  •     <Button    
  •        android:id="@+id/animation_btn"    
  •        android:layout_width="fill_parent"  
  •        android:layout_height="wrap_content"    
  •        android:text="@string/start_animation" />   
  •     <Button    
  •        android:id="@+id/one_shot_btn"    
  •        android:layout_width="fill_parent"  
  •        android:layout_height="wrap_content"    
  •        android:text="@string/play_once" />   
  • </LinearLayout>  
  • Java代码  
  • <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:id="@+id/animation_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="/blog_article/@drawable/qq_animation/index.html" /> <Button android:id="@+id/animation_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/start_animation" /> <Button android:id="@+id/one_shot_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/play_once" /> </LinearLayout>  
  • 注意这里的ImageView 通过android:src="/blog_article/@drawable/qq_animation/index.html"引用了前面定义的AnimationDrawable,下面是两个按钮用来控制播放动画和设置AnimationDrawable的oneshot属性。
    下面就是控制动画播放的类代码(src\org\goodev\animation\AnimActivity.java):
     
    view plaincopy to clipboardprint?
  • public class AnimActivity extends Activity {   
  •     /** Called when the activity is first created. */  
  •     AnimationDrawable mAd;   
  •     Button mPlayBtn;   
  •     Button mOneShotBtn;   
  •     boolean mIsOneShot;   
  •     
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState) {   
  •        super.onCreate(savedInstanceState);   
  •        setContentView(R.layout.main);   
  •     
  •        ImageView iv = (ImageView) findViewById(R.id.animation_view);   
  •        mAd = (AnimationDrawable) iv.getDrawable();   
  •     
  •        mPlayBtn = (Button) findViewById(R.id.animation_btn);   
  •        mPlayBtn.setOnClickListener(new OnClickListener() {   
  •            @Override  
  •            public void onClick(View view) {   
  •               startAnimation();   
  •            }   
  •        });   
  •     
  •        mOneShotBtn = (Button) findViewById(R.id.one_shot_btn);   
  •        mOneShotBtn.setOnClickListener(new OnClickListener() {   
  •            @Override  
  •            public void onClick(View view) {   
  •               if (mIsOneShot) {   
  •                   mOneShotBtn.setText("Play Once");   
  •               } else {   
  •                   mOneShotBtn.setText("Play Repeatly");   
  •               }   
  •               mAd.setOneShot(!mIsOneShot);   
  •               mIsOneShot = !mIsOneShot;   
  •            }   
  •        });   
  •     
  •     }   
  •     
  •     /**  
  •      * 通过AnimationDrawable的start函数播放动画,  
  •      * stop函数停止动画播放,  
  •      * isRunning来判断动画是否正在播放。  
  •      */  
  •     public void startAnimation() {   
  •        if (mAd.isRunning()) {   
  •            mAd.stop();   
  •        } else {   
  •            mAd.stop();   
  •            mAd.start();   
  •        }   
  •     }   
  • }   
  •    
  • Java代码  
  • public class AnimActivity extends Activity { /** Called when the activity is first created. */ AnimationDrawable mAd; Button mPlayBtn; Button mOneShotBtn; boolean mIsOneShot; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ImageView iv = (ImageView) findViewById(R.id.animation_view); mAd = (AnimationDrawable) iv.getDrawable(); mPlayBtn = (Button) findViewById(R.id.animation_btn); mPlayBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { startAnimation(); } }); mOneShotBtn = (Button) findViewById(R.id.one_shot_btn); mOneShotBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (mIsOneShot) { mOneShotBtn.setText("Play Once"); } else { mOneShotBtn.setText("Play Repeatly"); } mAd.setOneShot(!mIsOneShot); mIsOneShot = !mIsOneShot; } }); } /** * 通过AnimationDrawable的start函数播放动画, * stop函数停止动画播放, * isRunning来判断动画是否正在播放。 */ public void startAnimation() { if (mAd.isRunning()) { mAd.stop(); } else { mAd.stop(); mAd.start(); } } }  
  •  
    布局动画介绍
     
    布局动画和逐帧动画是由本质的不同的,逐帧动画是一帧帧图片组成的,而布局动画是渐变动画,OPhone通过改变UI的属性(大小、位置、透明度等)来实现动画效果。
     
    在OPhone显示系统中,每个view都对应一个矩阵来控制该view显示的位置,通过不同的方式来改变该控制矩阵就可以实现动画效果,例如旋转、移动、缩放等。
     
    不同的矩阵变换有不同的类来实现,android.view.animation.Animation类代表所有动画变换的基类,目前在OPhone系统中有如下五个实现(都位于android.view.animation包中):
    • l AlphaAnimation:实现alpha渐变,可以使界面逐渐消失或者逐渐显现
    • l TranslateAnimation:实现位置移动渐变,需要指定移动的开始和结束坐标
    • l ScaleAnimation: 实现缩放渐变,可以指定缩放的参考点
    • l RotateAnimation:实现旋转渐变,可以指定旋转的参考点,默认值为(0,0)左上角。
    • l AnimationSet: 代表上面的渐变组合
    有一个和渐变动画效果关系比较密切的类android.view.animation.Interpolator,该类定义了渐变动画改变的速率,可以设置为加速变化、减速变化或者重复变化。关于Interpolator详细信息请参考文档介绍。另外在OPhone SDK中的android.jar文件中也有各种interpolator的定义,感兴趣的可以参考android.jar中的\res\anim目录中的文件。
     
    动画的实现及应用
    上面这些渐变方式也可以在XML文件中定义,这些文件位于res\anim目录下。根元素为set代表AnimationSet,里面可以有多个渐变定义,如下是alpha渐变的定义(res\anim\alpha_anim.xml):
    view plaincopy to clipboardprint?
  • <set xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/decelerate_interpolator">   
  • <alpha    
  •     android:fromAlpha="0.0"    
  •     android:toAlpha="1.0"    
  •     android:duration="1000" />   
  • </set>  
  • Java代码  
  • <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" /> </set>  
  • 如果只定义一种渐变效果,则可以去除set元素,如下:
     
    view plaincopy to clipboardprint?
  • <alpha    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/accelerate_interpolator"  
  • android:fromAlpha="0.0"    
  • android:toAlpha="1.0"    
  • android:duration="1000" />  
  • Java代码  
  • <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />  
  •  
    上面定义的alpha从0(透明)到1(不透明)渐变,渐变时间为1000毫秒。
     
    view plaincopy to clipboardprint?
  • <scale    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/accelerate_interpolator"  
  • android:fromXScale="1"  
  • android:toXScale="1"  
  • android:fromYScale="0.1"  
  • android:toYScale="1.0"  
  • android:duration="500"  
  • android:pivotX="50%"  
  • android:pivotY="50%"  
  • android:startOffset="100" />  
  • Java代码  
  • <scale xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromXScale="1" android:toXScale="1" android:fromYScale="0.1" android:toYScale="1.0" android:duration="500" android:pivotX="50%" android:pivotY="50%" android:startOffset="100" />  
  •  
    上面是一个缩放渐变的定义,from... 和 to... 分别定义缩放渐变的开始和结束的缩放倍数,上面定义X轴都为1不缩放,而Y轴从0.1到1逐渐放大(开始高度为正常大小的0.1然后逐渐放大到正常大小)。缩放持续的时间为500毫秒,缩放的中心点(通过pivotX,pivotY定义)在控件的中间位置。startOffset指定了在缩放开始前等待的时间。
    view plaincopy to clipboardprint?
  • <rotate    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/accelerate_interpolator"  
  • android:fromDegrees="0.0"  
  • android:toDegrees="360"  
  • android:pivotX="50%"  
  • android:pivotY="50%"  
  • android:duration="500" />  
  • Java代码  
  • <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromDegrees="0.0" android:toDegrees="360" android:pivotX="50%" android:pivotY="50%" android:duration="500" />  
  •  
     
    上面定义了旋转变换,从0度变化到360度(旋转一周),时间为500毫秒,变换的中心点位控件的中心位置。
    view plaincopy to clipboardprint?
  • <translate    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/accelerate_interpolator"  
  • android:fromYDelta="-100%"    
  • android:toYDelta="0"  
  • android:duration="500" />  
  • Java代码  
  • <translate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" />  
  •  
     
    上面定义了位置变换,实现一种下落的效果。
     
    把上面不同的定义放到set中就可以实现不同的组合动画效果了,下面的示例实现了控件在下落的过程中逐渐显示的效果(res\anim\translate_alpha_anim.xml):
    view plaincopy to clipboardprint?
  • <set xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:interpolator="@android:anim/decelerate_interpolator">   
  • <translate    
  • android:fromYDelta="-100%"    
  • android:toYDelta="0"  
  • android:duration="500" />   
  • <alpha    
  • android:fromAlpha="0.0"    
  • android:toAlpha="1.0"  
  • android:duration="500" />   
  • </set>  
  • Java代码  
  • <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <translate android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" /> </set>  
  •  
     
    要把上面定义的动画效果应用的layout中,就要使用另外一个类:android.view.animation.LayoutAnimationController。该类把指定的效果应用到layout中的每个控件上去,使用layoutAnimation元素在xml文件中定义
    view plaincopy to clipboardprint?
  • LayoutAnimationController,该文件同样位于res/anim/目录下,下面是一个示例(res\anim\layout_anim_ctrl.xml):   
  • <layoutAnimation    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:delay="30%"  
  • android:animationOrder="reverse"  
  • android:animation="@anim/translate_alpha_anim" />  
  • Java代码  
  • LayoutAnimationController,该文件同样位于res/anim/目录下,下面是一个示例(res\anim\layout_anim_ctrl.xml): <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:delay="30%" android:animationOrder="reverse" android:animation="@anim/translate_alpha_anim" />  
  •  
    上面通过animation指定了使用哪个动画(通过修改该值可以测试不同的动画效果),animationOrder指定了通过逆序的方式应用动画(在垂直的LinearLayout中就是从下往上逐个控件应用),delay指定了layout中的每个控动画的延时时间为动画持续总时间的30%。
     
    定义好LayoutAnimationController后就可以在Layout中使用了,这里使用一个ListView做演示,布局代码如下:
    view plaincopy to clipboardprint?
  • <?xml version="1.0" encoding="utf-8"?>   
  • <LinearLayout    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:orientation="vertical"    
  • android:layout_width="fill_parent"  
  • android:layout_height="fill_parent">   
  •     <ListView    
  •     android:id="@+id/list"    
  •     android:layout_width="fill_parent"  
  •     android:layout_height="fill_parent"    
  •        
  •     android:persistentDrawingCache="animation|scrolling"  
  •     android:layoutAnimation="@anim/layout_anim_ctrl" />   
  • </LinearLayout>  
  • Java代码  
  • <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:persistentDrawingCache="animation|scrolling" android:layoutAnimation="@anim/layout_anim_ctrl" /> </LinearLayout>  
  •  
     
    上面的代码中通过layoutAnimation指定了前面定义的LayoutAnimationController,为了使动画效果比较流畅这里还通过persistentDrawingCache设置了控件的绘制缓存策略,一共有4中策略:
    PERSISTENT_NO_CACHE 说明不在内存中保存绘图缓存; 
    PERSISTENT_ANIMATION_CACHE 说明只保存动画绘图缓存;
    PERSISTENT_SCROLLING_CACHE 说明只保存滚动效果绘图缓存
    PERSISTENT_ALL_CACHES 说明所有的绘图缓存都应该保存在内存中。
     
    在Activity中并没有什么变化,代码如下(src\org\goodev\animation\ListActivity.java):
    view plaincopy to clipboardprint?
  • public class ListActivity extends Activity {   
  •     
  •     String[] mListItems =    
  •     { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};   
  •     
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState) {   
  •        super.onCreate(savedInstanceState);   
  •        setContentView(R.layout.list);   
  •     
  •        ArrayAdapter<String> listItemAdapter =    
  •            new ArrayAdapter<String>(this,   
  •               android.R.layout.simple_list_item_1, mListItems);   
  •        ListView lv = (ListView) this.findViewById(R.id.list);   
  •        lv.setAdapter(listItemAdapter);   
  •     }   
  • }  
  • Java代码  
  • public class ListActivity extends Activity { String[] mListItems = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list); ArrayAdapter<String> listItemAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems); ListView lv = (ListView) this.findViewById(R.id.list); lv.setAdapter(listItemAdapter); } }  
  •  
     
    控件动画介绍
    其实控件动画也是布局动画的一种,可以看做是自定义的动画实现,布局动画在XML中定义OPhone已经实现的几个动画效果(AlphaAnimation、TranslateAnimation、ScaleAnimation、RotateAnimation)而控件动画就是在代码中继承android.view.animation.Animation类来实现自定义效果。
     
    控件动画实现
    通过重写Animation的 applyTransformation (float interpolatedTime, Transformation t)函数来实现自定义动画效果,另外一般也会实现 initialize (int width, int height, int parentWidth, int parentHeight)函数,这是一个回调函数告诉Animation目标View的大小参数,在这里可以初始化一些相关的参数,例如设置动画持续时间、设置Interpolator、设置动画的参考点等。
    OPhone在绘制动画的过程中会反复的调用applyTransformation 函数,每次调用参数interpolatedTime值都会变化,该参数从0渐变为1,当该参数为1时表明动画结束。通过参数Transformation 来获取变换的矩阵(matrix),通过改变矩阵就可以实现各种复杂的效果。关于矩阵的详细信息可以参考android.graphics.Matrix的API文档(http://androidappdocs-staging.appspot.com/reference/android/graphics/Matrix.html )。
     
    下面来看一个简单的实现:
       
    view plaincopy to clipboardprint?
  • class ViewAnimation extends Animation {   
  •        public ViewAnimation() {   
  •        }   
  •     
  •        @Override  
  •         public void initialize(int width, int height, int parentWidth, int parentHeight) {   
  •            super.initialize(width, height, parentWidth, parentHeight);   
  •            setDuration(2500);   
  •            setFillAfter(true);   
  •            setInterpolator(new LinearInterpolator());   
  •        }   
  •     
  •        @Override  
  •         protected void applyTransformation(float interpolatedTime,   
  •               Transformation t) {   
  •            final Matrix matrix = t.getMatrix();   
  •            matrix.setScale(interpolatedTime, interpolatedTime);   
  •               
  •        }   
  •     }  
  • Java代码  
  • class ViewAnimation extends Animation { public ViewAnimation() { } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); setDuration(2500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); matrix.setScale(interpolatedTime, interpolatedTime); } }  
  •  
     
    上面的代码很简单,在initialize函数中设置变换持续的时间2500毫秒,然后设置Interpolator为LinearInterpolator并设置FillAfter为true这样可以在动画结束的时候保持动画的完整性。在applyTransformation函数中通过MatrixsetScale函数来缩放,该函数的两个参数代表X、Y轴缩放因子,由于interpolatedTime是从0到1变化所在这里实现的效果就是控件从最小逐渐变化到最大。调用View的startAnimation函数(参数为Animation)就可以使用自定义的动画了。代码如下(src\org\goodev\animation\ViewAnimActivity.java):
     
    view plaincopy to clipboardprint?
  • public class ViewAnimActivity extends Activity {   
  •     
  •     Button mPlayBtn;   
  •     ImageView mAnimImage;   
  •     
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState) {   
  •        super.onCreate(savedInstanceState);   
  •        setContentView(R.layout.view_anim_layout);   
  •        mAnimImage = (ImageView) this.findViewById(R.id.anim_image);   
  •     
  •        mPlayBtn = (Button) findViewById(R.id.play_btn);   
  •        mPlayBtn.setOnClickListener(new OnClickListener() {   
  •     
  •            @Override  
  •            public void onClick(View view) {   
  •               mAnimImage.startAnimation(new ViewAnimation());   
  •            }   
  •     
  •        });   
  •     
  •     }   
  • }  
  • Java代码  
  • public class ViewAnimActivity extends Activity { Button mPlayBtn; ImageView mAnimImage; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_anim_layout); mAnimImage = (ImageView) this.findViewById(R.id.anim_image); mPlayBtn = (Button) findViewById(R.id.play_btn); mPlayBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { mAnimImage.startAnimation(new ViewAnimation()); } }); } }  
  •  
     
    布局代码如下(res\layout\view_anim_layout.xml):
    view plaincopy to clipboardprint?
  • <?xml version="1.0" encoding="utf-8"?>   
  • <LinearLayout    
  • xmlns:android="http://schemas.android.com/apk/res/android"  
  • android:orientation="vertical"  
  • android:layout_width="fill_parent"  
  • android:layout_height="fill_parent"  
  • >    
  • <Button   
  • android:id="@+id/play_btn"  
  • android:layout_width="fill_parent"  
  • android:layout_height="wrap_content"  
  • android:text="Start Animation"  
  • />   
  • <ImageView    
  • android:id="@+id/anim_image"  
  • android:persistentDrawingCache="animation|scrolling"  
  • android:layout_width="fill_parent"    
  • android:layout_height="wrap_content"    
  • android:src="/blog_article/@drawable/ophone/index.html"  
  • />   
  • </LinearLayout>  
  • Java代码  
  • <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/play_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Start Animation" /> <ImageView android:id="@+id/anim_image" android:persistentDrawingCache="animation|scrolling" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="/blog_article/@drawable/ophone/index.html" /> </LinearLayout>  
  •  
    从上图可以看到ImageView是从左上角出来的,这是由于没有指定矩阵的变换参考位置,默认位置为(0,0),如果要想让ImageView从中间出来,可以通过矩阵变换来把参考点移动到中间来,如下实现:
       
    view plaincopy to clipboardprint?
  • class ViewAnimation extends Animation {   
  •        int mCenterX;//记录View的中间坐标   
  •        int mCenterY;   
  •        public ViewAnimation() {   
  •        }   
  •     
  •        @Override  
  •        public void initialize(int width, int height, int parentWidth, int parentHeight) {   
  •            super.initialize(width, height, parentWidth, parentHeight);   
  •            //初始化中间坐标值   
  •            mCenterX = width/2;    
  •            mCenterY = height/2;   
  •            setDuration(2500);   
  •            setFillAfter(true);   
  •            setInterpolator(new LinearInterpolator());   
  •        }   
  •     
  •        @Override  
  •        protected void applyTransformation(float interpolatedTime,   
  •               Transformation t) {   
  •            final Matrix matrix = t.getMatrix();   
  •            matrix.setScale(interpolatedTime, interpolatedTime);   
  •            //通过坐标变换,把参考点(0,0)移动到View中间   
  •            matrix.preTranslate(-mCenterX, -mCenterY);   
  •            //动画完成后再移回来   
  •            matrix.postTranslate(mCenterX, mCenterY);   
  •        }   
  •     }  
  • Java代码  
  • class ViewAnimation extends Animation { int mCenterX;//记录View的中间坐标 int mCenterY; public ViewAnimation() { } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); //初始化中间坐标值 mCenterX = width/2; mCenterY = height/2; setDuration(2500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); matrix.setScale(interpolatedTime, interpolatedTime); //通过坐标变换,把参考点(0,0)移动到View中间 matrix.preTranslate(-mCenterX, -mCenterY); //动画完成后再移回来 matrix.postTranslate(mCenterX, mCenterY); } }  
  •  
     
    preTranslate函数是在缩放前移动而postTranslate是在缩放完成后移动。现在ImageView就是从中间出来的了。这样通过操作Matrix 可以实现各种复杂的变换。由于操作Matrix是实现动画变换的重点,这里简单介绍下Matrix的常用操作:
    • Reset():重置该矩阵
    • setScale():设置矩阵缩放
    • setTranslate():设置矩阵移动
    • setRotate():设置矩阵旋转
    • setSkew(): 使矩阵变形(扭曲)
     
    矩阵也可以相乘,从线性代数中的矩阵运算中知道M1*M2 和M2*M1 是不一样的,所以在使用concat(m1,m2)函数的时候要注意顺序。
     
    另外需要注意的是Matrix提供的API在OPhone1.0和OPhone1.5中是有变化的,请注意查看相关文档。
     
    OPhone还提供了一个用来监听Animation事件的监听接口AnimationListener,如果你对Animatioin何时开始、何时结束、何时重复播放感兴趣则可以实现该接口。该接口提供了三个回调函数:onAnimationStart、onAnimationEnd、onAnimationRepeat。
     
    使用Camera实现3D变换效果
    最后来简单介绍下OPhone提供的android.graphics.Camera类,通过该类可以在2D条件下实现3D动画效果,该类可以看做一个视图显示的3D空间,然后可以在里面做各种操作。把上面的ViewAnimation修改为如下实现可以具体看看Camera的功能:
       
    view plaincopy to clipboardprint?
  •  class ViewAnimation extends Animation {   
  •        int mCenterX;//记录View的中间坐标   
  •        int mCenterY;   
  •        Camera camera = new Camera();   
  •        public ViewAnimation() {   
  •        }   
  •     
  •        @Override  
  •        public void initialize(int width, int height, int parentWidth,   
  •               int parentHeight) {   
  •            super.initialize(width, height, parentWidth, parentHeight);   
  •            //初始化中间坐标值   
  •            mCenterX = width/2;    
  •            mCenterY = height/2;   
  •            setDuration(2500);   
  •            setFillAfter(true);   
  •            setInterpolator(new LinearInterpolator());   
  •        }   
  •     
  •        @Override  
  •        protected void applyTransformation(float interpolatedTime,   
  •               Transformation t) {   
  • //         final Matrix matrix = t.getMatrix();   
  • //         matrix.setScale(interpolatedTime, interpolatedTime);   
  • //         //通过坐标变换,把参考点(0,0)移动到View中间   
  • //         matrix.preTranslate(-mCenterX, -mCenterY);   
  • //         //动画完成后再移回来   
  • //         matrix.postTranslate(mCenterX, mCenterY);   
  •            final Matrix matrix = t.getMatrix();   
  •            camera.save();   
  •            camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime));   
  •            camera.rotateY(360 * interpolatedTime);   
  •            camera.getMatrix(matrix);   
  •            matrix.preTranslate(-mCenterX, -mCenterY);   
  •            matrix.postTranslate(mCenterX, mCenterY);   
  •            camera.restore();   
  •        }   
  •     }  
  • Java代码  
  • class ViewAnimation extends Animation { int mCenterX;//记录View的中间坐标 int mCenterY; Camera camera = new Camera(); public ViewAnimation() { } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); //初始化中间坐标值 mCenterX = width/2; mCenterY = height/2; setDuration(2500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { // final Matrix matrix = t.getMatrix(); // matrix.setScale(interpolatedTime, interpolatedTime); // //通过坐标变换,把参考点(0,0)移动到View中间 // matrix.preTranslate(-mCenterX, -mCenterY); // //动画完成后再移回来 // matrix.postTranslate(mCenterX, mCenterY); final Matrix matrix = t.getMatrix(); camera.save(); camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime)); camera.rotateY(360 * interpolatedTime); camera.getMatrix(matrix); matrix.preTranslate(-mCenterX, -mCenterY); matrix.postTranslate(mCenterX, mCenterY); camera.restore(); } }  
  •  
    camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime))在第一次调用的时候interpolatedTime值为0,相当于把ImageView在Z轴后移1300像素,然后逐步的往前移动到0,同时camera.rotateY(360 * interpolatedTime)函数又把ImageView沿Y轴翻转360度


        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android按钮单击事件的四种常用写法总结 iis7站长之家
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    ▪Android提高之自定义Menu(TabMenu)实现方法
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3