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

http://www.cnblogs.com/SkyD/archive/2010/11/08/1871423.html

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开发,分享这点心得出来,有写的不对的欢迎指点一二^^

注意:此博客已停止更新,并迁移至blog.SkyDev.cc,后续都将在新地址更新。

时间: 2024-10-10 09:41:35

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

Android之绘图

一直对画画相关不感冒,但是Android的绘图机制还是要硬着头皮学 绘画主要是使用paint(画笔)在canvas(画布)进行各种图形的绘制,画矩形.圆.三角形等点线构成的2维图形 //矩形 drawRect (float left, float top, float right, float bottom, Paint paint) canvas.drawRect(50, 100, 200, 200, paint); //圆 drawCircle (float cx, float cy, fl

【转】Android Canvas绘图详解(图文)

转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡在网上的日子 发表于 2012-12-12 20:29 第 63165 次阅读 Canvas,android 15 Android中使用图形处理引擎,2D部分是android SDK内部自己提供,3D部分是用Open GL ES 1.0.今天我们主要要了解的是2D相关的,如果你想看3D的话那么可以跳

Android surfaceview详解

周末看<精通Android游戏开发>(Pro Android Games),里面讲到游戏的框架,其中一个重要的概念surfaceview,觉得不是很理解,于是花了一点时间研究了下,写下自己的心得. surface,这个单词的意思是浮在表面的,那么surfaceview就是浮在表面的view了.如果真的这样解释,估计有人要拍砖了.然而,话虽不能这么说,取这个名儿,多少还是有点关系的.surface是一个可见区域. 我们在屏幕上看到的这些view,在屏幕上看到的就是画面,在内存中就是一块内存区.绘

MySQL中InnoDB脏页刷新机制Checkpoint

我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中取数据,那为什么还要刷新脏页到磁盘呢?如果重做日志可以无限增大,同时缓冲池足够大,能够缓存所有数据,那么是不需要将缓冲池中的脏页刷新到磁盘.但是,通常会有以下几个问题: 服务器内存有限,缓冲池不够用,无法缓存全部数据 重做日志无限增大成本要求太高 宕机时如果重做全部日志恢复时间过长 事实上,当数据库

InnoDB脏页刷新机制Checkpoint

我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中取数据,那为什么还要刷新脏页到磁盘呢?如果重做日志可以无限增大,同时缓冲池足够大,能够缓存所有数据,那么是不需要将缓冲池中的脏页刷新到磁盘.但是,通常会有以下几个问题: 服务器内存有限,缓冲池不够用,无法缓存全部数据 重做日志无限增大成本要求太高 宕机时如果重做全部日志恢复时间过长 事实上,当数据库

Android SurfaceView实战 带你玩转flabby bird (上)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/42965779 ,本文出自:[张鸿洋的博客] 1.概述 哈,记得以前写过Android SurfaceView实战 打造抽奖转盘 , 同属于SurfaceView系列,基本可以从这篇博文中学习到SurfaceView的用法,以及利用SurfaceView做抽奖转盘.但是其中缺少一部分的知识点,就是与用户交互时界面的改变,所以今天给大家再带来本篇博文教大家如何做flabby bir

Android 2D绘图解析之 Canvas,Paint

原创文章,转载请注明 ( 来自:http://blog.csdn.net/leejizhou/article/details/51524948 李济洲的博客 ) [Android 2D绘图解析]系列文章将全面介绍Android绘图相关,这篇简单介绍下如何利用Android API进行一些简单图形的绘制,绘图的前提是需要继承自View,然后重写它的onDraw(Canvas canvas) 方法即可. 首先我们新建一个类继承自View,并重写onDraw方法. package com.leejz.

InnoDB Redo Flush及脏页刷新机制深入分析

概要: 我们知道InnoDB采用Write Ahead Log策略来防止宕机数据丢失,即事务提交时,先写重做日志,再修改内存数据页,这样就产生了脏页.既然有重做日志保证数据持久性,查询时也可以直接从缓冲池页中取数据,那为什么还要刷新脏页到磁盘呢?如果重做日志可以无限增大,同时缓冲池足够大,能够缓存所有数据,那么是不需要将缓冲池中的脏页刷新到磁盘.但是,通常会有以下几个问题: 服务器内存有限,缓冲池不够用,无法缓存全部数据 重做日志无限增大成本要求太高 宕机时如果重做全部日志恢复时间过长 事实上,

Android SurfaceView实战 带你玩转flabby bird (下)

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43063331,本文出自:[张鸿洋的博客] 1.概述 在Android SurfaceView实战 带你玩转flabby bird (上)中,我们完成了在游戏所需的所有的元素的绘制,包括 Bird鸟. Floor地板.Pipe 管道 .背景图以及分数等. 本篇博客将在上篇的基本上,继续带领大家向我们的目标进发,那么问题来了,我们的目标是: 就是这个效果图了. 首先我们明确下,当然