双缓冲绘图
来源:互联网 发布:飞思卡尔单片机官网 编辑:程序博客网 时间:2024/05/29 09:15
原文地址:http://blog.csdn.net/blogercn/article/details/7404485
对其代码做了一些修改,把刷新间隔由100ms改为了10ms,使得在真机上能看到分别;第三个例子做了稍多的修改,因为在我的真机上出现圆都画到最大后,就停止了,界面不再变动了,因此加入清除双缓冲画面的代码,使能重绘;去掉了对资源中的图片使用,使直接复制粘贴改包名就能运行。
双缓冲绘图
双缓冲是图像编程中很重要的概念,在电脑的图像处理中就常常使用双缓冲来加快图像显示速度,消除图像刷新时的闪烁现象,提升用户体验。双缓冲为图像加速,提升显示速度,提高显示质量的原理是:计算机访问显示屏和磁盘的速度远远小于CPU缓存和内存,每一次调用绘图函数往显示屏刷新数据,即使你的显示内容已经加载到了内存,但每一次访问显示屏,仍然会花费比内存大得多的时间,如果你的资源里有一百个图片,那么直接把他们全刷到屏幕上需要调用一百次drawBitmap方法,每次都需要访问显示屏,就会导致显示过程效率低下。这时如果使用双缓冲,创建一块屏幕客户区大小的BUFFER,把这一百个图片全画到BUFFER上,只就需要访问一百次内存,最后访问一次显示屏,把这块BUFFER刷到显示屏上就行了,如果内存与外设的速度比是一比十,那么几乎可以节约十分之九的时间,速度当然快了,这就是双缓冲技术。
那么安卓编程是否需要双缓冲?安卓surfaceview和双缓冲有什么关系?
下面通过三个例子说明,三个例子内容一样,5*8个不停放缩的实心圆:
1.不使用双缓冲,直接使用onDraw往屏幕刷数据。代码如下:
package com.Double;import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.drawable.BitmapDrawable;import android.os.Bundle;import android.view.View;public class TestDoubleActivity extends Activity { MyView mv; float m_circle_r = 10; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); mv = new MyView(this); setContentView(mv); Timer timer = new Timer(); timer.scheduleAtFixedRate(new MyTask(), 1, 10); } public class MyView extends View { MyView(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLUE); paint.setStrokeWidth(10); paint.setStyle(Style.FILL); if (m_circle_r >= (getWidth()/10)) { m_circle_r = 0; } else { m_circle_r++; } for (int i = 0; i < 5; i++) for (int j = 0; j < 8; j++) canvas.drawCircle((getWidth()/5)*i+(getWidth()/10), (getHeight()/8)*j+(getHeight()/16), m_circle_r, paint); } } private class MyTask extends TimerTask{ @Override public void run() { mv.postInvalidate(); } } }
2.使用双缓冲,同样的代码,创建一块和屏幕大小一样的Bitmap,把需要画的内存提前画在一个tmp的Bitmap上,最后把tmp刷到屏上,代码如下:
package com.myDouble;import java.util.Timer;import java.util.TimerTask;import android.app.Activity;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.graphics.drawable.BitmapDrawable;import android.os.Bundle;import android.view.View;import android.view.Window;import android.view.WindowManager;public class DoubleActivity extends Activity {MyView mv;float m_circle_r = 10;Bitmap tmp;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// setContentView(R.layout.main);mv = new MyView(this);setContentView(mv);Timer timer = new Timer();timer.scheduleAtFixedRate(new MyTask(), 1, 10);}public class MyView extends View {MyView(Context context) {super(context);}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubsuper.onDraw(canvas);Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setColor(Color.BLUE);paint.setStrokeWidth(10);paint.setStyle(Style.FILL);if (m_circle_r >= (getWidth() / 10)) {m_circle_r = 0;} else {m_circle_r++;}/* 创建Canvas对象 */Canvas mCanvas = new Canvas();/* 创建屏幕大小的缓冲区 tmp*/tmp = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);/* 设置将内容绘制在tmp上 */mCanvas.setBitmap(tmp);//把5*8个圆绘制在tmp上for (int i = 0; i < 5; i++)for (int j = 0; j < 8; j++)mCanvas.drawCircle((getWidth() / 5) * i + (getWidth() / 10),(getHeight() / 8) * j + (getHeight() / 16),m_circle_r, paint);//把tmp绘制在物理设备上canvas.drawBitmap(tmp, 0, 0, paint);}}private class MyTask extends TimerTask {@Overridepublic void run() {mv.postInvalidate();}}}
两个例子对比后发现,如果这些图形使用VC编写,不使用双缓冲动画会闪得很厉害,使用双缓冲动画就会很流畅。而在安卓上,不使用双缓冲图像也很顺畅,而使用了双缓冲后,动画反而比不使用更卡,根据结果的推测,应该是安卓平台onDraw已经经过了双缓冲优化,而再次使用双缓冲时,反而不仅仅增大了系统内存开销,而且比原来多使用了一次图像拷贝操作,是而效率反而低了不少。再看下面使用surface的例子:
package com.DoubleBuffer3;import android.app.Activity;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Paint.Style;import android.os.Bundle;import android.util.Log;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceHolder.Callback;import android.view.SurfaceView;public class DoubleBuffer3Activity extends Activity{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(new MySurfaceView(this));}}class MySurfaceView extends SurfaceView implements Runnable, Callback{private static final String TAG = "MySurfaceView";private SurfaceHolder mHolder; // 用于控制SurfaceViewprivate Thread t; // 声明一条线程private volatile boolean flag; // 线程运行的标识,用于控制线程private Canvas mCanvas; // 声明一张画布private Paint p; // 声明一支画笔float mRadius = 10;private int mWidth;public MySurfaceView(Context context){super(context);mHolder = getHolder(); // 获得SurfaceHolder对象mHolder.addCallback(this); // 为SurfaceView添加状态监听p = new Paint(); // 创建一个画笔对象p.setColor(Color.WHITE); // 设置画笔的颜色为白色setFocusable(true); // 设置焦点}/** * 当SurfaceView创建的时候,调用此函数 */@Overridepublic void surfaceCreated(SurfaceHolder holder){t = new Thread(this); // 创建一个线程对象flag = true; // 把线程运行的标识设置成truet.start(); // 启动线程}/** * 当SurfaceView的视图发生改变的时候,调用此函数 */@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height){mWidth = width;}/** * 当SurfaceView销毁的时候,调用此函数 */@Overridepublic void surfaceDestroyed(SurfaceHolder holder){flag = false; // 把线程运行的标识设置成falsemHolder.removeCallback(this);}@Overridepublic void run(){while (flag){synchronized (mHolder){Draw(); // 调用自定义画画方法}}}/** * 自定义一个方法,在画布上画一个圆 */protected void Draw(){mCanvas = mHolder.lockCanvas(); // 获得画布对象,开始对画布画画if (mCanvas != null){Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setColor(Color.BLUE);paint.setStrokeWidth(10);paint.setStyle(Style.FILL);if (mRadius >= (mWidth / 10)){mCanvas.drawColor(Color.BLACK);mHolder.unlockCanvasAndPost(mCanvas);mCanvas = mHolder.lockCanvas(); // 获得画布对象,开始对画布画画if(mCanvas != null){mCanvas.drawColor(Color.BLACK);mHolder.unlockCanvasAndPost(mCanvas);}mRadius = 0;}else{for (int i = 0; i < 5; i++)for (int j = 0; j < 8; j++)mCanvas.drawCircle((getWidth() / 5) * i + (getWidth() / 10),(getHeight() / 8) * j + (getHeight() / 16),mRadius, paint);mHolder.unlockCanvasAndPost(mCanvas); // 完成画画,把画布显示在屏幕上mRadius++;}Log.d(TAG, "mRadius: " + mRadius);}}}
这个例子我们把绘图工作放到了线程t里,在线程t里调用了我们自己的非重载函数Draw。一般我们都会使用这种方法更新用户UI。因为如果把UI放在系统线程,如果遇到阻塞超过5S,软件就会被系统干掉。所以,我们在安卓系统上,不需要再考虑双缓冲,系统已经实现了双缓冲,再次调用,反而会导致效率更进一步被压缩。如果确实需要提升显示效果,就使用surfaceview吧,如果仍然不行,就试着使用C,C++来实现这些复杂处理吧。效果图如下:
- 内存绘图、双缓冲绘图
- 内存绘图、双缓冲绘图
- 双缓冲绘图
- GDI双缓冲绘图
- vc双缓冲绘图?
- 双缓冲绘图
- GDI双缓冲绘图
- vc++ 双缓冲绘图
- MFC绘图,双缓冲
- VC双缓冲绘图
- VC6双缓冲绘图
- Windows 双缓冲绘图
- 双缓冲绘图
- 绘图双缓冲
- 双缓冲绘图
- VC 双缓冲绘图
- VC双缓冲绘图
- VC 双缓冲绘图
- QTP对象识别机制 (转载)
- Java学习经验和进阶 & Java Web开发review
- iOS gcd
- 浏览器兼容问题--亲身经历之血的教训
- PHP验证码
- 双缓冲绘图
- 对集成学习的初步理解
- P2P原理及UDP穿透简单说明
- 图片转换PDF
- 强类型DataSet的使用
- PHP获取http头信息
- SQL Server中的执行计划引擎入门
- bootstrap bagging boosting
- 第10章 写一个块设备驱动