Android:Android中的Surface和SurfaceView

本文详细介绍了Android中的Surface和SurfaceView,解释了Surface作为屏幕缓冲区的作用,以及SurfaceView如何控制Surface的位置和尺寸。重点讨论了SurfaceView与View的区别,SurfaceView适合于需要高帧率更新的场景,如游戏和摄像头取景。SurfaceHolder作为Surface的监听器,通过SurfaceHolder.Callback回调方法通知Surface的状态变化。最后,文章提到了使用SurfaceView时应注意的同步问题和实例演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在做android开发时需要自定义组件,在网上查资料时看到有些例子 extends SurfaceView,这篇文章主要讲解SurfaceView和View的区别,已经对SurfaceView的理解和用法。
先来讲解一下Surface.

Surface

简单的说Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都要画在Surface的Canvas上(后面有原因解释)。传统的view共享一块屏幕缓冲区,所有的绘制必须在UI线程中进行。

在SDK的文档中,对Surface的描述是这样的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原始缓冲区的句柄”,这句话包括下面两个意思:

  • 1、通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C++语言中,可以通过一个文件的句柄,就可以获得文件的内容一样。
  • 2、 原始缓冲区(a raw buffer)是用于保存当前窗口的像素数据的。

引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。

根据Java方面的常规知识,我们知道通常画图是在一个Canvas对象上面进行的,由此,可以推知一个Surface对象中应该包含有一个Canvas(画布)对象。因此,在前面提及的两个意思的基础上,可以再加上一条:

  • 3、Surface中有一个Canvas成员,专门用于画图的。

由以上的概括,我们总结如下:Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原始缓冲区是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原始缓冲区以及其它方面的内容。

Surface是用来管理数据的。(句柄)

SurfaceView

说SurfaceView是一个View也许不够严谨,然而从定义中pubilc classSurfaceView extends View{…..}显示SurfaceView确实是派生自View,但是SurfaceView却有自己的Surface,请看SurfaceView的源码:

  if (mWindow == null) {  
          mWindow = new MyWindow(this);  
          mLayout.type = mWindowType;  
          mLayout.gravity = Gravity.LEFT|Gravity.TOP;  
          mSession.addWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,  
          mVisible ? VISIBLE : GONE, mContentInsets);   
    }  

很明显,每个SurfaceView创建的时候都会创建一个MyWindow,new MyWindow(this)中的this正是SurfaceView自身,因此将SurfaceView和window绑定在一起,由第一部分我们知道,一个window对应一个Surface,因此SurfaceView也就内嵌了一个自己的Surface,可以认为SurfaceView是用来控制Surface中View的位置和尺寸的。
SurfaceView就是展示Surface中数据的地方,同时可以认为SurfaceView是用来控制Surface中View的位置和尺寸的。

View与SurfaceView的区别

大家都知道,传统View及其派生类的更新只能在UI线程,然而UI线程还同时处理其他交互逻辑,这就无法保证View更新的速度和帧率了,而SurfaceView可以用独立的线程进行绘制,因此可以提供更高的帧率,例如游戏,摄像头取景等场景就比较适合SurfaceView来实现。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。

所以基于以上,根据游戏特点,一般分成两类。

1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

SurfaceHolder

SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器,提供访问和控制SurfaceView内嵌的Surface 相关的方法。它通过三个回调方法,让我们可以感知到Surface的创建、销毁或者改变。

在SurfaceView中有一个方法getHolder,可以很方便地获得SurfaceView内嵌的Surface所对应的监听器接口SurfaceHolder(有点拗口吧)。

除下面将要提到的SurfaceHolder.Callback外,SurfaceHolder还提供了很多重要的方法,其中最重要的就是:

  • 1、abstract void
    addCallback(SurfaceHolder.Callbackcallback):为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
  • 2、abstract Canvas
    lockCanvas():获取一个Canvas对象,并锁定之。所得到的Canvas对象,其实就是Surface中一个成员。
  • 3、abstract Canvas lockCanvas(Rect
    dirty):同上。但只锁定dirty所指定的矩形区域,因此效率更高。
  • 4、abstract void unlockCanvasAndPost(Canvas
    canvas):当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。

