Android 的 SurfaceView 双缓冲应用
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
[代码] 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" > |
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 > |
13 |
< SurfaceView android:id = "@+id/SurfaceView01" |
14 |
android:layout_width = "fill_parent" android:layout_height = "fill_parent" ></ SurfaceView > |
[代码] TestSurfaceView.java
001 |
package com.testSurfaceView; |
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; |
018 |
public class TestSurfaceView extends Activity { |
019 |
/** Called when the activity is first created. */ |
020 |
Button btnSingleThread, btnDoubleThread; |
023 |
ArrayList<Integer> imgList = new ArrayList<Integer>(); |
024 |
int imgWidth, imgHeight; |
028 |
public void onCreate(Bundle savedInstanceState) { |
029 |
super .onCreate(savedInstanceState); |
030 |
setContentView(R.layout.main); |
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()); |
041 |
class ClickEvent implements View.OnClickListener { |
044 |
public void onClick(View v) { |
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(); |
057 |
class MyCallBack implements SurfaceHolder.Callback { |
060 |
public void surfaceChanged(SurfaceHolder holder, int format, int width, |
062 |
Log.i( "Surface:" , "Change" ); |
067 |
public void surfaceCreated(SurfaceHolder holder) { |
068 |
Log.i( "Surface:" , "Create" ); |
071 |
Field[] fields = R.drawable. class .getDeclaredFields(); |
072 |
for (Field field : fields) { |
073 |
if (! "icon" .equals(field.getName())) |
077 |
index = field.getInt(R.drawable. class ); |
078 |
} catch (IllegalArgumentException e) { |
081 |
} catch (IllegalAccessException e) { |
090 |
Bitmap bmImg = BitmapFactory.decodeResource(getResources(), |
092 |
imgWidth = bmImg.getWidth(); |
093 |
imgHeight = bmImg.getHeight(); |
097 |
public void surfaceDestroyed(SurfaceHolder holder) { |
098 |
Log.i( "Surface:" , "Destroy" ); |
107 |
class Load_DrawImage extends Thread { |
111 |
public Load_DrawImage( int x, int y) { |
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()); |
124 |
if (imgIndex == imgList.size()) |
127 |
sfh.unlockCanvasAndPost(c); |
135 |
class DrawImage extends Thread { |
138 |
public DrawImage( int x, int y) { |
145 |
if (bitmap != null ) { |
146 |
Canvas c = sfh.lockCanvas( new Rect( this .x, this .y, this .x |
147 |
+ imgWidth, this .y + imgHeight)); |
149 |
c.drawBitmap(bitmap, this .x, this .y, new Paint()); |
151 |
sfh.unlockCanvasAndPost(c); |
160 |
class LoadImage extends Thread { |
165 |
bitmap = BitmapFactory.decodeResource(getResources(), |
166 |
imgList.get(imgIndex)); |
168 |
if (imgIndex == imgList.size()) |