本文来自 http://blog.csdn.net/hellogv/ ,引用必须注明出处!

SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器,这次 我就用两篇文章来介绍SurfaceView的用法。网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类, 或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,我这里就直接在普通的用户界面调用 SurfaceHolder的lockCanvas和unlockCanvasAndPost。

先来看看程序运行的截图:

截图1主要演示了直接把正弦波绘画在SurfaceView上

对比上面的左右两图,右图用.lockCanvas(null),而左图用.lockCanvas(new Rect(oldX, 0, oldX + length,
    getWindowManager().getDefaultDisplay().getHeight())), 对比一下两个效果,由于左图是按指定Rect绘画,所以效率会比右图的全控件绘画高些,并且在清屏之后 (canvas.drawColor(Color.BLACK))不会留有上次绘画的残留。

接下来贴出main.xml的源码:

view plain copy to clipboard print ?
  1. <? xml   version = "1.0"   encoding = "utf-8" ?>
  2. < LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"
  3. android:layout_width = "fill_parent"   android:layout_height = "fill_parent"
  4. android:orientation = "vertical" >
  5. < LinearLayout   android:id = "@+id/LinearLayout01"
  6. android:layout_width = "wrap_content"   android:layout_height = "wrap_content" >
  7. < Button   android:id = "@+id/Button01"   android:layout_width = "wrap_content"
  8. android:layout_height = "wrap_content"   android:text = "简单绘画" > </ Button >
  9. < Button   android:id = "@+id/Button02"   android:layout_width = "wrap_content"
  10. android:layout_height = "wrap_content"   android:text = "定时器绘画" > </ Button >
  11. </ LinearLayout >
  12. < SurfaceView   android:id = "@+id/SurfaceView01"
  13. android:layout_width = "fill_parent"   android:layout_height = "fill_parent" > </ SurfaceView >
  14. </ LinearLayout >

接下来贴出程序源码:

view plain copy to clipboard print ?
  1. package  com.testSurfaceView;
  2. import  java.util.Timer;
  3. import  java.util.TimerTask;
  4. import  android.app.Activity;
  5. import  android.graphics.Canvas;
  6. import  android.graphics.Color;
  7. import  android.graphics.Paint;
  8. import  android.graphics.Rect;
  9. import  android.os.Bundle;
  10. import  android.util.Log;
  11. import  android.view.SurfaceHolder;
  12. import  android.view.SurfaceView;
  13. import  android.view.View;
  14. import  android.widget.Button;
  15. public   class  testSurfaceView  extends  Activity {
  16. /** Called when the activity is first created. */
  17. Button btnSimpleDraw, btnTimerDraw;
  18. SurfaceView sfv;
  19. SurfaceHolder sfh;
  20. private  Timer mTimer;
  21. private  MyTimerTask mTimerTask;
  22. int  Y_axis[], //保存正弦波的Y轴上的点
  23. centerY,//中心线
  24. oldX,oldY,//上一个XY点
  25. currentX;//当前绘制到的X轴上的点
  26. @Override
  27. public   void  onCreate(Bundle savedInstanceState) {
  28. super .onCreate(savedInstanceState);
  29. setContentView(R.layout.main);
  30. btnSimpleDraw = (Button) this .findViewById(R.id.Button01);
  31. btnTimerDraw = (Button) this .findViewById(R.id.Button02);
  32. btnSimpleDraw.setOnClickListener(new  ClickEvent());
  33. btnTimerDraw.setOnClickListener(new  ClickEvent());
  34. sfv = (SurfaceView) this .findViewById(R.id.SurfaceView01);
  35. sfh = sfv.getHolder();
  36. //动态绘制正弦波的定时器
  37. mTimer = new  Timer();
  38. mTimerTask = new  MyTimerTask();
  39. // 初始化y轴数据
  40. centerY = (getWindowManager().getDefaultDisplay().getHeight() - sfv
  41. .getTop()) / 2 ;
  42. Y_axis = new   int [getWindowManager().getDefaultDisplay().getWidth()];
  43. for  ( int  i =  1 ; i < Y_axis.length; i++) { // 计算正弦波
  44. Y_axis[i - 1 ] = centerY
  45. - (int ) ( 100  * Math.sin(i *  2  * Math.PI /  180 ));
  46. }
  47. }
  48. class  ClickEvent  implements  View.OnClickListener {
  49. @Override
  50. public   void  onClick(View v) {
  51. if  (v == btnSimpleDraw) {
  52. SimpleDraw(Y_axis.length-1 ); //直接绘制正弦波
  53. } else   if  (v == btnTimerDraw) {
  54. oldY = centerY;
  55. mTimer.schedule(mTimerTask, 0 ,  5 ); //动态绘制正弦波
  56. }
  57. }
  58. }
  59. class  MyTimerTask  extends  TimerTask {
  60. @Override
  61. public   void  run() {
  62. SimpleDraw(currentX);
  63. currentX++;//往前进
  64. if  (currentX == Y_axis.length -  1 ) { //如果到了终点,则清屏重来
  65. ClearDraw();
  66. currentX = 0 ;
  67. oldY = centerY;
  68. }
  69. }
  70. }
  71. /*
  72. * 绘制指定区域
  73. */
  74. void  SimpleDraw( int  length) {
  75. if  (length ==  0 )
  76. oldX = 0 ;
  77. Canvas canvas = sfh.lockCanvas(new  Rect(oldX,  0 , oldX + length,
  78. getWindowManager().getDefaultDisplay().getHeight()));// 关键:获取画布
  79. Log.i("Canvas:" ,
  80. String.valueOf(oldX) + ","  + String.valueOf(oldX + length));
  81. Paint mPaint = new  Paint();
  82. mPaint.setColor(Color.GREEN);// 画笔为绿色
  83. mPaint.setStrokeWidth(2 ); // 设置画笔粗细
  84. int  y;
  85. for  ( int  i = oldX +  1 ; i < length; i++) { // 绘画正弦波
  86. y = Y_axis[i - 1 ];
  87. canvas.drawLine(oldX, oldY, i, y, mPaint);
  88. oldX = i;
  89. oldY = y;
  90. }
  91. sfh.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
  92. }
  93. void  ClearDraw() {
  94. Canvas canvas = sfh.lockCanvas(null );
  95. canvas.drawColor(Color.BLACK);// 清除画布
  96. sfh.unlockCanvasAndPost(canvas);
  97. }
  98. }