2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。lockCanvas是为了防止同一时刻多个线程对同一canvas写入。

通过SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface 就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。

为了充分利用不同平台的资源,发挥平台的最优效果可以通过SurfaceHolder的setType函数来设置绘制的类型,目前接收如下的参数:

  • SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
  • SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
  • SURFACE_TYPE_GPU:适用于GPU加速的Surface
  • SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。

总结:从设计模式的高度来看,Surface、SurfaceView和SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型,或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。

SurfaceHolder.Callback

使用的SurfaceView的时候,一般情况下还要对其进行创建,销毁,改变时的情况进行监视,这就要用到SurfaceHolder.Callback. SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知View. SurfaceHolder.Callback主要是当底层的Surface被创建、销毁或者改变时提供回调通知,由于绘制必须在Surface被创建后才能进行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

SurfaceHolder.Callback中定义了三个接口方法:

  • 1、abstract void surfaceChanged(SurfaceHolder holder, int format, int
    width, int height):当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
  • 2、abstract void surfaceCreated(SurfaceHolder
    holder):当surface对象创建后,该方法就会被立即调用。
  • 3、abstract void surfaceDestroyed(SurfaceHolder
    holder):当surface对象在将要销毁前,该方法会被立即调用。

使用SurfaceView需要注意以下几点情况: SurfaceView和SurfaceHolder.Callback函数都从当前SurfaceView窗口线程中调用(一般而言就是程序的主线程)

这里写图片描述

注意

注意:

  • 1.所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。 渲染线程所要访问的各种变量应该作同步处理。
  • 2.由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。可以在主线程之外的线程中向屏幕绘图,这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。

  • 3.View是没有缓存机制的,每次绘图都是重新绘制 若要使用双缓冲机制的话。

1、自定义一个View 实现onDraw方法
2、调用这个方法,调用刷新onDraw()方法ssinView.postInvalidate();
3.需要格外创建一个Canvas,每次都

public void onDraw(Canvas canvas){ 
    if(x >= 480){ 
        x = 0; //清空画布  
        canvas.drawColor(Color.BLACK); //重新创建一个新的图 
        bitmap =Bitmap.createBitmap(480, 104, Config.ARGB_8888); 
    } 
    else{
   //在缓冲中将之前的图绘制好,然后在真正的Canvas上绘制缓冲中的图 
       Canvas c = new Canvas();
       c.setBitmap(bitmap); 
       int y = (int)(A*Math.sin(x/180.0f*Math.PI));
       System.out.println("==================y:"+y+"===x:"+x);
       c.drawPoint(x, y+A, paint); //canvas.save();
       canvas.drawBitmap(bitmap, 0, 0,paint); x+=5; 
     } 
}
  • 4..Surfaceview实现了双缓冲技术

提供了一个类SurfaceHolder
1、继承SurfaceView 实现Callback接口(在new SurfaceView的时候会自动回调)
2、刷新SurfaceView
// TODO Auto-generated method stub

super.run();
while(flag == true){ 
    //锁定,获得Canvas
    Canvas canvas = surfaceHolder.lockCanvas(null);
    onDraw(canvas); //释放锁
    surfaceHolder.unlockCanvasAndPost(canvas);
    asinSurfaceView.x += 5;
    try { 
        Thread.sleep(sleepTime);
    } catch (InterruptedException e){ 
    // TODO Auto-generated catch block
        e.printStackTrace(); 
    } 
}

Surfaceview控制Surface的绘制位置。

  1. 第一步:继承SurfaceView并实现SurfaceHolder.Callback接口
  2. 第二步: SurfaceView.getHolder()获得SurfaceHolder对象
  3. 第三步:SurfaceHolder.addCallback(callback)添加回调函数
  4. 第四步:SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布
  5. 第五步:Canvas绘画
  6. 第六步:SurfaceHolder.unlockCanvasAndPost(Canvas
    canvas)结束锁定画图,并提交改变,将图形显示。

