第2章 Android游戏开发中的前台渲染
在正式动手开发游戏之前,有必要先对Android的前台渲染技术进行学习,掌握了这些知识,才能更好地学习游戏的开发。本章将对Android游戏开发的前台渲染技术进行详细介绍,为以后各章节的游戏开发做好技术储备。
2.1 创建Android用户界面
任何程序都少不了用户界面,游戏也不例外,本节将对Android应用中常用的用户界面、布局管理以及简单的事件处理进行介绍。
2.1.1 布局管理
Android的控件有很多种,需要使用Layout对这些控件进行管理,以使这些控件显示在屏幕的正确位置。Android SDK中已经内置了5种布局模型,开发人员通过对这几种布局模型进行组合便可构建出各种复杂的用户界面。下面将对这5种布局模型进行详细的讲解。
1.线性布局
线性布局(LinearLayout)是Android应用程序中最简单的布局方式,有水平和竖直两种排列方式,通过对参数的设置可以控制各个控件在布局中的相对大小。接下来将通过一个简单的例子来介绍线性布局的使用方法,该例子最终的效果如图2-1所示,其开发步骤如下。
图2-1 线性布局
(1)创建一个新项目Sample_2_1,然后在res/layout下创建XML布局文件my_layout.xml,并在其中输入如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_1/res/layout目录下的my_layout.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="wrap_content"
6 > <!--定义了一个线性布局,方式是垂直的-->
7 <Button
8 android:layout_width="fill_parent"
9 android:layout_height="fill_parent"
10 android:text="上"
11 /> <!-- 向线性布局中添加一个普通按钮控件-->
12 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
13 android:orientation="horizontal"
14 android:layout_width="wrap_content"
15 android:layout_height="wrap_content"
16 > <!-- 向线性布局中添加一个水平的线性布局-->
17 <Button
18 android:layout_width="wrap_content"
19 android:layout_height="wrap_content"
20 android:text="左下"
21 /> <!--向水平的线性布局中添加一个普通按钮控件-->
22 <Button
23 android:layout_width="wrap_content"
24 android:layout_height="wrap_content"
25 android:text="右下"
26 /> <!--向水平的线性布局中添加一个普通按钮控件-->
27 </LinearLayout>
28 </LinearLayout>
● 第2~6行是一个垂直的线性布局,宽度填满整个屏幕,高度自动适应子控件的大小。
● 第7~11行向线性布局中添加一个按钮控件,宽度和高度全部填满父控件。
● 第12~16行又向外层的线性布局中添加一个水平的线性布局,高度和宽度全部适应子控件。
● 第17~26行往水平的线性控件中依次添加两个按钮控件。
(2)打开src/wyf/ytl目录下的Sample_2_1.java,将其代码修改为如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_1/src/wyf/ytl目录下的Sample_2_1.java。
1 package wyf.ytl;
2 import android.app.Activity; //引入相关类
3 import android.os.Bundle;
4 public class Sample_2_1 extends Activity { //定义一个Activity
5 public void onCreate(Bundle savedInstanceState) { //重写onCreate回调方法
6 super.onCreate(savedInstanceState); //调用父类的onCreate方法
7 setContentView(R.layout.my_layout); //设置当前用户界面
8 }
9 }
● 第5~8行重写Activity的onCreate方法,该方法在Activity创建时被系统调用。
● 第9行指定当前显示的主界面为my_layout.xml。
提示
为了不干扰读者的思路,本章例子中按钮上的文字全部写在程序中,读者可以将这些文字提取到strings.xml文件中,为以后程序的维护与修改提供便利。
2.表格布局
表格布局(TableLayout)是以行列的形式来管理子控件的,在表格布局中的每一行可以是一个View控件或者是一个TableRow控件。而TableRow控件中还可以添加子控件。下面通过一个简单的例子来说明表格布局的使用方法,运行效果如图2-2所示,其开发步骤如下。
图2-2 表格布局
(1)创建一个新项目Sample_2_2,然后在res/layout下创建XML布局文件my_layout.xml,并在其中输入如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_2/res/layout目录下的my_layout.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 > <!—定义一个表格布局,宽高全部填满父控件-->
6 <TextView
7 android:layout_width="fill_parent"
8 android:layout_height="wrap_content"
9 android:gravity="center"
10 android:text="表头"
11 /> <!—在表格的第一行填充一个文本控件-->
12 <TableRow
13 android:gravity="center"
14 > <!—再向表格中添加一行-->
15 <TextView
16 android:layout_width="wrap_content"
17 android:layout_height="wrap_content"
18 android:text="第0列"
19 > <!—在该行的第一列添加一个文本控件-->
20 </TextView>
21 <TextView
22 android:layout_width="wrap_content"
23 android:layout_height="wrap_content"
24 android:text="第1列"
25 > <!—在该行的第二列添加一个文本控件-->
26 </TextView>
27 </TableRow>
28 <TableRow
29 android:gravity="center"
30 > <!-- 向表格中添加一行,对齐方式为居住-->
31 <Button
32 android:layout_width="wrap_content"
33 android:layout_height="wrap_content"
34 android:text="按钮1"
35 /> <!—往该行第一列添加一个按钮控件-->
36 <Button
37 android:layout_width="wrap_content"
38 android:layout_height="wrap_content"
39 android:text="按钮2"
40 /> <!-- 向第二列添加一个按钮控件-->
41 </TableRow>
42 </TableLayout>
● 第2~5行定义了一个宽高全部填满屏幕的TableLayout。
● 第6~11行向TableLayout中的第一行添加一个TextView控件,并且设置对齐方式为居中。
● 第22~27行添加TableRow控件并继续添加两个自适应宽高的TextView控件。
● 第28~41行再向TableLayout中添加一行,并直接向该行中添加两列,每列一个按钮。
提示
表格布局中,每行可以是其他控件,而每列也可以是其他控件,在程序的开发过程中常常使用表格布局来排版。
(2)和前面的例子一样,编写完布局文件后,应该将该布局文件设为当前显示的用户界面,设置方法与线性布局的例子一样,因本书篇幅所限,就不再赘述,读者可以参考线性布局的例子自行更改。
3.相对布局
相对布局(RelativeLayout)也比较简单,其子控件是根据所设置的参照控件来进行布局的,设置的参照控件可以是父控件,也可以是其他的子控件。下面通过一个简单的例子来介绍相对布局的使用方法,该例子运行的效果如图2-3所示,其开发步骤如下。
图2-3相对布局
(1)创建项目 Sample_2_3 ,在 res/layout 下添加布局文件my_layout.xml,代码如下。
代码位置:见随书光盘中源代码/第2章/Sample_2_3/res/layout目录下的my_layout.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 > <!-- 定义一个相对布局,宽高填满整个窗口 -->
6 <Button
7 android:id="@+id/button1"
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content"
10 android:text="中间的按钮,很长很长很长"
11 android:layout_centerInParent="true"
12 >
13 </Button> <!--添加一个id为button1的按钮,位于屏幕的正中间-->
14 <Button
15 android:id="@+id/button2"
16 android:layout_width="wrap_content"
17 android:layout_height="wrap_content"
18 android:text="上面的按钮"
19 android:layout_above="@id/button1"
20 android:layout_alignLeft="@id/button1"
21 > <!-- 添加一个按钮,位于button1的左上方-->
22 </Button>
23 <Button
24 android:id="@+id/button3"
25 android:layout_width="wrap_content"
26 android:layout_height="wrap_content"
27 android:text="下面的按钮"
28 android:layout_below="@id/button1"
29 android:layout_alignRight="@id/button1"
30 >
31 </Button> <!-- 添加一个按钮,位于button1的右下方-->
32 </RelativeLayout>
● 在第2~5行定义了一个相对布局,宽高全部填满父控件,即填满整个屏幕。
● 第6~12行定义了一个参照按钮控件,宽高自适应,位置为父控件的正中间。
● 第14~22行添加一个按钮控件,宽高自适应,位于参照控件的左上方。
● 第23~31行继续添加一个按钮控件,宽高仍然是自适应,位于参照控件的右下方。
(2)接下来是更改Sample_2_3.java源代码,将刚刚编写的布局设为当前用户界面,设置方法同线性布局的例子一样,在此不再赘述,读者可查看随书光盘中源代码/第 2 章/Sample_2_3/src/wyf/ytl下的Sample_2_3.java文件。
4.单帧布局
单帧布局的使用方法更为简单,不需要任何特殊的配置,布局中的所有控件都被放置在布局的左上角。下面的例子是将 3 个ImageView控件添加到一个单帧布局中,效果如图2-4所示,其开发步骤如下。
图2-4 单帧布局
(1)创建一个名为 Sample_2_4 的新项目,在该项目的 res/layout 下添加名为 my_layout.xml的布局文件,其代码如下。
代码位置:见随书光盘中源代码/第2章/Sample_2_4/res/layout目录下的my_layout.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 > <!-- 添加一个占满全屏幕的单帧布局 -->
6 <ImageView
7 android:layout_width="wrap_content"
8 android:layout_height="wrap_content"
9 android:src="@drawable/big"
10 > <!--往单帧布局中添加一个ImageView控件-->
11 </ImageView>
12 <ImageView
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:src="@drawable/center"
16 > <!-- 往单帧布局中添加一个ImageView控件-->
17 </ImageView>
18 <ImageView
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:src="@drawable/small"
22 > <!-- 往单帧布局中添加一个ImageView控件--
23 </ImageView>
24 </FrameLayout>
● 在第2~5行定义了一个单帧布局,宽高全部填满父控件,即填满整个屏幕。
● 第6~23行向单帧布局中顺序添加3个ImageView控件。这些控件将全部对齐到布局的左上角,ImageView控件的android:src="@drawable/big"属性设置为res/drawable-mdpi下的图片文件。
提示
Android 平台中,图片等资源文件不支持大写字母,读者如希望使自己的资源图片名称更简洁、清晰,可以使用下划线来分割资源名中的单词。
(2)最后,同样是将刚编写的单帧布局设为当前显示的用户界面,设置方法同前面的例子完全相同,读者可以自行开发。
5.网格布局
下面介绍最后一个布局——网格布局。该布局自Android 4.0开始应用,其中所有控件的位置是排列在一个指定的网格中的, GridLayout 布局使用虚细线将布局划分为行、列和单元格,也支持一个控件在行、列上都有交错排列,下面的例子介绍了该布局的使用方法,运行效果如图2-5所示。
图2-5 网格布局
(1)创建新项目Sample_2_5,在res/layout下的main.xml进行布局,其代码如下。
代码位置:见随书光盘中源代码/第2章/Sample_2_5/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="wrap_content"
4 android:layout_height="wrap_content"
5 android:columnCount="4" <!--表示网格布局有4列-->
6 android:orientation="horizontal" > <!--定义一个水平排列的网格布局 -->
7 <EditText
8 android:layout_columnSpan="3" <!--控件占3列 -->
9 android:layout_gravity="fill" > <!--控件充满3列 -->
10 <requestFocus />
11 </EditText> <!--添加一个EditText控件-->
12 <Button
13 android:layout_width="wrap_content"
14 android:layout_column="3" <!--控件在第4列 -->
15 android:layout_row="0"
16 android:text="清除" /> <!--添加一个Button控件 -->
17 <Button
18 android:layout_width="50dp"
19 android:text="1" /> <!--添加一个Button控件 -->
20 ……//该处省略了部分相似的代码,读者可自行查看随书光盘中源代码
21 <Button
22 android:layout_width="50dp"
23 android:text="-" /> <!--添加一个Button控件 -->
24 <Button
25 android:layout_width="50dp"
26 android:layout_column="0"
27 android:layout_row="4"
28 android:layout_columnSpan="2"
29 android:layout_gravity="fill"
30 android:text="0" /> <!--添加一个Button控件 -->
31 <Button
32 android:layout_width="50dp"
33 android:text="." /> <!--添加一个Button控件 -->
34 <Button
35 android:layout_rowSpan="2" <!--控件占两行 -->
36 android:layout_gravity="fill" <!--控件充满两行 -->
37 android:text="+" /> <!--添加一个Button控件 -->
38 <Button
39 android:layout_columnSpan="3" <!--控件占3列 -->
40 android:layout_gravity="fill" <!--控件充满3列 -->
41 android:text="=" /> <!--添加一个Button控件 -->
42 </GridLayout>
● 第2~6行定义了一个网格布局,设置该布局为水平排列、共4列。
● 第7~16行向网格布局添加一个占3列的EditText和一个名为“清除”的Button。
● 第17~23行向网格布局添加了12个Button,第24~41行向网格布局中添加了5个Button,其中,控件可以指定在网格布局的哪个网格,网格布局的行、列从0开始,也可以指定某个控件占几行或几列,如“0”或“+”。
(2)设置当前显示的用户界面为刚编写的布局界面,设置的方法与前面的例子相同,读者可查看随书光盘中源代码/第 2章/ Sample_2_5/src/com/bn/sample_2_5下的Sample_2_5.java文件。
2.1.2 常用控件及其事件处理
Android中提供了大量丰富多彩的常用控件,Android平台已经完整地实现了这些控件功能,开发人员只需简单几个程序语句调用或参数设置的语句即可用其构建完整的用户界面。
Android中控件的使用方法一般有两种,一种是在XML中配置,而另一种是在Java程序中直接调用。在游戏开发过程中,很少使用在XML中配置的方法,一般都是在Java源代码中直接使用。
常用的控件有TextView、ImageView、CheckBox、RadioButton、Button、ImageButton、EditText、ToggleButton、AnalogClock、DigitalClock等。这些控件的使用方法基本相同,接下来将通过一个简单的例子讲解控件的使用方法,以及Android平台的事件处理,具体开发步骤如下。
(1)创建一个名为Sample_2_6的新项目。
(2)打开main.xml布局文件,将其中的代码替换成下列代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_6/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!--定义一个垂直的线性布局并填满这个屏幕 -->
7 <TextView
8 android:id="@+id/textView"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:text="您没有单击任何按钮"
12 /> <!-- 先添加一个文本控件-->
13 <Button
14 android:id="@+id/button"
15 android:layout_width="wrap_content"
16 android:layout_height="wrap_content"
17 android:text="普通按钮"
18 > <!-- 添加一个普通按钮控件 -->
19 </Button>
20 <ImageButton
21 android:id="@+id/imageButton"
22 android:layout_width="wrap_content"
23 android:layout_height="wrap_content"
24 android:src="@drawable/img"
25 > <!-- 添加一个ImageButton按钮控件 -->
26 </ImageButton>
27 <ToggleButton
28 android:id="@+id/toggleButton"
29 android:layout_width="wrap_content"
30 android:layout_height="wrap_content"
31 > <!-- 添加一个ToggleButton按钮控件 -->
32 </ToggleButton>
33 </LinearLayout>
● 第2~5行定义了一个垂直的线性布局,宽高全部填满整个屏幕。
● 第7~12行向线性布局中添加一个文本控件,宽度填满父控件,高度自适应子控件,用于显示被单击的按钮。
● 之后依次向布局中添加3种按钮,每个控件的宽度和高度全部设置为自适应子控件,并指定其控件的ID,使Java程序可以找到该控件。
提示
该程序中用到了图片资源,在使用图片之前,应该先将用到的图片资源放到Sample_2_6/res/drawable-mdpi文件夹下。
(3)打开系统自动创建的Sample_2_6.java文件,将其内容改为如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_6/src/wyf/ytl目录下的Sample_2_6.java。
1 package wyf.ytl; //声明包语句
2 import android.app.Activity; //引入相关类
3 import android.os.Bundle; //引入相关类
4 import android.view.View; //引入相关类
5 import android.view.View.OnClickListener; //引入相关类
6 import android.widget.Button; //引入相关类
7 import android.widget.ImageButton; //引入相关类
8 import android.widget.TextView; //引入相关类
9 import android.widget.ToggleButton; //引入相关类
10 public class Sample_2_6 extends Activity implements OnClickListener{
11 Button button; //普通按钮
12 ImageButton imageButton; //图片按钮
13 ToggleButton toggleButton; //开关按钮
14 TextView textView; //文本控件
15 /** Called when the activity is first created. */
16 @Override
17 public void onCreate(Bundle savedInstanceState) { //回调方法
18 super.onCreate(savedInstanceState);
19 setContentView(R.layout.main); //设置显示的View
20 textView = (TextView) this.findViewById(R.id.textView);//得到文本控件的引用
21 button = (Button) this.findViewById(R.id.button);
22 toggleButton = (ToggleButton) this.findViewById(R.id.toggleButton);
23 imageButton = (ImageButton) this.findViewById(R.id.imageButton);
24 imageButton.setOnClickListener(this); //为imageButton添加监听器
25 button.setOnClickListener(this); //为button添加监听器
26 toggleButton.setOnClickListener(this); //为toggleButton添加监听器
27 }
28 public void onClick(View v) { //重写的事件处理回调方法
29 if(v == button){ //单击的是普通按钮
30 textView.setText("您单击的是普通按钮");
31 }
32 else if(v == imageButton){ //单击的是图片按钮
33 textView.setText("您单击的是图片按钮");
34 }
35 else if(v == toggleButton){ //单击的是开关按钮
36 textView.setText("您单击的是开关按钮");
37 }
38 }
39 }
● 第11~14行声明了3种按钮及文本控件的引用。
● 第19行是将main布局文件设置为当前显示的View。
● 第20~23行通过布局文件中控件的ID得到各个控件的引用。
● 第24~26行为各个控件添加监听。
● 第28~38行重写了事件监听器的回调方法。当单击不同控件时分别做不同的处理。
(4)运行本例子,将看到如图2-6所示的效果,当用户单击按钮时,文本的内容会随之改变。
图2-6 控件及事件监听
提示
在此通过3种按钮控件来讲解Android中控件的使用方法以及控件事件的处理方法, Android中各个控件的使用方法基本相同,读者可以模仿本例学习其他控件的使用。
2.2 图形与动画在Android中的实现
程序开发过程中,图形、动画是必不可少的,本节将详细讲解图形、动画在Android中的实现,通过对本节的学习,使读者能够构建出简单的图形用户界面。
2.2.1 简单图形的绘制
在Android游戏的开发中,时时都需要绘制图形、图片,接下来将通过一个绘制图形的例子,让读者理解Android中图形绘制的机理,其开发步骤如下。
(1)创建一个新项目Sample_2_7,然后在src/wyf/ytl目录下创建文件MyView.java,并在其中输入下面的代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_7/src/wyf/ytl目录下的MyView.java。
1 package wyf.ytl; //声明包语句
2 import android.content.Context; //引入相关类
3 import android.graphics.Canvas; //引入相关类
4 import android.graphics.Color; //引入相关类
5 import android.graphics.Paint; //引入相关类
6 import android.graphics.RectF; //引入相关类
7 import android.util.AttributeSet; //引入相关类
8 import android.view.View; //引入相关类
9 public class MyView extends View{
10 public MyView(Context context, AttributeSet attrs) { //构造器
11 super(context, attrs);
12 }
13 protected void onDraw(Canvas canvas) { //重写的绘制方法
14 super.onDraw(canvas);
15 canvas.drawColor(Color.BLACK); //绘制黑色背景
16 Paint paint = new Paint(); //创建画笔
17 paint.setColor(Color.RED); //设置画笔颜色为红色
18 canvas.drawRect(10, 10, 110, 110, paint); //绘制矩形
19 canvas.drawText("这是字符串", 10, 130, paint); //字符串,以字符串下面//为基准
20 RectF rf1 = new RectF(10, 130, 110, 230); //定义一个矩形
21 canvas.drawArc(rf1, 0, 45, true, paint); //画弧,顺时针
22 canvas.drawLine(150, 10, 250, 110, paint); //画线
23 RectF rf2 = new RectF(150, 130, 250, 230); //定义一个矩形
24 canvas.drawOval(rf2, paint); //画圆
25 }
26 }
● 第10~12行为该类的构造器,View下构造器有3种重载方式,如需在XML中配置应用该View,必须实现该构造器。
● 第13行重写负责绘制的onDraw方法,第15行绘制View的背景色为黑色,而第16~17行创建并设置画笔。
● 第18行向View中绘制矩形,drawRect的前4个参数分别为左上角的x、y和右下角的x、y。
● 第 19 行向 View 中绘制字符串,需要注意的是,之后的两个参数为字符串所占矩形的左下角x、y。
● 第20~21行先创建一个矩形对象,然后根据矩形对象的大小和位置绘制弧形,给出起始的角度和终止的角度。
● 第22行绘制直线,前4个参数为起始点的x、y以及终点的x、y。第23~24行同样创建一个矩形对象,并根据矩形绘制圆。
(2)将res/layout下的main.xml文件的代码改为如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_7/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!--添加一个垂直的线性布局,并填满屏幕-->
7 <wyf.ytl.MyView
8 android:id="@+id/myView"
9 android:layout_width="fill_parent"
10 android:layout_height="fill_parent"
11 /> <!-- 添加自定义的View到线性布局中-->
12 </LinearLayout>
说明
代码的第7~10行在垂直的线性布局中添加自定义的View,并为它指定ID,宽度和高度全部填充父控件。
(3)在模拟器中运行该项目,观察的效果如图2-7所示。
图2-7 简单的图形绘制
提示
在XML中配置自定义的View需要使用全称类名,在该例子中,也可以使用除线性布局之外其他布局,效果是一样的。
2.2.2 贴图的艺术
前面的小节介绍了在Android平台下如何绘制基本的图形,但是只有这些图形往往是不能满足应用开发需求的,接下来将通过一个贴图的小例子,使读者学会如何在Android平台下绘制图片,详细步骤如下。
(1)创建一个新项目,取名为Sample_2_8。
(2)将程序中用到的图片img.png放到Sample_2_8/res/drawable-mdpi文件夹下。
(3)在该项目的src下创建文件MyView.java,并输入如下代码。
代码位置:见随书光盘中源代码/第二章/Sample_2_8/src/wyf/ytl目录下的MyView.java。
1 package wyf.ytl; //声明包语句
2 import android.content.Context; //引入Context类
3 import android.graphics.Bitmap; //引入Bitmap类
4 import android.graphics.BitmapFactory; //引入BitmapFactory类
5 import android.graphics.Canvas; //引入Canvas类
6 import android.graphics.Color; //引入Color类
7 import android.graphics.Paint; //引入Paint类
8 import android.util.AttributeSet; //引入AttributeSet类
9 import android.view.View; //引入View类
10 public class MyView extends View{ //继承自View
11 Bitmap myBitmap; //图片的引用
12 Paint paint; //画笔的引用
13 public MyView(Context context, AttributeSet attrs) { //构造器
14 super(context, attrs);
15 this.initBitmap(); //调用初始化图片的方法
16 }
17 public void initBitmap(){
18 paint = new Paint(); //创建一个画笔
19 myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img);
20 }
21 protected void onDraw(Canvas canvas) { //重写的绘制方法
22 super.onDraw(canvas);
23 paint.setAntiAlias(true); //打开抗锯齿
24 paint.setColor(Color.WHITE); //设置画笔的颜色
25 paint.setTextSize(15);
26 canvas.drawBitmap(myBitmap, 10, 10, paint); //绘制图片
27 canvas.save(); //保存画布状态
28 Matrix m1=new Matrix(); //创建Matrix对象
29 m1.setTranslate(500, 10); //平移矩阵
30 Matrix m2=new Matrix(); //创建Matrix对象
31 m2.setRotate(15); //以一定的角度旋转矩阵
32 Matrix m3=new Matrix(); //创建Matrix对象
33 m3.setConcat(m1, m2);
34 m1.setScale(0.8f, 0.8f); //缩放矩阵
35 m2.setConcat(m3, m1);
36 canvas.drawBitmap(myBitmap, m2, paint); //绘制图片
37 canvas.restore(); //恢复画布状态
38 canvas.save(); //保存画布状态
39 paint.setAlpha(180); //设置透明度
40 m1.setTranslate(200,100); //平移矩阵
41 m2.setScale(1.3f, 1.3f); //缩放矩阵
42 m3.setConcat(m1, m2);
43 canvas.drawBitmap(myBitmap, m3, paint); //绘制图片
44 paint.reset(); //恢复画笔设置
45 canvas.restore(); //恢复画布
46 paint.setTextSize(40); //设置字体大小
47 paint.setColor(0xffFFFFFF); //设置画笔颜色
48 canvas.drawText("图片的宽度:"+myBitmap.getWidth(),20,220,paint);
49 canvas.drawText("图片的高度:"+myBitmap.getHeight(),150,220,paint);
50 paint.reset(); //恢复画笔设置
51 }
52 }
● 第10行自定义一个继承自View的子类。第13~17行为MyView的构造器,在构造器中调用初始化图片的方法来初始化需要用到的图片。
● 第18~19行创建一个画笔,获取图片资源。
● 第21行重写了onDraw方法,用于绘制View中需要显示的内容。
● 第23~24行打开抗锯齿功能,并设置画笔的颜色为白色,第25行设置字体的大小。
● 第 27~37 行首先保存画布状态,然后设置相应的平移、旋转以及缩放,调用 setConcat方法,将对应的两个Matrix对象连接起来并绘制,最后恢复画布状态。
● 第38~45行首先保存画布状态,然后调用setTranslate方法平移矩阵,调用setScale方法缩放矩阵,调用setConcat方法链接两个Matrix对象并绘制图片,最后恢复画布状态。
● 第46~50行首先设置字体的大小和画笔的颜色,然后绘制两个字符串“图片的宽度”以及“图片的高度”,最后恢复画笔设置。
(4)打开/res/layout下的main.xml文件,将其代码替换为如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_8/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!-- 定义一个线性布局 -->
7 <wyf.ytl.MyView
8 android:id="@+id/myView"
9 android:layout_width="fill_parent"
10 android:layout_height="fill_parent"
11 /> <!--将自定义的View添加到布局中 -->
12 </LinearLayout>
● 第2~6行定义一个垂直的线性布局,宽度和高度填满整个屏幕。
● 第7~11行向线性布局中添加一个自定义的View并填满父控件。
(5)运行该项目其效果如图2-8所示。
图2-8 贴图实例
2.2.3 自定义动画的播放
Android 中主要有两种动画模式,一种是 tweened animation(渐变动画),即通过对场景里的对象不断做图像变换产生动画效果;另一种是 frame by frame(帧动画),即按顺序播放事先配置好的动画帧。
渐变动画有4种动画类型,alpha(透明度)、scale(尺寸伸缩)、translate(位置变换)和rotate (图形旋转)。接下来通过一个动画播放的例子来介绍,如何在Android中构建并播放自己的动画,详细步骤如下。
(1)创建新项目Sample_2_9,然后将项目中用到的图片img.png放到res/drawable-mdpi下。
(2)在res目录中新建anim文件夹,如图2-9所示。
图2-9 anim文件夹
(3)在新建的anim文件夹中新建文件myanim.xml,如图2-10所示。
图2-10 myanim.xml文件
(4)打开myanim.xml文件,输入如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_9/res/anim目录下的myanim.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <set xmlns:android="http://schemas.android.com/apk/res/android">
3 <alpha
4 android:fromAlpha="0.1"
5 android:toAlpha="1.0"
6 android:duration="2000"
7 /> <!-- 透明度的变换 -->
8 <scale
9 android:interpolator= "@android:anim/accelerate_decelerate_interpolator"
10 android:fromXScale="0.0"
11 android:toXScale="1.4"
12 android:fromYScale="0.0"
13 android:toYScale="1.4"
14 android:pivotX="50%"
15 android:pivotY="50%"
16 android:fillAfter="false"
17 android:duration="3000"
18 /> <!-- 尺寸的变换 -->
19 <translate
20 android:fromXDelta="30"
21 android:toXDelta="0"
22 android:fromYDelta="30"
23 android:toYDelta="50"
24 android:duration="3000"
25 /> <!-- 位置的变换 -->
26 <rotate
27 android:interpolator="@android:anim/accelerate_decelerate_interpolator"
28 android:fromDegrees="0"
29 android:toDegrees="+350"
30 android:pivotX="50%"
31 android:pivotY="50%"
32 android:duration="3000"
33 /> <!-- 旋转变换 -->
34 </set>
● 第1行定义了XML的版本以及编码方式。第3~7行定义透明度控制动画效果, fromAlpha属性为动画起始时透明度,toAlpha属性为动画结束时的透明度,duration为动画持续的时间。
● 第8~18行定义了尺寸变换动画,interpolator指定一个动画的插入器,fromXScale属性为动画起始时x坐标上的伸缩尺寸,toXScale属性为动画结束时x坐标上的伸缩尺寸,fromYScale属性为动画起始时y坐标上的伸缩尺寸,toYScale属性为动画结束时y坐标上的伸缩尺寸,pivotX和pivotY设置动画相对于自身的位置,fillAfter表示动画的转换在动画结束后是否被应用。
● 第19~25行定义了位置的变换动画,fromXDelta属性为动画起始时x坐标上的位置, toXDelta 属性为动画结束时 x 坐标上的位置,fromYDelta 属性为动画起始时 y 坐标上的位置, toYDelta属性为动画结束时y坐标上的位置。
● 第26~33行定义了旋转动画,interpolator同样为一个动画的插入器,fromDegrees属性为动画起始时物件的角度,toDegrees属性为动画结束时物件旋转的角度。
(5)打开res/layout下的main.xml文件,将其中的代码改为如下代码。
代码位置:见随书光盘中源代码/第二章/Sample_2_9/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!-- 定义一个垂直的线性布局-->
7 <ImageView
8 android:id="@+id/myImageView"
9 android:layout_width="fill_parent"
10 android:layout_height="fill_parent"
11 android:src="@drawable/img"
12 /> <!--添加一个id为myImageView的ImageView控件-->
13 </LinearLayout>
● 第2~6行定义一个垂直的线性布局,且填满整个屏幕。
● 第7~12行添加一个id为myImageView的ImageView控件到线性布局中,并设置其填充方式为填满整个父控件。
(6)将src/wyf/ytl下的Sample_2_9.java文件代码改为如下代码。
代码位置:见随书光盘中源代码/第二章/Sample_2_9/src/wyf/ytl目录下的Sample_2_9.java。
1 package wyf.ytl; //声明包语句
2 import android.app.Activity; //引入Activity类
3 import android.os.Bundle; //引入Bundle类
4 import android.view.animation.Animation; //引入Animation类
5 import android.view.animation.AnimationUtils; //引入AnimationUtils类
6 import android.widget.ImageView; //引入ImageView类
7 public class Sample_2_9 extends Activity {
8 Animation myAnimation; //动画的引用
9 ImageView myImageView; //ImageView的引用
10 /** Called when the activity is first created. */
11 @Override
12 public void onCreate(Bundle savedInstanceState) { //重写的onCreate回调方法
13 super.onCreate(savedInstanceState);
14 setContentView(R.layout.main); //设置当前显示的View
15 myAnimation= AnimationUtils.loadAnimation(this,R.anim.myanim);//加载动画
16 myImageView =(ImageView)this.findViewById(R.id.myImageView);//得到ImageView的引用
17 myImageView.startAnimation(myAnimation); //启动动画
18 }
19 }
● 第8行声明一个动画的引用,第12行重写了onCreate回调方法,该方法会在Activity第一次被创建时被调用。
● 第15行加载之前编写的动画。
● 第16~17行得到main中ImageView的引用并对其使用动画。
(7)运行该项目,运行效果如图2-11、图2-12和图2-13所示。
图2-11 刚开始启动动画
图2-12 动画运行过程中
图2-13 动画结束后
2.3 Android平台下的多媒体开发
前面的小节对Android中图形与动画进行了介绍,可是只有图形界面并不能满足游戏开发的需求,音频、视频在游戏开发过程中往往也会用得上,本节将对Android平台下的多媒体开发进行介绍。
2.3.1 音频的播放
Android平台中关于音频的播放有两种方式,一种是SoundPool,另一种是 MediaPlayer。SoundPool 适合短促但对反应速度要求较高的情况(如游戏中的爆炸声),而MediaPlayer则适合较长但对时间要求不高的情况。读者可以根据具体需求自行选择,下面将通过一个音频播放的例子详细介绍这两种声音播放的方式,具体步骤如下。
(1)创建一个新的Android项目,起名为Sample_2_10。
(2)在res目录下创建名为raw的文件夹,然后将程序中所用到的声音资源全部放进该文件夹,如图2-14所示。
图2-14 raw文件夹
(3)打开res/layout下的main.xml,用下面的代码替代之前的代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_10/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/ apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!--定义一个垂直的线性布局 -->
7 <TextView
8 android:id="@+id/textView"
9 android:layout_width="fill_parent"
10 android:layout_height="wrap_content"
11 android:text="没有播放任何声音"
12 /> <!--向线性布局中添加一个TextView控件 -->
13 <Button
14 android:id="@+id/button1"
15 android:layout_width="wrap_content"
16 android:layout_height="wrap_content"
17 android:text="使用MediaPlayer播放声音"
18 /> <!-- 向线性布局中添加一个Button控件 -->
19 <Button
20 android:id="@+id/button2"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 android:text="暂停MediaPlayer声音"
24 /> <!-- 向线性布局中添加一个Button控件 -->
25 <Button
26 android:id="@+id/button3"
27 android:layout_width="wrap_content"
28 android:layout_height="wrap_content"
29 android:text="使用SoundPool播放声音"
30 /> <!-- 向线性布局中添加一个Button控件 -->
31 <Button
32 android:id="@+id/button4"
33 android:layout_width="wrap_content"
34 android:layout_height="wrap_content"
35 android:text="暂停SoundPool声音"
36 /> <!-- 向线性布局中添加一个Button控件 -->
37 </LinearLayout>
● 第2~6行定义一个垂直的线性布局,并将宽和高设置成自动填满父控件。
● 第7~12行向线性布局中添加一个TextView控件,并为其添加ID。
● 第13~36行向线性布局中添加4个按钮控件,并分别为其添加ID、设置按钮上显示的文字。
(4)打开src/wyf/ytl下的Sample_2_10.java文件,将其代码替换成下面的代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_10/src/wyf/ytl目录下的Sample_2_10.java。
1 package wyf.ytl; //声明包语句
2 import java.util.HashMap; //引入HashMap类
3 import android.app.Activity; //引入Activity类
4 import android.content.Context; //引入Context类
5 import android.media.AudioManager; //引入AudioManager类
6 import android.media.MediaPlayer; //引入MediaPlayer类
7 import android.media.SoundPool; //引入SoundPool类
8 import android.os.Bundle; //引入Bundle类
9 import android.view.View; //引入View类
10 import android.view.View.OnClickListener; //引入OnClickListener类
11 import android.widget.Button; //引入Button类
12 import android.widget.TextView; //引入TextView类
13 public class Sample_2_10 extends Activity implements OnClickListener{
14 Button button1; //四个按钮的引用
15 Button button2;
16 Button button3;
17 Button button4;
18 TextView textView; //TextView的引用
19 MediaPlayer mMediaPlayer; // MediaPlayer的引用
20 SoundPool soundPool; // SoundPool的引用
21 HashMap<Integer, Integer> soundPoolMap;
22 /** Called when the activity is first created. */
23 @Override
24 public void onCreate(Bundle savedInstanceState) { //重写onCreate回调方法
25 super.onCreate(savedInstanceState);
26 initSounds(); //初始化声音
27 setContentView(R.layout.main); //设置显示的用户界面
28 textView = (TextView) this.findViewById(R.id.textView);//得到TextView的引用
29 button1 = (Button) this.findViewById(R.id.button1);//得到button1的引用
30 button2 = (Button) this.findViewById(R.id.button2);//得到button2的引用
31 button3 = (Button) this.findViewById(R.id.button3);//得到button3的引用
32 button4 = (Button) this.findViewById(R.id.button4);//得到button4的引用
33 button1.setOnClickListener(this); //为按钮添加监听
34 button2.setOnClickListener(this); //为按钮添加监听
35 button3.setOnClickListener(this); //为按钮添加监听
36 button4.setOnClickListener(this); //为按钮添加监听
37 }
38 public void initSounds(){ //初始化声音的方法
39 mMediaPlayer = MediaPlayer.create(this, R.raw.backsound);//初始化MediaPlayer
40 soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
41 soundPoolMap = new HashMap<Integer, Integer>();
42 soundPoolMap.put(1, soundPool.load(this, R.raw.dingdong, 1));
43 }
44 public void playSound(int sound, int loop) { //用SoundPoll播放声音的方法
45 AudioManager mgr = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
46 float streamVolumeCurrent = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
47 float streamVolumeMax = mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
48 float volume = streamVolumeCurrent/streamVolumeMax;
49 soundPool.play(soundPoolMap.get(sound), volume, volume, 1, loop, 1f);//播放声音
50 }
51 public void onClick(View v) { //实现接口中的方法
52 // TODO Auto-generated method stub
53 if(v == button1){ //单击使用MediaPlayer播放声音按钮
54 textView.setText("使用MediaPlayer播放声音");
55 if(!mMediaPlayer.isPlaying()){ //如果当前没有音乐正在播放
56 mMediaPlayer.start(); //播放声音
57 }
58 }
59 else if(v == button2){ //单击了暂停MediaPlayer声音按钮
60 textView.setText("暂停了MediaPlayer播放的声音");
61 if(mMediaPlayer.isPlaying()){ //如果当前有音乐正在播放
62 mMediaPlayer.pause(); //暂停声音
63 }
64 }
65 else if(v == button3){ //单击了使用SoundPool播放声音按钮
66 textView.setText("使用SoundPool播放声音");
67 this.playSound(1, 0); //播放声音
68 }
69 else if(v == button4){ //单击了暂停SoundPool声音按钮
70 textView.setText("暂停了SoundPool播放的声音");
71 soundPool.pause(1); //暂停SoundPool的声音
72 }}}
● 第24行为onCreate回调方法的开始,该方法在Activity第一次创建时被调用。
● 第28~32行得到程序中会用到的TextView及Button的引用,第33~36行为各个按钮添加监听。
● 第38~43行初始化所有声音资源,使用SoundPool时一般将声音放进一个HashMap中,便于声音的管理与操作。
● 第44~50行为用SoundPool播放声音的方法,程序的其他地方需要播放声音时,只需调用该方法即可。在该方法中,先对声音设备进行配置,然后调用SoundPool的play方法来播放声音。
● 第 51~72 行是对各个按钮被按下的事件进行处理,其中第 53~58 行为单击“使用MediaPlayer播放声音”按钮的处理代码,先判断当前mMediaPlayer是否正在播放,当没有在播放时,播放声音。
提示
SoundPool 初始化的过程是异步的,也就是说,当对 SoundPool 初始化时系统会自动启动一个后台线程来完成初始化工作。因此,并不会影响前台其他程序的运行,但也带来一个问题,调用初始化操作后不能立即播放,需要等待一下,否则可能会出错。另外,SoundPool可以同时播放多个音频文件,但MediaPlayer同一时间却只能播放一个。
(5)运行该程序得到的效果如图2-15所示,根据单击按钮的不同采用不同的声音播放方式播放声音。
图2-15 声音播放示例
2.3.2 视频的播放
前面已经介绍了音频的播放,本节将对Android中视频的播放进行简单的介绍,同样是通过一个例子来讲解视频的播放方法,详细步骤如下。
(1)创建 sdcard镜像文件,创建方法是在命令行下输入mksdcard 512M C:\sdcard.img命令。
(2)启动Eclipse,然后依次单击“Window/Preferences”菜单,弹出如图2-16所示的Preferences窗口。
图2-16 Preferences窗口
(3)依次单击“Android/Launch”,然后在右侧的“Default emulator options”中填上“-sdcard C:\sdcard.img”,然后单击“OK”按钮,如图2-16所示。
(4)运行模拟器,然后将Eclipse切换到DDMS视图,找到File Explorer窗口,通过右上角的“Push a file onto the device”按钮向 sdcard中添加程序中用到的视频文件bbb.3gp,如图2-17所示。
图2-17 DDMS视图
(5)创建一个名为Sample_2_11的项目。
(6)打开res/layout下的main.xml文件,更改其代码为下面的代码。
代码位置:见随书光盘中源代码/第二章/Sample_2_11/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!--添加一个垂直的线性布局 -->
7 <SurfaceView
8 android:id="@+id/surfaceView"
9 android:layout_width="320px"
10 android:layout_height="200px"
11 /> <!--添加一个SurfaceView用于播放视频 -->
12 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
13 android:layout_width="fill_parent"
14 android:layout_height="wrap_content"
15 > <!--添加一个线性布局 -->
16 <Button
17 android:id="@+id/play2_Button"
18 android:layout_width="wrap_content"
19 android:layout_height="wrap_content"
20 android:text="播放"
21 /> <!--添加一个按钮 -->
22 <Button
23 android:id="@+id/pause2_Button"
24 android:layout_width="wrap_content"
25 android:layout_height="wrap_content"
26 android:text="暂停"
27 /> <!--添加一个按钮 -->
28 </LinearLayout>
29 </LinearLayout>
● 第7~11行向线性布局中添加一个SurfaceView控件。
● 第12~28行在一个线性布局中添加两个按钮。
(7)打开src/wyf/ytl下的Sample_2_11.java文件,改为如下代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_11/src/wyf/ytl目录下的Sample_2_11.java。
1 package wyf.ytl; //声明包语句
2 import android.app.Activity; //引入Activity类
3 ……//该处省略了部分类的引入代码,读者可自行查阅随书光盘中的源代码
4 import android.widget.Button; //引入Button类
5 public class Sample_2_11 extends Activity implements OnClickListener,SurfaceHolder.Callback{
6 String path = "/sdcard/bbb.3gp"; //视频的路径
7 Button play_Button; //按钮的引用
8 Button pause_Button;
9 boolean isPause = false;
10 SurfaceHolder surfaceHolder;
11 MediaPlayer mediaPlayer; //MediaPlayer的引用
12 SurfaceView surfaceView;
13 public void onCreate(Bundle savedInstanceState) { //重写的方法
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.main); //设置当前显示的用户界面
16 play_Button = (Button) findViewById(R.id.play2_Button);
17 play_Button.setOnClickListener(this); //添加监听
18 pause_Button = (Button) findViewById(R.id.pause2_Button);
19 pause_Button.setOnClickListener(this); //添加监听
20 getWindow().setFormat(PixelFormat.UNKNOWN);
21 surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
22 surfaceHolder = surfaceView.getHolder();
23 surfaceHolder.addCallback(this); //添加回调
24 surfaceHolder.setFixedSize(176,144);
25 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
26 mediaPlayer = new MediaPlayer(); //创建MediaPlayer
27 }
28 public void onClick(View v) {
29 if(v == play_Button){ //按下播放电影按钮
30 isPause = false;
31 playVideo(path);
32 }
33 else if(v == pause_Button){ //按下暂停按钮,
34 if(isPause == false){ //如果正在播放则将其暂停
35 mediaPlayer.pause();
36 isPause = true;
37 }
38 else{ //如果暂停中怎么继续播放
39 mediaPlayer.start();
40 isPause = false;
41 }
42 }
43 }
44 private void playVideo(String strPath){ //自定义播放影片函数
45 if(mediaPlayer.isPlaying()==true){
46 mediaPlayer.reset();
47 }
48 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
49 mediaPlayer.setDisplay(surfaceHolder); //设置Video影片以//SurfaceHolder播放
50 try{
51 mediaPlayer.setDataSource(strPath); //设置路径
52 mediaPlayer.prepare();
53 }
54 catch (Exception e){
55 e.printStackTrace(); //打印异常信息
56 }
57 mediaPlayer.start(); //开始播放视频
58 }
59 public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {}
60 public void surfaceCreated(SurfaceHolder arg0) {}
61 public void surfaceDestroyed(SurfaceHolder arg0) {}
62 }
● 第13~27行重写onCreate方法,在方法中得到所需要使用的控件的引用。
● 第28~43行为事件响应的方法,当单击“播放”按钮时播放视频,当单击“暂停”按钮时暂停播放视频。
● 第44~58行为自定义的视频播放方法,如果视频正在播放则重置视频,设置一些视频的参数后开始播放视频。
● 第59~61行为接口中方法的空实现。
(8)运行该项目可在模拟器中看到如图2-18所示的效果。
图2-18 视频播放
2.3.3 Camera图像采集
本节将介绍如何制作一个简单的相机,步骤如下。
(1)创建一个名为Sample_2_12的新项目。
(2)为照相添加权限,添加方法是在AndroidManifest.xml文件中的</manifest>之前加上<uses- permission android:name="android.permission.CAMERA"/>即可。
(3)打开res/layout下的main.xml文件,更改其代码为下面的代码。
代码位置:见随书光盘中源代码/第2章/Sample_2_12/res/layout目录下的main.xml。
1 <?xml version="1.0" encoding="utf-8"?> <!-- XML的版本以及编码方式 -->
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent"
6 > <!--添加一个垂直的线性布局 -->
7 <SurfaceView
8 android:id="@+id/surfaceView"
9 android:layout_width="320px"
10 android:layout_height="240px"
11 /> <!--添加一个SurfaceView用于浏览 -->
12 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
13 android:layout_width="fill_parent"
14 android:layout_height="wrap_content"
15 > <!--添加一个线性布局 -->
16 <Button
17 android:id="@+id/button1"
18 android:layout_width="wrap_content"
19 android:layout_height="wrap_content"
20 android:text="打开"
21 /> <!--添加一个按钮 -->
22 <Button
23 android:id="@+id/button2"
24 android:layout_width="wrap_content"
25 android:layout_height="wrap_content"
26 android:text="关闭"
27 /> <!--添加一个按钮 -->
28 </LinearLayout>
29 </LinearLayout>
● 第2~6行定义一个垂直的线性布局。
● 第12行向垂直的线性布局内部添加一个线性布局。
● 第16~27向线性布局中添加两个按钮。
(4)打开src/wyf/ytl下的Sample_2_12.java文件,改为如下代码。
代码位置:见随书光盘中源代码/第二章/Sample_2_12/src/wyf/ytl目录下的Sample_2_12.java。
1 package wyf.ytl;
2 import java.io.IOException; //引入相关类
3 import android.app.Activity; //引入相关类
4 import android.hardware.Camera; //引入相关类
5 import android.os.Bundle; //引入相关类
6 import android.view.SurfaceHolder; //引入相关类
7 import android.view.SurfaceView; //引入相关类
8 import android.view.View; //引入相关类
9 import android.widget.Button; //引入相关类
10 public class Sample_2_12 extends Activity implements SurfaceHolder.Callback{
11 Camera myCamera; //Camera的引用
12 SurfaceView mySurfaceView; //SurfaceView的引用
13 SurfaceHolder mySurfaceHolder; //SurfaceHolder的引用
14 Button button1; //按钮的引用
15 Button button2;
16 boolean isPreview = false; //是否在浏览中
17 public void onCreate(Bundle savedInstanceState) { //重写的onCreate方法
18 super.onCreate(savedInstanceState);
19 setContentView(R.layout.main); //设置显示的用户界面
20 mySurfaceView = (SurfaceView) findViewById(R.id.surfaceView);//得到SurfaceView的引用
21 button1 = (Button) findViewById(R.id.button1);
22 button2 = (Button) findViewById(R.id.button2); //得到两个按钮的应用
23 mySurfaceHolder = mySurfaceView.getHolder(); //获得SurfaceHolder
24 mySurfaceHolder.addCallback(this);
25 mySurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
26 button1.setOnClickListener(new Button.OnClickListener(){ //打开的按钮监听
27 public void onClick(View arg0) { //单击事件
28 initCamera(); //调用初始化方法
29 }
30 });
31 button2.setOnClickListener(new Button.OnClickListener(){//关闭的按钮监听
32 public void onClick(View arg0) {
33 if(myCamera != null && isPreview){ //正在显示时
34 myCamera.stopPreview();
35 myCamera.release(); //释放myCamera
36 myCamera = null;
37 isPreview = false; //设成false
38 }
39 }
40 });
41 }
42 public void initCamera(){ //初始化相机资源
43 if(!isPreview){
44 myCamera = Camera.open(); //打开Camera
45 }
46 if(myCamera != null && !isPreview){
47 try {
48 myCamera.setPreviewDisplay(mySurfaceHolder);
49 myCamera.startPreview(); //立即运行Preview
50 } catch (IOException e) {
51 e.printStackTrace(); //打印错误信息
52 }
53 isPreview = true; //将标记位设置成true
54 }
55 }
56 public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}
57 public void surfaceCreated(SurfaceHolder holder) {//实现接口中的方法}
58 public void surfaceDestroyed(SurfaceHolder holder) {//实现接口中的方法}
59 }
● 第17~41行为重写的onCreate回调方法。
● 第20~22行得到所需要的控件的引用。
● 第23~25行得到SurfaceHolder并对其进行类型设置。
● 第26~41行为按钮添加监听器。
● 第44行打开Camera,所调用的open方法的返回值是一个Camera的对象。
● 第48~49行将Preiew画面呈现在SurfaceView里。
(5)运行该项目,在模拟器中得到的效果如图2-19所示。
图2-19 图像采集
2.4 小结
本章对Android游戏开发的前台渲染技术进行了详细介绍,通过本章的学习,读者应该能够构建出简单的图形界面。读者可以通过调试和编写本章的示例程序加深对前台渲染技术的理解,从而熟练掌握Android中图形的绘制与声音的播放。