SurfaceView介绍

    |     2016年8月15日   |   Android UI界面   |     0 条评论   |    2238

普通 View 的绘制和用户响应都在 UI 主线程。耗时操作要丢到别的线程,否则会卡住界面;但后台线程又不能改 UI,例如在自定义 View 的 onDraw 里从工作线程刷新是不允许的。

需要在别的线程画界面、要迅速刷新、或渲染很耗时,就用 SurfaceView。它内嵌一个 Surface,可以在后台线程绘制。


一、官方 API 怎么说

SurfaceView 继承 View,内嵌专门用于绘制的 Surface。你可以控制这个 Surface 的格式和尺寸;SurfaceView 负责它画在窗口的哪个位置。

Surface 是 Z-ordered 的,总在自己所在窗口的后面。SurfaceView 提供可见区域:区域内的 Surface 才看得见。排版受视图层级影响,兄弟结点会盖在上面——可用来放文字、按钮等遮盖物。若 Surface 上有透明控件,每次变化都要重算透明效果,影响性能。

通过 SurfaceHolder 访问 Surface,getHolder() 拿到接口。SurfaceView 变得可见时 Surface 被创建,隐藏前被销毁,以节省资源。要观察时机,重写 surfaceCreated / surfaceDestroyed。

核心是两条线程:UI 线程和渲染线程。注意:

  1. 所有 SurfaceView 和 SurfaceHolder.Callback 方法都应在 UI 线程(一般是主线程)调用。渲染线程访问的变量要同步。
  2. Surface 可能被销毁,只在 surfaceCreated 和 surfaceDestroyed 之间有效,渲染线程必须访问合法 Surface。

二、自己的理解

1. 定义

可以直接从内存或 DMA 等硬件接口取得图像数据,是重要的绘图容器。

特点是:可以在主线程之外向屏幕绘图,避免画图太重阻塞主线程,提高反应速度。游戏里背景、人物、动画尽量在 Canvas 上画出。

2. 实现

继承 SurfaceView 并实现 SurfaceHolder.Callback。原则:所有绘图必须在 Surface 创建之后开始(Surface 可理解为显存映射,写入内容可直接复制到显存,所以快),销毁之前必须结束。因此 Callback 的 surfaceCreated / surfaceDestroyed 就是绘图代码的边界。

需要重写:

  • surfaceChanged(SurfaceHolder holder,int format,int width,int height):大小改变时激发。
  • surfaceCreated(SurfaceHolder holder):创建时激发,一般在这里启动画图线程。
  • surfaceDestroyed(SurfaceHolder holder):销毁时激发,一般停掉、释放画图线程。

整个过程:

  1. 继承 SurfaceView 并实现 Callback
  2. getHolder() 得到 SurfaceHolder
  3. addCallback(callback) 添加回调
  4. lockCanvas() 得到 Canvas 并锁定
  5. 在 Canvas 上画
  6. unlockCanvasAndPost(canvas) 结束锁定并提交显示

3. SurfaceHolder

可以当成 Surface 的控制器:操纵 Canvas 上的效果和动画,控制表面、大小、像素。几个方法:

  • addCallback(SurfaceHolder.Callback callback):给当前持有者一个回调对象。
  • lockCanvas():锁定画布,之后用返回的 Canvas 作画。
  • lockCanvas(Rect dirty):只锁一块脏区。提交后只改这块,内存吃紧的游戏可避免重画 dirty 外像素。
  • unlockCanvasAndPost(Canvas canvas):结束锁定并提交改变。
步骤 调用 含义
拿控制器 getHolder() SurfaceHolder
监听生命周期 addCallback 创建/销毁/尺寸变化
开画 lockCanvas 独占 Canvas
提交 unlockCanvasAndPost 显示到屏幕

4. Surface 调用过程图

surfaceview_1


三、例子:矩形 + 计时器

import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
  
 public class ViewTest extends Activity {
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(new MyView(this));
     }
     //视图内部类
     class MyView extends SurfaceView implements SurfaceHolder.Callback
     {
         private SurfaceHolder holder;
         private MyThread myThread; 
         public MyView(Context context) {
             super(context);
             // TODO Auto-generated constructor stub
             holder = this.getHolder();
             holder.addCallback(this);
             myThread = new MyThread(holder);//创建一个绘图线程
         }
  
         @Override
         public void surfaceChanged(SurfaceHolder holder, int format, int width,
                 int height) {
             // TODO Auto-generated method stub
              
         }
  
         @Override
         public void surfaceCreated(SurfaceHolder holder) {
             // TODO Auto-generated method stub
             myThread.isRun = true;
             myThread.start();
         }
  
         @Override
         public void surfaceDestroyed(SurfaceHolder holder) {
             // TODO Auto-generated method stub
             myThread.isRun = false;
         }
          
     }
     //线程内部类
     class MyThread extends Thread
     {
         private SurfaceHolder holder;
         public boolean isRun ;
         public  MyThread(SurfaceHolder holder)
         {
             this.holder =holder; 
             isRun = true;
         }
         @Override
         public void run()
         {
             int count = 0;
             while(isRun)
             {
                 Canvas c = null;
                 try
                 {
                     synchronized (holder)
                     {
                         c = holder.lockCanvas();//锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
                         c.drawColor(Color.BLACK);//设置画布背景颜色
                         Paint p = new Paint(); //创建画笔
                         p.setColor(Color.WHITE);
                         Rect r = new Rect(100, 50, 300, 250);
                         c.drawRect(r, p);
                         c.drawText("这是第"+(count++)+"秒", 100, 310, p);
                         Thread.sleep(1000);//睡眠时间为1秒
                     }
                 }
                 catch (Exception e) {
                     // TODO: handle exception
                     e.printStackTrace();
                 }
                 finally
                 {
                     if(c!= null)
                     {
                         holder.unlockCanvasAndPost(c);//结束锁定画图,并提交改变。
  
                     }
                 }
             }
         }
     }
 }

一句话总结:SurfaceView 把 Surface 交给后台线程 lockCanvas / unlockCanvasAndPost;只在 Created 与 Destroyed 之间画,Callback 方法留在主线程。

转载请注明来源:SurfaceView介绍
本文链接地址:https://ai.zhousir.top/?p=1671
回复 取消