[转]Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法
本文转自:http://www.cnblogs.com/SkyD/archive/2010/11/08/1871423.html
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开发,分享这点心得出来,有写的不对的欢迎指点一二^^
[转]Android SurfaceView 绘图覆盖刷新及脏矩形刷新方法相关推荐
- Android SurfaceView 绘图及帧频处理方法修正
帧频处理 今天我在审视上篇示例代码时猛然发现我犯了个低级错误,致使帧频达不到预期,这个错误在这里: 这里设置每次绘制之后固定休眠33毫秒,以此来限制每秒帧频在30帧. 但实际上这里忽视了绘图及其他运算 ...
- Mysql - 脏页刷新机制
什么是脏页? Mysql InnoDB表基本都是基于B+树索引进行存储的,而数据存储的最小单元就是数据页(物理存储结构:表 -> 表空间(和索引)-> 段 -> 区 -> 页 ...
- 脏矩形(高效绘图 13.3)
脏矩形 有时候用CAShapeLayer或者其他矢量图形图层替代Core Graphics并不是那么切实可行.比如我们的绘图应用:我们用线条完美地完成了矢量绘制.但是设想一下如果我们能进一步提高应用的 ...
- Android SurfaceView 的使用
SurfaceView 一下demo的地址 SurfaceView 的作用: SurfaceView 适用被动更新,如频繁的刷新,在子线程刷新不会阻塞主线程,适用于界面频繁更新 (View适用主动更新 ...
- 详解与重构hyman《Android SurfaceView实战 打造抽奖转盘》
详解与重构hyman<Android SurfaceView实战 打造抽奖转盘> 作者:邵励治 一.概述--关于SurfaceView您不得不知道的二三事 1.SurfaceView是干什 ...
- Android SurfaceView实战 带你玩转flabby bird (下)
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43063331,本文出自:[张鸿洋的博客] 1.概述 在Android Surfa ...
- Android自定义控件实战——实现仿IOS下拉刷新上拉加载 PullToRefreshLayout
下拉刷新控件,网上有很多版本,有自定义Layout布局的,也有封装控件的,各种实现方式的都有.但是很少有人告诉你具体如何实现的,今天我们就来一步步实现自己封装的 PullToRefreshLayout ...
- Android SurfaceView的双缓冲机制,引起的闪屏问题
SurfaceView相关目录 SurfaceView要点 SurfaceView拥有独立的Surface(绘图表面) SurfaceView是用Zorder排序的,他默认在宿主Window的后面,S ...
- android.view.surface,Android SurfaceView 源码分析及使用
概述 SurfaceView 是 Android 中一种比较特殊的视图(View),它跟平时时候的 TextView.Button 最大的区别是它跟它的视图容器并不是在同一个视图层上,它的 UI 显示 ...
最新文章
- 推荐44个最具潜力的顶极深度学习开源框架和平台!!!
- 物理约束相关问题梳理
- optee中User TA的加载和运行
- 为什么初创企业应该计算 LTV / CAC,以及如何正确应用它?
- 基于matlab的卷积码实验报告,基于MATLAB的卷积码编译码设计仿真.doc
- 五种Linux桌面操作过程录制的文件
- logstash增量同步mysql数据到es
- ExtJS应用架构设计(二)
- SDCMS1.3.1 发布插件
- mysql卸载不干净 linux,CentOS下如何完全卸载MySQL?解决卸载不干净的问题
- Python爬虫——新浪微博登陆
- Layui 内置方法 - layer.style(重新定义层的样式)
- java上传微博图床,GitHub - echisan/wbp4j: Simple Java Api for 微博图床,使用简单的api即可完成上传图片...
- 移动宽带连接电信服务器不稳定,移动宽带速度为啥与电信差距那么大?工信部官宣告诉你...
- Spring 增强处理Advice
- 半加器、全加器及其应用
- 如何学习投资 推荐几本教材
- 高级项目管理-4、项目范围、进度、成本、质量管理
- EFM32G232F64时钟树
- 【C++】细说C语言头文件的路径
热门文章
- 9个元素换6次达到排序序列_全面讲解十大经典排序算法(Python实现)
- java接口的作用和意义_java什么是接口?接口有什么作用?接口如何使用?
- sqlserver2008r2 还原bak文件
- 正则表达式中或关系表示及查找英文字母最后一个
- [VB]多级目录创建函数,支持很深的目录创建。
- VB 获取计算机的分辨率大小
- VB 全局热键HOOK (不占系统资源版本)
- SpringBoot实战(五):配置健康检查与监控
- python两个 list 获取交集,并集,差集的方法
- chrome 浏览器的插件权限有多大?