Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法

  SurfaceView在Android中用作游戏开发是最适宜的,本文就将演示游戏开发中常用的两种绘图刷新策略在SurfaceView中的实现方法。

  首先我们来看一下本例需要用到的两个素材图片:

  bj.jpg就是一个渐变图,用作背景。

  question.png是一个半透明的图像,我们希望将它放在上面,围绕其圆心不断旋转。

  实现代码如下:


package SkyD.SurfaceViewTest;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Main extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(this));
}

// 自定义的SurfaceView子类
class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

// 背景图
private Bitmap BackgroundImage;
// 问号图
private Bitmap QuestionImage;

SurfaceHolder Holder;

public MySurfaceView(Context context) {
super(context);
BackgroundImage = BitmapFactory.decodeResource(getResources(),
R.drawable.bg);
QuestionImage = BitmapFactory.decodeResource(getResources(),
R.drawable.question);

Holder = this.getHolder();// 获取holder
Holder.addCallback(this);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// 启动自定义线程
new Thread(new MyThread()).start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
// 自定义线程类
class MyThread implements Runnable {
@Override
public void run() {
Canvas canvas = null;
int rotate = 0;// 旋转角度变量
while (true) {
try {
canvas = Holder.lockCanvas();// 获取画布
Paint mPaint = new Paint();
// 绘制背景
canvas.drawBitmap(BackgroundImage, 0, 0, mPaint);
// 创建矩阵以控制图片旋转和平移
Matrix m = new Matrix();
// 设置旋转角度
m.postRotate((rotate += 48) % 360,
QuestionImage.getWidth() / 2,
QuestionImage.getHeight() / 2);
// 设置左边距和上边距
m.postTranslate(47, 47);
// 绘制问号图
canvas.drawBitmap(QuestionImage, m, mPaint);
// 休眠以控制最大帧频为每秒约30帧
Thread.sleep(33);
} catch (Exception e) {
} finally {
Holder.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
}
}
}
}
}
}

  模拟器中的运行效果:

  (注:图中的问号图形是在不断旋转中的)

  这看起来不错,但是有一个问题:我们在代码中设置的帧频最大值是每秒30帧,而实际运行时的帧频根据目测就能看出是到不了30帧的,这是因为程序在每一帧都要对整个画面进行重绘,过多的时间都被用作绘图处理,所以难以达到最大帧频。

  脏矩形刷新

  接下来我们将采取脏矩形刷新的方法来优化性能,所谓脏矩形刷新,意为仅刷新有新变化的部分所在的矩形区域,而其他没用的部分就不去刷新,以此来减少资源浪费。

  我们可以通过在获取Canvas画布时,为其指派一个参数来声明我们需要画布哪个局部,这样就可以只获得这个部分的控制权:

  在这里为了便于观察,我将矩形区域设定为问号图形的1/4区域,也就是说在整个画面中我们仅仅更新问号图形的1/4大小那么点区域,其执行效果为:

  可以看到,仅有那1/4区域在快速刷新,其他部分都是静止不动的了,现在的刷新帧频差不多已经能达到最大帧频了,我们的优化起作用了:)

  不过别高兴的太早,实际上如果把刷新区域扩大到整个问号图形所在的矩形区域的话,你会发现优化作用变得微乎其微了,还是没法达到最大帧频的,因为更新区域增大了3倍,带来的资源消耗也就大幅增加。

  覆盖刷新

  这种情况下就应当考虑结合覆盖刷新方法再进一步优化了。

  试想一下,我们每次刷新时最大的消耗在哪?

  没错,在背景图绘制上,这个绘制区域非常大,会消耗我们很多资源,但实际上背景图在此例中是从不变化的,也就是说我们浪费了很多资源在无用的地方。

  那么可不可以只绘制一次背景,以后每次都只绘制会动的问号图形呢?

  完全可以,尝试修改一下代码,再前面加一个帧计数器,然后我们仅在第一帧的时候绘制背景:

  这样很简单,但是改后直接运行的话你会发现一个奇怪的状况:

  问号图案会变得有残影了。

  啊哈,这正是我使用半透明图案做范例的目的,通过这个重影,我们就能看出,覆盖刷新其实就是将每次的新的图形绘制到上一帧去,所以如果图像是半透明的,就要考虑重复叠加导致的问题了,而如果是完全不透明的图形则不会有任何问题。

  背景会在背景图和黑色背景之间来回闪。

  这个问题其实是源于SurfaceView的双缓冲机制,我理解就是它会缓冲前两帧的图像交替传递给后面的帧用作覆盖,这样由于我们仅在第一帧绘制了背景,第二帧就是无背景状态了,且通过双缓冲机制一直保持下来,解决办法就是改为在前两帧都进行背景绘制:

  现在就没有问题了(如果换成个不透明的图形的话就真没问题了):

  现在虽然还是达不到最大帧频,但是也算不错啦,在真机上跑的会更快些,接近最大帧频了。

  结语

  我这也是刚接触Android开发,分享这点心得出来,有写的不对的欢迎指点一二^^

