`

SurfaceView

 
阅读更多
动态壁纸是在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中SurfaceView截屏

    在Android开发中,`SurfaceView`是一个非常重要的视图组件,尤其在处理视频播放、游戏渲染等高性能图形操作时,它的优势在于拥有独立于主线程的渲染机制,能避免阻塞UI更新。然而,由于其特殊的性质,直接通过常规...

    android surfaceview 悬浮窗圆角 windowmanager

    在Android开发中,`SurfaceView`是一个特殊类型的`View`,它允许开发者在应用程序中创建一个独立于应用程序主线程的渲染表面。`SurfaceView`通常用于处理高性能的图形或者视频播放,因为它可以在单独的线程中进行...

    最简单的surfaceview画图和截图

    SurfaceView是Android系统提供的一种特殊的View,主要用于处理高性能、低延迟的图形绘制,例如游戏画面或者视频播放等场景。它的核心特性在于拥有自己的独立线程(SurfaceThread)来管理Surface的绘制,这使得...

    Android下使用SurfaceView播放视频文件

    在Android平台上,SurfaceView是一种特殊的视图,常用于处理高性能的图形渲染,比如播放视频或者游戏画面。在视频播放场景中,SurfaceView提供了一个高效且低延迟的显示机制,能够将视频帧直接绘制到Surface上,减少...

    Android SurfaceView 实现图片缩放 滑动

    在Android开发中,SurfaceView是一种特殊的视图,它允许开发者创建高性能的图形界面,尤其适合处理视频播放或游戏等需要连续刷新的场景。本话题主要探讨如何利用SurfaceView实现图片的缩放和滑动功能。 首先,理解...

    在surfaceview上放置控件

    在Android开发中,SurfaceView是一种特殊的视图,主要用于处理高性能的图形绘制,比如视频播放、游戏画面等。SurfaceView创建了一个独立的Surface,其后台线程可以无阻塞地进行绘图操作,提高了渲染效率。然而,由于...

    surfaceview 多点触控移动放大缩小

    SurfaceView是Android系统中一种特殊的View,它在处理多媒体或者高性能图形渲染时有着独特的优势。在移动设备上,用户交互通常涉及到触摸事件,包括单点触摸和多点触控。多点触控允许用户通过两个或更多的手指进行...

    SurfaceView增加放大缩小视频功能

    在Android开发中,`SurfaceView` 是一个非常重要的视图组件,主要用于处理视频播放、游戏图形等高性能的图像渲染任务。`SurfaceView` 提供了一个独立的绘图表面,可以与应用程序主线程分离,从而避免UI阻塞,提高...

    Android圆形相机预览窗口,圆形SurfaceView

    这个场景就涉及到了Android的SurfaceView以及如何定制它的显示效果。本文将深入探讨如何实现一个圆形的相机预览窗口。 首先,让我们理解SurfaceView的基本概念。SurfaceView是Android系统提供的一种用于显示多媒体...

    Android SurfaceView 实现实时显示摄像头视频

    `SurfaceView`是Android提供的一种用于高效显示多媒体数据的视图组件,尤其适合处理像视频流这样的实时数据。本篇文章将深入探讨如何利用`SurfaceView`实现实时显示摄像头视频。 首先,我们需要了解`SurfaceView`的...

    surfaceview

    SurfaceView在Android开发中是一个非常重要的视图组件,它被设计用于处理高性能、低延迟的图形渲染,尤其适合游戏和视频等对实时性要求较高的应用。这个示例项目旨在展示如何在Android平台上实现高速显示功能。 ...

    Android_surfaceView与layout屏幕适配总结

    本篇文章将深入探讨`SurfaceView`与`layout`在屏幕适配中的应用及其策略。 首先,`SurfaceView`是Android提供的一种特殊视图,它允许开发者在应用程序中直接进行硬件加速的图形绘制,如视频播放、游戏画面等高性能...

    SurfaceView放大和缩小

    在Android开发中,SurfaceView是一种特殊的视图,用于高效地显示动态图像,比如播放视频或者进行游戏渲染。标题“SurfaceView放大和缩小”涉及到的是如何在Android应用中实现对SurfaceView内的视频画面进行局部放大...

    Android中TextureView与SurfaceView用法区别总结

    SurfaceView和TextureView均继承于android.view.View 与其它View不同的是,两者都能在独立的线程中绘制和渲染,在专用的GPU线程中大大提高渲染的性能。 一、SurfaceView专门提供了嵌入视图层级的绘制界面,开发者...

    SurfaceView视频播放功能

    SurfaceView是Android系统中一种特殊的View,主要用于处理高性能、低延迟的图形绘制和视频播放等任务。在Android应用开发中,尤其是涉及到视频播放时,SurfaceView是一个非常关键的组件。这个例子是基于SurfaceView...

    多个surfaceview的切换有问题

    在Android开发中,SurfaceView是一种特殊的视图,用于在应用程序中显示视频流或者进行高性能的图形绘制。在处理多个SurfaceView的切换时,可能会遇到一些问题,这些问题可能与SurfaceView的生命周期、渲染机制以及...

    DrawImage SurfaceView 多层绘制透明图

    在Android开发中,`SurfaceView`是一个特殊类型的视图,常用于处理高性能的图形和视频渲染,例如游戏或相机预览。它拥有自己的独立于主线程的渲染表面,可以提供流畅的动画效果,且对CPU和GPU资源的利用更为高效。`...

Global site tag (gtag.js) - Google Analytics