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

[代码] main.xml
| 01 | <?xmlversion="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 |     <LinearLayoutandroid:id="@+id/LinearLayout01" | 
| 07 |         android:layout_width="wrap_content"android:layout_height="wrap_content">  | 
| 08 |         <Buttonandroid:id="@+id/Button01"android:layout_width="wrap_content" | 
| 09 |             android:layout_height="wrap_content"android:text="单个独立线程"></Button>  | 
| 10 |         <Buttonandroid:id="@+id/Button02"android:layout_width="wrap_content" | 
| 11 |             android:layout_height="wrap_content"android:text="两个独立线程"></Button>  | 
| 12 |     </LinearLayout>  | 
| 13 |     <SurfaceViewandroid:id="@+id/SurfaceView01" | 
| 14 |         android:layout_width="fill_parent"android:layout_height="fill_parent"></SurfaceView>  | 
| 15 | </LinearLayout> | 
[代码] TestSurfaceView.java
| 001 | packagecom.testSurfaceView;  | 
| 002 |   | 
| 003 | importjava.lang.reflect.Field;  | 
| 004 | importjava.util.ArrayList;  | 
| 005 | importandroid.app.Activity;  | 
| 006 | importandroid.graphics.Bitmap;  | 
| 007 | importandroid.graphics.BitmapFactory;  | 
| 008 | importandroid.graphics.Canvas;  | 
| 009 | importandroid.graphics.Paint;  | 
| 010 | importandroid.graphics.Rect;  | 
| 011 | importandroid.os.Bundle;  | 
| 012 | importandroid.util.Log;  | 
| 013 | importandroid.view.SurfaceHolder;  | 
| 014 | importandroid.view.SurfaceView;  | 
| 015 | importandroid.view.View;  | 
| 016 | importandroid.widget.Button;  | 
| 017 |   | 
| 018 | publicclassTestSurfaceView extendsActivity {  | 
| 019 |     /** Called when the activity is first created. */ | 
| 020 |     Button btnSingleThread, btnDoubleThread;  | 
| 021 |     SurfaceView sfv;  | 
| 022 |     SurfaceHolder sfh;  | 
| 023 |     ArrayList<Integer> imgList = newArrayList<Integer>();  | 
| 024 |     intimgWidth, imgHeight;  | 
| 025 |     Bitmap bitmap;//独立线程读取,独立线程绘图  | 
| 026 |   | 
| 027 |     @Override | 
| 028 |     publicvoidonCreate(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(newClickEvent());  | 
| 035 |         btnDoubleThread.setOnClickListener(newClickEvent());  | 
| 036 |         sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);  | 
| 037 |         sfh = sfv.getHolder();  | 
| 038 |         sfh.addCallback(newMyCallBack());// 自动运行surfaceCreated以及surfaceChanged  | 
| 039 |     }  | 
| 040 |   | 
| 041 |     classClickEvent implementsView.OnClickListener {  | 
| 042 |   | 
| 043 |         @Override | 
| 044 |         publicvoidonClick(View v) {  | 
| 045 |   | 
| 046 |             if(v == btnSingleThread) {  | 
| 047 |                 newLoad_DrawImage(0, 0).start();//开一条线程读取并绘图  | 
| 048 |             } elseif(v == btnDoubleThread) {  | 
| 049 |                 newLoadImage().start();//开一条线程读取  | 
| 050 |                 newDrawImage(imgWidth + 10, 0).start();//开一条线程绘图  | 
| 051 |             }  | 
| 052 |   | 
| 053 |         }  | 
| 054 |   | 
| 055 |     }  | 
| 056 |   | 
| 057 |     classMyCallBack implementsSurfaceHolder.Callback {  | 
| 058 |   | 
| 059 |         @Override | 
| 060 |         publicvoidsurfaceChanged(SurfaceHolder holder, intformat, intwidth,  | 
| 061 |                 intheight) {  | 
| 062 |             Log.i("Surface:", "Change");  | 
| 063 |   | 
| 064 |         }  | 
| 065 |   | 
| 066 |         @Override | 
| 067 |         publicvoidsurfaceCreated(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 |                     intindex = 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 |         publicvoidsurfaceDestroyed(SurfaceHolder holder) {  | 
| 098 |             Log.i("Surface:", "Destroy");  | 
| 099 |   | 
| 100 |         }  | 
| 101 |   | 
| 102 |     }  | 
| 103 |   | 
| 104 |     /**  | 
| 105 |      * 读取并显示图片的线程  | 
| 106 |      */ | 
| 107 |     classLoad_DrawImage extendsThread {  | 
| 108 |         intx, y;  | 
| 109 |         intimgIndex = 0;  | 
| 110 |   | 
| 111 |         publicLoad_DrawImage(intx, inty) {  | 
| 112 |             this.x = x;  | 
| 113 |             this.y = y;  | 
| 114 |         }  | 
| 115 |   | 
| 116 |         publicvoidrun() {  | 
| 117 |             while(true) {  | 
| 118 |                 Canvas c = sfh.lockCanvas(newRect(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, newPaint());  | 
| 123 |                 imgIndex++;  | 
| 124 |                 if(imgIndex == imgList.size())  | 
| 125 |                     imgIndex = 0;  | 
| 126 |   | 
| 127 |                 sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容  | 
| 128 |             }  | 
| 129 |         }  | 
| 130 |     };  | 
| 131 |   | 
| 132 |     /**  | 
| 133 |      * 只负责绘图的线程  | 
| 134 |      */ | 
| 135 |     classDrawImage extendsThread {  | 
| 136 |         intx, y;  | 
| 137 |   | 
| 138 |         publicDrawImage(intx, inty) {  | 
| 139 |             this.x = x;  | 
| 140 |             this.y = y;  | 
| 141 |         }  | 
| 142 |   | 
| 143 |         publicvoidrun() {  | 
| 144 |             while(true) {  | 
| 145 |                 if(bitmap != null) {//如果图像有效  | 
| 146 |                     Canvas c = sfh.lockCanvas(newRect(this.x, this.y, this.x  | 
| 147 |                             + imgWidth, this.y + imgHeight));  | 
| 148 |   | 
| 149 |                     c.drawBitmap(bitmap, this.x, this.y, newPaint());  | 
| 150 |   | 
| 151 |                     sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容  | 
| 152 |                 }  | 
| 153 |             }  | 
| 154 |         }  | 
| 155 |     };  | 
| 156 |   | 
| 157 |     /**  | 
| 158 |      * 只负责读取图片的线程  | 
| 159 |      */ | 
| 160 |     classLoadImage extendsThread {  | 
| 161 |         intimgIndex = 0;  | 
| 162 |   | 
| 163 |         publicvoidrun() {  | 
| 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号
浙公网安备 33010602011771号