注意一下 for (int i = oldX + 1; i < length; i++) {// 绘画正弦波 这句,在.lockCanvas()指定Rect内减少循环画线的次数,可以提高绘图效率。

上一篇 简 单介绍了SurfaceView的基本使用,这次就介绍SurfaceView与多线程的混搭。SurfaceView与多线程混搭,是为了防止动画闪烁 而实现的一种多线程应用。android的多线程用法与JAVA的多线程用法完全一样,本文不做多线程方面的介绍了。直接讲解SurfaceView与多 线程的混合使用,即开一条线程专门读取图片,另外一条线程专门绘图。

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

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

main.xml的源码:

view plain copy to clipboard print ?
  1. <? xml   version = "1.0"   encoding = "utf-8" ?>
  2. < LinearLayout   xmlns:android = "http://schemas.android.com/apk/res/android"
  3. android:layout_width = "fill_parent"   android:layout_height = "fill_parent"
  4. android:orientation = "vertical" >
  5. < LinearLayout   android:id = "@+id/LinearLayout01"
  6. android:layout_width = "wrap_content"   android:layout_height = "wrap_content" >
  7. < Button   android:id = "@+id/Button01"   android:layout_width = "wrap_content"
  8. android:layout_height = "wrap_content"   android:text = "单个独立线程" > </ Button >
  9. < Button   android:id = "@+id/Button02"   android:layout_width = "wrap_content"
  10. android:layout_height = "wrap_content"   android:text = "两个独立线程" > </ Button >
  11. </ LinearLayout >
  12. < SurfaceView   android:id = "@+id/SurfaceView01"
  13. android:layout_width = "fill_parent"   android:layout_height = "fill_parent" > </ SurfaceView >
  14. </ LinearLayout >

本文程序的源码:

view plain copy to clipboard print ?
  1. package  com.testSurfaceView;
  2. import  java.lang.reflect.Field;
  3. import  java.util.ArrayList;
  4. import  android.app.Activity;
  5. import  android.graphics.Bitmap;
  6. import  android.graphics.BitmapFactory;
  7. import  android.graphics.Canvas;
  8. import  android.graphics.Paint;
  9. import  android.graphics.Rect;
  10. import  android.os.Bundle;
  11. import  android.util.Log;
  12. import  android.view.SurfaceHolder;
  13. import  android.view.SurfaceView;
  14. import  android.view.View;
  15. import  android.widget.Button;
  16. public   class  testSurfaceView  extends  Activity {
  17. /** Called when the activity is first created. */
  18. Button btnSingleThread, btnDoubleThread;
  19. SurfaceView sfv;
  20. SurfaceHolder sfh;
  21. ArrayList<Integer> imgList = new  ArrayList<Integer>();
  22. int  imgWidth, imgHeight;
  23. Bitmap bitmap;//独立线程读取,独立线程绘图
  24. @Override
  25. public   void  onCreate(Bundle savedInstanceState) {
  26. super .onCreate(savedInstanceState);
  27. setContentView(R.layout.main);
  28. btnSingleThread = (Button) this .findViewById(R.id.Button01);
  29. btnDoubleThread = (Button) this .findViewById(R.id.Button02);
  30. btnSingleThread.setOnClickListener(new  ClickEvent());
  31. btnDoubleThread.setOnClickListener(new  ClickEvent());
  32. sfv = (SurfaceView) this .findViewById(R.id.SurfaceView01);
  33. sfh = sfv.getHolder();
  34. sfh.addCallback(new  MyCallBack()); // 自动运行surfaceCreated以及surfaceChanged
  35. }
  36. class  ClickEvent  implements  View.OnClickListener {
  37. @Override
  38. public   void  onClick(View v) {
  39. if  (v == btnSingleThread) {
  40. new  Load_DrawImage( 0 ,  0 ).start(); //开一条线程读取并绘图
  41. } else   if  (v == btnDoubleThread) {
  42. new  LoadImage().start(); //开一条线程读取
  43. new  DrawImage(imgWidth +  10 ,  0 ).start(); //开一条线程绘图
  44. }
  45. }
  46. }
  47. class  MyCallBack  implements  SurfaceHolder.Callback {
  48. @Override
  49. public   void  surfaceChanged(SurfaceHolder holder,  int  format,  int  width,
  50. int  height) {
  51. Log.i("Surface:" ,  "Change" );
  52. }
  53. @Override
  54. public   void  surfaceCreated(SurfaceHolder holder) {
  55. Log.i("Surface:" ,  "Create" );
  56. // 用反射机制来获取资源中的图片ID和尺寸
  57. Field[] fields = R.drawable.class .getDeclaredFields();
  58. for  (Field field : fields) {
  59. if  (! "icon" .equals(field.getName())) // 除了icon之外的图片
  60. {
  61. int  index =  0 ;
  62. try  {
  63. index = field.getInt(R.drawable.class );
  64. } catch  (IllegalArgumentException e) {
  65. // TODO Auto-generated catch block
  66. e.printStackTrace();
  67. } catch  (IllegalAccessException e) {
  68. // TODO Auto-generated catch block
  69. e.printStackTrace();
  70. }
  71. // 保存图片ID
  72. imgList.add(index);
  73. }
  74. }
  75. // 取得图像大小
  76. Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
  77. imgList.get(0 ));
  78. imgWidth = bmImg.getWidth();
  79. imgHeight = bmImg.getHeight();
  80. }
  81. @Override
  82. public   void  surfaceDestroyed(SurfaceHolder holder) {
  83. Log.i("Surface:" ,  "Destroy" );
  84. }
  85. }
  86. /*
  87. * 读取并显示图片的线程
  88. */
  89. class  Load_DrawImage  extends  Thread {
  90. int  x, y;
  91. int  imgIndex =  0 ;
  92. public  Load_DrawImage( int  x,  int  y) {
  93. this .x = x;
  94. this .y = y;
  95. }
  96. public   void  run() {
  97. while  ( true ) {
  98. Canvas c = sfh.lockCanvas(new  Rect( this .x,  this .y,  this .x
  99. + imgWidth, this .y + imgHeight));
  100. Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
  101. imgList.get(imgIndex));
  102. c.drawBitmap(bmImg, this .x,  this .y,  new  Paint());
  103. imgIndex++;
  104. if  (imgIndex == imgList.size())
  105. imgIndex = 0 ;
  106. sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
  107. }
  108. }
  109. };
  110. /*
  111. * 只负责绘图的线程
  112. */
  113. class  DrawImage  extends  Thread {
  114. int  x, y;
  115. public  DrawImage( int  x,  int  y) {
  116. this .x = x;
  117. this .y = y;
  118. }
  119. public   void  run() {
  120. while  ( true ) {
  121. if  (bitmap !=  null ) { //如果图像有效
  122. Canvas c = sfh.lockCanvas(new  Rect( this .x,  this .y,  this .x
  123. + imgWidth, this .y + imgHeight));
  124. c.drawBitmap(bitmap, this .x,  this .y,  new  Paint());
  125. sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
  126. }
  127. }
  128. }
  129. };
  130. /*
  131. * 只负责读取图片的线程
  132. */
  133. class  LoadImage  extends  Thread {
  134. int  imgIndex =  0 ;
  135. public   void  run() {
  136. while  ( true ) {
  137. bitmap = BitmapFactory.decodeResource(getResources(),
  138. imgList.get(imgIndex));
  139. imgIndex++;
  140. if  (imgIndex == imgList.size()) //如果到尽头则重新读取
  141. imgIndex = 0 ;
  142. }
  143. }
  144. };
  145. }

