SurfaceView

SurfaceView是Android中一种特殊View,它允许在单独的线程中进行绘图,提高程序响应速度,常用于游戏开发。Surface代表一块屏幕缓冲区,与Window关联,而SurfaceView提供对Surface的控制,支持离屏渲染,保证高帧率。SurfaceHolder作为Surface的监听器,提供Surface状态变更的回调。Canvas则用于在Surface上进行绘制。

一、SurfaceView简介


SurfaceView可以在主线程之外的线程中向屏幕绘图上。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。在游戏开发中多用到SurfaceView,游戏中的背景、人物、动画等等尽量在画布canvas中画出。


什么是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)的涂鸦场所。由此,可以推知一个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中的内容)。

大家都知道,传统View及其派生类的更新只能在UI线程,然而UI线程还同时处理其他交互逻辑,这就无法保证View更新的速度和帧率了,而SurfaceView可以用独立的线程进行绘制,因此可以提供更高的帧率,例如游戏,摄像头取景等场景就比较适合SurfaceView来实现。

结合 SurfaceView 与 Surface 的关系,可得知整个屏幕都是经过了 Surface 的绘制,而 SurfaceView 则提供了手动绘制指定区域的机会。

下图为 SurfaceView 与 Surface 的关系。



二、相关类


SurfaceHolder

SurfaceHolder是一个接口,其作用是一个关于Surface的监听器,提供访问和控制 SurfaceView 内嵌的 Surface 相关的方法。它能够感知 Surface 的存在,并对 Surface 进行操作。

在SurfaceView中有一个方法 getHolder() ,可以很方便地获得SurfaceView所对应的Surface所对应的 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写入。因此要特别注意,只有当 unlockCanvasAndPost 之后,才能再获取 Canvas。


SurfaceHolder.Callback

SurfaceHolder.Callback接口可以感知到Surface的创建、销毁或者改变。

SurfaceHolder.Callback主要是当底层的Surface被创建、销毁或者改变时提供回调通知。我们知道,对画布绘制必须在Surface被创建后才能进行,因此SurfaceHolder.Callback中的surfaceCreated 和surfaceDestroyed 这两个方法就成了绘图处理代码的边界。

要实现该接口,只需要在 SurfaceHolder 中调用 addCallback() 方法即可。

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

@Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) 
    {
        //当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) 
    {
        //当surface对象创建后,该方法就会被立即调用。
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        //当surface对象在将要销毁前,该方法会被立即调用。

    }

Canvas

Canvas 对象,可以把它当做画布,Canvas 的方法大多数是设置画布的大小、形状、画布背景颜色等等,要想在画布上面画画,一般要与 Paint对象结合使用Paint就是画笔的风格,颜料的色彩之类的。

当然了,你也可以在上面花图片,这些都是通过调用 drawXXX() 来实现的。

// 创建画笔     
Paint paint = new Paint();  
// 设置红色       
paint.setColor(Color.RED);
 // 画一个圆   
canvas.drawCircle(60, 20, 10, paint); 

三、演示

注意了,如果你的自定义了 MySurfaceView:

如果你的 activity 显示内容就只是一个 MySurfaceView 的话,那么你只重写public MySurfaceView(Context context)就可以,然后在activity的onCreate方法中使用类似于下面的代码加载:

setContentView(new MySurfaceView(this));

如果你的 MySurfaceView 是放在一个xml布局文件中比如main.xml中,那么你的 MySurfaceView 中只需要重写public MySurfaceView(Context context, AttributeSet attrs),加载时就要这样:

setContentView(R.layout.main);


绘制线程:

// 绘制线程  
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;  
         }  
}  

自定义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);  
         }  
}

Activity

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

运行结果是这样的:


以上部分内容转载或参考来源如下:

http://technicalsearch.iteye.com/blog/1967616

在此表示感谢。
转载请注明来源,版权归原作者所有,未经同意严禁用于任何商业用途。
微博:http://weibo.com/theworldsong
邮箱:theworldsong@foxmail.com



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值