[置顶] Android 的 SurfaceView 双缓冲应用
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法。
本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:
对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。
程序运行截图

[代码] main.xml
01 |
<?xml version="1.0" encoding="utf-8"?> |
02 |
<LinearLayoutxmlns: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 |
} |

浙公网安备 33010602011771号