要创建一个新的SurfaceView,需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。

SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它对SurfaceHolder对象的引用,其中包含了当前有效的Surface。

一个典型的Surface View设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。

下面的框架代码展示了使用Canvas所绘制的Surface View的实现。在Surface View控件中创建了一个新的由Thread派生的类,并且所有的UI更新都是在这个新类中处理的。

  1. import android.content.Context;
  2. import android.graphics.Canvas;
  3. import android.view.SurfaceHolder;
  4. import android.view.SurfaceView;
  5. public class MySurfaceView extends SurfaceView implements SurfaceHolder. Callback {
  6. private SurfaceHolder holder;
  7. private MySurfaceViewThread mySurfaceViewThread;
  8. private boolean hasSurface;
  9. MySurfaceView(Context context) {
  10. super(context);
  11. init();
  12. }
  13. private void init() {
  14. //创建一个新的SurfaceHolder, 并分配这个类作为它的回调(callback)
  15. holder =  getHolder ();
  16. holder.addCallback(this);
  17. hasSurface =  false ;
  18. }
  19. public void resume() {
  20. //创建和启动图像更新线程
  21. if ( mySurfaceViewThread == null) {
  22. mySurfaceViewThread =  new MySurfaceViewThread();
  23. if ( hasSurface == true)
  24. mySurfaceViewThread.start();
  25. }
  26. }
  27. public void pause() {
  28. // 杀死图像更新线程
  29. if (mySurfaceViewThread != null) {
  30. mySurfaceViewThread.requestExitAndWait();
  31. mySurfaceViewThread =  null ;
  32. }
  33. }
  34. public void surfaceCreated(SurfaceHolder holder) {
  35. hasSurface =  true ;
  36. if (mySurfaceViewThread != null)
  37. mySurfaceViewThread.start();
  38. }
  39. public void surfaceDestroyed(SurfaceHolder holder) {
  40. hasSurface =  false ;
  41. pause();
  42. }
  43. public void surfaceChanged(SurfaceHolder holder,int format,int w,int h) {
  44. if (mySurfaceViewThread != null)
  45. mySurfaceViewThread.onWindowResize(w, h);
  46. }
  47. class MySurfaceViewThread extends Thread {
  48. private boolean done;
  49. MySurfaceViewThread() {
  50. super();
  51. done =  false ;
  52. }
  53. @Override
  54. public void run() {
  55. SurfaceHolder  surfaceHolder =  holder ;
  56. // 重复绘图循环,直到线程停止
  57. while (!done) {
  58. // 锁定surface,并返回到要绘图的Canvas
  59. Canvas  canvas =  surfaceHolder .lockCanvas();
  60. // 待实现:在Canvas上绘图
  61. // 解锁Canvas,并渲染当前图像
  62. surfaceHolder.unlockCanvasAndPost(canvas);
  63. }
  64. }
  65. public void requestExitAndWait() {
  66. // 把这个线程标记为完成,并合并到主程序线程
  67. done =  true ;
  68. try {
  69. join();
  70. } catch (InterruptedException ex) { }
  71. }
  72. public void onWindowResize(int w, int h) {
  73. // 处理可用的屏幕尺寸的改变
  74. }
  75. }
  76. }
												

