Android 2D Graphics

Android 为我们提供了一个用来绘制图片与动画2D的图像库,这两个包分别是android.graphics.drawable 和 android.view.animation ,在这两个包中可以找到相同的类去呈现绘图与动画的两个不同面。 在这个文档中将介绍如何在你的Android应用程序中使用这个库。我们将讨论基础类Drawable对象如何绘图,如何使用一对Drawable的子类,还有如何去创建图片和动画。

1 Drawable Drawable

是一个通用的抽象类,它的目的是告诉你什么东西是可以画的。你会发现基于Drawable类扩展 出各种绘图的类包括:BitmapDrawable ShapeDrawable PictureDrawable LayerDrawable,当然你 可以继承它来创建你自己的绘图类. 有三种方法可以定义和实例化一个Drawable,保存一个图片到你工程资源中,使用XML文件来描述 Drawable属性或者用一个正常的类去构造。下面我们将讨论两种技术(对一个有开发经验的开发者来说构 造并不是最新的技术) 

 

1.1 从资源图像文件中创建 一个比较简单的方法是添加一个图片到你的程序中,然后通过资源文件引用这个文件,支持的文件类型 有PNG(首选的) JPG(可接受的)GIF(不建议),显然这种对于显示应用程序的图标跟来说是首选的方 法,也可以用来显示LOGO,其余的图片可以用在例如游戏中。 把一个图片资源,添加你的文件到你工程中res/drawable/目录中去,从这里,你就可以引用它到你的代码 或你的XML布局中,另一种方法,引用它也可以用资源编号,比如你选择一个文件只要去掉后缀就可以了 (例如:my_image.png 引用它是就是my_image) 

1.2 从XML文件中创建 到如今,你应该比较熟悉按Android的原则去开发一个用户接口,因此,你也应该理解了定义一个XML文 件对于对象的作用与灵活的重要性。这个理念无数次用于Drawables 如果你想创建一个Drawable对象,而这个对象并不依赖于变量或用户的交换,把它定义到XML中去应该 是一个不错的方法。即使你期望在你的应用程序中改变其属性来增加用户体验。你应该考虑把对象放入 XML中,因为你可以随时修改其属性。 当你在你的XML中定义了一个Drawable,保存这个XML文件到你工程目录下res/drawable目录中,然 后通过调用Resource.getDrawable()来检索并实例化,传递给它XML文件中的资源ID号。 任何Drawable的子类都支持inflate这个方法,这个方法会通过XML来实例化你的程序。任何Drawable 都支持XML的扩展来利用特殊的XML属性来帮助定义对象的属性,可以查看任何Drawable子类文档来看 如何定义XML文件 下面的XML定义了TransitionDrawable:  

 

<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image_expand">
<item android:drawable="@drawable/image_collapse">

</transition> 


  2 ShapeDrawable

当你想去画一些动态的二维图片,一个ShapeDrawable对象可能会对你有很大的帮助。通过ShapeDrawable,你可以通过编程画出任何你想到的图像与样式
ShapeDrawable继承了Drawable,所以你可以调用Drawable里有的函数,比如视图的背景,通过setBackgroundDrawable()设置。当然,你可以在自定义的视图布局中画你的图形,因为ShapeDrawable有自己的draw()方法。你可以创建一个视图的子类去画ShapeDrawable在View.OnDraw()方法期间。这是一个基本的视图扩展类去画ShapeDrawable

 

public class CustomDrawableView extends View {
private ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
int x = 10;
int y = 10;
int width = 300;
int height = 50;
mDrawable = new ShapeDrawable(new OvalShape());
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}

 

在这个构造函数中,ShapeDrawable是定义了个ovalShape类型,然后又设置了颜色与边界,如果你不去设置边界,这个图形是不会画出来的,但是,如果你不设置颜色,它默认为黑色。通过视图的自定义,你可以通过各种方法画出你喜欢的东东来,下面是个简单的例子。

 