时间: 2024-10-23 18:35:06

Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法的相关文章

Android SurfaceView 绘图覆盖刷新及“.NET研究”脏矩形刷新方法

SurfaceView在Android中用作游戏开发是最适宜的,本文就将演示游戏开发中常用的两种绘图刷新策略在SurfaceView中的实现方法. 首先我们来看一下本例需要用到的两个素材图片: bj.jpg就是一个渐变图,用作背景. question.png是一个半透明的图像,我们希望将它放在上面,围绕其圆心不断旋转. 实现代码如下: package SkyD.SurfaceViewTest; import android.app.Activity; import android.content

android SurfaceView绘制实现原理解析

在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行绘制.又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应.在本文中,我们就详细分析SurfaceView的实现原理.         在前面Android控件TextView的实现原理分析一文中提到,普通的Andro

Android SurfaceView游戏开发示例

当我们需要开发一个复杂游戏的时候,而且对程序的执行效率要求很高时,View类就不能满足需求了,这时必须用 SurfaceView类进行开发. 例如,对速度要求很高的游戏时,View类就不能满足需求了,这时必须使用SurfaceView类进 行开发.例如,对速度要求很高的游戏,可以使用双缓冲来显示.游戏中的背景.人物.动画等都需要绘制在一个画布(Canvas) 上,而SurfaceView可以直接访问一个画布,SurfaceView 是提供给需要直接画像素而不是使用窗体部件的应用使用的. 每个 S

Android SurfaceView学习示例

SurfaceView是View的子类,使用的方式与任何View所派生的类都是完全相同的,可以像其他View那样应用动画,并把它们放 到布局中. SurfaceView封装的Surface支持使用本章前面所描述的所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库. 使用OpenGL,你可以再Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依 靠硬件加速(可用的时候)来极大地提高性能. 对于显示动态的3D图像来说,例如,那些使用Go

canvas-请问大家Android Canvas绘图填充问题,有专门的填充函数(方法)吗?

问题描述 请问大家Android Canvas绘图填充问题,有专门的填充函数(方法)吗? 已经使用paint画了一个封闭矩形,接下来目的是点击一下该区域后直接填充为所选颜色. 注意:不是直接画一个填充好的. 要用stroke和fill吗? 解决方案 这是没有的,当然你可以用开源别人写好的封装好的代码.你在ontouchevent里面判断一下手指按下的区域是否在该矩形内.如果在,在该区域画一个填充的矩形后更新界面.

Android SurfaceView的运用

下面就贴上一个小程序代码,主要运用SurfaceView来实现在屏幕上画一个圆,你可以通过按方向键和触摸屏幕来改变圆的位 置 代码: Activity package com.view; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class MainActivity extends Acti

事件-android surfaceview 和activity之间数据传递

问题描述 android surfaceview 和activity之间数据传递 想实现一个功能 就是在mainActivity中放置一个surfaceview和一个Textview 在surfaceview中添加点击onTouch事件 每次点击 让自己定义的surfaceview的变量 step加一 并时时在mainActivity的TextView中更新step的显示 不知道问题有没有描述清楚 就是想在一个view中时时显示另外一个view中定义的变量 解决方案 public class M

lockcanvas-一个关于android surfaceView的问题,求大神回答

问题描述 一个关于android surfaceView的问题,求大神回答 surfaceView据说双缓存,两画布换来换去,但是我post后画布有时候还有有时候就没了 surfaceCreated方法里,最后的 canvas=sfh.lockCanvas(); sfh.unlockCanvasAndPost(canvas); 多加几个少加几个出现不同的情况,这个是怎么回事,大神求帮忙TT 下面是代码 clock.java package com.app.jtj.clock; import an

android 下拉刷新的时候正在刷新不出来

问题描述 android 下拉刷新的时候正在刷新不出来 android 下拉刷新的时候正在刷新不出来是怎么回事,到松手刷新的时候直接回到原始状态,不会出现正在刷新的字样 解决方案 http://download.csdn.net/detail/hyundaihs/8504837用这个吧 解决方案二: http://download.csdn.net/detail/hyundaihs/8504837用这个吧 解决方案三: 找到原因了,是因为在刷新的过程中几个状态没有判断好,还有就是运用到访问数据的