动态壁纸是在Android 2.1新增的一个功能。动态壁纸可以添加到Android的桌面,具有交互式的动画背景效果。在本教程中,我们将教会你如何去制作一个交互式的动态壁纸。
动态壁纸是一个Android应用程序,包括一个服务(WallpaperService)。该服务必须包括一个引擎(WallpaperService.Engine)。该引擎是连接用户、桌面、系统之间的桥梁。它也可以绘制桌面壁纸。
首先,必须由内在的Engine类创建一个WallpaperService类。该服务必须在AndroidManifest.xml中声明为"android.service.wallpaper.WallpaperService",这样它才会作为动态壁纸被手机识别。而且还要在服务配置中附加"android.permission.BIND_WALLPAPER"的权限许可:
01 <service
02 android:name="LiveWallpaperService"
03 android:enabled="true"
04 android:icon="@drawable/icon"
05 android:label="@string/app_name"
06 android:permission="android.permission.BIND_WALLPAPER">
07
08 <intent-filter android:priority="1" >
09 <action android:name="android.service.wallpaper.WallpaperService" />
10 </intent-filter>
11 <meta-data
12 android:name="android.service.wallpaper"
13 android:resource="@xml/wallpaper" />
14
15 </service>
创建一个XML文件,放置在应用程序目录下的/res/xml/中。它用来描述你的动态壁纸。
1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <wallpaper
4 xmlns:android="http://schemas.android.com/apk/res/android"
5 android:thumbnail="@drawable/thumbnail"
6 android:description="@string/description"
7 android:settingsActivity="PreferenceActivity"/>
再创建一个xml的属性文件 attrs.xml ,代码如下:
01 <declare-styleable name="Wallpaper">
02 <!-- Component name of an activity that allows the user to modify
03 the current settings for this wallpaper. -->
04 <attr name="settingsActivity" />
05
06 <!-- Reference to a the wallpaper's thumbnail bitmap. -->
07
08 <attr name="thumbnail" format="reference" />
09
10 <!-- Name of the author of this component, e.g. Google. -->
11 <attr name="author" format="reference" />
12
13
14 <!-- Short description of the component's purpose or behavior. -->
15 <attr name="description" />
16 </declare-styleable>
动态壁纸的服务代码如下:
001 package net.androgames.blog.sample.livewallpaper;
002
003 import android.content.SharedPreferences;
004 import android.service.wallpaper.WallpaperService;
005 import android.view.MotionEvent;
006 import android.view.SurfaceHolder;
007
008 /**
009 * Android Live Wallpaper Archetype
010 * @author antoine vianey
011 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013 public class LiveWallpaperService extends WallpaperService {
014
015 @Override
016 public Engine onCreateEngine() {
017 return new SampleEngine();
018 }
019
020 @Override
021 public void onCreate() {
022 super.onCreate();
023 }
024
025 @Override
026 public void onDestroy() {
027 super.onDestroy();
028 }
029
030 public class SampleEngine extends Engine {
031
032 private LiveWallpaperPainting painting;
033
034 SampleEngine() {
035 SurfaceHolder holder = getSurfaceHolder();
036 painting = new LiveWallpaperPainting(holder,
037 getApplicationContext());
038 }
039
040 @Override
041 public void onCreate(SurfaceHolder surfaceHolder) {
042 super.onCreate(surfaceHolder);
043 // register listeners and callbacks here
044 setTouchEventsEnabled(true);
045 }
046
047 @Override
048 public void onDestroy() {
049 super.onDestroy();
050 // remove listeners and callbacks here
051 painting.stopPainting();
052 }
053
054 @Override
055 public void onVisibilityChanged(boolean visible) {
056 if (visible) {
057 // register listeners and callbacks here
058 painting.resumePainting();
059 } else {
060 // remove listeners and callbacks here
061 painting.pausePainting();
062 }
063 }
064
065 @Override
066 public void onSurfaceChanged(SurfaceHolder holder, int format,
067 int width, int height) {
068 super.onSurfaceChanged(holder, format, width, height);
069 painting.setSurfaceSize(width, height);
070 }
071
072 @Override
073 public void onSurfaceCreated(SurfaceHolder holder) {
074 super.onSurfaceCreated(holder);
075 // start painting
076 painting.start();
077 }
078
079 @Override
080 public void onSurfaceDestroyed(SurfaceHolder holder) {
081 super.onSurfaceDestroyed(holder);
082 boolean retry = true;
083 painting.stopPainting();
084 while (retry) {
085 try {
086 painting.join();
087 retry = false;
088 } catch (InterruptedException e) {}
089 }
090 }
091
092 @Override
093 public void onOffsetsChanged(float xOffset, float yOffset,
094 float xStep, float yStep, int xPixels, int yPixels) {
095 }
096
097 @Override
098 public void onTouchEvent(MotionEvent event) {
099 super.onTouchEvent(event);
100 painting.doTouchEvent(event);
101 }
102
103 }
104
105 }
当壁纸的显示、状态或大小变化是,会调用Engine的onCreate, onDestroy, onVisibilityChanged, onSurfaceChanged, onSurfaceCreated 和 onSurfaceDestroyed方法。有了这些方法,动态壁纸才能展现出动画效果。而通过设置setTouchEventsEnabled(true),并且调用onTouchEvent(MotionEvent event)方法,来激活触摸事件。
我们在绘画墙纸的时候,也会使用一个单独的绘画线程:
001 package net.androgames.blog.sample.livewallpaper;
002
003 import android.content.Context;
004 import android.graphics.Canvas;
005 import android.view.MotionEvent;
006 import android.view.SurfaceHolder;
007
008 /**
009 * Android Live Wallpaper painting thread Archetype
010 * @author antoine vianey
011 * GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013 public class LiveWallpaperPainting extends Thread {
014
015 /** Reference to the View and the context */
016 private SurfaceHolder surfaceHolder;
017 private Context context;
018
019 /** State */
020 private boolean wait;
021 private boolean run;
022
023 /** Dimensions */
024 private int width;
025 private int height;
026
027 /** Time tracking */
028 private long previousTime;
029 private long currentTime;
030
031 public LiveWallpaperPainting(SurfaceHolder surfaceHolder,
032 Context context) {
033 // keep a reference of the context and the surface
034 // the context is needed if you want to inflate
035 // some resources from your livewallpaper .apk
036 this.surfaceHolder = surfaceHolder;
037 this.context = context;
038 // don't animate until surface is created and displayed
039 this.wait = true;
040 }
041
042 /**
043 * Pauses the live wallpaper animation
044 */
045 public void pausePainting() {
046 this.wait = true;
047 synchronized(this) {
048 this.notify();
049 }
050 }
051
052 /**
053 * Resume the live wallpaper animation
054 */
055 public void resumePainting() {
056 this.wait = false;
057 synchronized(this) {
058 this.notify();
059 }
060 }
061
062 /**
063 * Stop the live wallpaper animation
064 */
065 public void stopPainting() {
066 this.run = false;
067 synchronized(this) {
068 this.notify();
069 }
070 }
071
072 @Override
073 public void run() {
074 this.run = true;
075 Canvas c = null;
076 while (run) {
077 try {
078 c = this.surfaceHolder.lockCanvas(null);
079 synchronized (this.surfaceHolder) {
080 currentTime = System.currentTimeMillis();
081 updatePhysics();
082 doDraw(c);
083 previousTime = currentTime;
084 }
085 } finally {
086 if (c != null) {
087 this.surfaceHolder.unlockCanvasAndPost(c);
088 }
089 }
090 // pause if no need to animate
091 synchronized (this) {
092 if (wait) {
093 try {
094 wait();
095 } catch (Exception e) {}
096 }
097 }
098 }
099 }
100
101 /**
102 * Invoke when the surface dimension change
103 */
104 public void setSurfaceSize(int width, int height) {
105 this.width = width;
106 this.height = height;
107 synchronized(this) {
108 this.notify();
109 }
110 }
111
112 /**
113 * Invoke while the screen is touched
114 */
115 public void doTouchEvent(MotionEvent event) {
116 // handle the event here
117 // if there is something to animate
118 // then wake up
119 this.wait = false;
120 synchronized(this) {
121 notify();
122 }
123 }
124
125 /**
126 * Do the actual drawing stuff
127 */
128 private void doDraw(Canvas canvas) {}
129
130 /**
131 * Update the animation, sprites or whatever.
132 * If there is nothing to animate set the wait
133 * attribute of the thread to true
134 */
135 private void updatePhysics() {
136 // if nothing was updated :
137 // this.wait = true;
138 }
139
140 }
如果桌面壁纸是可见状态下,系统服务通知有新的东西,这个类会优先把它绘制在画布上。如果没有动画了,updatePhysics会通知线程去等待。通常SurfaceView在有两个画布交替绘制的时候,会在画布上绘制上一次......
如果要让你的动态墙纸有配置功能,只要创建一个PreferenceActivity,并将它在wallpaper.xml文件中声明。同时让SharedPreference对象可以找到你的配置选项。
教程就写到这里,如果还有什么不懂,你可以通过Eclipse来浏览完整的源代码:SampleLiveWallpaper=>[url]http://code.google.com/p/androgames-sample/
[/url]
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
标签: SurfaceView 双缓冲 Android SDK
代码片段(3)
[图片] 程序运行截图
[代码] main.xml
01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03 android:layout_width="fill_parent" android:layout_height="fill_parent"
04 android:orientation="vertical">
05
06 <LinearLayout android:id="@+id/LinearLayout01"
07 android:layout_width="wrap_content" android:layout_height="wrap_content">
08 <Button android:id="@+id/Button01" android:layout_width="wrap_content"
09 android:layout_height="wrap_content" android:text="单个独立线程"></Button>
10 <Button android:id="@+id/Button02" android:layout_width="wrap_content"
11 android:layout_height="wrap_content" android:text="两个独立线程"></Button>
12 </LinearLayout>
13 <SurfaceView android:id="@+id/SurfaceView01"
14 android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
15 </LinearLayout>
[代码] TestSurfaceView.java
001 package com.testSurfaceView;
002
003 import java.lang.reflect.Field;
004 import java.util.ArrayList;
005 import android.app.Activity;
006 import android.graphics.Bitmap;
007 import android.graphics.BitmapFactory;
008 import android.graphics.Canvas;
009 import android.graphics.Paint;
010 import android.graphics.Rect;
011 import android.os.Bundle;
012 import android.util.Log;
013 import android.view.SurfaceHolder;
014 import android.view.SurfaceView;
015 import android.view.View;
016 import android.widget.Button;
017
018 public class TestSurfaceView extends Activity {
019 /** Called when the activity is first created. */
020 Button btnSingleThread, btnDoubleThread;
021 SurfaceView sfv;
022 SurfaceHolder sfh;
023 ArrayList<Integer> imgList = new ArrayList<Integer>();
024 int imgWidth, imgHeight;
025 Bitmap bitmap;//独立线程读取,独立线程绘图
026
027 @Override
028 public void onCreate(Bundle savedInstanceState) {
029 super.onCreate(savedInstanceState);
030 setContentView(R.layout.main);
031
032 btnSingleThread = (Button) this.findViewById(R.id.Button01);
033 btnDoubleThread = (Button) this.findViewById(R.id.Button02);
034 btnSingleThread.setOnClickListener(new ClickEvent());
035 btnDoubleThread.setOnClickListener(new ClickEvent());
036 sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
037 sfh = sfv.getHolder();
038 sfh.addCallback(new MyCallBack());// 自动运行surfaceCreated以及surfaceChanged
039 }
040
041 class ClickEvent implements View.OnClickListener {
042
043 @Override
044 public void onClick(View v) {
045
046 if (v == btnSingleThread) {
047 new Load_DrawImage(0, 0).start();//开一条线程读取并绘图
048 } else if (v == btnDoubleThread) {
049 new LoadImage().start();//开一条线程读取
050 new DrawImage(imgWidth + 10, 0).start();//开一条线程绘图
051 }
052
053 }
054
055 }
056
057 class MyCallBack implements SurfaceHolder.Callback {
058
059 @Override
060 public void surfaceChanged(SurfaceHolder holder, int format, int width,
061 int height) {
062 Log.i("Surface:", "Change");
063
064 }
065
066 @Override
067 public void surfaceCreated(SurfaceHolder holder) {
068 Log.i("Surface:", "Create");
069
070 // 用反射机制来获取资源中的图片ID和尺寸
071 Field[] fields = R.drawable.class.getDeclaredFields();
072 for (Field field : fields) {
073 if (!"icon".equals(field.getName()))// 除了icon之外的图片
074 {
075 int index = 0;
076 try {
077 index = field.getInt(R.drawable.class);
078 } catch (IllegalArgumentException e) {
079 // TODO Auto-generated catch block
080 e.printStackTrace();
081 } catch (IllegalAccessException e) {
082 // TODO Auto-generated catch block
083 e.printStackTrace();
084 }
085 // 保存图片ID
086 imgList.add(index);
087 }
088 }
089 // 取得图像大小
090 Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
091 imgList.get(0));
092 imgWidth = bmImg.getWidth();
093 imgHeight = bmImg.getHeight();
094 }
095
096 @Override
097 public void surfaceDestroyed(SurfaceHolder holder) {
098 Log.i("Surface:", "Destroy");
099
100 }
101
102 }
103
104 /**
105 * 读取并显示图片的线程
106 */
107 class Load_DrawImage extends Thread {
108 int x, y;
109 int imgIndex = 0;
110
111 public Load_DrawImage(int x, int y) {
112 this.x = x;
113 this.y = y;
114 }
115
116 public void run() {
117 while (true) {
118 Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
119 + imgWidth, this.y + imgHeight));
120 Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
121 imgList.get(imgIndex));
122 c.drawBitmap(bmImg, this.x, this.y, new Paint());
123 imgIndex++;
124 if (imgIndex == imgList.size())
125 imgIndex = 0;
126
127 sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
128 }
129 }
130 };
131
132 /**
133 * 只负责绘图的线程
134 */
135 class DrawImage extends Thread {
136 int x, y;
137
138 public DrawImage(int x, int y) {
139 this.x = x;
140 this.y = y;
141 }
142
143 public void run() {
144 while (true) {
145 if (bitmap != null) {//如果图像有效
146 Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
147 + imgWidth, this.y + imgHeight));
148
149 c.drawBitmap(bitmap, this.x, this.y, new Paint());
150
151 sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
152 }
153 }
154 }
155 };
156
157 /**
158 * 只负责读取图片的线程
159 */
160 class LoadImage extends Thread {
161 int imgIndex = 0;
162
163 public void run() {
164 while (true) {
165 bitmap = BitmapFactory.decodeResource(getResources(),
166 imgList.get(imgIndex));
167 imgIndex++;
168 if (imgIndex == imgList.size())//如果到尽头则重新读取
169 imgIndex = 0;
170 }
171 }
172 };
173 }
动态壁纸是一个Android应用程序,包括一个服务(WallpaperService)。该服务必须包括一个引擎(WallpaperService.Engine)。该引擎是连接用户、桌面、系统之间的桥梁。它也可以绘制桌面壁纸。
首先,必须由内在的Engine类创建一个WallpaperService类。该服务必须在AndroidManifest.xml中声明为"android.service.wallpaper.WallpaperService",这样它才会作为动态壁纸被手机识别。而且还要在服务配置中附加"android.permission.BIND_WALLPAPER"的权限许可:
01 <service
02 android:name="LiveWallpaperService"
03 android:enabled="true"
04 android:icon="@drawable/icon"
05 android:label="@string/app_name"
06 android:permission="android.permission.BIND_WALLPAPER">
07
08 <intent-filter android:priority="1" >
09 <action android:name="android.service.wallpaper.WallpaperService" />
10 </intent-filter>
11 <meta-data
12 android:name="android.service.wallpaper"
13 android:resource="@xml/wallpaper" />
14
15 </service>
创建一个XML文件,放置在应用程序目录下的/res/xml/中。它用来描述你的动态壁纸。
1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <wallpaper
4 xmlns:android="http://schemas.android.com/apk/res/android"
5 android:thumbnail="@drawable/thumbnail"
6 android:description="@string/description"
7 android:settingsActivity="PreferenceActivity"/>
再创建一个xml的属性文件 attrs.xml ,代码如下:
01 <declare-styleable name="Wallpaper">
02 <!-- Component name of an activity that allows the user to modify
03 the current settings for this wallpaper. -->
04 <attr name="settingsActivity" />
05
06 <!-- Reference to a the wallpaper's thumbnail bitmap. -->
07
08 <attr name="thumbnail" format="reference" />
09
10 <!-- Name of the author of this component, e.g. Google. -->
11 <attr name="author" format="reference" />
12
13
14 <!-- Short description of the component's purpose or behavior. -->
15 <attr name="description" />
16 </declare-styleable>
动态壁纸的服务代码如下:
001 package net.androgames.blog.sample.livewallpaper;
002
003 import android.content.SharedPreferences;
004 import android.service.wallpaper.WallpaperService;
005 import android.view.MotionEvent;
006 import android.view.SurfaceHolder;
007
008 /**
009 * Android Live Wallpaper Archetype
010 * @author antoine vianey
011 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013 public class LiveWallpaperService extends WallpaperService {
014
015 @Override
016 public Engine onCreateEngine() {
017 return new SampleEngine();
018 }
019
020 @Override
021 public void onCreate() {
022 super.onCreate();
023 }
024
025 @Override
026 public void onDestroy() {
027 super.onDestroy();
028 }
029
030 public class SampleEngine extends Engine {
031
032 private LiveWallpaperPainting painting;
033
034 SampleEngine() {
035 SurfaceHolder holder = getSurfaceHolder();
036 painting = new LiveWallpaperPainting(holder,
037 getApplicationContext());
038 }
039
040 @Override
041 public void onCreate(SurfaceHolder surfaceHolder) {
042 super.onCreate(surfaceHolder);
043 // register listeners and callbacks here
044 setTouchEventsEnabled(true);
045 }
046
047 @Override
048 public void onDestroy() {
049 super.onDestroy();
050 // remove listeners and callbacks here
051 painting.stopPainting();
052 }
053
054 @Override
055 public void onVisibilityChanged(boolean visible) {
056 if (visible) {
057 // register listeners and callbacks here
058 painting.resumePainting();
059 } else {
060 // remove listeners and callbacks here
061 painting.pausePainting();
062 }
063 }
064
065 @Override
066 public void onSurfaceChanged(SurfaceHolder holder, int format,
067 int width, int height) {
068 super.onSurfaceChanged(holder, format, width, height);
069 painting.setSurfaceSize(width, height);
070 }
071
072 @Override
073 public void onSurfaceCreated(SurfaceHolder holder) {
074 super.onSurfaceCreated(holder);
075 // start painting
076 painting.start();
077 }
078
079 @Override
080 public void onSurfaceDestroyed(SurfaceHolder holder) {
081 super.onSurfaceDestroyed(holder);
082 boolean retry = true;
083 painting.stopPainting();
084 while (retry) {
085 try {
086 painting.join();
087 retry = false;
088 } catch (InterruptedException e) {}
089 }
090 }
091
092 @Override
093 public void onOffsetsChanged(float xOffset, float yOffset,
094 float xStep, float yStep, int xPixels, int yPixels) {
095 }
096
097 @Override
098 public void onTouchEvent(MotionEvent event) {
099 super.onTouchEvent(event);
100 painting.doTouchEvent(event);
101 }
102
103 }
104
105 }
当壁纸的显示、状态或大小变化是,会调用Engine的onCreate, onDestroy, onVisibilityChanged, onSurfaceChanged, onSurfaceCreated 和 onSurfaceDestroyed方法。有了这些方法,动态壁纸才能展现出动画效果。而通过设置setTouchEventsEnabled(true),并且调用onTouchEvent(MotionEvent event)方法,来激活触摸事件。
我们在绘画墙纸的时候,也会使用一个单独的绘画线程:
001 package net.androgames.blog.sample.livewallpaper;
002
003 import android.content.Context;
004 import android.graphics.Canvas;
005 import android.view.MotionEvent;
006 import android.view.SurfaceHolder;
007
008 /**
009 * Android Live Wallpaper painting thread Archetype
010 * @author antoine vianey
011 * GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013 public class LiveWallpaperPainting extends Thread {
014
015 /** Reference to the View and the context */
016 private SurfaceHolder surfaceHolder;
017 private Context context;
018
019 /** State */
020 private boolean wait;
021 private boolean run;
022
023 /** Dimensions */
024 private int width;
025 private int height;
026
027 /** Time tracking */
028 private long previousTime;
029 private long currentTime;
030
031 public LiveWallpaperPainting(SurfaceHolder surfaceHolder,
032 Context context) {
033 // keep a reference of the context and the surface
034 // the context is needed if you want to inflate
035 // some resources from your livewallpaper .apk
036 this.surfaceHolder = surfaceHolder;
037 this.context = context;
038 // don't animate until surface is created and displayed
039 this.wait = true;
040 }
041
042 /**
043 * Pauses the live wallpaper animation
044 */
045 public void pausePainting() {
046 this.wait = true;
047 synchronized(this) {
048 this.notify();
049 }
050 }
051
052 /**
053 * Resume the live wallpaper animation
054 */
055 public void resumePainting() {
056 this.wait = false;
057 synchronized(this) {
058 this.notify();
059 }
060 }
061
062 /**
063 * Stop the live wallpaper animation
064 */
065 public void stopPainting() {
066 this.run = false;
067 synchronized(this) {
068 this.notify();
069 }
070 }
071
072 @Override
073 public void run() {
074 this.run = true;
075 Canvas c = null;
076 while (run) {
077 try {
078 c = this.surfaceHolder.lockCanvas(null);
079 synchronized (this.surfaceHolder) {
080 currentTime = System.currentTimeMillis();
081 updatePhysics();
082 doDraw(c);
083 previousTime = currentTime;
084 }
085 } finally {
086 if (c != null) {
087 this.surfaceHolder.unlockCanvasAndPost(c);
088 }
089 }
090 // pause if no need to animate
091 synchronized (this) {
092 if (wait) {
093 try {
094 wait();
095 } catch (Exception e) {}
096 }
097 }
098 }
099 }
100
101 /**
102 * Invoke when the surface dimension change
103 */
104 public void setSurfaceSize(int width, int height) {
105 this.width = width;
106 this.height = height;
107 synchronized(this) {
108 this.notify();
109 }
110 }
111
112 /**
113 * Invoke while the screen is touched
114 */
115 public void doTouchEvent(MotionEvent event) {
116 // handle the event here
117 // if there is something to animate
118 // then wake up
119 this.wait = false;
120 synchronized(this) {
121 notify();
122 }
123 }
124
125 /**
126 * Do the actual drawing stuff
127 */
128 private void doDraw(Canvas canvas) {}
129
130 /**
131 * Update the animation, sprites or whatever.
132 * If there is nothing to animate set the wait
133 * attribute of the thread to true
134 */
135 private void updatePhysics() {
136 // if nothing was updated :
137 // this.wait = true;
138 }
139
140 }
如果桌面壁纸是可见状态下,系统服务通知有新的东西,这个类会优先把它绘制在画布上。如果没有动画了,updatePhysics会通知线程去等待。通常SurfaceView在有两个画布交替绘制的时候,会在画布上绘制上一次......
如果要让你的动态墙纸有配置功能,只要创建一个PreferenceActivity,并将它在wallpaper.xml文件中声明。同时让SharedPreference对象可以找到你的配置选项。
教程就写到这里,如果还有什么不懂,你可以通过Eclipse来浏览完整的源代码:SampleLiveWallpaper=>[url]http://code.google.com/p/androgames-sample/
[/url]
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
标签: SurfaceView 双缓冲 Android SDK
代码片段(3)
[图片] 程序运行截图
[代码] main.xml
01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03 android:layout_width="fill_parent" android:layout_height="fill_parent"
04 android:orientation="vertical">
05
06 <LinearLayout android:id="@+id/LinearLayout01"
07 android:layout_width="wrap_content" android:layout_height="wrap_content">
08 <Button android:id="@+id/Button01" android:layout_width="wrap_content"
09 android:layout_height="wrap_content" android:text="单个独立线程"></Button>
10 <Button android:id="@+id/Button02" android:layout_width="wrap_content"
11 android:layout_height="wrap_content" android:text="两个独立线程"></Button>
12 </LinearLayout>
13 <SurfaceView android:id="@+id/SurfaceView01"
14 android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
15 </LinearLayout>
[代码] TestSurfaceView.java
001 package com.testSurfaceView;
002
003 import java.lang.reflect.Field;
004 import java.util.ArrayList;
005 import android.app.Activity;
006 import android.graphics.Bitmap;
007 import android.graphics.BitmapFactory;
008 import android.graphics.Canvas;
009 import android.graphics.Paint;
010 import android.graphics.Rect;
011 import android.os.Bundle;
012 import android.util.Log;
013 import android.view.SurfaceHolder;
014 import android.view.SurfaceView;
015 import android.view.View;
016 import android.widget.Button;
017
018 public class TestSurfaceView extends Activity {
019 /** Called when the activity is first created. */
020 Button btnSingleThread, btnDoubleThread;
021 SurfaceView sfv;
022 SurfaceHolder sfh;
023 ArrayList<Integer> imgList = new ArrayList<Integer>();
024 int imgWidth, imgHeight;
025 Bitmap bitmap;//独立线程读取,独立线程绘图
026
027 @Override
028 public void onCreate(Bundle savedInstanceState) {
029 super.onCreate(savedInstanceState);
030 setContentView(R.layout.main);
031
032 btnSingleThread = (Button) this.findViewById(R.id.Button01);
033 btnDoubleThread = (Button) this.findViewById(R.id.Button02);
034 btnSingleThread.setOnClickListener(new ClickEvent());
035 btnDoubleThread.setOnClickListener(new ClickEvent());
036 sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
037 sfh = sfv.getHolder();
038 sfh.addCallback(new MyCallBack());// 自动运行surfaceCreated以及surfaceChanged
039 }
040
041 class ClickEvent implements View.OnClickListener {
042
043 @Override
044 public void onClick(View v) {
045
046 if (v == btnSingleThread) {
047 new Load_DrawImage(0, 0).start();//开一条线程读取并绘图
048 } else if (v == btnDoubleThread) {
049 new LoadImage().start();//开一条线程读取
050 new DrawImage(imgWidth + 10, 0).start();//开一条线程绘图
051 }
052
053 }
054
055 }
056
057 class MyCallBack implements SurfaceHolder.Callback {
058
059 @Override
060 public void surfaceChanged(SurfaceHolder holder, int format, int width,
061 int height) {
062 Log.i("Surface:", "Change");
063
064 }
065
066 @Override
067 public void surfaceCreated(SurfaceHolder holder) {
068 Log.i("Surface:", "Create");
069
070 // 用反射机制来获取资源中的图片ID和尺寸
071 Field[] fields = R.drawable.class.getDeclaredFields();
072 for (Field field : fields) {
073 if (!"icon".equals(field.getName()))// 除了icon之外的图片
074 {
075 int index = 0;
076 try {
077 index = field.getInt(R.drawable.class);
078 } catch (IllegalArgumentException e) {
079 // TODO Auto-generated catch block
080 e.printStackTrace();
081 } catch (IllegalAccessException e) {
082 // TODO Auto-generated catch block
083 e.printStackTrace();
084 }
085 // 保存图片ID
086 imgList.add(index);
087 }
088 }
089 // 取得图像大小
090 Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
091 imgList.get(0));
092 imgWidth = bmImg.getWidth();
093 imgHeight = bmImg.getHeight();
094 }
095
096 @Override
097 public void surfaceDestroyed(SurfaceHolder holder) {
098 Log.i("Surface:", "Destroy");
099
100 }
101
102 }
103
104 /**
105 * 读取并显示图片的线程
106 */
107 class Load_DrawImage extends Thread {
108 int x, y;
109 int imgIndex = 0;
110
111 public Load_DrawImage(int x, int y) {
112 this.x = x;
113 this.y = y;
114 }
115
116 public void run() {
117 while (true) {
118 Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
119 + imgWidth, this.y + imgHeight));
120 Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
121 imgList.get(imgIndex));
122 c.drawBitmap(bmImg, this.x, this.y, new Paint());
123 imgIndex++;
124 if (imgIndex == imgList.size())
125 imgIndex = 0;
126
127 sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
128 }
129 }
130 };
131
132 /**
133 * 只负责绘图的线程
134 */
135 class DrawImage extends Thread {
136 int x, y;
137
138 public DrawImage(int x, int y) {
139 this.x = x;
140 this.y = y;
141 }
142
143 public void run() {
144 while (true) {
145 if (bitmap != null) {//如果图像有效
146 Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
147 + imgWidth, this.y + imgHeight));
148
149 c.drawBitmap(bitmap, this.x, this.y, new Paint());
150
151 sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
152 }
153 }
154 }
155 };
156
157 /**
158 * 只负责读取图片的线程
159 */
160 class LoadImage extends Thread {
161 int imgIndex = 0;
162
163 public void run() {
164 while (true) {
165 bitmap = BitmapFactory.decodeResource(getResources(),
166 imgList.get(imgIndex));
167 imgIndex++;
168 if (imgIndex == imgList.size())//如果到尽头则重新读取
169 imgIndex = 0;
170 }
171 }
172 };
173 }
发表评论
-
ScrollView嵌套Edittext
2015-04-08 18:26 839scrollview 中加入多个控件如 edittext 后会 ... -
android 布局式跑马灯,非TextView
2015-04-07 10:51 493如题,简单的实现了跑马灯效果,把Scroll.java放入an ... -
Android圆角图片
2015-03-11 17:44 692my_wane_shape.xml 快速圆角背景边框实现, ... -
SQLite多线程读写实践及常见问题总结
2015-02-13 17:06 941基本操作的部分,大家都很熟悉了,这里根据个人切身经验,总结了一 ... -
android加速度感应
2015-01-19 10:25 14711.android测量数据 (1)android设备坐标系 ... -
MatrixCursor的使用
2015-01-19 09:49 1043ContentProvider对外共享数据的时候的query( ... -
Android 获取控件的宽高高级用法(MeasureSpec)
2015-01-15 14:23 995一个MeasureSpec封装了父 ... -
Android_GridView_GridView概述及实现水平滑动
2015-01-14 17:14 11501.GridView简介 GridView是ViewGroup ... -
Android MMS,SMS之常用Uri
2014-09-19 16:32 1321Android MMS,SMS之常用Uri Android ... -
android短彩信查询以及MMS表结构
2014-09-19 16:31 2230android短信的数据库的Uri是不公开的, 读取起来时灰常 ... -
使用Android自带DownloadManager下载文件
2014-08-19 11:04 766SDK在API Level 9中加入了DownloadMan ... -
android textview里链接点击事件,增加图片
2014-08-07 16:45 1143Android系统默认给TextView插入图片提供了三种方 ... -
android Home事件汇总
2014-07-18 11:30 1001方法一:android 4.0以后无法通过更改页面的类型来 ... -
Android风格与主题(style and theme)
2014-07-16 16:35 670Android xml风格和主题文 ... -
Android中播放声音的两种方法
2014-05-30 15:09 667在Android中,音频、视 ... -
android 杀进程方法
2014-05-26 17:43 1005关闭应用的方法: 1.System.exit(0); ... -
android service 生命周期
2014-04-21 16:16 786有了 Service 类我们如何启动他呢,有两种方法: ... -
解决ADB端口被占用的问题
2014-04-21 16:14 830究其源就是adb server没启动 经过分析整理如下: ... -
输入法隐藏打开
2013-12-23 14:24 816首次进入activity,如果有个edittex ... -
google经纬度互转
2013-07-11 16:34 934https://developers.google.com/ ...
相关推荐
在Android开发中,`SurfaceView`是一个非常重要的视图组件,尤其在处理视频播放、游戏渲染等高性能图形操作时,它的优势在于拥有独立于主线程的渲染机制,能避免阻塞UI更新。然而,由于其特殊的性质,直接通过常规...
在Android开发中,`SurfaceView`是一个特殊类型的`View`,它允许开发者在应用程序中创建一个独立于应用程序主线程的渲染表面。`SurfaceView`通常用于处理高性能的图形或者视频播放,因为它可以在单独的线程中进行...
SurfaceView是Android系统提供的一种特殊的View,主要用于处理高性能、低延迟的图形绘制,例如游戏画面或者视频播放等场景。它的核心特性在于拥有自己的独立线程(SurfaceThread)来管理Surface的绘制,这使得...
在Android平台上,SurfaceView是一种特殊的视图,常用于处理高性能的图形渲染,比如播放视频或者游戏画面。在视频播放场景中,SurfaceView提供了一个高效且低延迟的显示机制,能够将视频帧直接绘制到Surface上,减少...
在Android开发中,SurfaceView是一种特殊的视图,它允许开发者创建高性能的图形界面,尤其适合处理视频播放或游戏等需要连续刷新的场景。本话题主要探讨如何利用SurfaceView实现图片的缩放和滑动功能。 首先,理解...
在Android开发中,SurfaceView是一种特殊的视图,主要用于处理高性能的图形绘制,比如视频播放、游戏画面等。SurfaceView创建了一个独立的Surface,其后台线程可以无阻塞地进行绘图操作,提高了渲染效率。然而,由于...
SurfaceView是Android系统中一种特殊的View,它在处理多媒体或者高性能图形渲染时有着独特的优势。在移动设备上,用户交互通常涉及到触摸事件,包括单点触摸和多点触控。多点触控允许用户通过两个或更多的手指进行...
在Android开发中,`SurfaceView` 是一个非常重要的视图组件,主要用于处理视频播放、游戏图形等高性能的图像渲染任务。`SurfaceView` 提供了一个独立的绘图表面,可以与应用程序主线程分离,从而避免UI阻塞,提高...
这个场景就涉及到了Android的SurfaceView以及如何定制它的显示效果。本文将深入探讨如何实现一个圆形的相机预览窗口。 首先,让我们理解SurfaceView的基本概念。SurfaceView是Android系统提供的一种用于显示多媒体...
`SurfaceView`是Android提供的一种用于高效显示多媒体数据的视图组件,尤其适合处理像视频流这样的实时数据。本篇文章将深入探讨如何利用`SurfaceView`实现实时显示摄像头视频。 首先,我们需要了解`SurfaceView`的...
SurfaceView在Android开发中是一个非常重要的视图组件,它被设计用于处理高性能、低延迟的图形渲染,尤其适合游戏和视频等对实时性要求较高的应用。这个示例项目旨在展示如何在Android平台上实现高速显示功能。 ...
本篇文章将深入探讨`SurfaceView`与`layout`在屏幕适配中的应用及其策略。 首先,`SurfaceView`是Android提供的一种特殊视图,它允许开发者在应用程序中直接进行硬件加速的图形绘制,如视频播放、游戏画面等高性能...
在Android开发中,SurfaceView是一种特殊的视图,用于高效地显示动态图像,比如播放视频或者进行游戏渲染。标题“SurfaceView放大和缩小”涉及到的是如何在Android应用中实现对SurfaceView内的视频画面进行局部放大...
SurfaceView和TextureView均继承于android.view.View 与其它View不同的是,两者都能在独立的线程中绘制和渲染,在专用的GPU线程中大大提高渲染的性能。 一、SurfaceView专门提供了嵌入视图层级的绘制界面,开发者...
SurfaceView是Android系统中一种特殊的View,主要用于处理高性能、低延迟的图形绘制和视频播放等任务。在Android应用开发中,尤其是涉及到视频播放时,SurfaceView是一个非常关键的组件。这个例子是基于SurfaceView...
在Android开发中,SurfaceView是一种特殊的视图,用于在应用程序中显示视频流或者进行高性能的图形绘制。在处理多个SurfaceView的切换时,可能会遇到一些问题,这些问题可能与SurfaceView的生命周期、渲染机制以及...
在Android开发中,`SurfaceView`是一个特殊类型的视图,常用于处理高性能的图形和视频渲染,例如游戏或相机预览。它拥有自己的独立于主线程的渲染表面,可以提供流畅的动画效果,且对CPU和GPU资源的利用更为高效。`...