CustomDrawableView mCustomDrawableView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCustomDrawableView = new CustomDrawableView(this);
setContentView(mCustomDrawableView);
}

 

如果你想用XML文件配置来取代原有布局来画自定义的drawable,于是你自定义的Drawable类必须重载view(Context,AttributeSet)构造函数。下面是范例:

 

<com.example.shapedrawable.CustomDrawableView 
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

ShapeDrawable类(像很多其他Drawable类型在android.graphics.drawable包)允许你定义drawable公共方法的各种属性。有些属性你可以需要调整,包括,透明度,颜色过滤,不透明度,颜色。

 

3 NinePatchDrawable
NinePatchDrawable 绘画的是一个可以伸缩的位图图像,Android会自动调整大小来容纳显示的内容。一个例子就是NinePatch为背景,使用标准的Android按钮,按钮必须伸缩来容纳长度变化的字符NinePatchDrawable是一个标准的PNG图像,它包括额外的1个像素的边界,你必须保存它后缀为.9.png,并且保持到工程的res/drawable目录中。
这个边界是用来确定图像的可伸缩和静态区域。你可以在左边和上边的线上画一个或多个黑色的1个像素指出可伸缩的部分(你可以需要很多可伸缩部分),它的相对位置在可伸缩部分相同,所以大的部分总是很大的。

 

你还有可以在图像的右边和下边画一条可选的drawable区域(有效的,内边距线)。如果你的视图对象设置NinePath为背景然后指定特殊的视图字体,它将自行伸缩使所有的文本来适应根据右线与底部线设计好的区域(如果有的话),当然内边距线不包括其中,Android可以使用左边的线与上面的线来定义一个drawable区域。我们来澄清一下这两条不同的线,左边跟顶部的线来定义哪些图像的像素允许在伸缩时被复制。底部与右边的线用来定义一个相对位置内的图像,视图的内容就放入其中。下面是一个例子用NinePatch 文件来定义个按钮。NinePath通过左边的线与顶部的线定义一个可伸缩的区域而底部的线与右边的线可以定义个可画区域,在上面的图片中,灰色点的线定义了个图像的区域为了图像的伸缩,图像底部的粉红色的矩形框定义了个视图内向允许呈现的区域,如果内容不适合这个区别,它们将会将图像拉伸。

 

