第9章 图形图像处理技术
(教学录像:2小时56分钟)
图形图像处理技术在Android中非常重要,特别是在开发益智类游戏或者2D游戏时,都离不开图形图像处理技术的支持。本章将对Android中的图形图像处理技术进行详细介绍。
通过阅读本章,您可以:
★ 了解常用的绘图类
★ 掌握如何绘制几何图形
★ 掌握如何绘制文本
★ 掌握如何绘制路径及绕路径文本
★ 掌握如何绘制图片
★ 掌握如何为图形添加旋转、缩放、倾斜和平移特效
★ 掌握如何使用BitmapShader渲染图像
★ 掌握如何实现逐帧动画
★ 掌握如何实现补间动画
9.1 常用绘图类
教学录像:光盘\TM\lx\9\常用绘图类.exe
在Android中,绘制图像时最常应用的就是Paint类、Canvas类、Bitmap类和BitmapFactory类。其中,Paint类代表画笔,Canvas类代表画布。在现实生活中,有画笔和画布就可以作画了,在Android中也是如此,通过Paint类和Canvas类即可绘制图像。下面将对这4个类进行详细介绍。
9.1.1 Paint类
Paint类代表画笔,用来描述图形的颜色和风格,如线宽、颜色、透明度和填充效果等信息。使用Paint类时,首先需要创建该类的对象,这可以通过该类提供的构造方法来实现。通常情况下,只需要使用无参数的构造方法来创建一个使用默认设置的Paint对象,具体代码如下:
- Paint paint=new Paint();
创建Paint类的对象后,还可以通过该对象提供的方法来对画笔的默认设置进行改变,例如,改变画笔的颜色、笔触宽度等。用于改变画笔设置的常用方法如表9.1所示。
表9.1 Paint类的常用方法
方 法 | 描 述 |
setARGB(int a, int r, int g, int b) | 用于设置颜色,各参数值均为0~255之间的整数,分别用于表示透明度、红色、绿色和蓝色值 |
setColor(int color) | 用于设置颜色,参数color可以通过Color类提供的颜色常量指定,也可以通过Color.rgb(int red,int green,int blue)方法指定 |
setAlpha(int a) | 用于设置透明度,值为0~255之间的整数 |
setAntiAlias(boolean aa) | 用于指定是否使用抗锯齿功能,如果使用,会使绘图速度变慢 |
setDither(boolean dither) | 用于指定是否使用图像抖动处理,如果使用,会使图像颜色更加平滑和饱满,使图像更加清晰 |
setPathEffect(PathEffect effect) | 用于设置绘制路径时的路径效果,如点划线 |
setShader(Shader shader) | 用于设置渐变,可以使用LinearGradient(线性渐变)、RadialGradient(径向渐变)或者SweepGradient(角度渐变) |
setShadowLayer(float radius, float dx, float dy, int color) | 用于设置阴影,参数radius为阴影的角度;dx和dy为阴影在x轴和y轴上的距离;color为阴影的颜色。如果参数radius的值为0,那么将没有阴影 |
setStrokeCap(Paint.Cap cap) | 用于当画笔的填充样式为STROKE或FILL_AND_STROKE时,设置笔刷的图形样式,参数值可以是Cap.BUTT、Cap.ROUND或Cap.SQUARE。主要体现在线的端点上 |
setStrokeJoin(Paint.Join join) | 用于设置画笔转弯处的连接风格,参数值为Join.BEVEL、Join.MITER或Join.ROUND |
setStrokeWidth(float width) | 用于设置笔触的宽度 |
setStyle(Paint.Style style) | 用于设置填充风格,参数值为Style.FILL、Style.FILL_AND_STROKE或Style.STROKE |
setTextAlign(Paint.Align align) | 用于设置绘制文本时的文字对齐方式,参数值为Align.CENTER、Align.LEFT或Align.RIGHT |
setTextSize(float textSize) | 用于设置绘制文本时的文字的大小 |
setFakeBoldText(boolean fakeBoldText) | 用于设置是否为粗体文字 |
setXfermode(Xfermode xfermode) | 用于设置图形重叠时的处理方式,如合并、取交集或并集,经常用来制作橡皮的擦除效果 |
例如,要定义一个画笔,指定该画笔的颜色为红色,并带一个浅灰色的阴影,可以使用下面的代码:
- Paint paint=new Paint();
- paint.setColor(Color. RED);
- paint.setShadowLayer(2, 3, 3, Color.rgb(180, 180, 180));
应用该画笔,在画布上绘制一个带阴影的矩形的效果如图9.1所示。
图9.1 绘制带阴影的矩形
说明:关于如何在画布上绘制矩形,将在9.1.2节进行介绍。
例9.1 在Ecbpse中创建Android项目,名称为9.1,分别定义一个线性渐变、径向渐变和角度渐变的画笔,并应用这3个画笔绘制3个矩形。(实例位置:光盘\TM\sl\9\9.1)
关键代码如下:
- Paint paint=new Paint(); //定义一个默认的画笔
- //线性渐变
- Shader shader=new LinearGradient(0, 0, 50, 50, Color.RED, Color.GREEN, Shader.TileMode.MIRROR);
- paint.setShader(shader); //为画笔设置渐变器
- canvas.drawRect(10, 70, 100, 150, paint); //绘制矩形
- //径向渐变
- shader=new RadialGradient(160, 110, 50, Color.RED, Color.GREEN, Shader.TileMode.MIRROR);
- paint.setShader(shader); //为画笔设置渐变器
- canvas.drawRect(115,70,205,150, paint); //绘制矩形
- //角度渐变
- shader=new SweepGradient(265,110,new int[]{Color.RED,Color.GREEN,Color.BLUE},null);
- paint.setShader(shader); //为画笔设置渐变器
- canvas.drawRect(220, 70, 310, 150, paint); //绘制矩形
运行本实例,将显示如图9.2所示的运行结果。
图9.2 绘制以渐变色填充的矩形
9.1.2 Canvas类
Canvas类代表画布,通过该类提供的方法,可以绘制各种图形(如矩形、圆形和线条等)。通常情况下,要在Android中绘图,需要先创建一个继承自View类的视图,并且在该类中重写其onDraw(Canvas canvas)方法,然后在显示绘图的Activity中添加该视图。下面将通过一个具体的实例来说明如何创建用于绘图的画布。
例9.2 在Eclipse中创建Android项目,名称为9.2,实现创建绘图画布的功能。(实例位置:光盘\TM\sl\9\9.2)
(1)创建一个名称为DrawView的类(该类继承自android.view.View类),并添加构造方法和重写onDraw(Canvas canvas)方法,关键代码如下:
- public class DrawView extends View {
- /**
- * 功能:构造方法
- */
- public DrawView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- /*
- * 功能:重写onDraw()方法
- */
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
- }
说明:上面加粗的代码为重写onDraw()方法的代码。在重写的onDraw()方法中,可以编写绘图代码,参数canvas就是要进行绘图的画布。
(2)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,并在帧布局管理器中添加步骤(1)中创建的自定义视图。修改后的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <com.mingrisoft.DrawView
- android:id="@+id/drawView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </FrameLayout>
(3)在DrawView的onDraw()方法中,添加以下代码,用于绘制一个带阴影的红色矩形。
- Paint paint=new Paint(); //定义一个采用默认设置的画笔
- paint.setColor(Color.RED); //设置颜色为红色
- paint.setShadowLayer(2, 3, 3, Color.rgb(180, 180, 180)); //设置阴影
- canvas.drawRect(40, 40, 200, 100, paint); //绘制矩形
运行本实例,将显示如图9.3所示的运行结果。
图9.3 创建绘图画布并绘制带阴影的矩形
9.1.3 Bitmap类
Bitmap类代表位图,是Android系统中图像处理的一个重要类。使用该类,不仅可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,而且还可以指定格式保存图像文件。对于这些操作,都可以通过Bitmap类提供的方法来实现。Bitmap类提供的常用方法如表9.2所示。
表9.2 Bitmap类的常用方法
方 法 | 描 述 |
compress(Bitmap.CompressFormat format, int quality, OutputStream stream) | 用于将Bitmap对象压缩为指定格式并保存到指定的文件输出流中,其中format参数值可以是Bitmap.CompressFormat.PNG、Bitmap.CompressFormat. JPEG和Bitmap.CompressFormat.WEBP |
createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) | 用于从源位图的指定坐标点开始,“挖取”指定宽度和高度的一块图像来创建新的Bitmap对象,并按Matrix指定规则进行变换 |
createBitmap(int width, int height, Bitmap.Config config) | 用于创建一个指定宽度和高度的新的Bitmap对象 |
createBitmap(Bitmap source, int x, int y, int width, int height) | 用于从源位图的指定坐标点开始,“挖取”指定宽度和高度的一块图像来创建新的Bitmap对象 |
createBitmap(int[] colors, int width, int height, Bitmap.Config config) | 使用颜色数组创建一个指定宽度和高度的新的Bitimap对象,其中,数组元素的个数为width*height |
createBitmap(Bitmap src) | 用于使用源位图创建一个新的Bitmap对象 |
createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) | 用于将源位图缩放为指定宽度和高度的新的Bitmap对象 |
isRecycled() | 用于判断Bitmap对象是否被回收 |
recycle() | 强制回收Bitmap对象 |
说明:表9.2中给出的方法不包括对图像进行缩放和旋转的方法,关于如何使用Bitmap类对图像进行缩放和旋转,将在9.3节进行介绍。
例如,创建一个包括4个像素(每个像素对应一种颜色)的Bitmap对象的代码如下:
- Bitmap bitmap=Bitmap.createBitmap(new int[]{Color.RED,Color.GREEN,Color.BLUE,Color.MAGENTA}, 4, 1, Config.RGB_565);
9.1.4 BitmapFactory类
在Android中,还提供了一个BitmapFactory类,该类为一个工具类,用于从不同的数据源来解析、创建Bitmap对象。BitmapFactory类提供的创建Bitmap对象的常用方法如表9.3所示。
表9.3 BitmapFactory类的常用方法
方 法 | 描 述 |
decodeFile(String pathName) | 用于从给定的路径所指定的文件中解析、创建Bitmap对象 |
decodeFileDescriptor(FileDescriptor fd) | 用于从FileDescriptor对应的文件中解析、创建Bitmap对象 |
decodeResource(Resources res, int id) | 用于根据给定的资源id,从指定的资源中解析、创建Bitmap对象 |
decodeStream(InputStream is) | 用于从指定的输入流中解析、创建Bitmap对象 |
例如,要解析SD卡上的图片文件img01.jpg并创建对应的Bitmap对象,可以使用下面的代码:
- String path="/sdcard/pictures/bccd/img01.jpg";
- Bitmap bm=BitmapFactory.decodeFile(path);
要解析Drawable资源中保存的图片文件img02.jpg并创建对应的Bitmap对象,可以使用下面的代码:
- Bitmap bm=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.img02);
9.2 绘制2D图像
教学录像:光盘\TM\lx\9\绘制2D图像.exe
Android提供了非常强大的本机二维图形库,用于绘制2D图像。在Android应用中,比较常用的是绘制几何图形、文本、路径和图片等,下面分别进行介绍。
9.2.1 绘制几何图形
常见的几何图形包括点、线、弧、圆形、矩形等。在Android中,Canvas类提供了丰富的绘制几何图形的方法,通过这些方法,可以绘制出各种几何图形。常用的绘制几何图形的方法如表9.4所示。
表9.4 Canvas类提供的绘制几何图形的方法
说明:表9.4中给出的绘图效果使用的画笔均为以下代码所定义的画笔。
- Paint paint=new Paint(); //创建一个采用默认设置的画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- paint.setColor(Color.RED); //设置颜色为红色
- paint.setStrokeWidth(2); //笔触的宽度为2像素
- paint.setStyle(Style.STROKE); //填充样式为描边
例9.3 在Eclipse中创建Android项目,名称为9.3,实现绘制由5个不同颜色的圆形组成的图案。(实例位置:光盘\TM\sl\9\9.3)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。修改后的代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/frameLayout1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- </FrameLayout>
(2)打开默认创建的MainActivity,在该文件中,创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,关键代码如下:
- public class MyView extends View{
- public MyView(Context context) {
- super(context);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
- }
(3)在MainActivity的onCreate()方法中,获取布局文件中添加的帧布局管理器,并将步骤(2)中创建的MyView视图添加到该帧布局管理器中,关键代码如下:
- FrameLayout ll=(FrameLayout)findViewById(R.id.frameLayout1); //获取布局文件中添加的帧布局管理器
- ll.addView(new MyView(this)); //将自定义的MyView视图添加到帧布局管理器中
(4)在MyView的onDraw()方法中,首先指定画布的背景色,然后创建一个采用默认设置的画笔,并设置该画笔使用抗锯齿功能,接着设置画笔笔触的宽度,再设置填充样式为描边,最后设置画笔颜色并绘制圆形。具体代码如下:
- canvas.drawColor(Color.WHITE); //指定画布的背景色为白色
- Paint paint=new Paint(); //创建采用默认设置的画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- paint.setStrokeWidth(3); //设置笔触的宽度
- paint.setStyle(Style.STROKE); //设置填充样式为描边
- paint.setColor(Color.BLUE);
- canvas.drawCircle(50, 50, 30, paint); //绘制蓝色的圆形
- paint.setColor(Color.YELLOW);
- canvas.drawCircle(100, 50, 30, paint); //绘制黄色的圆形
- paint.setColor(Color.BLACK);
- canvas.drawCircle(150, 50, 30, paint); //绘制黑色的圆形
- paint.setColor(Color.GREEN);
- canvas.drawCircle(75, 90, 30, paint); //绘制绿色的圆形
- paint.setColor(Color.RED);
- canvas.drawCircle(125, 90, 30, paint); //绘制红色的圆形
运行本实例,将显示如图9.4所示的运行结果。
图9.4 绘制5个不同颜色的圆形
9.2.2 绘制文本
在Android中,虽然可以通过TextView或图片显示文本,但是在开发游戏,特别是开发RPG(角色)类游戏时,会包含很多文字,使用TextView和图片显示文本不太合适,这时,就需要通过绘制文本的方式来实现。Canvas类提供了一系列绘制文本的方法,下面分别进行介绍。
- drawText()方法
drawText()方法用于在画布的指定位置绘制文字。该方法比较常用的语法格式如下:
- drawText(String text, float x, float y, Paint paint)
在该语法中,参数text用于指定要绘制的文字;x用于指定文字起始位置的X坐标;y用于指定文字起始位置的Y坐标;paint用于指定使用的画笔。
例如,要在画布上输出文字“明日科技”,可以使用下面的代码:
- Paint paintText=new Paint();
- paintText.setTextSize(20);
- canvas.drawText("明日科技", 165,65, paintText);
- drawPosText()方法
drawPosText()方法也用于在画布上绘制文字,与drawText()方法不同的是,使用该方法绘制字符串时,需要为每个字符指定一个位置。该方法比较常用的语法格式如下:
- drawPosText(String text, float[] pos, Paint paint)
在该语法中,参数text用于指定要绘制的文字;pos用于指定每一个字符的位置;paint用于指定要使用的画笔。
例如,要在画布上分两行输出文字“很高兴见到你”,可以使用下面的代码:
- Paint paintText=new Paint();
- paintText.setTextSize(24);
- float[] pos= new float[]{80,215, 105,215, 130,215,80,240, 105,240, 130,240};
- canvas.drawPosText("很高兴见到你", pos, paintText);
例9.4 在Eclipse中创建Android项目,名称为9.4,实现绘制一个游戏对白界面。(实例位置:光盘\TM\sl\9\9.4)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器并为其设置背景,用于显示自定义的绘图类,修改后的代码如下:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/frameLayout1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@drawable/background"
- android:orientation="vertical" >
- </FrameLayout>
(2)打开默认创建的MainActivity,在该文件中,创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,关键代码如下:
- public class MyView extends View{
- public MyView(Context context) {
- super(context);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
- }
(3)在MainActivity的onCreate()方法中,获取布局文件中添加的帧布局管理器,并将步骤(2)中创建的MyView视图添加到该帧布局管理器中,关键代码如下:
- FrameLayout ll=(FrameLayout)findViewById(R.id.frameLayout1); //获取布局文件中添加的帧布局管理器
- ll.addView(new MyView(this)); //将自定义的MyView视图添加到帧布局管理器中
(4)在MyView的onDraw()方法中,首先创建一个采用默认设置的画笔,然后设置画笔颜色以及对齐方式、文字大小和使用抗锯齿功能,再分别通过drawText()和drawPosText()方法绘制文字。具体代码如下:
- Paint paintText=new Paint(); //创建一个采用默认设置的画笔
- paintText.setColor(0xFFFF6600); //设置画笔颜色
- paintText.setTextAlign(Align.LEFT); //设置文字左对齐
- paintText.setTextSize(24); //设置文字大小
- paintText.setAntiAlias(true); //使用抗锯齿功能
- canvas.drawText("不,我不想去!", 520,75, paintText); //通过drawText()方法绘制文字
- float[] pos= new float[]{400,260, 425,260, 450,260, 475,260,
- 363,290, 388,290, 413,290, 438,290, 463,290, 488,290, 513,290}; //定义代表文字位置的数组
- canvas.drawPosText("你想和我一起去探险吗?", pos, paintText); //通过drawPosText()方法绘制文字
运行本实例,将显示如图9.5所示的运行结果。
图9.5 在画布上绘制文字
9.2.3 绘制路径
在Android中提供了绘制路径的功能。绘制一条路径可以分为创建路径和将定义好的路径绘制在画布上两部分,下面分别进行介绍。
- 创建路径
要创建路径,可以使用android.graphics.Path类来实现。Path类包含一组矢量绘图方法,如画圆、矩形、弧、线条等。常用的绘图方法如表9.5所示。
表9.5 Path类的常用绘图方法
方 法 | 描 述 |
addArc(RectF oval, float startAngle, float sweepAngle) | 添加弧形路径 |
addCircle(float x, float y, float radius, Path.Direction dir) | 添加圆形路径 |
addOval(RectF oval, Path.Direction dir) | 添加椭圆形路径 |
addRect(RectF rect, Path.Direction dir) | 添加矩形路径 |
addRoundRect(RectF rect, float rx, float ry, Path.Direction dir) | 添加圆角矩形路径 |
moveTo(float x, float y) | 设置开始绘制直线的起始点 |
lineTo(float x, float y) | 在moveTo()方法设置的起始点与该方法指定的结束点之间画一条直线,如果在调用该方法之前没使用moveTo()方法设置起始点,那么将从(0,0)点开始绘制直线 |
quadTo(float x1, float y1, float x2, float y2) | 用于根据指定的参数绘制一条线段轨迹 |
close() | 闭合路径 |
说明:在使用addCircle()、addOval()、addRect()和addRoundRect()方法时,需要指定Path.Direction类型的常量,可选值为Path.Direction.CW(顺时针)和Path.Direction.CCW(逆时针)。
例如,要创建一个顺时针旋转的圆形路径,可以使用下面的代码:
- Path path=new Path(); //创建并实例化一个path对象
- path.addCircle(150, 200, 60, Path.Direction.CW); //在path对象中添加一个圆形路径
要创建一个折线,可以使用下面的代码:
- Path mypath=new Path(); //创建并实例化一个mypath对象
- mypath.moveTo(50, 100); //设置起始点
- mypath.lineTo(100, 45); //设置第1段直线的结束点
- mypath.lineTo(150, 100); //设置第2段直线的结束点
- mypath.lineTo(200, 80); //设置第3段直线的结束点
将该路径绘制到画布上的效果如图9.6所示。
图9.6 绘制3条线组成的折线
要创建一个三角形路径,可以使用下面的代码:
- Path path=new Path(); //创建并实例化一个path对象
- path.moveTo(50,50); //设置起始点
- path.lineTo(100, 10); //设置第1条边的结束点,也是第2条边的起始点
- path.lineTo(150, 50); //设置第2条边的结束点,也是第3条边的起始点
- path.close(); //闭合路径
将该路径绘制到画布上的效果如图9.7所示。
图9.7 绘制一个三角形
说明:在创建三角形路径时,如果不使用close()方法闭合路径,那么绘制的将是两条线组成的折线,如图9.8所示。
图9.8 绘制两条线组成的折线
- 将定义好的路径绘制在画布上
使用Canvas类提供的drawPath()方法,可以将定义好的路径绘制在画布上。
说明:在Android的Canvas类中,还提供了另一个应用路径的方法drawTextOnPath(),也就是沿着指定的路径绘制字符串。使用该方法可绘制环形文字。
例9.5 在Eclipse中创建Android项目,名称为9.5,实现在屏幕上绘制圆形路径、折线路径、三角形路径以及绕路径的环形文字。(实例位置:光盘\TM\sl\9\9.5)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中,获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先创建一个画笔,并设置画笔的相关属性,然后创建并绘制一个圆形路径、折线路径和三角形路径,最后再绘制绕路径的环形文字。具体代码如下:
- Paint paint=new Paint(); //创建一个画笔
- paint.setAntiAlias(true); //设置使用抗锯齿功能
- paint.setColor(0xFFFF6600); //设置画笔颜色
- paint.setTextSize(18); //设置文字大小
- paint.setStyle(Style.STROKE); //设置填充方式为描边
- //绘制圆形路径
- Path pathCircle=new Path(); //创建并实例化一个path对象
- pathCircle.addCircle(70, 70, 40, Path.Direction.CCW); //添加逆时针的圆形路径
- canvas.drawPath(pathCircle, paint); //绘制路径
- //绘制折线路径
- Path pathLine=new Path(); //创建并实例化一个Path对象
- pathLine.moveTo(150, 100); //设置起始点
- pathLine.lineTo(200, 45); //设置第1段直线的结束点
- pathLine.lineTo(250, 100); //设置第2段直线的结束点
- pathLine.lineTo(300, 80); //设置第3段直线的结束点
- canvas.drawPath(pathLine, paint); //绘制路径
- //绘制三角形路径
- Path pathTr=new Path(); //创建并实例化一个path对象
- pathTr.moveTo(350,80); //设置起始点
- pathTr.lineTo(400, 30); //设置第1条边的结束点,也是第2条边的起始点
- pathTr.lineTo(450, 80); //设置第2条边的结束点,也是第3条边的起始点
- pathTr.close(); //闭合路径
- canvas.drawPath(pathTr, paint); //绘制路径
- //绘制绕路径的环形文字
- String str="风萧萧兮易水寒,壮士一去兮不复还";
- Path path=new Path(); //创建并实例化一个path对象
- path.addCircle(550, 100, 48, Path.Direction.CW); //添加顺时针的圆形路径
- paint.setStyle(Style.FILL); //设置画笔的填充方式
- canvas.drawTextOnPath(str, path,0, -18, paint); //绘制绕路径文字
运行本实例,将显示如图9.9所示的运行结果。
图9.9 绘制路径及绕路径文字
9.2.4 绘制图片
在Android中,Canvas类不仅可以绘制几何图形、文件和路径,还可用来绘制图片。要想使用Canvas类绘制图片,只需要使用Canvas类提供的如表9.6所示的方法将Bitmap对象中保存的图片绘制到画布上即可。
表9.6 Canvas类提供的绘制图片的常用方法
方 法 | 描 述 |
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) | 用于从指定点绘制从源位图中“挖取”的一块 |
drawBitmap(Bitmap bitmap, float left, float top, Paint paint) | 用于在指定点绘制位图 |
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) | 用于从指定点绘制从源位图中“挖取”的一块 |
例如,从源位图上“挖取”从(0,0)点到(500,300)点的一块图像,然后绘制到画布的(50,50)点到(450,350)点所指区域,可以使用下面的代码:
- Rect src=new Rect(0,0,500,300); //设置挖取的区域
- Rect dst=new Rect(50,50,450,350); //设置绘制的区域
- canvas.drawBitmap(bm, src, dst, paint); //绘制图片
例9.6 在Eclipse中创建Android项目,名称为9.6,实现在屏幕上绘制指定位图,以及从该位图上“挖取”一块绘到屏幕的指定区域。(实例位置:光盘\TM\sl\9\9.6)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类,并在该帧布局管理器中添加一个ImageView组件。关键代码如下:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/frameLayout1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <ImageView
- android:id="@+id/imageView1"
- android:layout_width="100px"
- android:paddingTop="5px"
- android:layout_height="25px"/>
- </FrameLayout>
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中,获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MainActivity中,声明一个ImageView组件的对象,关键代码如下:
- private ImageView iv;
(4)在MainActivity的onCreate()文件中,获取布局文件中添加的ImageView组件,关键代码如下:
- iv=(ImageView)findViewById(R.id.imageView1); //获取布局文件中添加的ImageView组件
(5)在MyView的onDraw()方法中,首先创建一个画笔,并指定要绘制图片的路径,获取获取要绘制图片所对应的Bitmap对象,再在画布的指定位置绘制Bitmap对象,以及从源图片中挖取指定区域并绘制挖取到的图像,最后使用颜色数组创建一个Bitmap对象,并将其在ImageView中显示。具体代码如下:
- Paint paint = new Paint(); //创建一个采用默认设置的画笔
- String path = "/sdcard/pictures/bccd/img01.png"; //指定图片文件的路径
- Bitmap bm = BitmapFactory.decodeFile(path); //获取图片文件对应的Bitmap对象
- canvas.drawBitmap(bm, 0, 30, paint); //将获取的Bitmap对象绘制在画布的指定位置
- Rect src = new Rect(95, 150, 175, 240); //设置挖取的区域
- Rect dst = new Rect(420, 30, 500, 120); //设置绘制的区域
- canvas.drawBitmap(bm, src, dst, paint); //绘制挖取到的图像
- Bitmap bitmap = Bitmap.createBitmap(new int[] { Color.RED, Color.GREEN, Color.BLUE,
- Color.MAGENTA }, 4, 1,Config.RGB_565); //使用颜色数组创建一个Bitmap对象
- iv.setImageBitmap(bitmap); //为ImageView指定要显示的位图
(6)重写onDestroy()方法,在该方法中回收ImageView组件中使用的Bitmap资源,具体代码如下:
- @Override
- protected void onDestroy() {
- //获取ImageView组件中使用的BitmapDrawabele资源
- BitmapDrawable b = (BitmapDrawable) iv.getDrawable();
- if (b != null && !b.getBitmap().isRecycled()) {
- b.getBitmap().recycle(); //回收资源
- }
- super.onDestroy();
- }
运行本实例,将显示如图9.10所示的运行结果。
图9.10 绘制图片
9.2.5 范例1:绘制Android的机器人
例9.7 在Eclipse中创建Android项目,名称为9.7,实现在屏幕上绘制Android机器人。(实例位置:光盘\TM\sl\9\9.7)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的AndroidIco,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先创建一个画笔,并设置画笔的相关属性,然后绘制机器人的头、眼睛、天线、身体、胳膊和腿,具体代码如下:
- Paint paint=new Paint(); //采用默认设置创建一个画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- paint.setColor(0xFFA4C739); //设置画笔的颜色为绿色
- //绘制机器人的头
- RectF rectf_head=new RectF(10, 10, 100, 100);
- rectf_head.offset(100, 20);
- canvas.drawArc(rectf_head, -10, -160, false, paint); //绘制弧
- //绘制眼睛
- paint.setColor(Color.WHITE); //设置画笔的颜色为白色
- canvas.drawCircle(135, 53, 4, paint); //绘制圆
- canvas.drawCircle(175, 53, 4, paint); //绘制圆
- paint.setColor(0xFFA4C739); //设置画笔的颜色为绿色
- //绘制天线
- paint.setStrokeWidth(2); //设置笔触的宽度
- canvas.drawLine(120, 15, 135, 35, paint); //绘制线
- canvas.drawLine(190, 15, 175, 35, paint); //绘制线
- //绘制身体
- canvas.drawRect(110, 75, 200, 150, paint); //绘制矩形
- RectF rectf_body=new RectF(110,140,200,160);
- canvas.drawRoundRect(rectf_body, 10, 10, paint); //绘制圆角矩形
- //绘制胳膊
- RectF rectf_arm=new RectF(85,75,105,140);
- canvas.drawRoundRect(rectf_arm, 10, 10, paint); //绘制左侧的胳膊
- rectf_arm.offset(120, 0); //设置在X轴上偏移120像素
- canvas.drawRoundRect(rectf_arm, 10, 10, paint); //绘制右侧的胳膊
- //绘制腿
- RectF rectf_leg=new RectF(125,150,145,200);
- canvas.drawRoundRect(rectf_leg, 10, 10, paint); //绘制左侧的腿
- rectf_leg.offset(40, 0); //设置在X轴上偏移40像素
- canvas.drawRoundRect(rectf_leg, 10, 10, paint); //绘制右侧的腿
运行本实例,将显示如图9.11所示的运行结果。
图9.11 在屏幕上绘制Android机器人
9.2.6 范例2:实现简易涂鸦板
例9.8 在Eclipse中创建Android项目,名称为9.8,实现用于实现手绘功能的简易涂鸦板。(实例位置:光盘\TM\sl\9\9.8)
(1)创建一个名称为DrawView的类,该类继承自android.view.View类。在该类中,首先定义程序中所需的属性,然后添加构造方法,并重写onDraw(Canvas canvas)方法,关键代码如下:
- public class DrawView extends View {
- private int view_width = 0; //屏幕的宽度
- private int view_height = 0; //屏幕的高度
- private float preX; //起始点的X坐标值
- private float preY; //起始点的Y坐标值
- private Path path; //路径
- public Paint paint = null; //画笔
- Bitmap cacheBitmap = null; //定义一个内存中的图片,该图片将作为缓冲区
- Canvas cacheCanvas = null; //定义cacheBitmap上的Canvas对象
- /**
- * 功能:构造方法
- */
- public DrawView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- /*
- * 功能:重写onDraw()方法
- */
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- }
- }
(2)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,并在帧布局管理器中添加步骤(1)中创建的自定义视图。修改后的代码如下:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <com.mingrisoft.DrawView
- android:id="@+id/drawView1"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
(3)在DrawView类的构造方法中,首先获取屏幕的宽度和高度,并创建一个与该View相同大小的缓存区,然后创建一个新的画面,并实例化一个路径,再将内存中的位图绘制到cacheCanvas中,最后实例化一个画笔,并设置画笔的相关属性。关键代码如下:
- view_width = context.getResources().getDisplayMetrics().widthPixels; //获取屏幕的宽度
- view_height = context.getResources().getDisplayMetrics().heightPixels; //取屏幕的高度
- //创建一个与该View相同大小的缓存区
- cacheBitmap = Bitmap.createBitmap(view_width, view_height,Config.ARGB_8888);
- cacheCanvas = new Canvas(); //创建一个新的画布
- path = new Path();
- cacheCanvas.setBitmap(cacheBitmap); //在cacheCanvas上绘制cacheBitmap
- paint = new Paint(Paint.DITHER_FLAG);
- paint.setColor(Color.RED); //设置默认的画笔颜色
- //设置画笔风格
- paint.setStyle(Paint.Style.STROKE); //设置填充方式为描边
- paint.setStrokeJoin(Paint.Join.ROUND); //设置笔刷的图形样式
- paint.setStrokeCap(Paint.Cap.ROUND); //设置画笔转弯处的连接风格
- paint.setStrokeWidth(1); //设置默认笔触的宽度为1像素
- paint.setAntiAlias(true); //使用抗锯齿功能
- paint.setDither(true); //使用抖动效果
(4)在DrawView类的onDraw()方法中,添加以下代码,用于设置背景颜色、绘制cacheBitmap、绘制路径以及保存当前绘图状态到栈中,并调用restore()方法恢复所保存的状态。
- canvas.drawColor(0xFFFFFFFF); //设置背景颜色
- Paint bmpPaint = new Paint(); //采用默认设置创建一个画笔
- canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint); //绘制cacheBitmap
- canvas.drawPath(path, paint); //绘制路径
- canvas.save(Canvas.ALL_SAVE_FLAG); //保存canvas的状态
- canvas.restore(); //恢复canvas之前保存的状态,防止保存后对canvas执行的操作对后续的绘制有影响
(5)在DrawView类中,重写onTouchEvent()方法,为该视图添加触摸事件监听器,在该方法中,首先获取触摸事件发生的位置,然后应用switch语句对事件的不同状态添加响应代码,最后调用invalidate()方法更新视图。具体代码如下:
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- //获取触摸事件发生的位置
- float x = event.getX();
- float y = event.getY();
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- path.moveTo(x, y); //将绘图的起始点移到(x,y)坐标点的位置
- preX = x;
- preY = y;
- break;
- case MotionEvent.ACTION_MOVE:
- float dx = Math.abs(x - preX);
- float dy = Math.abs(y - preY);
- if (dx >= 5 || dy >= 5) { //判断是否在允许的范围内
- path.quadTo(preX, preY, (x + preX) / 2, (y + preY) / 2);
- preX = x;
- preY = y;
- }
- break;
- case MotionEvent.ACTION_UP:
- cacheCanvas.drawPath(path, paint); //绘制路径
- path.reset();
- break;
- }
- invalidate();
- return true; //返回true,表明处理方法已经处理该事件
- }
(6)编写clear()方法,用于实现橡皮擦功能,具体代码如下:
- public void clear() {
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); //设置图形重叠时的处理方式
- paint.setStrokeWidth(50); //设置笔触的宽度
- }
(7)编写保存当前绘图的save()方法,在该方法中,调用saveBitmap()方法将当前绘图保存为PNG图片。save()方法的具体代码如下:
- public void save() {
- try {
- saveBitmap("myPicture");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
(8)编写保存绘制好的位图的方法saveBitmap(),在该方法中,首先在SD卡上创建一个文件,然后创建一个文件输出流对象,并调用Bitmap类的compress()方法将绘图内容压缩为PNG格式输出到刚刚创建的文件输出流对象中,最后将缓冲区的数据全部写出到输出流中,并关闭文件输出流对象。saveBitmap()方法的具体代码如下:
- //保存绘制好的位图
- public void saveBitmap(String fileName) throws IOException {
- File file = new File("/sdcard/pictures/" + fileName + ".png");//创建文件对象
- file.createNewFile(); //创建一个新文件
- FileOutputStream fileOS = new FileOutputStream(file); //创建一个文件输出流对象
- //将绘图内容压缩为PNG格式输出到输出流对象中
- cacheBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOS);
- fileOS.flush(); //将缓冲区中的数据全部写出到输出流中
- fileOS.close(); //关闭文件输出流对象
- }
注意:如果在程序中,需要向SD卡上保存文件,那么需要在AndroidManifest.xml文件中赋予相应的权限,具体代码如下:
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
(9)在res目录中,创建一个menu目录,并在该目录中创建一个名称为toolsmenu.xml的菜单资源文件,在该文件中编写实例中所应用的功能菜单,关键代码如下:
- <menu xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:title="@string/color">
- <menu >
- <!-- 定义一组单选菜单项 -->
- <group android:checkableBehavior="single" >
- <!-- 定义子菜单 -->
- <item android:id="@+id/red" android:title="@string/color_red"/>
- <item android:id="@+id/green" android:title="@string/color_green"/>
- <item android:id="@+id/blue" android:title="@string/color_blue"/>
- </group>
- </menu>
- </item>
- <item android:title="@string/width">
- <menu >
- <!-- 定义子菜单 -->
- <group>
- <item android:id="@+id/width_1" android:title="@string/width_1"/>
- <item android:id="@+id/width_2" android:title="@string/width_2"/>
- <item android:id="@+id/width_3" android:title="@string/width_3"/>
- </group>
- </menu>
- </item>
- <item android:id="@+id/clear" android:title="@string/clear"/>
- <item android:id="@+id/save" android:title="@string/save"/>
- </menu>
说明:在上面的代码中,应用了字符串资源,这些资源均保存在res/values目录中的strings.xml文件中,具体代码请参见光盘。
(10)在默认创建的DrawActivity中,为实例添加选项菜单。
首先,重写onCreateOptionsMenu()方法,在该方法中,实例化一个MenuInflater对象,并调用该对象的inflate()方法解析步骤(9)中创建的菜单文件,具体代码如下:
- //创建选项菜单
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflator = new MenuInflater(this); //实例化一个MenuInflater对象
- inflator.inflate(R.menu.toolsmenu, menu); //解析菜单文件
- return super.onCreateOptionsMenu(menu);
- }
然后,重写onOptionsItemSelected()方法,分别对各个菜单项被选择时做出相应的处理,具体代码如下:
- //当菜单项被选择时,做出相应的处理
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- DrawView dv = (DrawView) findViewById(R.id.drawView1); //获取自定义的绘图视图
- dv.paint.setXfermode(null); //取消擦除效果
- dv.paint.setStrokeWidth(1); //初始化画笔的宽度
- switch (item.getItemId()) {
- case R.id.red:
- dv.paint.setColor(Color.RED); //设置画笔的颜色为红色
- item.setChecked(true);
- break;
- case R.id.green:
- dv.paint.setColor(Color.GREEN); //设置画笔的颜色为绿色
- item.setChecked(true);
- break;
- case R.id.blue:
- dv.paint.setColor(Color.BLUE); //设置画笔的颜色为蓝色
- item.setChecked(true);
- break;
- case R.id.width_1:
- dv.paint.setStrokeWidth(1); //设置笔触的宽度为1像素
- break;
- case R.id.width_2:
- dv.paint.setStrokeWidth(5); //设置笔触的宽度为5像素
- break;
- case R.id.width_3:
- dv.paint.setStrokeWidth(10); //设置笔触的宽度为10像素
- break;
- case R.id.clear:
- dv.clear(); //擦除绘画
- break;
- case R.id.save:
- dv.save(); //保存绘画
- break;
- }
- return true;
- }
运行本实例,将显示一个简易涂鸦板,在屏幕上可以随意绘画,单击屏幕右上方的菜单按钮,将弹出选项菜单,主要用于完成更改画笔颜色、画笔宽度、擦除绘画和保存绘画功能。实例运行效果如图9.12所示。
图9.12 在简易涂鸦板上绘画
说明:选择“保存绘画”菜单项,可以将当前绘图保存到SD卡的pictures目录中,文件名为myPicture.png。
9.3 为图形添加特效
教学录像:光盘\TM\lx\9\为图形添加特效.exe
在Android中,不仅可以绘制图形,还可以为图形添加特效。例如,对图形进行旋转、缩放、倾斜、平移和渲染等,下面将分别进行介绍。
9.3.1 旋转图像
使用Android提供的android.graphics.Matrix类的setRotate()、postRotate()和preRotate()方法,可以对图像进行旋转。
说明:在Android API中,提供了setXXX()、postXXX()和preXXX()3种方法,其中,setXXX()方法用于直接设置Matrix的值,每使用一次setXXX()方法,整个Matrix都会改变;postXXX()方法用于采用后乘的方式为Matrix设置值,可以连续多次使用post完成多个变换;preXXX()方法用于采用前乘的方式为Matrix设置值,使用preXXX()方法设置的操作最先发生。
由于这3个方法除了方法名不同外,语法格式等均相同,下面将以setRotate()方法为例来进行介绍。setRotate()方法有以下两种语法格式。
[√]setRotate(float degrees)
使用该语法格式可以控制Matrix进行旋转,float类型的参数用于指定旋转的角度。例如,创建一个Matrix的对象,并将其旋转30°,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setRotate(30); //将Matrix的对象旋转30°
[√]setRotate(float degrees, float px, float py)
使用该语法格式可以控制Matrix以参数px和py为轴心进行旋转,float类型的参数用于指定旋转的角度。例如,创建一个Matrix的对象,并将其以(10,10)为轴心旋转30°,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setRotate(30,10,10); //将Matrix的对象旋转30°
创建Matrix的对象并对其进行旋转后,还需要应用该Matrix对图像或组件进行控制。在Canvas类中提供了一个drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法,可以在绘制图像的同时应用Matrix上的变化。例如,需要将一个图像旋转30°后绘制到画布上,可以使用下面的代码:
- Paint paint=new Paint();
- Bitmap bitmap=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.rabbit);
- Matrix matrix=new Matrix();
- matrix.setRotate(30);
- canvas.drawBitmap(bitmap, matrix, paint);
例9.9 在Eclipse中创建Android项目,名称为9.9,实现应用Matrix旋转图像。(实例位置:光盘\TM\sl\9\9.9)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔,并绘制一张背景图像,然后在(0,0)点的位置绘制要旋转图像的原图,再绘制以(0,0)点为轴心旋转30°的图像,最后绘制以(87,87)点为轴心旋转90°的图像,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.background);
- canvas.drawBitmap(bitmap_bg, 0, 0, paint); //绘制背景图像
- Bitmap bitmap_rabbit=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.rabbit);
- canvas.drawBitmap(bitmap_rabbit, 0, 0, paint); //绘制原图
- //应用setRotate(float degrees)方法旋转图像
- Matrix matrix=new Matrix();
- matrix.setRotate(30); //以(0,0)点为轴心旋转30°
- canvas.drawBitmap(bitmap_rabbit, matrix, paint); //绘制图像并应用matrix的变换
- //应用setRotate(float degrees, float px, float py)方法旋转图像
- Matrix m=new Matrix();
- m.setRotate(90,87,87); //以(87,87)点为轴心旋转90°
- canvas.drawBitmap(bitmap_rabbit, m, paint); //绘制图像并应用matrix的变换
运行本实例,将显示如图9.13所示的运行结果。
图9.13 旋转图像
9.3.2 缩放图像
使用Android提供的android.graphics.Matrix类的setScale()、postScale()和preScale()方法,可对图像进行缩放。由于这3个方法除了方法名不同外,语法格式等均相同,下面将以setScale()方法为例来进行介绍。setScale()方法有以下两种语法格式。
[√]setScale(float sx, float sy)
使用该语法格式可以控制Matrix进行缩放,参数sx和sy用于指定X轴和Y轴的缩放比例。例如,创建一个Matrix的对象,并将其在X轴上缩放30%,在Y轴上缩放20%,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setScale(0.3f, 0.2f); //缩放Matrix对象
[√]setScale(float sx, float sy, float px, float py)
使用该语法格式可以控制Matrix以参数px和py为轴心进行缩放,参数sx和sy用于指定X轴和Y轴的缩放比例。例如,创建一个Matrix的对象,并将其以(100,100)为轴心,在X轴和Y轴上均缩放30%,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix. setScale (30,30,100,100); //缩放Matrix对象
创建Matrix的对象并对其进行缩放后,还需要应用该Matrix对图像或组件进行控制。同旋转图像一样,也可应用Canvas类中提供的drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法,在绘制图像的同时应用Matrix上的变化。下面通过一个具体的实例来说明如何对图像进行缩放。
例9.10 在Eclipse中创建Android项目,名称为9.10,实现应用Matrix缩放图像。(实例位置:光盘\TM\sl\9\9.10)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔,并绘制一张背景图像,然后绘制以(0,0)点为轴心,在X轴和Y轴上均缩放200%的图像,再绘制以(156,156)点为轴心、在X轴和Y轴上均缩放80%的图像,最后在(0,0)点的位置绘制要缩放图像的原图,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- paint.setAntiAlias(true);
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.background);
- canvas.drawBitmap(bitmap_bg, 0, 0, paint); //绘制背景
- Bitmap bitmap_rabbit=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.rabbit);
- //应用setScale(float sx, float sy)方法缩放图像
- Matrix matrix=new Matrix();
- matrix.setScale(2f, 2f); //以(0,0)点为轴心将图像在X轴和Y轴上均缩放200%
- canvas.drawBitmap(bitmap_rabbit, matrix, paint); //绘制图像并应用matrix的变换
- //应用setScale(float sx, float sy, float px, float py) 方法缩放图像
- Matrix m=new Matrix();
- m.setScale(0.8f,0.8f,156,156); //以(156,156)点为轴心将图像在X轴和Y轴上均缩放80%
- canvas.drawBitmap(bitmap_rabbit, m, paint); //绘制图像并应用matrix的变换
- canvas.drawBitmap(bitmap_rabbit, 0, 0, paint); //绘制原图
运行本实例,将显示如图9.14所示的运行结果。
图9.14 缩放图像
9.3.3 倾斜图像
使用Android提供的android.graphics.Matrix类的setSkew()、postSkew()和preSkew()方法,可对图像进行倾斜。由于这3个方法除了方法名不同外,语法格式等均相同,下面将以setSkew()方法为例来进行介绍。setSkew()方法有以下两种语法格式。
[√]setSkew(float kx, float ky)
使用该语法格式可以控制Matrix进行倾斜,参数kx和ky用于指定在X轴和Y轴上的倾斜量。例如,创建一个Matrix的对象,并将其在X轴上倾斜0.3,在Y轴上不倾斜,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setSkew(0.3f, 0); //倾斜Matrix对象
[√]setSkew(float kx, float ky, float px, float py)
使用该语法格式可以控制Matrix以参数px和py为轴心进行倾斜,参数sx和sy用于指定在X轴和Y轴上的倾斜量。例如,创建一个Matrix的对象,并将其以(100,100)为轴心,在X轴和Y轴上均倾斜0.1,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix. setSkew (0.1f,0.1f,100,100); //倾斜Matrix对象
创建Matrix的对象并对其进行倾斜后,还需要应用该Matrix对图像或组件进行控制。同旋转图像一样,也可应用Canvas类中提供的drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法,在绘制图像的同时应用Matrix上的变化。下面通过一个具体的实例来说明如何对图像进行倾斜。
例9.11 在Eclipse中创建Android项目,名称为9.11,实现应用Matrix倾斜图像。(实例位置:光盘\TM\sl\9\9.11)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔并绘制一张背景图像,然后绘制以(0,0)点为轴心,在X轴上倾斜2、在Y轴上倾斜1的图像,再绘制以(78,69)点为轴心,在X轴上倾斜-0.5的图像,最后在(0,0)点的位置绘制要缩放图像的原图,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- paint.setAntiAlias(true);
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.background);
- canvas.drawBitmap(bitmap_bg, 0, 0, paint); //绘制背景
- Bitmap bitmap_rabbit=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.rabbit);
- //应用setSkew(float sx, float sy)方法倾斜图像
- Matrix matrix=new Matrix();
- matrix.setSkew(2f, 1f); //以(0,0)点为轴心将图像在X轴上倾斜2,在Y轴上倾斜1
- canvas.drawBitmap(bitmap_rabbit, matrix, paint); //绘制图像并应用matrix的变换
- //应用setSkew(float sx, float sy, float px, float py) 方法倾斜图像
- Matrix m=new Matrix();
- m.setSkew(-0.5f, 0f,78,69); //以(78,69)点为轴心将图像在X轴上倾斜-0.5
- canvas.drawBitmap(bitmap_rabbit, m, paint); //绘制图像并应用matrix的变换
- canvas.drawBitmap(bitmap_rabbit, 0, 0, paint); //绘制原图
运行本实例,将显示如图9.15所示的运行结果。
图9.15 倾斜图像
9.3.4 平移图像
使用Android提供的android.graphics.Matrix类的setTranslate()、postTranslate()和preTranslate()方法,可对图像进行平移。由于这3个方法除了方法名不同外,语法格式等均相同,下面将以setTranslate()方法为例来进行介绍。setTranslate()方法的语法格式如下:
- setTranslate (float dx, float dy)
在该语法中,参数dx和dy用于指定将Matrix移动到的位置的x和y坐标。
例如,创建一个Matrix的对象,并将其平移到(100,50)的位置,可以使用下面的代码:
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setTranslate(100,50); //将对象平移到(100,50)的位置
创建Matrix的对象并对其进行平移后,还需要应用该Matrix对图像或组件进行控制。同旋转图像一样,也可应用Canvas类中提供的drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法,在绘制图像的同时应用Matrix上的变化。下面通过一个具体的实例来说明如何对图像进行平移。
例9.12 在Eclipse中创建Android项目,名称为9.12,实现应用Matrix将图像旋转后再平移。(实例位置:光盘\TM\sl\9\9.12)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔,并绘制一张背景图像,然后在(0,0)点的位置绘制要缩放图像的原图,再创建一个Matrix的对象,并将其旋转30°后平移到指定位置,最后绘制应用matrix变换的图像,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.background);
- canvas.drawBitmap(bitmap_bg, 0, 0, paint); //绘制背景
- Bitmap bitmap_rabbit=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.rabbit);
- canvas.drawBitmap(bitmap_rabbit, 0, 0, paint); //绘制原图
- Matrix matrix=new Matrix(); //创建一个Matrix的对象
- matrix.setRotate(30); //将matrix旋转30°
- matrix.postTranslate(100,50); //将matrix平移到(100,50)的位置
- canvas.drawBitmap(bitmap_rabbit, matrix, paint); //绘制图像并应用matrix的变换
运行本实例,将显示如图9.16所示的运行结果。
图9.16 旋转并平移图像
9.3.5 使用BitmapShader渲染图像
在Android中,提供的BitmapShader类主要用来渲染图像。如果需要将一张图片裁剪成椭圆形或圆形等形状并显示到屏幕上,就可以使用BitmapShader类来实现。使用BitmapShader来渲染图像的基本步骤如下。
(1)创建BitmapShader类的对象,可以通过以下构造方法进行创建:
- BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
其中,参数bitmap用于指定一个位图对象,通常是要用来渲染的原图像;参数tileX用于指定在水平方向上图像的重复方式;参数tileY用于指定在垂直方向上图像的重复方式。
例如,要创建一个在水平方向上重复、在垂直方向上镜像的BitmapShader对象,可以使用下面的代码:
- BitmapShader bitmapshader= new BitmapShader(bitmap_bg,TileMode.REPEAT,TileMode.MIRROR);
说明:Shader.TileMode类型的参数包括CLAMP、MIRROR和REPEAT 3个可选值,其中,CLAMP为使用边界颜色来填充剩余的空间;MIRROR为采用镜像方式;REPEAT为采用重复方式。
(2)通过Paint的setShader()方法来设置渲染对象。
(3)在绘制图像时,使用已经设置了setShader()方法的画笔。
下面通过一个具体的实例来说明如何使用BitmapShader渲染图像。
例9.13 在Eclipse中创建Android项目,名称为9.13,应用BitmapShader实现平铺的画布背景和椭圆形的图片。(实例位置:光盘\TM\sl\9\9.13)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔,并设置其使用抗锯齿功能,然后应用BitmapShader实现平铺的画布背景,这里使用的是一张机器人图片,接下来绘制一张椭圆形的图片,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.android);
- //创建一个在水平和垂直方向都重复的BitmapShader对象
- BitmapShader bitmapshader= new BitmapShader(bitmap_bg,TileMode.REPEAT,TileMode.REPEAT);
- paint.setShader(bitmapshader); //设置渲染对象
- canvas.drawRect(0, 0, view_width, view_height, paint); //绘制一个使用BitmapShader渲染的矩形
- Bitmap bm=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.img02);
- //创建一个在水平方向上重复,在垂直方向上镜像的BitmapShader对象
- BitmapShader bs= new BitmapShader(bm,TileMode.REPEAT,TileMode.MIRROR);
- paint.setShader(bs); //设置渲染对象
- RectF oval=new RectF(0,0,280,180);
- canvas.translate(40, 20); //将画面在X轴上平移40像素,在Y轴上平移20像素
- canvas.drawOval(oval, paint); //绘制一个使用BitmapShader渲染的椭圆形
运行本实例,将显示如图9.17所示的运行结果。
图9.17 显示平铺背景和椭圆形的图片
9.3.6 范例1:实现带描边的圆角图片
例9.14 在Eclipse中创建Android项目,名称为9.14,实现带描边的圆角图片。(实例位置:光盘\TM\sl\9\9.14)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在MyView的onDraw()方法中,首先定义一个画笔,并绘制一张背景图像,然后定义一个要绘制的圆角矩形的区域,并将画布在X轴上平移40像素,在Y轴上平移20像素,再绘制一个黑色的2像素的圆角矩形,作为图片的描边,最后绘制一个使用BitmapShader渲染的圆角矩形图片,具体代码如下:
- Paint paint=new Paint(); //定义一个画笔
- paint.setAntiAlias(true); //使用抗锯齿功能
- Bitmap bitmap_bg=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.background);
- canvas.drawBitmap(bitmap_bg, 0, 0, paint); //绘制背景
- RectF rect=new RectF(0,0,280,180);
- canvas.translate(40, 20); //将画布在X轴上平移40像素,在Y轴上平移20像素
- //为图片添加描边
- paint.setStyle(Style.STROKE); //设置填充样式为描边
- paint.setColor(Color.BLACK); //设置颜色为黑色
- paint.setStrokeWidth(2); //设置笔触宽度为2像素
- canvas.drawRoundRect(rect, 10, 10, paint); //绘制一个描边的圆角矩形
- paint.setStyle(Style.FILL); //设置填充样式为填充
- Bitmap bm=BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.img02);
- //创建一个在水平方向上重复,在垂直方向上镜像的BitmapShader对象
- BitmapShader bs= new BitmapShader(bm,TileMode.REPEAT,TileMode.MIRROR);
- paint.setShader(bs); //设置渲染对象
- canvas.drawRoundRect(rect, 10, 10, paint); //绘制一个使用BitmapShader渲染的圆角矩形图片
运行本实例,将显示如图9.18所示的运行结果。
图9.18 绘制带描边的圆角图片
9.3.7 范例2:实现放大镜效果
例9.15 在Eclipse中创建Android项目,名称为9.15,实现放大镜效果。(实例位置:光盘\TM\sl\9\9.15)
(1)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的线性布局管理器和TextView组件删除,然后添加一个帧布局管理器,用于显示自定义的绘图类。
(2)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
(3)在内部类MyView中,定义源图像、放大镜图像、放大镜的半径、放大倍数、放大镜的左边距和顶边距等,具体代码如下:
- private Bitmap bitmap; //源图像,也就是背景图像
- private ShapeDrawable drawable;
- private final int RADIUS = 57; //放大镜的半径
- private final int FACTOR = 2; //放大倍数
- private Matrix matrix = new Matrix();
- private Bitmap bitmap_magnifier; //放大镜位图
- private int m_left = 0; //放大镜的左边距
- private int m_top = 0; //放大镜的顶边距
(4)在内部类MyView的构造方法中,首先获取要显示的源图像,然后创建一个BitmapShader对象,用于指定渲染图像,接下来创建一个圆形的drawable,并设置相关属性,最后获取放大镜图像,并计算放大镜的默认左、右边距,具体代码如下:
- Bitmap bitmap_source = BitmapFactory.decodeResource(getResources(),
- R.drawable.source); //获取要显示的源图像
- bitmap = bitmap_source;
- BitmapShader shader = new BitmapShader(Bitmap.createScaledBitmap(
- bitmap_source, bitmap_source.getWidth() * FACTOR,
- bitmap_source.getHeight() * FACTOR, true), TileMode.CLAMP,
- TileMode.CLAMP); //创建BitmapShader对象
- //圆形的drawable
- drawable = new ShapeDrawable(new OvalShape());
- drawable.getPaint().setShader(shader);
- drawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2); //设置圆的外切矩形
- bitmap_magnifier = BitmapFactory.decodeResource(getResources(),
- R.drawable.magnifier); //获取放大镜图像
- m_left = RADIUS - bitmap_magnifier.getWidth() / 2; //计算放大镜的默认左边距
- m_top = RADIUS - bitmap_magnifier.getHeight() / 2; //计算放大镜的默认右边距
(5)在MyView的onDraw()方法中,分别绘制背景图像、放大镜图像和放大后的图像,具体代码如下:
- canvas.drawBitmap(bitmap, 0, 0, null); //绘制背景图像
- canvas.drawBitmap(bitmap_magnifier, m_left, m_top, null); //绘制放大镜图像
- drawable.draw(canvas); //绘制放大后的图像
(6)在内部类MyView中,重写onTouchEvent()方法,实现当用户触摸屏幕时,放大触摸点附近的图像,具体代码如下:
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int x = (int) event.getX(); //获取当前触摸点的X轴坐标
- final int y = (int) event.getY(); //获取当前触摸点的Y轴坐标
- matrix.setTranslate(RADIUS - x FACTOR, RADIUS - y FACTOR); //平移到绘制shader的起始位置
- drawable.getPaint().getShader().setLocalMatrix(matrix);
- drawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS); //设置圆的外切矩形
- m_left = x - bitmap_magnifier.getWidth() / 2; //计算放大镜的左边距
- m_top = y - bitmap_magnifier.getHeight() / 2; //计算放大镜的右边距
- invalidate(); //重绘画布
- return true;
- }
运行本实例,将显示如图9.19所示的运行结果,放大镜的位置跟随触摸点的改变而改变。
图9.19 实现放大镜效果
9.4 Android中的动画
教学录像:光盘\TM\lx\9\ Android中的动画.exe
在应用Android进行项目开发时,特别是在进行游戏开发时,经常需要涉及动画。Android中的动画通常可以分为逐帧动画和补间动画两种。下面将分别介绍如何实现这两种动画。
9.4.1 实现逐帧动画
逐帧动画就是顺序播放事先准备好的静态图像,利用人眼的“视觉暂留”原理,给用户造成动画的错觉。实现逐帧动画比较简单,只需要以下两个步骤。
(1)在Android XML资源文件中定义一组用于生成动画的图片资源,可以使用包含一系列<item></item>子标记的<animation-list></animation-list>标记来实现,具体语法格式如下:
- <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="true|false">
- <item android:drawable="@drawable/图片资源名1" android:duration="integer"/>
- … <!-- 省略了部分<item></item>标记 -->
- <item android:drawable="@drawable/图片资源名n" android:duration="integer"/>
- </animation-list>
在上面的语法中,android:oneshot属性用于设置是否循环播放,默认值为true,表示循环播放;android:drawable属性用于指定要显示的图片资源;android:duration属性指定图片资源持续的时间。
(2)使用步骤(1)中定义的动画资源。通常情况下,可以将其作为组件的背景使用。例如,可以在布局文件中添加一个线性布局管理器,然后将该布局管理器的android:background属性设置为所定义的动画资源。也可以将定义的动画资源作为ImageView的背景使用。
说明:在Android中还支持在Java代码中创建逐帧动画。具体的步骤是:首先创建AnimationDrawable对象,然后调用addFrame()方法向动画中添加帧,每调用一次addFrame()方法,将添加一个帧。
9.4.2 实现补间动画
补间动画就是通过对场景里的对象不断进行图像变化来产生动画效果。在实现补间动画时,只需要定义动画开始和结束的关键帧,其他过渡帧由系统自动计算并补齐。在Android中,提供了4种补间动画。
- 透明度渐变动画(AlphaAnimation)
透明度渐变动画就是指通过View组件透明度的变化来实现View的渐隐渐显效果。它主要通过为动画指定开始时的透明度、结束时的透明度以及持续时间来创建动画。同逐帧动画一样,也可以在XML文件中定义透明度渐变动画的资源文件,基本的语法格式如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@[package:]anim/interpolator_resource">
- <alpha
- android:repeatMode="reverse|restart"
- android:repeatCount="次数|infinite"
- android:duration="Integer"
- android:fromAlpha="float"
- android:toAlpha="float" />
- </set>
在上面的语法中,各属性说明如表9.7所示。
表9.7 定义透明度渐变动画时常用的属性
属 性 | 描 述 |
android:interpolator | 用于控制动画的变化速度,使得动画效果可以匀速、加速、减速或抛物线速度等各种速度变化,其属性值如表9.8所示 |
android:repeatMode | 用于设置动画的重复方式,可选值为reverse(反向)或restart(重新开始) |
android:repeatCount | 用于设置动画的重复次数,属性可以是代表次数的数值,也可以是infinite(无限循环) |
android:duration | 用于指定动画持续的时间,单位为毫秒 |
android:fromAlpha | 用于指定动画开始时的透明度,值为0.0代表完全透明,值为1.0代表完全不透明 |
android:toAlpha | 用于指定动画结束时的透明度,值为0.0代表完全透明,值为1.0代表完全不透明 |
表9.8 android:interpolator属性的常用属性值
属性值 | 描 述 |
@android:anim/linear_interpolator | 动画一直在做匀速改变 |
@android:anim/accelerate_interpolator | 动画在开始的地方改变较慢,然后开始加速 |
@android:anim/decelerate_interpolator | 动画在开始的地方改变速度较快,然后开始减速 |
@android:anim/accelerate_decelerate_interpolator | 动画在开始和结束的地方改变速度较慢,在中间的时候加速 |
@android:anim/cycle_interpolator | 动画循环播放特定的次数,变化速度按正弦曲线改变 |
@android:anim/bounce_interpolator | 动画结束的地方采用弹球效果 |
@android:anim/anticipate_overshoot_interpolator | 在动画开始的地方先向后退一小步,再开始动画,到结束的地方再超出一小步,最后回到动画结束的地方 |
@android:anim/overshoot_interpolator | 动画快速到达终点并超出一小步,最后回到动画结束的地方 |
@android:anim/anticipate_interpolator | 在动画开始的地方先向后退一小步,再快速到达动画结束的地方 |
例如,定义一个让View组件从完全透明到完全不透明、持续时间为2秒钟的动画,可以使用下面的代码:
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <alpha android:fromAlpha="0"
- android:toAlpha="1"
- android:duration="2000"/>
- </set>
- 旋转动画(RotateAnimation)
旋转动画就是通过为动画指定开始时的旋转角度、结束时的旋转角度以及持续时间来创建动画。在旋转时,还可以通过指定轴心点坐标来改变旋转的中心。同透明度渐变动画一样,也可以在XML文件中定义旋转动画资源文件,基本的语法格式如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@[package:]anim/interpolator_resource">
- <rotate
- android:fromDegrees="float"
- android:toDegrees="float"
- android:pivotX="float"
- android:pivotY="float"
- android:repeatMode="reverse|restart"
- android:repeatCount="次数|infinite"
- android:duration="Integer"/>
- </set>
在上面的语法中,各属性说明如表9.9所示。
表9.9 定义旋转动画时常用的属性
属 性 | 描 述 |
android:interpolator | 用于控制动画的变化速度,使得动画效果可以匀速、加速、减速或抛物线速度等各种速度变化,其属性值见表9.8 |
android:fromDegrees | 用于指定动画开始时的旋转角度 |
android:toDegrees | 用于指定动画结束时的旋转角度 |
android:pivotX | 用于指定轴心点的X坐标 |
android:pivotY | 用于指定轴心点的Y坐标 |
android:repeatMode | 用于设置动画的重复方式,可选值为reverse(反向)或restart(重新开始) |
android:repeatCount | 用于设置动画的重复次数,属性可以是代表次数的数值,也可以是infinite(无限循环) |
android:duration | 用于指定动画持续的时间,单位为毫秒 |
例如,定义一个让图片从0°转到360°、持续时间为2秒钟、中心点在图片的中心的动画,可以使用下面的代码:
- <rotate
- android:fromDegrees="0"
- android:toDegrees="360"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="2000">
- </rotate>
- 缩放动画(ScaleAnimation)
缩放动画就是通过为动画指定开始时的缩放系数、结束时的缩放系数以及持续时间来创建动画。在缩放时,还可以通过指定轴心点坐标来改变缩放的中心。同透明度渐变动画一样,也可以在XML文件中定义缩放动画资源文件,基本的语法格式如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@[package:]anim/interpolator_resource">
- <scale
- android:fromXScale="float"
- android:toXScale="float"
- android:fromYScale="float"
- android:toYScale="float"
- android:pivotX="float"
- android:pivotY="float"
- android:repeatMode="reverse|restart"
- android:repeatCount="次数|infinite"
- android:duration="Integer"/>
- </set>
在上面的语法中,各属性说明如表9.10所示。
表9.10 定义缩放动画时常用的属性
属 性 | 描 述 |
android:interpolator | 用于控制动画的变化速度,使得动画效果可以匀速、加速、减速或抛物线速度等各种速度变化,其属性值见表9.8 |
android:fromXScale | 用于指定动画开始时水平方向上的缩放系数,值为1.0表示不变化 |
android:toXScale | 用于指定动画结束时水平方向上的缩放系数,值为1.0表示不变化 |
android:fromYScale | 用于指定动画开始时垂直方向上的缩放系数,值为1.0表示不变化 |
android:toYScale | 用于指定动画结束时垂直方向上的缩放系数,值为1.0表示不变化 |
android:pivotX | 用于指定轴心点的X坐标 |
android:pivotY | 用于指定轴心点的Y坐标 |
android:repeatMode | 用于设置动画的重复方式,可选值为reverse(反向)或restart(重新开始) |
android:repeatCount | 用于设置动画的重复次数,属性值以是代表次数的数值,也可以是infinite(无限循环) |
android:duration | 用于指定动画持续的时间,单位为毫秒 |
例如,定义一个以图片的中心为轴心点,将图片放大2倍的、持续时间为2秒钟的动画,可以使用下面的代码:
- <scale android:fromXScale="1"
- android:fromYScale="1"
- android:toXScale="2.0"
- android:toYScale="2.0"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="2000"/>
- 平移动画(Translate Animation)
平移动画就是通过为动画指定开始时的位置、结束时的位置以及持续时间来创建动画。同透明度渐变动画一样,也可以在XML文件中定义平移动画资源文件,基本的语法格式如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@[package:]anim/interpolator_resource">
- <translate
- android:fromXDelta="float"
- android:toXDelta="float"
- android:fromYDelta="float"
- android:toYDelta="float"
- android:repeatMode="reverse|restart"
- android:repeatCount="次数|infinite"
- android:duration="Integer"/>
- </set>
在上面的语法中,各属性说明如表9.11所示。
表9.11 定义平移动画时常用的属性
属 性 | 描 述 |
android:interpolator | 用于控制动画的变化速度,使得动画效果可以匀速、加速、减速或抛物线速度等各种速度变化,其属性值见表9.8 |
android:fromXDelta | 用于指定动画开始时水平方向上的起始位置 |
android:toXDelta | 用于指定动画结束时水平方向上的起始位置 |
android:fromYDelta | 用于指定动画开始时垂直方向上的起始位置 |
android:toYDelta | 用于指定动画结束时垂直方向上的起始位置 |
android:repeatMode | 用于设置动画的重复方式,可选值为reverse(反向)或restart(重新开始) |
android:repeatCount | 用于设置动画的重复次数,属性可以是代表次数的数值,也可以是infinite(无限循环) |
android:duration | 用于指定动画持续的时间,单位为毫秒 |
例如,定义一个让图片从(0,0)点到(300,300)点、持续时间为2秒钟的动画,可以使用下面的代码:
- <translate
- android:fromXDelta="0"
- android:toXDelta="300"
- android:fromYDelta="0"
- android:toYDelta="300"
- android:duration="2000">
- </translate>
9.4.3 范例1:忐忑的精灵
例9.16 在Eclipse中创建Android项目,名称为9.16,使用逐帧动画实现一个忐忑的精灵动画。(实例位置:光盘\TM\sl\9\9.16)
(1)在新建项目的res目录中,首先创建一个名称为anim的目录,并在该目录中添加一个名称为fairy.xml的XML资源文件,然后在该文件中定义组成动画的图片资源,具体代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/img001" android:duration="60"/>
- <item android:drawable="@drawable/img002" android:duration="60"/>
- <item android:drawable="@drawable/img003" android:duration="60"/>
- <item android:drawable="@drawable/img004" android:duration="60"/>
- <item android:drawable="@drawable/img005" android:duration="60"/>
- <item android:drawable="@drawable/img006" android:duration="60"/>
- </animation-list>
(2)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的TextView组件删除,然后为默认添加的线性布局管理器设置android:id和android:background属性。将android:background属性为步骤(1)中创建的动画资源,修改后的代码如下:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@anim/fairy"
- android:id="@+id/ll"
- android:orientation="vertical" >
- </LinearLayout>
(3)打开默认创建的MainActivity,在该文件中,首先创建一个名称为MyView的内部类,该类继承自android.view.View类,并添加构造方法和重写onDraw(Canvas canvas)方法,然后在onCreate()方法中获取布局文件中添加的帧布局管理器,并将MyView视图添加到该帧布局管理器中。
- LinearLayout ll=(LinearLayout)findViewById(R.id.ll); //获取布局文件中添加的线性布局管理器
- final AnimationDrawable anim=(AnimationDrawable)ll.getBackground(); //获取AnimationDrawable对象
- //为线性布局管理器添加单击事件监听器
- ll.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if(flag){
- anim.start(); //开始播放动画
- flag=false;
- }else{
- anim.stop(); //停止播放动画
- flag=true;
- }
- }
- });
运行本实例并单击屏幕,将播放自定义的逐帧动画,如图9.20所示。当动画播放时,单击屏幕,将停止动画的播放,再次单击屏幕,将继续播放动画。
图9.20 忐忑的精灵
9.4.4 范例2:旋转、平移、缩放和透明度渐变的补间动画
例9.17 在Eclipse中创建Android项目,名称为9.17,实现旋转、平移、缩放和透明度渐变的补间动画。(实例位置:光盘\TM\sl\9\9.17)
(1)在新建项目的res目录中,创建一个名称为anim的目录,并在该目录中创建实现旋转、平移、缩放和透明度渐变的动画资源文件。
① 创建名称为anim_alpha.xml的XML资源文件,在该文件中定义一个实现透明度渐变的动画,该动画的渐变过程为“完全不透明→完全透明→完全不透明”,具体代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <alpha android:fromAlpha="1"
- android:toAlpha="0"
- android:fillAfter="true"
- android:repeatMode="reverse"
- android:repeatCount="1"
- android:duration="2000"/>
- </set>
② 创建名称为anim_rotate.xml的XML资源文件,在该文件中定义一个实现旋转的动画,该动画为从0°旋转到720°,再从360°旋转到0°,具体代码如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <rotate
- android:interpolator="@android:anim/accelerate_interpolator"
- android:fromDegrees="0"
- android:toDegrees="720"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="2000">
- </rotate>
- <rotate
- android:interpolator="@android:anim/accelerate_interpolator"
- android:startOffset="2000"
- android:fromDegrees="360"
- android:toDegrees="0"
- android:pivotX="50%"
- android:pivotY="50%"
- android:duration="2000">
- </rotate>
- </set>
③ 创建名称为anim_scale.xml的XML资源文件,在该文件中定义一个实现缩放的动画,该动画首先将原图像放大2倍,再逐渐收缩为图像的原尺寸,具体代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:fromXScale="1"
- android:interpolator="@android:anim/decelerate_interpolator"
- android:fromYScale="1"
- android:toXScale="2.0"
- android:toYScale="2.0"
- android:pivotX="50%"
- android:pivotY="50%"
- android:fillAfter="true"
- android:repeatCount="1"
- android:repeatMode="reverse"
- android:duration="2000"/>
- </set>
④ 创建名称为anim_translate.xml的XML资源文件,在该文件中定义一个实现平移的动画,该动画为从屏幕的左侧移动到屏幕的右侧,再从屏幕的右侧返回到左侧,具体代码如下:
- <?xml version="1.0" encoding="utf-8"?>
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate
- android:fromXDelta="0"
- android:toXDelta="860"
- android:fromYDelta="0"
- android:toYDelta="0"
- android:fillAfter="true"
- android:repeatMode="reverse"
- android:repeatCount="1"
- android:duration="2000">
- </translate>
- </set>
(2)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的TextView组件删除,然后在默认添加的线性布局管理器中添加一个水平线性布局管理器和一个ImageView组件,再向该水平线性布局管理器中添加4个Button组件,最后设置ImageView组件的左边距和要显示的图片,具体代码请参见光盘。
(3)打开默认创建的MainActivity,在onCreate()方法中,首先获取动画资源文件中创建的动画资源,然后获取要应用动画效果的ImageView,再获取“旋转”按钮,并为该按钮添加单击事件监听器,在重写的onClick()方法中,播放旋转动画。具体代码如下:
- final Animation rotate=AnimationUtils.loadAnimation(this, R.anim.anim_rotate); //获取旋转动画资源
- final Animation translate=AnimationUtils.loadAnimation(this, R.anim.anim_translate); //获取平移动画资源
- final Animation scale=AnimationUtils.loadAnimation(this, R.anim.anim_scale); //获取缩放动画资源
- final Animation alpha=AnimationUtils.loadAnimation(this, R.anim.anim_alpha); //获取透明度变化动画资源
- final ImageView iv=(ImageView)findViewById(R.id.imageView1); //获取要应用动画效果的ImageView
- Button button1=(Button)findViewById(R.id.button1); //获取“旋转”按钮
- button1.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- iv.startAnimation(rotate); //播放旋转动画
- }
- });
获取“平移”按钮,并为该按钮添加单击事件监听器,在重写的onClick()方法中,播放平移动画,关键代码如下:
- iv.startAnimation(translate); //播放平移动画
获取“缩放”按钮,并为该按钮添加单击事件监听器,在重写的onClick()方法中,播放缩放动画,关键代码如下:
- iv.startAnimation(scale); //播放缩放动画
获取“透明度渐变”按钮,并为该按钮添加单击事件监听器,在重写的onClick()方法中,播放透明度渐变动画,关键代码如下:
- iv.startAnimation(alpha); //播放透明度渐变动画
运行本实例,单击“旋转”按钮,屏幕中的小猫将旋转,如图9.21所示;单击“平移”按钮,屏幕中的小猫将从屏幕的左侧移动到右侧,再从右侧返回左侧;单击“缩放”按钮,屏幕中的小猫将放大2倍,再恢复为原来的大小;单击“透明度渐变”按钮,屏幕中的小猫将逐渐隐藏,再逐渐显示。
图9.21 旋转图像动画
9.5 经典范例
9.5.1 在GridView中显示SD卡上的全部图片
例9.18 在Eclipse中创建Android项目,名称为9.18,实现在GridView中显示SD卡上的全部图片。(实例位置:光盘\TM\sl\9\9.18)
(1)修改新建项目的res\layout目录下的布局文件main.xml,添加一个id属性为gridView1的GridView组件,并设置其列数为4,也就是每行显示4张图片。关键代码如下:
- <GridView android:id="@+id/gridView1"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:layout_marginTop="10px"
- android:horizontalSpacing="3px"
- android:verticalSpacing="3px"
- android:numColumns="4"
- />
(2)打开默认添加的MainActivity,定义一个用于保存图片路径的List集合对象,关键代码如下:
- private List<String> imagePath = new ArrayList<String>(); //图片文件的路径
(3)定义一个保存合法的图片文件格式的字符串数组,并编写根据文件路径判断文件是否为图片文件的方法,具体代码如下:
- private static String[] imageFormatSet = new String[] { "jpg", "png", "gif" }; //合法的图片文件格式
- //判断是否为图片文件
- private static boolean isImageFile(String path) {
- for (String format : imageFormatSet) { //遍历数组
- if (path.contains(format)) { //判断是否为合法的图片文件
- return true;
- }
- }
- return false;
- }
(4)编写getFiles()方法,用于遍历指定路径。在该方法中,采用递归调用的方式来实现遍历指定路径下的全部文件(包括子文件中的文件),关键代码如下:
- private void getFiles(String url) {
- File files = new File(url); //创建文件对象
- File[] file = files.listFiles();
- try {
- for (File f : file) { //通过for循环遍历获取到的文件数组
- if (f.isDirectory()) { //如果是目录,也就是文件夹
- getFiles(f.getAbsolutePath()); //递归调用
- } else {
- if (isImageFile(f.getPath())) { //如果是图片文件
- imagePath.add(f.getPath()); //将文件的路径添加到List集合中
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace(); //输出异常信息
- }
- }
(5)在主活动的onCreate()方法中,获得SD卡的路径,并调用getFiles()方法获取SD卡上的全部图片,当SD卡上不存在图片文件时返回。具体代码如下:
- String sdpath = Environment.getExternalStorageDirectory() + "/"; //获得SD卡的路径
- getFiles(sdpath); //调用getFiles()方法获取SD卡上的全部图片
- if(imagePath.size()<1){ //如果不存在图片文件
- return;
- }
(6)首先获取GridView组件,然后创建BaseAdapter类的对象,并重写其中的getView()、getItemId()、getItem()和getCount()方法,其中最主要的是重写getView()方法来设置要显示的图片,最后将BaseAdapter适配器与GridView相关联,具体代码如下:
- GridView gridview = (GridView) findViewById(R.id.gridView1); //获取GridView组件
- BaseAdapter adapter = new BaseAdapter() {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ImageView imageview; //声明ImageView的对象
- if (convertView == null) {
- imageview = new ImageView(MainActivity.this); //实例化ImageView的对象
- /************* 设置图像的宽度和高度 ******************/
- imageview.setAdjustViewBounds(true);
- imageview.setMaxWidth(150);
- imageview.setMaxHeight(113);
- /**************************************************/
- imageview.setPadding(5, 5, 5, 5); //设置ImageView的内边距
- } else {
- imageview = (ImageView) convertView;
- }
- //为ImageView设置要显示的图片
- Bitmap bm=BitmapFactory.decodeFile(imagePath.get(position));
- imageview.setImageBitmap(bm);
- return imageview; //返回ImageView
- }
- /*
- * 功能:获得当前选项的ID
- */
- @Override
- public long getItemId(int position) {
- return position;
- }
- /*
- * 功能:获得当前选项
- */
- @Override
- public Object getItem(int position) {
- return position;
- }
- /*
- * 获得数量
- */
- @Override
- public int getCount() {
- return imagePath.size();
- }
- };
- gridview.setAdapter(adapter); //将适配器与GridView关联
在SD卡上上传如图9.22所示的图片文件。运行本实例,将显示如图9.23所示的运行结果。
图9.22 在SD卡上上传文件
图9.23 在GridView中显示SD卡上的全部图片
9.5.2 迷途奔跑的野猪
例9.19 在Eclipse中创建Android项目,名称为9.19,实现迷途的野猪来回奔跑的动画。(实例位置:光盘\TM\sl\9\9.19)
(1)在新建项目的res目录中,创建一个名称为anim的目录,并在该目录中创建实现野猪做向右奔跑动作和做向左奔跑动作的逐帧动画资源文件。
① 创建名称为motionright.xml的XML资源文件,在该文件中定义一个野猪做向右奔跑动作的动画,该动画由两帧组成,也就是由两个预先定义好的图片组成,具体代码如下:
- <animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/pig1" android:duration="40" />
- <item android:drawable="@drawable/pig2" android:duration="40" />
- </animation-list>
② 创建名称为motionleft.xml的XML资源文件,在该文件中定义一个野猪做向左奔跑动作的动画,该动画也由两帧组成,具体代码如下:
- <animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/pig3" android:duration="40" />
- <item android:drawable="@drawable/pig4" android:duration="40" />
- </animation-list>
(2)在amin目录中,创建实现野猪向右侧奔跑和向左侧奔跑的补间动画资源文件。
① 创建名称为tramslateright.xml的XML资源文件,在该文件中定义一个实现野猪向右侧奔跑的补间动画,该动画为在水平方向上向右平移850像素,持续时间为3秒钟,具体代码如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android">
- <translate
- android:fromXDelta="0"
- android:toXDelta="850"
- android:fromYDelta="0"
- android:toYDelta="0"
- android:duration="3000">
- </translate>
- </set>
② 创建名称为translate left.xml的XML资源文件,在该文件中定义一个实现野猪向左侧奔跑的补间动画,该动画为在水平方向上向左平移850像素,持续时间为3秒钟,具体代码如下:
- <set xmlns:android="http://schemas.android.com/apk/res/android" >
- <translate
- android:fromXDelta="850"
- android:toXDelta="0"
- android:fromYDelta="0"
- android:toYDelta="0"
- android:duration="3000">
- </translate>
- </set>
(3)修改新建项目的res\layout目录下的布局文件main.xml,将默认添加的TextView组件删除,然后在默认添加的线性布局管理器中添加一个ImageView组件,并设置该组件的背景为逐帧动画资源motionright,最后设置ImageView组件的顶外边距和左外边距,关键代码如下:
- <ImageView
- android:id="@+id/imageView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@anim/motionright"
- android:layout_marginTop="280px"
- android:layout_marginLeft="30px"/>
(4)打开默认创建的MainActivity,在onCreate()方法中,首先获取要应用动画效果的ImageView,并获取向右奔跑和向左奔跑的补间动画资源,然后获取ImageView应用的逐帧动画以及线性布局管理器,并显示一个消息提示框,再为线性布局管理器添加触摸监听器,在重写的onTouch()方法中,开始播放逐帧动画并播放向右奔跑的补间动画,最后为向右奔跑和向左奔跑的动画添加动画监听器,并在重写的onAnimationEnd()方法中改变要使用的逐帧动画和补间动画、播放动画,实现野猪来回奔跑的动画效果。具体代码如下:
- final ImageView iv=(ImageView)findViewById(R.id.imageView1); //获取要应用动画效果的ImageView
- //获取向右奔跑的动画资源
- final Animation translateright=AnimationUtils.loadAnimation(this, R.anim.translateright);
- //获取向左奔跑的动画资源
- final Animation translateleft=AnimationUtils.loadAnimation(this, R.anim.translateleft);
- anim=(AnimationDrawable)iv.getBackground(); //获取应用的帧动画
- LinearLayout ll=(LinearLayout)findViewById(R.id.linearLayout1); //获取线性布局管理器
- Toast.makeText(this,"触摸屏幕开始播放...", Toast.LENGTH_SHORT).show(); //显示一个消息提示框
- ll.setOnTouchListener(new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- anim.start(); //开始播放帧动画
- iv.startAnimation(translateright); //播放向右奔跑的动画
- return false;
- }
- });
- translateright.setAnimationListener(new AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {}
- @Override
- public void onAnimationRepeat(Animation animation) {}
- @Override
- public void onAnimationEnd(Animation animation) {
- iv.setBackgroundResource(R.anim.motionleft); //重新设置ImageView应用的帧动画
- iv.startAnimation(translateleft); //播放向左奔跑的动画
- anim=(AnimationDrawable)iv.getBackground(); //获取应用的帧动画
- anim.start(); //开始播放帧动画
- }
- });
- translateleft.setAnimationListener(new AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {}
- @Override
- public void onAnimationRepeat(Animation animation) {}
- @Override
- public void onAnimationEnd(Animation animation) {
- iv.setBackgroundResource(R.anim.motionright); //重新设置ImageView应用的帧动画
- iv.startAnimation(translateright); //播放向右奔跑的动画
- anim=(AnimationDrawable)iv.getBackground(); //获取应用的帧动画
- anim.start(); //开始播放帧动画
- }
- });
运行本实例,触摸屏幕后,屏幕中的野猪将从左侧奔跑到右侧,如图9.24所示,撞到右侧的栅栏后,转身向左侧奔跑,直到撞上左侧的栅栏,再转身向右侧奔跑,如此反复。
图9.24 迷途奔跑的野猪
9.6 小 结
本章主要介绍了在Android中进行图形图像处理的相关技术,包括如何绘制2D图像、为图形添加特效以及实现动画等内容。在介绍2D图像的绘制时,主要介绍了如何绘制几何图形、文本、路径和图片等,在进行游戏开发时,经常需要应用到这些内容,需要读者重点掌握;在介绍如何实现动画效果时,主要介绍了如何实现逐帧动画和补间动画,其中,逐帧动画主要通过图片的变化来形成动画效果,而补间动画则主要体现在位置、大小、旋转度、透明度变化方面,并且只需要指定起始帧和结束帧,其他过渡帧将由系统自动计算得出。
9.7 实践与练习
编写Android项目,实现探照灯效果。(答案位置:光盘\TM\sl\9\9.20)
编写Android项目,实现闪烁的星星动画。(答案位置:光盘\TM\sl\9\9.21)