Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系

article/2025/11/3 15:11:51

一、Surface

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

1.      通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;

2.      原生缓冲器(rawbuffer)是用于保存当前窗口的像素数据的。

引伸地,可以认为Android中的Surface就是一个用来画图形 (graphics)或图像(image)的地方。通常画图是在一个Canvas对象上面进行的,由此,可以推知一 个Surface对象中应该包含有一个Canvas对象,事实上的确如此,而且这一点可以很容易通过debug运行程序的方式得到证明(将光标停留在对象 变量surface上,会弹出一个对话框,其中红色方框的内容,就表面surface中有一个CompatileCanvas成员变量)当然,看源代码也 是可以证明这一点:

所以,Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原生缓冲器是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。

二、SurfaceView

通过SurfaceView就可以看到Surface的部分或者全部的内容:

SurfaceView是Android中View的子类。事实上,在Android中所有用于界面展示的类皆为View的子类,包括那些不可见的、各种各样的Layout。

在Android中Surface是从Object派生而来,且实现了 Parcelable接口。看到Parcelable就让人能很自然地想到数据容器,SurfaceView就是用来展示Surface中的数据的。在这 个层面上而言,Surface就是管理数据的地方,SurfaceView就是展示数据的地方。

三、SurfaceHolder

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

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

1.        abstract void addCallback(SurfaceHolder.Callback callback)

为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。

2.        abstract Canvas lockCanvas()

获取一个Canvas对象,并锁定之。所得到的Canvas对象,其实就是Surface中一个成员。

3.        abstract Canvas lockCanvas(Rectdirty)

同上。但只锁定dirty所指定的矩形区域,因此效率更高。

4.        abstract void unlockCanvasAndPost(Canvascanvas)

当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。

5.      public abstract void setType (int type)

         设置Surface的类型,接收如下的参数:

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对象了。需要注意的是,在高版本的Android SDK中,setType这个方法已经被depreciated了。

2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。

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

四、SurfaceHolder.Callback

前面已经讲到SurfaceHolder是一个接口,它通过回到方法的方式,让我们可 以感知到Surface的创建、销毁或者改变。其实这一点是通过其内部的静态子接口SurfaceHolder.Callback来实现的。 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对象在将要销毁前,该方法会被立即调用。

 

在Android SDK文档中,关于SurfaceView的描述里面,有一段这样的话:

这个类的目的之一,就是提供一个可以用另外一个线程(第二个线程)进行屏幕渲染的surface(译注:即UI线程和绘制线程可以分离)。如果你打算这样使用,那么应当注意一些线程方面的语义:

-           所有SurfaceView和 SurfaceHolder.Callback中声明的方法,必须在运行SurfaceView窗口中的线程中调用(典型地,就是应用的主线程。译注:即 UI线程),因为它们需要正确地将同时被绘制线程访问的各种状态进行同步。

-           必须保证,只有在背后的Surface有效的时候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()这两个方法调用之间,访问它。

 

下面,我们通过一个非常简单例子来实际感受一下(代码摘自http://www.cnblogs.com/xuling/archive/2011/06/06/android.html,并做了一些结构性的改动),请留意代码中的注释:

1.        在Eclipse中创建一个Android Project项目TestSurfaceView,并选择生成缺省的Activity TestSurfaceViewActivity

2.        创建一个绘制线程如下:

package com.pat.testsurfaceview;  import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.Rect;  
importandroid.view.SurfaceHolder;  // 绘制线程  
public class MyThread extendsThread  
{  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);  // 创建画笔  Paintp = 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类如下:

package com.pat.testsurfaceview;  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;  publicMySurfaceView(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(SurfaceHolderholder)  {  // 结束线程。当这个方法调用时,说明Surface即将要被销毁了  myThread.setRun(false);  }  
}

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

package com.pat.testsurfaceview;  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));  }  
}

运行结果:

很显然,我们可以在MyThread的run方法中,做很多更有意思的事情。弄清楚了 Surface、SurfaceView、SurfaceHolder和SurfaceHolder.Callback这些概念,以及它们之间的关系,对 我们更好地使用它们应该会有相当大的帮助。

 


http://chatgpt.dhexx.cn/article/9APuH8Ld.shtml

相关文章

android surfaceview学习(一)

推荐阅读: https://blog.csdn.net/luoshengyang/article/details/8661317 https://www.cnblogs.com/xuling/archive/2011/06/06/android.html https://blog.csdn.net/zcmain/article/details/14454953 1、surfaceview默认是黑色的背景,并且给sfv设置背景…

Activity、Fragment和Surface的生命周期

这里首先推荐大家一本Android的学习书籍:《第一行代码》,这本书还是写得非常好的。好了,进入正题,我们先来了解一下Activity、Fragment的的生命周期中经历的几个阶段。 ActivityFragment 下面我们假设有如下的场景,Act…

Java进度条

转自:点击打开链接 进度条用在很多的地方,比如登录验证等待过程、程序加载、数据下载、数据更新与删除等,它可以一目了然的让用户知道程序执行的进度。下面给大家看一个登录验证的进度条,首先上效果图: 在代码中未连接…

java窗口进度条_java进度条窗口具体如何制作?

可视化窗口是大部分游戏的常态,今天我们就来了解下如何编写进度条窗口,快跟小编一起看看吧。 一、在包右击new->other->WindowBuilder->JFrame->name叫Guithreadpackage guithread; import java.awt.BorderLayout; import java.awt.EventQue…

java进度条_「java进度条」Java JProgressBar类(进度条) - seo实验室

