Android OpenGL动态壁纸
首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。
1、动态壁纸的Service组件
WallpaperService提供了基本的动态壁纸的实现。public class GLWallpaperService extends WallpaperService {
@Override
public Engine onCreateEngine() {
return new GLEngine();
}
}
2、创建自定义GLSurfaceView
GLSurfaceView会调用getHolder()来添加界面,只需重载getHolder方法,返回动态壁纸的渲染表面。onWallpaperDestroy在销毁动态壁纸时被调用,使用GLSurfaceView的onDetachedFromWindow方法。class WallpaperGLSurfaceView extends GLSurfaceView {
public WallpaperGLSurfaceView(Context context) {
super(context);
}
@Override
public SurfaceHolder getHolder() {
return GLEngine.this.getSurfaceHolder();
}
public void onWallpaperDestroy() {
super.onDetachedFromWindow();
}
}
3、Engine类
GLEngine继承Engine类,当动态壁纸创建时,调用GLEngine的onCreate方法来初始化,销毁时调用onDestroy方法。当动态壁纸可见或者隐藏时,onVisibilityChanged方法会被调用。public class GLEngine extends Engine {
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
}
@Override
public 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-data
android: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() {
@Override
public void run() {
mShaderRender.handleOffsetsChanged(xOffset, yOffset);
}
});
}
7、GLWallpaperService类
public class GLWallpaperService extends WallpaperService {
@Override
public Engine onCreateEngine() {
return new GLEngine();
}
public class GLEngine extends Engine {
private WallpaperGLSurfaceView mSurfaceView;
private OpenGLParticleShaderRender mShaderRender;
private boolean mRender;
@Override
public 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.0
mSurfaceView.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();
}
}
@Override
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();
}
@Override
public void onOffsetsChanged(final float xOffset, final float yOffset, float xOffsetStep,
float yOffsetStep, int xPixelOffset, int yPixelOffset) {
mSurfaceView.queueEvent(new Runnable() {
@Override
public void run() {
LogUtil.log("GLEngine", "xOffset = " + xOffset + ", yOffset = " + yOffset);
mShaderRender.handleOffsetsChanged(xOffset, yOffset);
}
});
}
class WallpaperGLSurfaceView extends GLSurfaceView {
public WallpaperGLSurfaceView(Context context) {
super(context);
}
@Override
public 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;
@Override
public 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);
}
@Override
public 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();
}
@Override
public 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);
}
}
}
显示如下