Android自定义动态壁纸开发
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义动态壁纸开发相关的知识,希望对你有一定的参考价值。
参考技术A看到有些手机酷炫的动态壁纸,有没有好奇过他们是如何实现的,其实我们自己也可以实现。
如果你了解使用过SurfaceView的话,那么开发一款动态壁纸对你来说其实非常简单。
动态壁纸的本质其实就是一个服务在维护一个动态壁纸引擎Engine,所以我们看到的动态效果其实是通过这个引擎画出来的。而维护这个引擎的服务,就是WallpaperService。本篇文章并不讨论内部实现原理,只是让大家知道如何去实现动态壁纸,所以就不详细说了。
大体上可分为三个步骤:
创建自定义WallpaperService继承WallpaperService
在Manifest中注册该Service并添加相关属性
创建所需要的xml文件
1.创建自定义WallpaperService
2.Manifest注册
一定要添加的几个地方:permission、intent-filter、meta-data。
3.创建需要的xml文件
这个xml文件就是Manifest中meta-data中的resource需要的文件:
需要注意第二个属性:settingsActivity,这个属性可以设置也可以不设置,他是启动一个设置动态壁纸的界面,一般情况下其实用不到这个界面,我们一般会使用PreferenceActivity去实现。下面是添加该属性和不添加该属性的区别:
完成这些之后,就是我们设计动态壁纸的时候了。回到自定义的Wallpaper类中:
我们当时在类中自定义了一个内部类MyEngine继承自Engine。这个Engine就是用来绘制的。关于Engine的几个主要方法如下:
onOffsetsChanged要注意一下,还记得有的手机滑动桌面时候背景图片会跟着左右移动吗,这个方法就可以实现这个效果,在手势滑动的每一帧都会回调依次。一下是个人理解的参数的含义:
xOffset:x方向滑动的百分比(与桌面分页数有关)
yOffset:y方向滑动百分比(一般用不到)
xOffsetStep:x方向每个分页所占的百分比(1 / xOffsetStep = 桌面的分页数)
yOffsetStep:同
xPixelOffset:x放下像素偏移量
y。。。。。
4.绘制。
实现绘制的方式,就跟SurfaceView的绘制一样了:
我只是简单的话了一个红色背景,效果如下:
当然可以实现很多不同的效果,这个就根据不同的需求去实现了。
Android使用WallpaperService和openGL ES生成的动态图形设置动态壁纸
针对使用WallpaperService和openGL ES得到的动态图形设置动态壁纸的说明
1.首先创建一个MyselfWallpaperService类继承于WallpaperService类。在该类中的onCreateEngine()方法中返回一个return new MyEngine()。然后在该类内部,定义一个类MyEngine类继承自WallpaperService.Engine类
这里重点说明:在实现MyEngine类时,一定要在MysekfWallpaperService类中,因为MyEngine类是Engine的子类,而Engine是WallpaperService类的内部类。这一点一定注意。
2.然后在MyselfWallpaperService类中定义一个MySurfaceView类,该类继承自GLSurfaceView类,然后该类中的getHolder()函数中,返回的是MyserfWallpaperService.MyEngine.this.getSurfaceHolder();
3.而后,在MyEngine类中创建openGL ES中需要用到的render类。其中具体实现这里不在赘述。
4.而后在res/xml目录下(没有的有键创建即可)创建一个wallpaper.xml文件,其中的代码为:
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android ="http://schemas.android.com/apk/res/android"/>
5.而后在activity_main.xml文件中设置:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/set_btn"
android:text="设置动态壁纸"
/>
</LinearLayout>
6.然后注册该服务类。
在AndroidManifest.xml中的<application中设置:
<!--配置动态壁纸-->
<service android:name=".MyselfWallpaperService"
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>
7.最后,开通联网权限:
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
以下为代码实现:
MyselfWallpaperService类代码
- package com.example.lab11;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.service.wallpaper.WallpaperService;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class MyselfWallpaperService extends WallpaperService {
// 实现WallpaperService必须实现的抽象方法
@Override
public Engine onCreateEngine() {
// 返回自定义的Engine
return new MyEngine();
}
//定义Engine类
public class MyEngine extends WallpaperService.Engine {
private long startTime;//保存开始时间
//定义一个surfaceView对象
private MySurfaceView mySurfaceView;
//定义一个Render对象
private MyRenderer myRenderer;
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
//初始化surfaceView对象
mySurfaceView = new MySurfaceView(MyselfWallpaperService.this);
myRenderer = new MyRenderer(MyselfWallpaperService.this);
mySurfaceView.setRenderer(myRenderer);
}
@Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if(myRenderer != null){
if(visible){ //表示显示
mySurfaceView.onResume(); //表示显示壁纸
}else {
mySurfaceView.onPause(); //表示暂停壁纸
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
mySurfaceView.onWallpaperDestroy(); //销毁surfaceView对象
}
//定义MySurfaceView类
public class MySurfaceView extends GLSurfaceView implements SurfaceHolder.Callback {
public MySurfaceView(Context context) {
super(context);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public SurfaceHolder getHolder() {
return MyselfWallpaperService.MyEngine.this.getSurfaceHolder();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onResume() {
super.onResume();
}
public void onWallpaperDestroy() {
super.onDetachedFromWindow();
}
}
//设置的Render类
public class MyRenderer implements GLSurfaceView.Renderer {
// 立方体的顶点坐标(一共是36个顶点,组成12个三角形)
private float[] cubeVertices = {
-0.6f, -0.6f, -0.6f, -0.6f, 0.6f, -0.6f,
0.6f, 0.6f, -0.6f, 0.6f, 0.6f, -0.6f,
0.6f, -0.6f, -0.6f, -0.6f, -0.6f, -0.6f,
-0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f,
0.6f, 0.6f, 0.6f, 0.6f, 0.6f, 0.6f,
-0.6f, 0.6f, 0.6f, -0.6f, -0.6f, 0.6f,
-0.6f, -0.6f, -0.6f, 0.6f, -0.6f, -0.6f,
0.6f, -0.6f, 0.6f, 0.6f, -0.6f, 0.6f,
-0.6f, -0.6f, 0.6f, -0.6f, -0.6f, -0.6f,
0.6f, -0.6f, -0.6f, 0.6f, 0.6f, -0.6f,
0.6f, 0.6f, 0.6f, 0.6f, 0.6f,0.6f,
0.6f, -0.6f, 0.6f, 0.6f, -0.6f, -0.6f,
0.6f, 0.6f, -0.6f, -0.6f, 0.6f, -0.6f,
-0.6f, 0.6f, 0.6f, -0.6f, 0.6f, 0.6f,
0.6f, 0.6f, 0.6f, 0.6f, 0.6f, -0.6f,
-0.6f, 0.6f, -0.6f, -0.6f, -0.6f,-0.6f,
-0.6f, -0.6f, 0.6f, -0.6f, -0.6f, 0.6f,
-0.6f, 0.6f, 0.6f, -0.6f, 0.6f, -0.6f
};
// 定义立方体所需要的6个面(一共是12个三角形所需的顶点)
private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, };
// 定义纹理贴图的坐标数据
private float[] cubeTextures = {
1.0000f, 1.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f
};
private Context context;
private FloatBuffer cubeVerticesBuffer;
private ByteBuffer cubeFacetsBuffer;
private FloatBuffer cubeTexturesBuffer;
// 定义本程序所使用的纹理
private int texture;
public MyRenderer(Context main) {
this.context = main;
// 将立方体的顶点位置数据数组包装成FloatBuffer;
cubeVerticesBuffer = floatBufferUtil(cubeVertices);
// 将立方体的6个面(12个三角形)的数组包装成ByteBuffer
cubeFacetsBuffer = ByteBuffer.wrap(cubeFacets);
// 将立方体的纹理贴图的坐标数据包装成FloatBuffer
cubeTexturesBuffer = floatBufferUtil(cubeTextures);
startTime=System.currentTimeMillis(); //系统当前时间
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// 关闭抗抖动
gl.glDisable(GL10.GL_DITHER);
// 设置系统对透视进行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0, 0, 0, 0);
// 设置阴影平滑模式
gl.glShadeModel(GL10.GL_SMOOTH);
// 启用深度测试
gl.glEnable(GL10.GL_DEPTH_TEST);
// 设置深度测试的类型
gl.glDepthFunc(GL10.GL_LEQUAL);
// 启用2D纹理贴图
gl.glEnable(GL10.GL_TEXTURE_2D);
// 装载纹理
loadTexture(gl);
float matAmbient[]=new float[]{1,1,1,1}; //定义材质的环境光
float matDiffuse[]=new float[]{1,1,1,1}; //定义材质的散射光
//设置材质的环境光
// gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, matAmbient,0);
//设置材质的散射光
//gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, matDiffuse,0);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置3D视窗的大小及位置
gl.glViewport(0, 0, width, height);
// 将当前矩阵模式设为投影矩阵
gl.glMatrixMode(GL10.GL_PROJECTION);
// 初始化单位矩阵
gl.glLoadIdentity();
// 计算透视视窗的宽度、高度比
float ratio = (float) width / height;
// 调用此方法设置透视视窗的空间大小。
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
float lightAmbient[]=new float[]{0.2f,0.2f,0.2f,1}; //定义环境光
float lightDiffuse[]=new float[]{1,1,1,1}; //定义散射光
float lightSpecular[]=new float[]{1,1,1,1}; //定义散射光
float lightPos[]=new float[]{1,1,1,1}; //定义光源的位置
gl.glEnable(GL10.GL_LIGHTING); //启用光源
gl.glEnable(GL10.GL_LIGHT0); //启用0号光源
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient,0); //设置环境光
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0); //设置散射光
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightSpecular, 0); //设置散射光
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPos, 0); //设置光源的位置
}
public void onDrawFrame(GL10 gl) {
// 清除屏幕缓存和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// 启用顶点坐标数据
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// 启用贴图坐标数组数据
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // ①
// 设置当前矩阵模式为模型视图。
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
// 把绘图中心移入屏幕2个单位
gl.glTranslatef(0f, 0.0f, -2.0f);
//旋转
long elapsed = System.currentTimeMillis() - startTime; //计算逝去的时间
gl.glRotatef(elapsed * (30f / 1000f), 0, 1, 0); //在y轴上旋转30度
gl.glRotatef(elapsed * (15f / 1000f), 1, 0, 0); //在x轴上旋转15度
// 设置顶点的位置数据
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeVerticesBuffer);
// 设置贴图的坐标数据
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, cubeTexturesBuffer); // ②
// 执行纹理贴图
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); // ③
// 按cubeFacetsBuffer指定的面绘制三角形
gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacetsBuffer.remaining(),
GL10.GL_UNSIGNED_BYTE, cubeFacetsBuffer);
// 绘制结束
gl.glFinish();
// 禁用顶点、纹理坐标数组
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// 递增角度值以便每次以不同角度绘制
}
private void loadTexture(GL10 gl) {
Bitmap bitmap = null;
try
{
// 加载位图
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.bg);
int[] textures = new int[1];
// 指定生成N个纹理(第一个参数指定生成一个纹理)
// textures数组将负责存储所有纹理的代号
gl.glGenTextures(1, textures, 0);
// 获取textures纹理数组中的第一个纹理
texture = textures[0];
// 通知OpenGL将texture纹理绑定到GL10.GL_TEXTURE_2D目标中
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
// 设置纹理被缩小(距离视点很远时被缩小)时的滤波方式
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_LINEAR);
// 设置在横向、纵向上都是平铺纹理
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_REPEAT);
// 加载位图生成纹理
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
}
finally
{
// 生成纹理之后,回收位图
if (bitmap != null)
bitmap.recycle();
}
}
}
// 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
private FloatBuffer floatBufferUtil(float[] arr) {
FloatBuffer mBuffer;
// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
// 数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
mBuffer = qbb.asFloatBuffer();
mBuffer.put(arr);
mBuffer.position(0);
return mBuffer;
}
}
}
MainActivity类代码:
package com.example.lab11;
import androidx.appcompat.app.AppCompatActivity;
import android.app.WallpaperManager;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button mSetBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSetBtn = (Button)findViewById(R.id.set_btn);
mSetBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startLiveWallpaperPreView(getPackageName(),MyselfWallpaperService.class.getName());
}
});
}
public void startLiveWallpaperPreView(String packageName, String classFullName) {
ComponentName componentName = new ComponentName(packageName, classFullName);
Intent intent;
if (android.os.Build.VERSION.SDK_INT < 16) {
intent = new Intent(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER);
} else {
intent = new Intent("android.service.wallpaper.CHANGE_LIVE_WALLPAPER");
intent.putExtra("android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT", componentName);
}
startActivityForResult(intent, 0);
}
}
以上是关于Android自定义动态壁纸开发的主要内容,如果未能解决你的问题,请参考以下文章
桌面太单调?一起用Python做个自定义动态壁纸,竟然还可以放视频!