本节书摘来自异步社区《Android 应用案例开发大全(第二版)》一书中的第2章,第2.4节壁纸的实现,作者 吴亚峰 , 于复兴 , 杜化美,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.4 壁纸的实现
Android 应用案例开发大全(第二版)
上一节介绍了壁纸的框架,让读者对壁纸的整体框架有了初步认识,本节将要对壁纸实现服务类GLWallpaperService、动态壁纸类LiveWallpaper、自定义渲染器类TDRender的开发进行详细介绍。
2.4.1 壁纸服务类——GLWallpaperService
该类是本项目中最基础的一个类,没有这个类就不可能使用壁纸这个功能。这个类为开发人员提供了壁纸服务,开发人员可以通过继承该类,重写此类中的方法来实现壁纸的后续开发。本小节只对该类中的两个地方进行介绍,一个是setRender方法,另一个是触控的响应事件onTouchEvent。
(1)首先对setRender方法进行介绍,具体代码如下所示。
1 public void setRenderer(Renderer renderer) {
2 checkRenderThreadState(); // 检查线程是否启动
3 if (mEGLConfigChooser == null) { // 创建EGLConfigChooser
4 mEGLConfigChooser = new SimpleEGLConfigChooser(true);
5 }
6 if (mEGLContextFactory == null) { // 创建EGLContextFactory
7 mEGLContextFactory = new DefaultContextFactory();
8 }
9 if (mEGLWindowSurfaceFactory == null) { // 创建EGLWindowSurfaceFactory
10 mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
11 }
12 mGLThread = new GLThread(renderer, mEGLConfigChooser,
13 mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper);
14 mGLThread.start(); // 启动线程
15 this.renderer=(TDRender) renderer; // 设置渲染器
16 }
第2~8行为检查渲染器线程是否启动,然后判断是否已经创建了EGLConfigChooser对象,如果没有则创建,再判断是否已创建EGLContextFactory对象,同样如果没有则创建。
第9~16行先判断是否已经创建了EGLWindowSurfaceFactory对象,如果没有则创建。然后启动渲染线程,再将渲染器设置为自定义的渲染器。
(2)下面将对屏幕触控的相应事件进行介绍,屏幕的触控事件分为3部分:第一部分是滑动屏幕使背景图跟着屏幕移动,第二部分是点击屏幕下方修改标志位,最后一部分是手指抬起时判断是否进行喂食,具体代码如下所示。
1 private float mPreviousX; // 上次的触控位置_y_坐标
2 private TDRender renderer; // 场景渲染器
3 @Override
4 public void onTouchEvent(MotionEvent e) {
5 float y = e.getY(); // 获得触控点的_y_坐标
6 float x = e.getX(); // 获得触控点的_x_坐标
7 switch (e.getAction()) {
8 case MotionEvent.ACTION_DOWN:
9 Constant.feeding = true; // 将喂食标志位设为true
10 break;
11 case MotionEvent.ACTION_MOVE:
12 float dy = y - mPreviousY; // 计算触控笔_y_位移
13 float dx = x - mPreviousX; // 计算触控笔_y_位移
14 if (dx < 0){ // 触摸左边_x_为正,触摸右边_x_为负
15 if (Constant.CameraX < Constant.MaxCameraMove) {// 摄像机移动最大值
16 if(dx<- Constant.Thold){
17 Constant.feeding = false; // 喂食标志位
18 }
19 Constant.CameraX = Constant.cameraX - dx / Constant.Camera
ove_SCALE ;
20 Constant.TargetX=Constant.CameraX;
21 }} else {
22 if (Constant.CameraX < -Constant.MaxCameraMove {// 摄像机移动最大值
23 if(dx> Constant.Thold){
24 Constant.feeding = false; // 喂食标志位
25 }
26 Constant.CmeraX = Constant.CameraX - dx / Constant.CameraMove
_SCALE ;
27 Constant.TargetX=Constant.CameraX;
28 }}
29 MatrixUtil.setCamera ( // 将摄像机的位置信息存入到矩阵中
30 Constant.CameraX, // 摄像机_x_位置
31 Constant.CameraY, // 摄像机_y_位置
32 Constant.CameraZ, // 摄像机_y_位置
33 Constant.TargetX, // 观测点的_x_位置
34 Constant.TargetY, // 观测点的_y_位置
35 Constant.TargetZ, // 观测点的_z_位置
36 Constant.UpX, // UP向量的x分量
37 Constant.UpY, // UP向量的y分量
38 Constant.UpZ); // UP向量的z分量
39 break;
40 case MotionEvent.ACTION_UP:
41 if (Constant.feeding) { // 标志位,如果开始喂食
42 if (Constant.isFeed) {
43 Constant.isFeed = false; // 把标志位置为false
44 Vector3f[]AB=IntersectantUtil
45 .calculateABPosition( // 通过变换获得世界坐标系中的点
46 x, // 触控点_x_坐标
47 y, // 触控点_y_坐标
48 Constant.SCREEN_WIDTH,// 屏幕宽度
49 Constant.SCREEN_HEGHT, // 屏幕长度
50 Constant.leftABS, // 视角left绝对值
51 Constant.topABS, // 视角top绝对值
52 Constant.nearABS, // 视角near值
53 Constant.farABS); // 视角far值
54 Vector3f Start = AB[0]; // 获得拾取后世界坐标系中被触控到的近平面的点
55 Vector3f End = AB[1];// 获得拾取后世界坐标系中被触控到的远平面的点
56 if (renderer.feedFish != null) { // 判断不为空,则开始喂食
57 renderer.feedFish.startFeed(Start, End); // 开始喂食
58 }}}
59 requestRender(); // 重绘画面
60 break;
61 }
62 mPreviousX = x; // 记录触控笔位置
63 super.onTouchEvent(e);
64 }}
第1~10行定义上次触控点的x、y坐标,创建渲染器对象,获得触控点的x坐标和y坐标,对ACTION_DOWN事件进行监听,当触发时将喂食标志位设为true。
第11~21行获得手指在屏幕上的触控点,从而得到手指在屏幕上的移动距离,然后按照一定比例移动摄像机x坐标,同时,如果摄像机x坐标达到阈值,则摄像机不会向滑动方向移动。
第22~32行将摄像机的位置信息存入到矩阵中,设置摄像机位置的坐标、观测点的坐标和up向量,最后再次记录触控笔的x、y位置。
第33~46行为判断喂食的标志位,喂食有两个标志位,一个是在点击喂食的时候为true,另一个是在没有喂食之前为true,因为滑动屏幕不能喂食,当前喂的食物在没有消失之前也不能喂食,所以,用了两个标志位对其进行控制。然后通过矩阵变化获取触控点的世界坐标系坐标。
第47~57行通过拾取计算得到触控点在世界坐标系中的起点(近平面点)、终点(远平面点)坐标,并且开始进行喂食。然后重绘画面,记录触控笔的位置,回调父类的方法。
2.4.2 动态壁纸类——LiveWallpaper
只有壁纸的服务类还远远不够,还要新建一个类继承自壁纸服务类,然后重写壁纸服务类的onCreateEngine方法才行,具体代码如下所示。
1 package com.bn.ld.wallpaper;
2 import com.bn.gl.GLWallpaperService;
3 public class LiveWallpaper extends GLWallpaperService{
4 private TDRender renderer; // 场景渲染器
5 @Override
6 public Engine onCreateEngine() { // 重写onCreateEngine方法
7 renderer=new TDRender(this);
8 return new GLEngine() { {
9 setRenderer(renderer); // 设置渲染器
10 setRenderMode(RENDERMODE_CONTINUOUSLY); // 设置主动渲染
11 }};}}
说明
定义渲染器类,继承自GLWallpaperService类 ,创建私有的场景渲染器对象。然后重写onCreateEngine方法,将渲染器设置为自定义的渲染器,并且把渲染器的渲染模式设置为主动渲染。
2.4.3 自定义渲染器类——TDRender
下面将介绍自定义的渲染器代码,在自定义的渲染器类里,可以进行鱼、鱼群、气泡、水草、背景图、石头、鱼食的绘制和初始化纹理等。
(1)由于该类中绘制代码比较多,在此就先介绍整个渲染器的框架,具体代码如下所示。
1 package com.bn.ld.wallpaper;
2 ……//此处省略部分类和包的引入代码,读者可自行查阅光盘的源代码
3 public class TDRender extends GLSurfaceView implements GLSurfaceView.Renderer,
4 GLWallpaperService.Renderer {
5 public TDRender(Context context) {
6 super(context); // 获得上下文对象
7 }
8 ……//此处省略相关成员变量的声明代码,读者可自行查阅光盘的源代码
9 public void onDrawFrame(GL10 gl) {
10 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁
11 gl.glShadeModel(GL10.GL_SMOOTH); // 设置着色模型为平滑着色
12 gl.glFrontFace(GL10.GL_CCW); // 设置为默认卷绕顺序逆时针
13 gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
14 GL10.GL_DEPTH_BUFFER_BIT); // 清除缓存
15 gl.glMatrixMode(GL10.GL_MODELVIEW); // 设置当前矩阵为模式矩阵
16 gl.glLoadIdentity(); // 设置当前矩阵为单位矩阵
17 GLU.gluLookAt( // 设置摄像机
18 gl,
19 Constant.CameraX, // 摄像机_x_位置
20 Constant.CameraY, // 摄像机_y_位置
21 Constant.CameraZ, // 摄像机_z_位置
22 Constant.TargetX, // 观测点的_x_位置
23 Constant.TargetY, // 观测点的_y_位置
24 Constant.TargetZ, // 观测点的_z_位置
25 Constant.UpX, // UP向量x分量
26 Constant.UpY, // UP向量y分量
27 Constant.UpZ); // UP向量z分量
28 ……此处代码是绘制代码,将在后面给出
29 }
30 public void onSurfaceChanged(GL10 gl, int width, int height) {
31 gl.glViewport(0, 0, width, height); // 设置视窗大小及位置
32 gl.glMatrixMode(GL10.GL_PROJECTION); // 设置当前矩阵为投影矩阵
33 gl.glLoadIdentity(); // 设置当前矩阵为单位矩阵
34 float ratio = (float) width / height; // 计算透视投影的比例
35 Constant.SCREEN_HEGHT=height; // 获取手机的高
36 Constant.SCREEN_WIDTH=width; // 获取手机的宽
37 Constant.leftABS=ratio*Constant.View_SCALE; // 透视投影的left绝对值
38 Constant.topABS=1 * Constant.View_SCALE; // 透视投影的top绝对
39 Constant.SCREEN_SCALEX=Constant.View_SCALE*((ratio>1)?ratio:(1/ratio));
40 gl.glFrustumf(-Constant.leftABS, Constant.leftABS, -Constant.topABS, // 产生透视投影矩阵
41 Constant.topABS, Constant.nearABS,Constant.farABS);
42 MatrixUtil.setCamera ( // 将摄像机的位置信息存入到矩阵中
43 Constant.CameraX, // 摄像机_x_位置
44 Constant.CameraY, // 摄像机_y_位置
45 Constant.CameraZ, // 摄像机_z_位置
46 Constant.TargetX, // 观测点的_x_位置
47 Constant.TargetY, // 观测点的_y_位置
48 Constant.TargetZ, // 观测点的_z_位置
49 Constant.UpX, // UP向量的x分量
50 Constant.UpY, // UP向量的y分量
51 Constant.UpZ); // UP向量的z分量
52 if (backgrounds == null) {
53 backgrounds = new BackGround(); // 初始化背景图
54 }}
55 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
56 gl.glDisable(GL10.GL_DITHER); // 关闭抗抖动
57 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
58 GL10.GL_FASTEST); // 快速模式
59 gl.glClearColor(0, 0, 0, 0); // 设置屏幕背景色黑色RGBA
60 gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度测试
61 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁
62 ……此处代码是加载图片、OBJ文件、创建各种对象,将在后面介绍
63 }}
第1~8行为部分类和包的引入代码、相关成员变量的声明,这些代码在此处省略,读者可自行查阅光盘代码。创建构造器并获得上下文对象。
第9~29行为进行绘制时的各种设置,如打开背面剪裁、设置平滑着色、设置默认卷绕方式、设置模式矩阵和单位矩阵,最后设置摄像机位置,初始化背景图对象。
第30~54行重写onSurfaceChanged方法设置视口的大小,将当前矩阵设为投影矩阵、设置单位矩阵、产生透视投影矩阵、并且生成摄像机观察矩阵。
第55~63行重写onSurfaceCreated方法。关闭抗抖动、设置Hint模式为快速模式、设置屏幕背景颜色、启用深度检测、打开背面剪裁。
(2)下面开始对本案例中鱼类、气泡、鱼食、石头等对象的创建以及本案例中用到的纹理ID的初始化进行详细介绍,具体代码如下所示。
1 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
2 gl.glDisable(GL10.GL_DITHER); // 关闭抗抖动
3 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
4 GL10.GL_FASTEST); // 快速模式
5 gl.glClearColor(0, 0, 0, 0); // 设置屏幕背景色
6 gl.glEnable(GL10.GL_DEPTH_TEST); // 启用深度测试
7 gl.glEnable(GL10.GL_CULL_FACE);
8 if (fishAl.size() == 0) {
9 bubbles = initTexture(gl, "bubble.png"); // 气泡的纹理ID
10 fish0 = initTexture(gl, "fish0.png"); // 鱼的纹理ID
11 fish1 = initTexture(gl, "fish1.png");
12 fish2 = initTexture(gl, "fish2.png");
13 fish3 = initTexture(gl, "fish3.png");
14 fish4 = initTexture(gl, "fish4.png");
15 fish5 = initTexture(gl, "fish5.png");
16 fishfood = initTexture(gl, "fishfood.png"); // 鱼食的纹理ID
17 waterweeds = initTexture(gl, "waterweeds.png");// 水草的纹理ID
18 background = initTexture(gl, "background.png");// 背景的纹理ID
19 stone=initTexture(gl, "stone.png"); // 石头的纹理
20 fishAl.add(new SingleFish(fish1, TDRender.this, "fish1.obj", // 将单个鱼加入鱼类列表
21 new Vector3f(-1, 2, 0), // 初始位置
22 new Vector3f(-0.02f, -0.02f, 0.00f), new Vector3f(0, 0, 0), // 初始速度和外力
23 new Vector3f(0, 0, 0), 70)); // 初始吸引力和鱼的质量
24 fishAl.add(new SingleFish(fish0, TDRender.this, "fish0.obj",
25 new Vector3f(-7, 5, 0), // 初始位置
26 new Vector3f(-0.04f, 0.01f, -0.04f),new Vector3f(0, 0, 0), // 初始速度和外力
27 new Vector3f(0, 0, 0), 150)); // 初始吸引力和鱼的质量
28 fishAl.add(new SingleFish(fish2, TDRender.this, "fish2.obj",
29 new Vector3f(-0, 3, 0), // 初始位置
30 new Vector3f(0.02f, 0.01f, -0.01f),new Vector3f(0, 0, 0), // 初始速度和外力
31 new Vector3f(0, 0, 0), 70)); // 初始吸引力和鱼的质量
32 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj",
33 new Vector3f(-1, 0, 0), // 初始位置
34 new Vector3f(-0.03f, 0.02f, -0.02f), new Vector3f(0, 0, 0), // 初始速度和外力
35 new Vector3f(0, 0, 0), 90)); // 初始吸引力和鱼的质量
36 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj",
37 new Vector3f(-5, 0, 0), // 初始位置
38 new Vector3f(-0.02f, 0.03f, -0.02f), new Vector3f(0, 0, 0), // 初始速度和外力
39 new Vector3f(0, 0, 0), 50)); // 初始吸引力和鱼的质量
40 fishAl.add(new SingleFish(fish4, TDRender.this, "fish4.obj",
41 new Vector3f(-5, 3, 0), // 初始位置
42 new Vector3f(-0.01f, 0.02f, -0.04f), new Vector3f(0, 0, 0), // 初始速度和外力
43 new Vector3f(0, 0, 0), 60)); // 初始吸引力和鱼的质量
44 fishAl.add(new SingleFish(fish5, TDRender.this, "fish5.obj",
45 new Vector3f(1, -3, 0), // 初始位置
46 new Vector3f(0.03f, 0.01f, -0.03f),new Vector3f(0, 0, 0f), // 初始速度和外力
47 new Vector3f(0, 0, 0), 80)); // 初始吸引力和鱼的质量
48 fishAl.add(new SingleFish(fish5, TDRender.this, "fish5.obj",
49 new Vector3f(-4, -1, 0), // 初始位置
50 new Vector3f(0.045f, 0.02f, -0.05f),new Vector3f(0, 0, 0f), // 初始速度和外力
51 new Vector3f(0, 0, 0), 40)); // 初始吸引力和鱼的质量
52 }
53 if (waterweeds1 == null) { // 创建水草类对象
54 waterweeds1 = LoadUtil.loadFromFileVertexOnly("waterweeds.obj",
55 this.getResources()); // 加载水草模型
56 }
57 if(stones==null){ // 创建石头类对象
58 stones=LoadUtil.loadFromFileVertexOnly("stone.obj",
59 this.getResources()); // 加载石头模型
60 }
61 if (fishfoods == null) { // 创建鱼食对象
62 fishfoods = LoadUtil.loadFromFileVertexOnly("fishfood.obj",
63 this.getResources()); // 加载鱼食模型
64 }
65 if (bubbleControl == null) { // 创建气泡的Control对象
66 bubbleControl = new BubbleControl(bubbles);
67 }
68 if (fishControl == null) { // 创建鱼类的Control对象
69 fishControl = new FishControl(fishAl, TDRender.this);
70 }
71 if (fishSchool == null) { // 创建鱼群的Control
72 fishSchool = new FishSchoolControl(fish3, TDRender.this);
73 }
74 if (singleFood == null) { // 食物类
75 singleFood = new FeedFish(fishfoods, TDRender.this);
76 }
77 if (feedFish == null) { // 创建控制喂食的对象
78 feedFish = new SingleFood(TDRender.this);
79 }}
第2~7行为关闭抗抖动,将Hint的模式设置为快速模式,设置屏幕背景颜色为黑色,启用深度检测,打开背面剪裁。
第8~52行获得图片的纹理ID ,对单个鱼进行初始化设置鱼的初始位置、初始速度、受到的外力和吸引力,以及鱼的质量(力的缩放比)。
第53~79行创建背景图、水草、石头、气泡、鱼食、群鱼、鱼群对象。并且只有当这些对象为空的时候才会对这些对象进行实例化。
(3)下面开始对3D水族馆动态壁纸案例中各个对象的绘制进行介绍,绘制代码在onDrawFrame方法中,具体代码如下所示。
1 public void onDrawFrame(GL10 gl) {
2 gl.glEnable(GL10.GL_CULL_FACE); // 设置为打开背面剪裁
3 gl.glShadeModel(GL10.GL_SMOOTH); // 设置着色模型为平滑着色
4 gl.glFrontFace(GL10.GL_CCW); // 设置为默认卷绕顺序逆时针为正
5 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // 清除颜色缓存
6 gl.glMatrixMode(GL10.GL_MODELVIEW); // 设置当前矩阵为模式矩阵
7 gl.glLoadIdentity(); // 设置矩阵为单位矩阵
8 GLU.gluLookAt( gl, // 设置摄像机
9 Constant.CameraX, // 摄像机_x_位置
10 Constant.CameraY, // 摄像机_y_位置
11 Constant.CameraZ, // 摄像机_z_位置
12 Constant.TargetX, // 观测点的_x_位置
13 Constant.TargetY, // 观测点的_y_位置
14 Constant.TargetZ, // 观测点的_z_位置
15 Constant.UpX, // UP向量x分量
16 Constant.UpY, // UP向量y分量
17 Constant.UpZ); // UP向量z分量
18 gl.glPushMatrix(); // 保护矩阵
19 if (backgrounds != null) { // 背景图不为空就绘制
20 backgrounds.drawSelf(gl,background); // 绘制背景图
21 }
22 gl.glPopMatrix(); // 恢复矩阵
23 gl.glPushMatrix(); // 保护矩阵
24 if (waterweeds1 != null&&stones!=null) { // 如果没有绘制过水草和石头则绘制
25 gl.glPushMatrix(); // 左后边的水草、石头
26 gl.glTranslatef(-17, -5.5f,-14); // 平移坐标系
27 stones.drawSelf(gl,stone); // 绘制石头
28 gl.glTranslatef(1.5f, 0, 1); // 平移坐标系
29 waterweeds1.drawSelf(gl,waterweeds); // 绘制水草
30 gl.glTranslatef(2, 0, 1); // 平移坐标系
31 gl.glPushMatrix(); // 保护矩阵
32 gl.glScalef(1.5f, 1.5f, 1.5f); // 设置缩放
33 stones.drawSelf(gl,stone); // 绘制石头
34 gl.glPopMatrix(); // 恢复矩阵
35 ……下面的水草和石头的绘制和上面的类似就不再给出程序,读者可自行查看源代码
36 }
37 gl.glPopMatrix(); // 恢复矩阵
38 gl.glPushMatrix(); // 保护矩阵
39 if (fishControl != null) {
40 fishControl.drawSelf(gl); // 绘制鱼
41 }
42 if (fishSchool != null) {
43 fishSchool.drawSelf(gl); // 绘制鱼群
44 }
45 gl.glPopMatrix(); // 恢复矩阵
46 gl.glPushMatrix(); // 保护矩阵
47 if (singleFood != null) {
48 singleFood.drawSelf(gl); // 绘制鱼食
49 }
50 gl.glPopMatrix(); // 恢复矩阵
51 gl.glPushMatrix(); // 保护矩阵
52 gl.glEnable(GL10.GL_BLEND); // 开启混合
53 gl.glBlendFunc(GL10.GL_SRC_ALPHA,
54 GL10.GL_ONE_MINUS_SRC_ALPHA); // 设置源混合因子与目标混合因子
55 gl.glTranslatef(0, 0, 15f); // 平移
56 if (bubbleControl != null) {
57 bubbleControl.drawSelf(gl); // 绘制气泡
58 }
59 gl.glDisable(GL10.GL_BLEND); // 关闭混合
60 gl.glPopMatrix(); // 恢复矩阵
61 }
第1~17行设置背面剪裁、卷绕方式、平滑着色、清除颜色缓存和深度缓存,设置当前矩阵为模式矩阵,设置当前矩阵为单位矩阵,设置摄像机位置。
第18~36行绘制背景图、水草和石头。由于水草和石头要重复绘制很多次,在此就不一一列举了,只给了出其中的部分代码,其余代码读者可自行查看光盘中的源代码。
第37~61行绘制群鱼、鱼群、鱼食和气泡。因为气泡是半透明的,所以要开启混合(绘制气泡时要根据气泡的位置对气泡进行排序),并且要将气泡在最后绘制,最后不要忘了关闭混合。
(4)最后将对初始化纹理的initTexture方法进行介绍,具体代码如下所示。
1 public int initTexture(GL10 gl, String pname){ // 初始化纹理
2 int[] textures = new int[1]; // 纹理ID
3 gl.glGenTextures(1, textures, 0); // 生成纹理ID
4 int currTextureId = textures[0];
5 gl.glBindTexture(GL10.GL_TEXTURE_2D, currTextureId); // 绑定纹理
6 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
7 GL10.GL_NEAREST); // 最近点采样
8 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
9 GL10.GL_LINEAR); // 线性纹理过滤
10 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
11 GL10.GL_CLAMP_TO_EDGE); // 纹理的横向拉伸方式
12 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
13 GL10.GL_CLAMP_TO_EDGE); // 纹理的纵向拉伸方式
14 InputStream in = null; // 创建输入流
15 try {
16 in = this.getResources().getAssets().open(pname); // 加载纹理图片
17 } catch (IOException e1) {
18 e1.printStackTrace(); // 异常处理
19 }
20 Bitmap bitmapTmp; // 创建bitmap对象
21 try {
22 bitmapTmp = BitmapFactory.decodeStream(in); // 对获取的图片解码
23 } finally {
24 try {
25 in.close(); // 关闭输入流
26 } catch (IOException e) {
27 e.printStackTrace(); // 异常处理
28 }}
29 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmapTmp, 0); // 指定纹理
30 bitmapTmp.recycle(); // 释放bitmap
31 return currTextureId;
32 }
第2~13行定义纹理ID、生成纹理ID、绑定纹理、设置纹理的过滤方式分为最近点采样和线性纹理过滤,设置纹理的拉伸方式分为横向拉伸方式和纵向拉伸方式。
第14~32行创建输入流,读取纹理图片,进行异常处理。创建bitmap对象,对获取的图片进行解码,关闭输入流,异常处理,最后释放bitmap。