SurfaceView使用方法简介-来自网络相关推荐

  1. Linux用户认证方法简介

    Linux用户认证方法简介 当今IT环境中,任何计算机系统都要充分考虑设计.使用和运行过程中的安全性.所以在目前主流操作系统的各个环节当中都增加了很多安全方面的功能和特性,而在众多的安全特性和功能中有 ...

  2. 华为徐文伟:用数学和系统工程方法推进未来网络研究

    来源:华为 在2021第五届未来网络发展大会上,来自产业界.学术界.研究机构等领域的专家.行业领袖,围绕网络操作系统.6G通信.网络安全.工业互联网等热点话题,共同探讨新型网络技术的攻关与变革.华为董 ...

  3. 4种方法可以缓解网络瓶颈

    在解决网络瓶颈问题时,网络专业人员通常依赖于有限的技术:增加链路吞吐量,配置端口通道或集成服务质量(QoS).虽然这些仍然是有效的方法,但网络工程师在2018年还有一些额外的技巧.这里有四种现代方式, ...

  4. 验证Xcode真伪的方法,来自苹果官网

    验证Xcode真伪的方法,来自苹果官网 Xcode的验证你的版本 2015年9月22日  注意:中文为有道翻译,看下验证方法即可. 我们最近将应用程序从应用程序商店,还建有Xcode的假冒版本有可能对 ...

  5. 关于blog [转贴]来自网络

    关于blog   [转贴]来自网络 BLOG更是借助新技术将信息共享无限延伸,那么有两个概念就是不能不提的.这其中包括RSS和TrackBack. RSS 简单来说, RSS是一种技术规范的简称, R ...

  6. 几种常用的差异分析方法简介

    几种常用的差异分析方法简介 如今在生物学研究中,差异分析越来越普遍,也有许多做差异分析的方法可供选择.但是在实际应用中,大多数人不知道该使用哪种方法来处理自己的数据,所以今天我就来介绍下目前几种常用的 ...

  7. 数据库之Oracle笔试面试题收集(来自网络)

    数据库之Oracle笔试面试题收集(来自网络) 问题: 1.解释冷备份和热备份的不同点及各自的优点? 2.解释归档和非归档模式之间的不同和它们各自的优缺点? ********************* ...

  8. 三维重建技术(2)各种方法简介

    转自:三维重建技术 各种方法简介, G换一种活法 这个博主也是转载的,可是来源不可查,所以暂把来源写为这个博主的博客 三维重建技术通过深度数据获取.预处理.点云配准与融合.生成表面等过程,把真实场景刻 ...

  9. 舆情监测系统功能简介,网络舆情监测系统平台有哪些?

    一般来说,互联网舆情监测服务平台具有及时.全面.准确的特点,对维护社会稳定发展具有重要的现实意义.那么,哪些TOOM舆情监测小编带您了解舆情监测系统功能简介,网络舆情监测系统平台有哪些? 一.什么是舆 ...