Draw9-path工具提供了一个非常简单的方法去创建你的NinePath图像,. 利用WYSIWY图形编辑器,如果你定义的区域可伸缩区域有超出图纸范围的危险它甚至会提出警告。下面是一个简单的XML来演示如何在一对按钮上添加NinePatch(NinePath图片保存在res/drawable/my_button_background.9.png

 

<Button id="@+id/tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:text="Tiny"
android:textSize="8sp"
android:background="@drawable/my_button_background"/>
<Button id="@+id/big"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:text="Biiiiiiig text!"
android:textSize="30sp"
android:background="@drawable/my_button_background"/>

 

需要注意的是宽度与高度需要设置为wrap_content,使按钮适合文字。

 

下面是两个按钮基于XML跟NinePath图片的呈现,随着按钮通过文字大小的变化,背景图像也会去适应。

 

4 Tween Animation 

一个tween动画将对视图对象中的内容进行一系列简单的转换(位置,大小,旋转,透明性)。如果你有 一个文本视图对象,你可以移动它,旋转它,让它变大或让它变小,如果文字下面还有背景图像,背景 图像也会随着文件进行转换。Animation package 提供了所有的类来供tween 动画使用。 Tween 动画定义了一个动画指令的队列,定义可以使用XML也可以再Android的代码中,像定义布局一 样,我们建议使用XML来定义,因为它具备的阅读性,重用性,可以交换性大大超过了硬编码。在下面 的例子中,我们使用XML(参考AnimationSet 类或者其它的动画类来学习如何在代码中定义) 动画的指令定义了你想要发生什么样的转换,当他们发生了,应该执行多长时间,转换可以是连续的也 可以使同时的。例如,你让文本内容从左边移动到右边,然后旋转180度,或者在移动的过程中同时旋 转,没个转换需要设置一些特殊的参数(开始和结束的大小尺寸的大小变化,开始和结束的旋转角度等 等,也可以设置些基本的参数(例如,开始时间与周期),如果让几个转换同时发生,可以给它们设置 相同的开始时间,如果按序列的话,计算开始时间加上其周期。 动画的XML文件还是在你工程中res/anim目录,这个文件必须包含一个根元素,可以使<alpha> <scale> <translate> <rotate> 插值元素或者是把上面的元素都放入<set>元素组中,默认情况下,所以的动画指令都是同时发生的, 为了让他们按序列发生,需要设置一个特殊的属性startOffset,下面的例子会演示。 下面的ApiDemos XML文件定义了视图对象的伸缩功能,同时发生旋转 。

<set android:shareInterpolator="false"><scale
android:interpolator="@android:anim/
accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />
<set android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400"
android:fillBefore="false" />
<rotate
android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400" />
</set></set> 


屏幕坐标(本例中没有使用)是(0,0)在左上角,并向右增加下去。还有许多值,像pivotX指定对象是相对自己还是父类。必须使用正确的格式来设置(”50” 是相对于父
类50% “50%”相对于自己%50)您可以决定如何转变在一段时间内适用于指定的插值,Android包括了几个插值子类指定不同的速度曲线,例如:AccelerateInterpolator 告诉了开始比较慢,速度慢慢的变快,每个都有一个属性值,可以用于在XML中。保存hyperspace_jump.xml到你工程中的res/anim目录,下面的JAVA代码将引用它来布局一
个ImageView对象。

 

ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this,
R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

 

作为替代startAnimation(),你可以通过Animation.setStartTime来定义开始时间,通过view.setAnimationl来指定这个动画关于XML的参数属性有效的标记,可以参见Available Resources中动画的讨论。

 

注意:你的动画不顾后果的移动或调整大小,视图不会去适应你的动画而去自动调整,即使如此,动画将会超出视图的范围,但不会被截断,然而,截断发生在你的动画如果超出了父类的视图。 

 

5 Frame Animation

创建一个连续不同的图片的序列是传统动画的场景,按照指令播放,就像滚动的电影,在Android中基础类AnimationDrawable就是专门来负责帧动画。 

 

虽然你可以在代码中定义帧动画,可以使用AnimationDrawable类的API.,它是非常简单通过XML文件 列出动画中的所有帧,像上面的动画tween,这种类别动画的XML文件放入工程中的res/anim目录。既 然这样,指令按照周期去执行每帧动画。 在XML文件包含一个<animation-list>根节点元素和好几个子节点<item>来定义每帧。一个资源分别定 义了帧的名字与帧的持续时间。下面为范例:

 

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1"
android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2"
android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3"
android:duration="200" />
</animation-list> 

 

这个动画播放三个帧动画,通过设置android:oneshot属性为true,它将会在最后一帧停下来,如果设置 为false这个动画将循环播放。这个文件保存到工程目录res/anim目录下为rocket_thrust.xml,你也 可以添加一个背景图片到视图中,然后开始播放。下面为范例:

 

AnimationDrawable rocketAnimation; 

publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.anim.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rocketAnimation.start();
return true;
}
return super.onTouchEvent(event);
}

 

  一个比较需要特别注意的是,在AnimationDrawable调用onCreate()过程中不能调用start(),这是因 为AnimationDrawable不能在不完全的窗口上运行,如果你想立即播放动画,没有必要的交互,你可以 再onWindowFocusChanged()方法中调用它。这样它将成为窗口焦点。

 

完毕。^_^ 

 

posted @ 2012-02-11 00:59  Healtheon  阅读(3170)  评论(0编辑  收藏  举报