java进度条 简介 利用JprogressBar类可以实现一个进度条。它一般是一种颜色部分或完全填充的矩形。缺省情况下,进度条配备了一个凹陷的边框,并水平放置。 进度条还可以选择显示一个字符串,这个字符串在进度条矩形的中央位置上显示。这个字符串缺省时为耗时任务已完成的百分比…

Java 进度条

效果 代码 思路:使用NumberFormat类来格式化数字,显示完后,退格覆盖重新显示新的数字 import java.text.NumberFormat;public class Test {public static void main(String[] args) throws Exception {System.out.print("Progress:"…

Java图形化界面---进度条

目录 一、进度条的介绍 二、创建进度条 三、简单案例 四、子线程创建进度条 (1)为什么要使用子线程创建进度条 (2)子线程创建进度条的代码 五、创建进度对话框 (1)如何创建进度对话框 (2&…

Java图形化界面编程超详细知识点(7)——进度条

目录 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 4.1 创建进度条 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 进度条是图形界面中广泛使用的GUI组件,当复制一个较大的文件时,操作系统会显示一个进度条&#…

Java制作进度条

【效果图】 【描述】 进度条宽度保持和窗口宽度一致,进度最小值为0,最大值为100,每100毫秒进度值1,进度条中间显示进度百分比 【代码】 package test;import java.awt.*; import java.awt.event.*;import javax.swing.*;public…

QT:QGraphicsScene与QGraphicsView使用render()函数渲染图片

render()函数看官方文档,会有很多不懂得地方,以下为实践出来的效果。 当我们想要用QPaint() 绘制我们在QGraphicsView看到的部分时,不仅要绘制正确的图片大小,还需要使用 render() 渲染正确的大小。当我们只想要绘制坐标系中矩形…

Qt图形特效:QGraphicsOpacityEffect

一、描述 不透明度效果使源具有不透明度。该效果对于使源半透明非常有用,类似于淡入/淡出序列。可以使用setOpacity()函数修改不透明度。 二、属性成员 1、opacity : qreal。此属性保存不透明度。该值应在0.0到1.0的范围内,其中0.0是完全透明的&#…

图形视图(02):【类】QGraphicsScene [官翻]

文章目录 详述事件处理和传播 公共类型enum ItemIndexMethodenum SceneLayer 属性backgroundBrush: QBrushbspTreeDepth: intfocusOnTouch: boolfont: QFontforegroundBrush: QBrushitemIndexMethod: ItemIndexMethodminimumRenderSize: qrealpalette: QPalette**sceneRect**: …

Qt中使用QGraphicsScene重写drawBackGround绘制背景

Qt中使用QGraphicsScene重写drawBackGround绘制背景 需求解释 我是想这学习Qt的界面设计,希望能够对界面背景进行优化然后使用Qt的界面与图形画背景网格。首先查资料 通过查找博客发现很简单,就是新建一个类继承于QGraphicsScene,于是我就…

图形视图框架QGraphicsScene(场景,概念)

QGraphicsScene 该类充当 QGraphicsItems 的容器。它与 QGraphicsView 一起使用,用于在 2D 表面上可视化图形项目,例如线条、矩形、文本甚至自定义项目。 QGraphicsScene具有的功能: 提供用管理大量数据项的高速接口传播事件到每一个图形项…

qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果

应大家的要求,还是把完整的工程文件贴出来,大家省点事:http://www.kuaipan.cn/file/id_48923272389086450.htm 先看看运行效果,我用的群创7寸屏,主机是mini2440,分辨率是800*480,程序写比较粗糙…

Qt QGraphicsScene、QGraphicsView类实现仪表盘

Qt QGraphicsScene、QGraphicsView类实现仪表盘 【1】UI界面设计【2】效果【3】QGraphicsScene简介【4】QGraphicsEllipseItem简介【5】QGraphicsPolygonItem简介【6】QGraphicsLineItem简介【7】QGraphicsView简介【8】仪表源码头文件源码 【1】UI界面设计 【2】效果 【3】QGr…

qt QGraphicsScene 简单例子

坐标 graphicsView是Widget这个界面的子控件, 他的位置是左上角坐标为(30,30) 他的宽度为431,高度为431. 如果在graphicsView 这个子控件中, 继续加入 子控件A,由于graphicsView比较特殊。 根据它的align…

QGraphicsScene设置SceneRect

简要说明 QGraphicsScene场景区域,可在构造QGraphicsScene对象时设定,也可通过函数setSceneRect设定。QGraphicsScene场景区域中坐标原点的位置,会影响到图形项的坐标设定,进而影响图形项在场景中的显示位置。以将图片显示在中心…

QT QGraphicsScene / QGraphicsView中 sceneRect 使用详细说明

接触QT不久,发现QT的Graphics View框架坐标使用和平常不一样,尤其sceneRect的作用。 官方文档关于这部分介绍真是蛋疼,网上说的也寥寥。没法自己研究了一下,其实这块内容QT设计的很方便,只是文档讲解的太烂&#xff0c…

【QT】GraphicsView、QGraphicsScene、QGraphicsEllipseItem的使用

GraphicsView、QGraphicsScene、QGraphicsEllipseItem的使用 前言实验目的重构GraphicsView类新建GraphicsView、QGraphicsScene层绘制Item图形控件绘制圆形绘制矩形绘制三角形绘制文字绘制直线 组合、打散、前置和后置组合打散控件前置控件后置 绑定功能事件总结 前言 对于绘…