最新文章

  1. 旷视首席科学家孙剑:深度学习变革视觉计算
  2. 演练:开发和使用自定义服务器控件
  3. sizeof运算求结构体大小
  4. boost::describe模块实现枚举转字符串的测试程序
  5. 在c语言中卖水果的程序,非常难的C语言问题!!!(悬赏80)
  6. 角标越界 Java_【新人求助】利用占位符操作数据库是总是提示数组角标越界是怎么回事 - Java论坛 - 51CTO技术论坛_中国领先的IT技术社区...
  7. php中引用的真正理解-变量引用、函数引用、对象引用
  8. 微信小程序底部导航栏中间突出
  9. 笔记本能安装联想智能云教室吗_挑战Jupyter Notebook:云协作、云硬件,上云的Notebook编程环境...
  10. 用.NET开发MSN聊天机器人 - MSN聊天机器人开发揭秘
  11. halcon之屌炸天的变形匹配(1)
  12. SQL Sever——远程过程调用失败(0x800706be)
  13. java类 家族成员 姓氏_极其罕见的四大姓氏,若你还姓这个,恭喜你,你的家族大有来头!...
  14. 快速计算平方根数(约翰·卡马克)
  15. 华为云备份会上传私密相册吗_2 亿部华为手机背后,这个功能不能忽视
  16. 浙江印发政府数字化转型工作方案,多处提及电子签名、签章、印章
  17. 教你快速制作一个简单的网页
  18. php百度地图接口两点测距,百度地图Api 根据两个坐标点计算距离
  19. 入职数字ic设计后的一些工作心得
  20. python学习——CMD中快速执行python文件

热门文章

  1. swift抛出异常_swift之异常处理
  2. 领英工具-领英精灵可以先体验么?
  3. 牛皮凉席软席与硬席应该怎么选?
  4. Code Jam - Store Credit for Python
  5. 微软输入法打带声调的拼音
  6. 立创eda专业版PCB设计中板框圆角设置
  7. 用Three.js做一个简单的3D场景
  8. 【JaveWeb】JavaWeb
  9. [其他芯片] 知识变现之CH376实现U盘读写方案
  10. 21天自学c语言漫画版,产品经理学技术:漫画版C语言学习(一)