实例演示

下面,我们通过一个非常简单例子来实际感受一下,请留意代码中的注释:
1、在Android Studio中创建一个Android Project项目TestSurfaceView,并选择生成缺省的Activity TestSurfaceViewActivity
2、创建一个绘制线程如下:

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
importandroid.view.SurfaceHolder;

// 绘制线程
public class MyThread extends Thread
{
         private SurfaceHolder holder;
         private boolean run;

         public MyThread(SurfaceHolder holder)
         {
                   this.holder = holder;
                   run = true;
         }

         @Override
         public void run()
         {
                   int counter = 0;
                   Canvas canvas = null;
                   while(run)
                   {
                            // 具体绘制工作
                            try
                            {
                                     // 获取Canvas对象,并锁定之
                                     canvas= holder.lockCanvas();

                                     // 设定Canvas对象的背景颜色
                                     canvas.drawColor(Color.WHITE);

                                     // 创建画笔
                                     Paint p = new Paint();
                                     // 设置画笔颜色
                                     p.setColor(Color.BLACK);
                                     // 设置文字大小
                                     p.setTextSize(30);

                                     // 创建一个Rect对象rect
                                     Rect  rect = new Rect(100, 50, 380, 330);
                                     // 在canvas上绘制rect
                                     canvas.drawRect(rect,p);
                                     // 在canvas上显示时间
                                     canvas.drawText("Interval = " + (counter++) + " seconds.", 100, 410, p);
                                     Thread.sleep(1000);
                            }
                            catch(Exception e)
                            {
                                     e.printStackTrace();
                            }
                            finally
                            {
                                     if(canvas != null)
                                     {
                                               // 解除锁定,并提交修改内容
                                               holder.unlockCanvasAndPost(canvas);
                                     }
                            }
                   }
         }

         public boolean isRun()
         {
                   return run;
         }

         public void setRun(boolean run)
         {
                   this.run = run;
         }
}

3、自定义一个SurfaceView类如下:

import android.content.Context;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback
{
         private SurfaceHolder holder;
         private MyThread myThread;

         public MySurfaceView(Context context)
         {
                   super(context);

                   // 通过SurfaceView获得SurfaceHolder对象
                   holder = getHolder();

                   // 为holder添加回调结构SurfaceHolder.Callback
                   holder.addCallback(this);

                   // 创建一个绘制线程,将holder对象作为参数传入,这样在绘制线程中就可以获得holder
                   // 对象,进而在绘制线程中可以通过holder对象获得Canvas对象,并在Canvas上进行绘制
                   myThread = new MyThread(holder);
         }

         // 实现SurfaceHolder.Callback接口中的三个方法,都是在主线程中调用,而不是在绘制线程中调用的
         @Override
         public void  surfaceChanged(SurfaceHolder holder, int format, int width, int height)
         {
         }

         @Override
         public void  surfaceCreated(SurfaceHolder holder)
         {
                   // 启动线程。当这个方法调用时,说明Surface已经有效了
                   myThread.setRun(true);
                   myThread.start();
         }

         @Override
         public void surfaceDestroyed(SurfaceHolder holder)
         {
                   // 结束线程。当这个方法调用时,说明Surface即将要被销毁了
                   myThread.setRun(false);
         }
}

4、修改TestSurfaceViewActivity.java代码,使之如下:

import android.app.Activity;
import android.os.Bundle;

public class TestSurfaceViewActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        setContentView(new MySurfaceView(this));
    }
}

http://www.360doc.com/content/13/0103/14/7724936_257842268.shtml
http://blog.youkuaiyun.com/tgww88/article/details/7973476
http://www.cnblogs.com/technology-fans/archive/2012/02/29/2373928.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值