本文是一个android动态壁纸的例子,利用android_ndk调用底层的C++代码,使用OpenGLES来绘制动态壁纸。仅作参考。

首先是定义我们自己的Renderer类,FireWallpaperRenderer实现了GLWallpaperService.Renderer接口(GLWallpaperService的代码在《android利用OpenGLES开发动态壁纸用到的GLWallpaperService类》的那篇博客里

import java.io.IOException;import java.io.InputStream;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.opengl.GLUtils;import android.util.Log;public class FireWallpaperRenderer implements GLWallpaperService.Renderer {//用于纹理映射的绑定,并把绑定后的地址传递给C++代码,供其调用private int[] texture = new int[2];//用于加载Bitmap的contextprivate Context mContext;//FireWallpaperRenderer构造函数,用来初始化mContextpublic FireWallpaperRenderer(Context context){mContext = context;}/** * 渲染场景的代码,这里我们是通过调用底层C++的代码来实现的, * 这样能得到更好地运行速度 * */@Overridepublic void onDrawFrame(GL10 gl) {//调用本地onDrawFrame方法FireNativeMethod.onDrawFrame(gl);}/** * 处理屏幕尺寸发生变化时的代码, * 用来重新设置场景的大小和其他一些属性, * 也是通过调用底层C++代码来实现 * */@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {//调用本地onSurfaceChanged方法FireNativeMethod.onSurfaceChanged(gl, width, height);}/** * 初始化OpenGL场景, * 用来设置场景的一些属性, * 也是通过调用底层C++代码来实现 * */@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {//用来绑定Bitmap纹理bindTexture(gl, mContext);//调用本地setTexture方法,把纹理绑定的地址传递给C++代码,以供其调用FireNativeMethod.setTexture(texture);//调用本地onSurfaceCreated方法FireNativeMethod.onSurfaceCreated(gl, config);}/** * 用来绑定Bitmap纹理的代码, * 因为暂时没有找到在C++代码中绑定Bitmap纹理的方法, * 所以暂且在java层绑定Bitmap纹理 * */private void bindTexture(GL10 gl, Context context) {//生成纹理gl.glGenTextures(2, texture, 0);//加载BitmapBitmap bitmap = loadBitmap(context, R.drawable.floor);if (bitmap != null) {Log.i("firewallpaperrenderer", "bind the floor texture");//如果bitmap加载成功,则生成此bitmap的纹理映射gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);//设置纹理映射的属性gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_NEAREST);//生成纹理映射GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);//释放bitmap资源bitmap.recycle();}bitmap = loadBitmap(context, R.drawable.fire);if (bitmap != null) {//同上Log.i("firewallpaperrenderer", "bind the fire texture");gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_NEAREST);gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,GL10.GL_NEAREST);GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);bitmap.recycle();}}/** * 加载Bitmap的方法, * 用来从res中加载Bitmap资源 * */private Bitmap loadBitmap(Context context, int resourceId) {InputStream is = context.getResources().openRawResource(resourceId);Bitmap bitmap = null;try {// 利用BitmapFactory生成Bitmapbitmap = BitmapFactory.decodeStream(is);} finally {try {// 关闭流is.close();is = null;} catch (IOException e) {e.printStackTrace();}}return bitmap;}}

然后定义我们的WallpaperService,FireWallpaperService继承自GLWallpaperService:

public class FireWallpaperService extends GLWallpaperService {//定义FireWallpaperRenderer实例private FireWallpaperRenderer mRenderer;public Engine onCreateEngine() {if (mRenderer == null) {mRenderer = new FireWallpaperRenderer(this);}return new FireWallpaperEngine();}class FireWallpaperEngine extends GLWallpaperService.GLEngine {public FireWallpaperEngine() {//设置Renderer和RendererModesetRenderer(mRenderer);setRenderMode(RENDERMODE_CONTINUOUSLY);}}}

完成后编辑Manifest.xml文件,如下:

application android:icon="@drawable/icon" android:label="@string/app_name"><service android:name=".FireWallpaperService" android:label="@string/firewallpaper"android:permission="android.permission.BIND_WALLPAPER"><intent-filter><action android:name="android.service.wallpaper.WallpaperService" /></intent-filter><meta-data android:name="android.service.wallpaper"android:resource="@xml/wallpaper" /></service></application>


Manifest.xml文件中,FireWallpaperService使用到的wallpaper文件

(<meta-dataandroid:name="android.service.wallpaper"android:resource="@xml/wallpaper" />)在res/xml目录下定义,内容如下:

<?xml version="1.0" encoding="utf-8"?><wallpaper xmlns:android="http://schemas.android.com/apk/res/android"android:description="@string/description" android:thumbnail="@drawable/firelivewallpaper" />


然后是我们的本地方法类——FireNativeMethod:

import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;public class FireNativeMethod {//加载本地库文件static{System.loadLibrary("fire_native_method");}//渲染场景本地方法public static native void onDrawFrame(GL10 gl);//处理屏幕尺寸变化本地方法public static native void onSurfaceChanged(GL10 gl, int width, int height);//初始化OpenGL场景本地方法public static native void onSurfaceCreated(GL10 gl, EGLConfig config);//传递纹理映射地址本地方法public static native void setTexture(int [] textureString);}


之后就是我们的本地C++代码部分了。

首先我们要定义和篝火粒子属性设置相关的类的头文件(Fire.h):

#ifndef _FIRE#define _FIRE#include <GLES2/gl2.h>#include <GLES2/gl2ext.h>#include <GLES/gl.h>#include <stdlib.h>#include <math.h>//声明用来调用纹理映射的数组extern GLuint *texture;/** * Fire类 * */class Fire{public:Fire(void);~Fire(void);void InitFire();   //初始化篝火粒子//下面两个方法用来设置篝火粒子的属性值,以供渲染篝火粒子所用void PrepareFire();void ActivateFireParticles();void RenderFire();  //渲染篝火粒子// 粒子结构体struct PARTICLE {float X,Y,Z; // 当前位置float sX,sY,sZ; // 当前移动速度float tX,tY,tZ; // 目标移动速度float R,B,G; // 粒子颜色float Size; // 粒子大小bool Active; // 粒子是否可见int Age; // 粒子的生命值int MaxAge; // 粒子消失前的最大生命值};//粒子结构体数组PARTICLE * FireParticles;//粒子个数int FireParticleCount;//每帧活动粒子数int ActivateFirePerFrame;};#endif


然后是其相关的C++文件(Fire.cpp):

#include "Fire.h"//初始化纹理数组GLuint *texture = 0;Fire::Fire(void) {}/** * 释放粒子结构体数组 * */Fire::~Fire(void) {delete[] FireParticles;FireParticles = 0;}/** * 初始化粒子 * */void Fire::InitFire() {int p;//粒子的个数FireParticleCount = 500;//每帧粒子个数ActivateFirePerFrame = 40;//初始化粒子结构体数组FireParticles = new PARTICLE[FireParticleCount];//初始粒子不可见for (p = 0; p < FireParticleCount; p++) {FireParticles[p].Active = false;}}void Fire::PrepareFire() {int p;for (p = 0; p < FireParticleCount; p++) {// 调整粒子的速度FireParticles[p].sX += (FireParticles[p].tX - FireParticles[p].sX)/ 10.0f;FireParticles[p].sY += (FireParticles[p].tY - FireParticles[p].sY)/ 20.0f;FireParticles[p].sZ += (FireParticles[p].tZ - FireParticles[p].sZ)/ 10.0f;// 通过新的速度调整粒子的位置FireParticles[p].X += FireParticles[p].sX;FireParticles[p].Y += FireParticles[p].sY;FireParticles[p].Z += FireParticles[p].sZ;// 调整粒子尺寸FireParticles[p].Size -= 0.002f;if (FireParticles[p].Size < 0.0f) {FireParticles[p].Active = false;}// 调整粒子颜色FireParticles[p].R -= 0.01f;FireParticles[p].G -= 0.03f;if (FireParticles[p].R < 0.1f)FireParticles[p].R = 0.1f;if (FireParticles[p].G < 0.1f)FireParticles[p].G = 0.0f;// 最后检查粒子的生命值,如果生命值大于最大生命值,设置粒子不可见FireParticles[p].Age++;if (FireParticles[p].Age > FireParticles[p].MaxAge) {FireParticles[p].Active = false;}}}void Fire::ActivateFireParticles() {int p;for (p = 0; p < FireParticleCount; p++) {if (!FireParticles[p].Active) {// 设置粒子的起始位置FireParticles[p].X = (((float) ((rand() % 50) + 1)) / 100.0f)- 0.25f;FireParticles[p].Y = 0.0f;FireParticles[p].Z = (((float) ((rand() % 50) + 1)) / 100.0f)- 0.25f;// 设置粒子的目标速度FireParticles[p].tX = 0.0f;FireParticles[p].tY = 0.01f;FireParticles[p].tZ = 0.0f;// 生成粒子随机速度FireParticles[p].sX = (((float) ((rand() % 30) + 1)) / 1000.0f)- 0.015f;FireParticles[p].sY = (((float) ((rand() % 50) + 1)) / 1000.0f);FireParticles[p].sZ = (((float) ((rand() % 30) + 1)) / 1000.0f)- 0.015f;//设置粒子可见FireParticles[p].Active = true;// 设置粒子的生命值为0FireParticles[p].Age = 0;// 设置粒子的最大生命值为300FireParticles[p].MaxAge = 300;// 设置粒子颜色FireParticles[p].R = 0.7f;FireParticles[p].G = 0.6f;FireParticles[p].B = 0.0f;// 设置粒子尺寸FireParticles[p].Size = (((float) ((rand() % 15))) / 100.0f);return;}}}void Fire::RenderFire() {for (int i = 0; i < ActivateFirePerFrame; i++) {ActivateFireParticles();}PrepareFire();int p;GLfloat fogColor[4] = { 0.0f, 0.0f, 0.0f, 0.5f };glFogx(GL_FOG_MODE, GL_LINEAR); // 设置雾模式glFogfv(GL_FOG_COLOR, fogColor); // 设置雾颜色glFogf(GL_FOG_DENSITY, 0.35f); // 设置雾密度glHint(GL_FOG_HINT, GL_DONT_CARE); // Fog Hint ValueglFogf(GL_FOG_START, 1.0f); // 设置雾的开始深度glFogf(GL_FOG_END, 2.0f); // 设置雾的结束深度glEnable(GL_FOG); // 启用雾模式GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };GLfloat LightPosition[] = { 0.0f, 0.1f, 0.0f, 1.0f };glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);glEnable(GL_LIGHT1);glEnable(GL_LIGHTING);/** * 开始渲染地面 * *///启用纹理,并绑定地面纹理,禁用混合glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, texture[0]);glDisable(GL_BLEND);//设置颜色glColor4f(1.0f, 1.0f, 1.0f, 1.0f);//法线向量float normals[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.0f, 1.0f, 0.0f };//纹理坐标float texCoords[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f };//顶点坐标float vertecies[] = { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f, 0.5f, 0.0f, -0.5f };//绘制地面for (int y = 0; y < 20; y++) {for (int x = 0; x < 20; x++) {glPushMatrix();glTranslatef(-5.0f, 0.0f, 5.0f);glTranslatef((float) x / 2, 0.0f, -(float) y / 2);glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glEnableClientState(GL_NORMAL_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertecies);glTexCoordPointer(2, GL_FLOAT, 0, texCoords);glNormalPointer(GL_FLOAT, 0, normals);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);glDisableClientState(GL_VERTEX_ARRAY);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glDisableClientState(GL_NORMAL_ARRAY);glPopMatrix();}}//禁用光照和雾模式glDisable(GL_LIGHTING);glDisable(GL_FOG);/** * 渲染地面结束 * *//** * 开始渲染篝火 * */// 启用纹理,绑定我们的粒子纹理glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, texture[1]);// 禁用深度测试glDisable(GL_DEPTH_TEST);// 启用混合glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE);//绘制我们的篝火for (p = 0; p < FireParticleCount; p++) {if (FireParticles[p].Active) {glColor4f(FireParticles[p].R, FireParticles[p].G,FireParticles[p].B, 1.0f);glPushMatrix();glTranslatef(FireParticles[p].X, FireParticles[p].Y,FireParticles[p].Z);glNormal3f(0.0f, 0.0f, 1.0f);float vertecies[] = { -FireParticles[p].Size,-FireParticles[p].Size, 0.0f, FireParticles[p].Size,-FireParticles[p].Size, 0.0f, -FireParticles[p].Size,FireParticles[p].Size, 0.0f, FireParticles[p].Size,FireParticles[p].Size, 0.0f };glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_TEXTURE_COORD_ARRAY);glVertexPointer(3, GL_FLOAT, 0, vertecies);glTexCoordPointer(2, GL_FLOAT, 0, texCoords);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);glDisableClientState(GL_VERTEX_ARRAY);glDisableClientState(GL_TEXTURE_COORD_ARRAY);glPopMatrix();}}/** * 渲染篝火结束 * *///重新启用深度测试glEnable(GL_DEPTH_TEST);}


接着是我们处理OpenGL初始化、设置和渲染的方法的头文件——Stdx.h:

#ifndef _STDX#define _STDX#include "Fire.h"//初始化OpenGL场景extern void InitGL();//处理屏幕尺寸变化extern void SizeChanged(int width ,int height);//渲染场景extern void RendererGL();//计算场景透视extern void gluPerspective(double fovy, double aspect, double zNear,double zFar);#endif


然后是其相关的C++文件——main.cpp:

#include "Stdx.h"Fire Fire;  //Fire实例float aspectRatio;   //用于透视计算void InitGL() {glShadeModel(GL_SMOOTH);   // 启用平滑模式glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // 设置黑色背景glClearDepthf(1.0f);    // 设置深度缓存glEnable(GL_DEPTH_TEST);   // 启用深度测试glDepthFunc(GL_LEQUAL);   // 深度测试类型glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   // 获得良好的透视效果Fire.InitFire();   //初始化粒子}void SizeChanged(int width ,int height) {glViewport(0, 0, width, height);  //重设窗口大小glMatrixMode(GL_PROJECTION);  //启用投影矩阵glLoadIdentity();     //重置投影矩阵aspectRatio = float(width) / float(height);  //获得屏幕宽、高比gluPerspective(45.0, aspectRatio, 0.1, 100.0);  //计算透视glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵glLoadIdentity();   //重置模型视图矩阵}void RendererGL() {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // 首先清除颜色和深度缓存glLoadIdentity();// 重设模型视图矩阵glTranslatef(0.0f, -0.5f, -2.2f);  //移动场景Fire.RenderFire(); //渲染篝火}void gluPerspective(double fovy, double aspect, double zNear, double zFar) {glMatrixMode(GL_PROJECTION);   //启用投影矩阵glLoadIdentity();   //重置投影矩阵//计算透视double xmin, xmax, ymin, ymax;ymax = zNear * tan(fovy * M_PI / 360.0);ymin = -ymax;xmin = ymin * aspect;xmax = ymax * aspect;//设置透视glFrustumf(xmin, xmax, ymin, ymax, zNear, zFar);glMatrixMode(GL_MODELVIEW);  //启用模型视图矩阵glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // 获得良好的透视效果}


之后我们需要把我们的本地方法类——FireNativeMethod利用javah命令生成正确的头文件,然后在其对应的C++文件(com_ygc_FireNativeMethod.cpp)中定义头文件中的方法,在其方法中调用相应的本地代码实现其功能:

#include "com_ygc_FireNativeMethod.h"#include "Stdx.h"/* * 调用RendererGL方法渲染场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onDrawFrame  (JNIEnv *env, jclass cls, jobject obj){RendererGL();}/* * 调用SizeChanged调整场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceChanged  (JNIEnv *env, jclass cls, jobject obj, jint width, jint height){SizeChanged(width,height);}/* * 调用InitGL初始化场景 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_onSurfaceCreated  (JNIEnv *env, jclass cls, jobject obj1, jobject obj2){InitGL();}/* * 获得纹理绑定地址 */JNIEXPORT void JNICALL Java_com_ygc_FireNativeMethod_setTexture  (JNIEnv *env, jclass cls, jintArray tex){texture = (GLuint *)env->GetIntArrayElements(tex,0);}


最后是我们的Android.mk文件:

# Copyright (C) 2009 The Android Open Source Project## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##      http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.#LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := fire_native_methodLOCAL_SRC_FILES := com_ygc_FireNativeMethod.cpp Fire.cpp main.cppLOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -lGLESv2LOCAL_LDLIBS+=-L$(SYSROOT)/usr/lib -lGLESv1_CMLOCAL_LDLIBS+= -L$(SYSROOT)/usr/lib -lloginclude $(BUILD_SHARED_LIBRARY)

更多相关文章

  1. Android4.0和Android4.1全屏方法
  2. Android SDK更新失败的解决方法
  3. 关于 android 输入法 adjustPan无效的解决方法
  4. Android实现EditText控件禁止输入内容的方法(附测试demo)
  5. Android RetainFragment状态保存的方法
  6. Android Volley框架使用方法详解
  7. H5页面调用android方法传json格式
  8. Android提交数据到服务的四种方法!!!

随机推荐

  1. 让你明白,Android常用组件四大天王
  2. Android中TASK和PROCESS的区别,获取最近运
  3. android DatePicker和TimePicke用法
  4. 一个错误
  5. 【高通SDM660平台 Android(安卓)10.0】(1
  6. Android中将assets中的文件拷贝到sd卡
  7. Android 音量调节
  8. android 安装apk代码
  9. Android通过手势实现的缩放处理
  10. Android 音乐播放器。