随笔13

     这篇文章的内容, 原来是写在随笔10 里面的, 写着写着内容多了, 还是放到新的文章里面吧, 后面再添加其他的也方便.

     前面我们已经知道了, View 的更新都是在 UI Main 线程中进行的, 有没有例外的呢? 答案是确定的!!!!

  • SurfaceView,  这是一个可以"异步" 并且"局部" 更新的 View,  适用于动作游戏和频繁更新的页面.  
  1. 异步, 更新 SurFaceView 的显示内容, 我们可以开多一个线程来进行, 这个线程通过 SurfaceView对象的变量 SurfaceHolder对象, 获取一个Canvas 对象, 在该Canvas 对象上画图, 最后把 Canvas 的内容更新到屏幕上(标准的双缓冲). 这个过程是不必在UI Main 线程中进行的.
  2. 局部, 上面的Canvas(画布) 对象, 默认是获取整个SurfaceView 的视图大小的画布, 但是有时候, 我们可能更新的地方只有某个角落. 我们可以在获取 Canvas 对象的时候, 通过 Rect 对象参数来指定一个矩形区域, 在本次的更新中, 将只更新这个区域, 其他区域将不会被更新到,  从而提高效率.
  3. 在头两个画面中, 由于Canvas对象都为空, 所以这两个画面不存在局部更新, 都是全屏更新 . 为什么头两个画面的 Canvas对象为空, 请看下面的内容.

     在官方文档中, "Dev Guide" -> 左边菜单点击 "Graphics", 在出来的文档中, 最后有一段话值得注意:

Draw with a Canvas

On a SurfaceView

Note: On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. For example, you can clear the previous state of the Canvas by filling in a color with drawColor() or setting a background image with drawBitmap() . Otherwise, you will see traces of the drawings you previously performed.

1 2 3 4  => 更新的帧数 

A B        => 1,2帧 更新Canvas, 后面的帧不再画东西

      A B  => 保留的前一个Canvas状态

A B A B  => 显示内容, 只考虑更新区域(局部或全屏).

  • 在第一帧的时候, 获取的 canvas为空, 画上东西A, 画布状态 canvasA, 其前一个状态为空.
  • 到第二帧的时候, 获取的 canvas 为canvasA 的前一个状态(- -, 让人看不懂)为空, 画上东西B, 画布状态 canvasB, 其前一个状态为 canvasA.
  • 到第三帧的时候, 获取的 canvas 为canvasB 的前一个状态 canvasA, 没画东西, 画布状态 canvasA, 其前一个状态为 canvasB.

              如果有画东西C, 如果画的地方没有drawColor() 或 drawBitmap(), 那么画布状态为 canvasAC, 注意是重叠 , 而非覆盖, 所以在进行动画的区域, 请先清屏了再画, 不然将有前两帧的画面痕迹.

              drawColor() 是全屏清屏, drawBitmap() 可以自己控制范围, 该方法的重载方法很多.

  • 到第四帧的时候, 获取的 canvas 为canvasA 的前一个状态 canvasB, 没画东西, 画布状态 canvasB, 其前一个状态为 canvasA.

              如果第三帧有画东西C, 并且这帧也画了东西D, 那画布状态为 canvasBD, 其前一个状态为 canvasAC.

  • 照此循环下去, 结果就是两个画面不停的交替, 本人测试的结果确实是这样.

       在N-2>0的情况下, 第N 帧获取的画布竟然是 N-2 帧的画布, 而不是延续上一帧的画布, 并且画布被锁定了, 想换都不行.

      本人菜鸟, 不是很理解android 这样做的用意. 让画面交替显示, 明显大部分情况下都不需要这样做吧, 就算需要交替画面, 我们也可以自己控制的. 并且第二帧过后, 我们每个SurfaceView 都将拥有2 个canvas 资源, 难道canvas 资源不值钱!!!!! 

      难道是 N-1 帧的canvas 在"页面显示后" 至 "新页面显示前" 这个过程中被系统或其他资源 "绑架" 了, 不给其他人动用, 本人无限 YY 中!!!!!

  • 如果动画只在局部更新, 为了让局部更新区域外的区域能显示同一个画面, 我们需要在第一和第二帧连续画同样的东西. 

        画  AB  AC   D    E    F    G   =>A画在不更新的区域, BCD...G 画在更新区域

     显示  AB  AC  AD  AE  AF  AG

  • 上面的情况, 如果第一和第二帧还没画 A ,但是在后面要显示A, 怎么办呢? 其实很简单, 在需要显示A 的时候, 把动画变为全屏更新, 连续画 A 两次后, 再把动画切换为局部更新模式.

               局部更新  ->全屏更新->局部更新

        画    B  C  D  E   AF   AG     H    I    J

     显示    B  C  D  E   AF   AG    AH  AI  AJ   =>设定A占有的区域比其他的多

  • 如果动画是全屏更新, 那只要我们连续两帧画同样的东西, 那画面就能一直显示同一个画面.

        画 A B C C  D   E    F

     显示 A B C C CD CE CF    =>设定C占有的区域比其他的多.

 

ps. 获取全屏更新的画布---------->canvas = Holder.lockCanvas();

     获取局部更新的画布----------->canvas = Holder.lockCanvas(new Rect(30,30,160,160));     //范围Rect对象自己定义.

     Holder 为SurfaceView 的SurfaceHolder 对象变量.

      双缓冲技术, 双即二! 一个缓冲是什么?  另一个缓冲又是什么? 本人一直的理解是, 一个缓冲是内存(甚至是CPU的缓存), 一个是显示设备.

      画图的耗时和显示的耗时相比, 前者很多情况下明显要大于后者, 如果画图的操作过于复杂, 那这个差距就更大了. 一边画, 一边更新到屏幕上, 由于更新的频率快并且不均匀, 将造成一个后果--闪烁.

      对于闪烁问题, 我们的前辈很聪明, 给出了一个双缓冲的方法. 很多情况下是, 我们先把显示内容画在内存中的一张位图上, 画好了, 再把这张位图拷贝到屏幕上. 这样做的好处是, 位图拷贝到屏幕上的耗时是比较稳定的, 在合理频率下, 画面的显示就会很稳定. 至于画图的操作有多复杂多困难, 跟画面显示没直接的关系了, 画面显示只能内存打交道, 画图也是在内存中暗地里进行着.

      以上是个人的理解, 没有参考什么权威说法之类的东东, 反正我不认同那些说 SurfaceView 因为用了两个canvas, 所以是双缓冲的说法.

      现在比较新的UI 框架, 有哪个的内置控件(组件)不是双缓冲的呢? 一般人能想到的, 那些大公司的高手没可能想不到, 控件都闪烁, 还不给人笑死.

你可能感兴趣的:(c,android,UI,框架,文档,each)