Android OpenGL动态壁纸
首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。
1、动态壁纸的Service组件
WallpaperService提供了基本的动态壁纸的实现。public class GLWallpaperService extends WallpaperService {@Overridepublic Engine onCreateEngine() {return new GLEngine();}}
2、创建自定义GLSurfaceView
GLSurfaceView会调用getHolder()来添加界面,只需重载getHolder方法,返回动态壁纸的渲染表面。onWallpaperDestroy在销毁动态壁纸时被调用,使用GLSurfaceView的onDetachedFromWindow方法。class WallpaperGLSurfaceView extends GLSurfaceView {public WallpaperGLSurfaceView(Context context) {super(context);}@Overridepublic SurfaceHolder getHolder() {return GLEngine.this.getSurfaceHolder();}public void onWallpaperDestroy() {super.onDetachedFromWindow();}
}
3、Engine类
GLEngine继承Engine类,当动态壁纸创建时,调用GLEngine的onCreate方法来初始化,销毁时调用onDestroy方法。当动态壁纸可见或者隐藏时,onVisibilityChanged方法会被调用。public class GLEngine extends Engine {@Overridepublic void onCreate(SurfaceHolder surfaceHolder) {super.onCreate(surfaceHolder);}@Overridepublic void onVisibilityChanged(boolean visible) {super.onVisibilityChanged(visible);}@Overridepublic void onDestroy() {super.onDestroy();}}
完善动态壁纸的生命周期,在onCreate中创建GLSurfaceView,onDestroy中销毁。private WallpaperGLSurfaceView mSurfaceView;@Override
public void onCreate(SurfaceHolder surfaceHolder) {super.onCreate(surfaceHolder);mSurfaceView = new WallpaperGLSurfaceView(GLWallpaperService.this);
}public void onVisibilityChanged(boolean visible) {super.onVisibilityChanged(visible);if (mRender) {if (visible) {mSurfaceView.onResume();} else {mSurfaceView.onPause();}}
}@Override
public void onDestroy() {super.onDestroy();mSurfaceView.onWallpaperDestroy();
}
4、添加配置
在AndroidManifest.xml中添加<uses-feature android:name="android.software.live_wallpaper" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
表明这个应用包含一个动态壁纸,并且还需要OpenGL ES 2.0或以上版本。
还需要添加动态壁纸Service的引用
<service android:name=".opengl.GLWallpaperService"android:label="@string/app_name"android:permission="android.permission.BIND_WALLPAPER" ><intent-filter><action android:name="android.service.wallpaper.WallpaperService" /></intent-filter><meta-dataandroid:name="android.service.wallpaper"android:resource="@xml/wallpaper" />
</service>
在资源目录res/xml下,添加wapaper.xml文件
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"android:thumbnail="@drawable/ic_wallpaper" />
5、体验动态壁纸
(1) 切换到设备的主屏幕上,按住屏幕的空白部分,直至弹出一个菜单。(2) 选择"动态壁纸",从弹出的列表中选择我们自己的动态壁纸。
(3) 选择"设置壁纸"
6、随主屏幕滚动背景
当你在主屏幕上的不同页面之间来回滑动时,动态壁纸没有移动,通过Engine的onOffsetsChanged方法,可以是实现动态壁纸的滚动效果。public void onOffsetsChanged(final float xOffset, final float yOffset, float xOffsetStep,float yOffsetStep, int xPixelOffset, int yPixelOffset) {mSurfaceView.queueEvent(new Runnable() {@Overridepublic void run() {mShaderRender.handleOffsetsChanged(xOffset, yOffset);}});
}
7、GLWallpaperService类
public class GLWallpaperService extends WallpaperService {@Overridepublic Engine onCreateEngine() {return new GLEngine();}public class GLEngine extends Engine {private WallpaperGLSurfaceView mSurfaceView;private OpenGLParticleShaderRender mShaderRender;private boolean mRender;@Overridepublic void onCreate(SurfaceHolder surfaceHolder) {super.onCreate(surfaceHolder);mSurfaceView = new WallpaperGLSurfaceView(GLWallpaperService.this);ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);ConfigurationInfo configurationInfo = am.getDeviceConfigurationInfo();boolean supportEs2 = configurationInfo.reqGlEsVersion >= 0x20000;if (supportEs2) {// 选择OpenGL ES 2.0mSurfaceView.setEGLContextClientVersion(2);mShaderRender = new OpenGLParticleShaderRender();// 设置渲染mSurfaceView.setRenderer(mShaderRender);mRender = true;} else {Toast.makeText(GLWallpaperService.this ,"This device does not support OpenGL ES 2.0",Toast.LENGTH_LONG).show();}}@Overridepublic void onVisibilityChanged(boolean visible) {super.onVisibilityChanged(visible);if (mRender) {if (visible) {mSurfaceView.onResume();} else {mSurfaceView.onPause();}}}@Overridepublic void onDestroy() {super.onDestroy();mSurfaceView.onWallpaperDestroy();}@Overridepublic void onOffsetsChanged(final float xOffset, final float yOffset, float xOffsetStep,float yOffsetStep, int xPixelOffset, int yPixelOffset) {mSurfaceView.queueEvent(new Runnable() {@Overridepublic void run() {LogUtil.log("GLEngine", "xOffset = " + xOffset + ", yOffset = " + yOffset);mShaderRender.handleOffsetsChanged(xOffset, yOffset);}});}class WallpaperGLSurfaceView extends GLSurfaceView {public WallpaperGLSurfaceView(Context context) {super(context);}@Overridepublic SurfaceHolder getHolder() {return GLEngine.this.getSurfaceHolder();}public void onWallpaperDestroy() {super.onDetachedFromWindow();}}}private class OpenGLParticleShaderRender implements GLSurfaceView.Renderer {private Particle mParticle;private ParticleProgram mParticleProgram;private ParticleShooter mRedParticleShooter, mGreenParticleShooter, mBlueParticleShooter;private long mGlobalStartTime;private float[] projectionMatrix = new float[16];private float[] viewMatrix = new float[16];private float[] viewProjectionMatrix = new float[16];private float xOffset, yOffset;@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.1f);GLES20.glEnable(GLES20.GL_BLEND);GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);mParticleProgram = new ParticleProgram(GLWallpaperService.this);mParticle = new Particle(10000);mGlobalStartTime = System.nanoTime();final Geometry.Vector particleDirection = new Geometry.Vector(0f, 0.5f, 0f);mRedParticleShooter = new ParticleShooter(new Geometry.Point(-1, 0, 0),particleDirection, Color.rgb(250, 50, 5));mGreenParticleShooter = new ParticleShooter(new Geometry.Point(0, 0, 0),particleDirection, Color.rgb(25, 255, 25));mBlueParticleShooter = new ParticleShooter(new Geometry.Point(1, 0, 0),particleDirection, Color.rgb(5, 50, 255));final float angleVarianceInDegrees = 5f;final float speedVariance = 1f;mRedParticleShooter = new ParticleShooter(new Geometry.Point(-1, 0, 0),particleDirection, Color.rgb(250, 50, 5), angleVarianceInDegrees, speedVariance);mGreenParticleShooter = new ParticleShooter(new Geometry.Point(0, 0, 0),particleDirection, Color.rgb(25, 255, 25), angleVarianceInDegrees, speedVariance);mBlueParticleShooter = new ParticleShooter(new Geometry.Point(1, 0, 0),particleDirection, Color.rgb(5, 50, 255), angleVarianceInDegrees, speedVariance);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// 设置视图尺寸GLES20.glViewport(0, 0, width, height);// 创建透视投影Matrix.perspectiveM(projectionMatrix, 0, 45, (float)width / (float)height, 1f, 10f);updateViewMatrices();}@Overridepublic void onDrawFrame(GL10 gl) {// 清空屏幕GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT);float currentTime = (System.nanoTime() - mGlobalStartTime) / 1000000000f;mRedParticleShooter.addParticles(mParticle, currentTime, 5);mGreenParticleShooter.addParticles(mParticle, currentTime, 5);mBlueParticleShooter.addParticles(mParticle, currentTime, 5);mParticleProgram.setUniform(viewProjectionMatrix);mParticleProgram.setCurrentTime(currentTime);mParticle.bindData(mParticleProgram);mParticle.draw();}private void handleOffsetsChanged(float xOffset, float yOffset) {this.xOffset = (xOffset - 0.5f) * 2.5f;this.yOffset = (yOffset - 0.5f) * 2.5f;updateViewMatrices();}private void updateViewMatrices() {// 定义模型矩阵Matrix.setIdentityM(viewMatrix, 0);Matrix.translateM(viewMatrix, 0, 0f - xOffset, -1.5f - yOffset, -5f);Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);}}}
显示如下