SurfaceView实现双缓冲避免游戏闪屏

来源:互联网 发布:软件安装调试方案 编辑:程序博客网 时间:2024/06/06 05:34
双缓冲技术是游戏开发中的一个重要的技术,主要原理是:当一个动画争先显示时,程序又在改变它,前面还没有显示完,程序又请求重新绘制,这样屏幕就会不停地闪烁。而双驵冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上
双缓冲是为了防止动画闪烁而实现的一种多线程应用,基于SurfaceView的双缓冲实现很简单,开一条线程并在其中绘图即可。本文介绍基于SurfaceView的双缓冲实现,以及介绍类似的更高效的实现方法。 

本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:

对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都 “边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高双缓冲的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。

[1].[图片] 程序运行截图 

[2].[代码] 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>

[3].[代码] TestSurfaceView.java 

001package com.testSurfaceView;
002 
003import java.lang.reflect.Field;
004import java.util.ArrayList;
005import android.app.Activity;
006import android.graphics.Bitmap;
007import android.graphics.BitmapFactory;
008import android.graphics.Canvas;
009import android.graphics.Paint;
010import android.graphics.Rect;
011import android.os.Bundle;
012import android.util.Log;
013import android.view.SurfaceHolder;
014import android.view.SurfaceView;
015import android.view.View;
016import android.widget.Button;
017 
018public 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(00).start();//开一条线程读取并绘图
048            else if (v == btnDoubleThread) {
049                new LoadImage().start();//开一条线程读取
050                new DrawImage(imgWidth + 100).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}

0 0
原创粉丝点击