当前位置: 首页 > news >正文

Android SurfaceView 组件介绍,挖洞原理详解

文章目录

    • 组件介绍
      • 基本概念
      • 关键特性
      • 使用场景
    • SurfaceHolder介绍
      • 主要功能
      • 使用示例
    • SurfaceView 挖洞原理
      • 工作机制
    • 使用SurfaceView展示图片示例
      • 创建一个自定义的 SurfaceView类
      • 在 Activity 中使用 ImageSurfaceView
      • 注意事项
      • 效果展示

组件介绍

在 Android 开发中,SurfaceView 是一个非常特别的组件,它提供了一个专门的绘制表面,允许在主线程之外的线程上进行绘制操作。这使得 SurfaceView 非常适合需要高性能绘制和更新的场景,如视频播放和游戏渲染。这是因为它可以减少和避免 UI 线程的阻塞和延迟。

基本概念

SurfaceView 继承自 View 类,但与普通的 View 不同,它内部使用了一个独立的绘图表面(即 Surface),这个 Surface 可以在主 UI 线程之外的线程上进行控制和绘制。这样做的好处是,即使绘制操作很复杂,也不会影响到 UI 线程的响应性和流畅性。

关键特性

  1. 独立的绘图表面

    • SurfaceViewSurface 是独立于应用窗口的其余部分,并且可以在单独的线程中进行更新和渲染。这意味着它不会受到其他视图层级更新的影响,从而提高了渲染效率。
  2. 线程安全

    • 由于 Surface 可以从任何线程进行访问和修改,因此 SurfaceView 特别适用于后台线程渲染内容,如视频播放和动态图形。
  3. 可见性管理

    • SurfaceView 在屏幕上的可见性由 Android 的窗口管理器直接处理。当 SurfaceView 变为不可见时,它的 Surface 可能会被销毁,因此开发者需要在适当的生命周期回调中管理资源和绘制状态。

使用场景

  • 视频播放SurfaceView 常用于视频播放应用,因为视频解码和渲染可以在单独的线程上进行,避免 UI 线程阻塞。
  • 实时游戏渲染:游戏中的图形渲染需要高频率的更新和高性能,SurfaceView 提供的独立绘图表面能够满足这些要求。
  • 相机预览:相机应用中,SurfaceView 可用于显示相机的实时预览流。

SurfaceHolder介绍

在Android开发中,SurfaceHolder是一个接口,用于控制和监视Surface对象的状态。Surface是一个特殊的对象,它承载了一个可以绘制的矩形区域。这个区域可以由你的应用程序或其他应用程序来绘制。SurfaceView类就是围绕Surface提供一个可视组件的实现,而SurfaceHolder提供了对这个Surface的控制和管理。

主要功能

1. 访问和控制Surface

  • SurfaceHolder允许开发者直接访问Surface对象,这意味着你可以管理和绘制到Surface上的图形内容。通过SurfaceHolder,你可以获取SurfaceCanvas,并在这个Canvas上进行绘制操作。

2. 监听Surface的状态变化:

  • SurfaceHolder提供了一个回调机制,通过实现SurfaceHolder.Callback接口,你可以监听Surface的创建、改变和销毁事件。这些回调方法是:
    • surfaceCreated(SurfaceHolder holder): 当Surface第一次创建时调用,你应该在这个回调中开始绘制的操作。
    • surfaceChanged(SurfaceHolder holder, int format, int width, int height): 当Surface的格式或大小发生变化时调用。
    • surfaceDestroyed(SurfaceHolder holder): 当Surface即将被销毁时调用,你应该在这个回调中停止绘制操作,并进行清理。

3. 控制Surface的格式和尺寸:

  • SurfaceHolder允许开发者设置Surface的尺寸、格式和类型。例如,你可以指定Surface的分辨率和颜色深度。

使用示例

在使用SurfaceView时,你通常会与SurfaceHolder打交道。

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {private SurfaceHolder surfaceHolder;private DrawingThread drawingThread;public MySurfaceView(Context context) {super(context);init();}private void init() {surfaceHolder = getHolder();surfaceHolder.addCallback(this);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {drawingThread = new DrawingThread(holder);drawingThread.setRunning(true);drawingThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {// Handle changes}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {boolean retry = true;drawingThread.setRunning(false);while (retry) {try {drawingThread.join();retry = false;} catch (InterruptedException e) {// handle interruption}}}private class DrawingThread extends Thread {private SurfaceHolder surfaceHolder;private boolean isRunning = false;public DrawingThread(SurfaceHolder holder) {this.surfaceHolder = holder;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run() {while (isRunning) {Canvas canvas = null;try {canvas = surfaceHolder.lockCanvas();synchronized (surfaceHolder) {if (canvas != null) {// Perform drawing on the canvas}}} finally {if (canvas != null) {surfaceHolder.unlockCanvasAndPost(canvas);}}}}}
}

SurfaceHolder是一个非常强大的工具,它为Surface的管理提供了广泛的控制功能,同时使得在单独的线程中进行复杂的绘图操作成为可能,从而不影响应用程序的主UI线程的响应性。这在需要高性能绘图更新,如游戏或媒体播放器等应用中尤其重要。

SurfaceView 挖洞原理

在 Android 中,SurfaceView 是一个用于直接绘制图形的视图,通常用于游戏或视频播放等高性能需求的应用场景。SurfaceView 的挖洞原理主要涉及到 SurfaceView 的工作机制和它与窗口系统之间的交互。

工作机制

  1. 双缓冲机制SurfaceView 使用双缓冲机制,即前缓冲区和后缓冲区。前缓冲区显示在屏幕上,后缓冲区用于绘制新的内容。当后缓冲区绘制完成后,前后缓冲区交换,新的内容就会显示在屏幕上。这种机制可以减少屏幕闪烁和绘制延迟。

  2. 独立 SurfaceSurfaceView 创建了一个独立的 Surface,它与主 UI 线程分离,允许在另一个线程上进行绘制操作。这使得在 SurfaceView 上进行复杂的图形绘制时不会阻塞主 UI 线程,提高了绘制性能。

  3. SurfaceHolderSurfaceView 通过 SurfaceHolder 来管理其 Surface 的生命周期和绘制操作。SurfaceHolder 提供了一系列的回调方法,例如 surfaceCreatedsurfaceChangedsurfaceDestroyed,用于监听 Surface 的创建、改变和销毁。

SurfaceView 挖洞的原理实际上是利用了 SurfaceView 绘制的特性,通过创建一个透明或空洞的区域,使得底层的内容可以透过 SurfaceView 显示出来。具体步骤如下:

  1. 设置透明背景:将 SurfaceView 的背景设置为透明,使得 SurfaceView 背景区域不绘制任何内容,从而形成“挖洞”效果。

    <SurfaceViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/transparent" />
    
  2. 绘制透明区域:在 SurfaceView 的 Canvas 上绘制一个透明区域,使得该区域不会绘制任何内容,从而露出底层视图。例如,可以使用 Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) 方法清空指定区域。

  3. 层次叠加:利用视图的层次叠加关系,将 SurfaceView 放置在其他视图之上,并通过透明区域露出下层视图的内容。例如,可以在 SurfaceView 下方放置一个 ImageView 或其他自定义视图,通过透明区域显示底层视图的内容。

以下是一个简单的代码示例,演示了如何在 SurfaceView 中实现透明区域(“挖洞”):

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {private Paint mPaint;private Rect mRect;public MySurfaceView(Context context) {super(context);getHolder().addCallback(this);mPaint = new Paint();mPaint.setColor(Color.RED); // 设置绘制颜色mRect = new Rect(100, 100, 300, 300); // 定义透明区域}@Overridepublic void surfaceCreated(SurfaceHolder holder) {draw();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}private void draw() {Canvas canvas = getHolder().lockCanvas();if (canvas != null) {canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // 清空整个画布canvas.drawRect(mRect, mPaint); // 绘制矩形区域getHolder().unlockCanvasAndPost(canvas);}}
}

这个示例创建了一个自定义的 SurfaceView,并在其中绘制了一个矩形区域。通过 PorterDuff.Mode.CLEAR 模式,可以清空指定区域,使得该区域变得透明,从而实现“挖洞”效果。

使用SurfaceView展示图片示例

要在 Android 中使用 SurfaceView 在单独的线程中展示一张图片,我们可以通过创建一个自定义的 SurfaceView 类来实现。

示例代码:

创建一个自定义的 SurfaceView类

这个类将包含加载图片和在 SurfaceView 上绘制图片的逻辑:


public class ImageSurfaceView extends SurfaceView implements SurfaceHolder.Callback {private DrawThread drawThread;private Bitmap imageBitmap;public ImageSurfaceView(Context context,int imageResource) {super(context);this.imageBitmap  = BitmapFactory.decodeResource(context.getResources(),imageResource);getHolder().addCallback(this);}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {//开启新线程 绘制图片drawThread = new DrawThread(getHolder(), imageBitmap);drawThread.setRunning(true);drawThread.start();}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {//停止绘制线程boolean retry = true;drawThread.setRunning(false);while (retry) {try {drawThread.join();retry = false;} catch (InterruptedException e) {e.printStackTrace();}}}//新开一个线程用来绘制图片到Canvasprivate class DrawThread extends Thread{private SurfaceHolder surfaceHolder;private boolean isRunning = false;private Bitmap imageBitmap;public DrawThread(SurfaceHolder holder,Bitmap imageBitmap) {this.surfaceHolder = holder;this.imageBitmap = imageBitmap;}public void setRunning(boolean isRunning) {this.isRunning = isRunning;}@Overridepublic void run(){while(isRunning){Canvas canvas = null;//尝试获取canvas 绘制图片try {canvas = surfaceHolder.lockCanvas();if (canvas != null) {synchronized (surfaceHolder) {//绘制图片canvas.drawBitmap(imageBitmap, 0, 0, null);}}}finally {if (canvas != null) {surfaceHolder.unlockCanvasAndPost(canvas);}}}}}}

在 Activity 中使用 ImageSurfaceView

import android.app.Activity;
import android.os.Bundle;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 替换 R.drawable.your_image 为你的实际图片资源ImageSurfaceView imageSurfaceView = new ImageSurfaceView(this, R.drawable.your_image);setContentView(imageSurfaceView);}
}

注意事项

  • 确保你的图片资源位于 res/drawable 文件夹中。
  • ImageSurfaceView 类中的 DrawThreadsurfaceDestroyed 方法被调用时应该被正确地停止,以避免可能的内存泄露或崩溃。
  • 这个例子展示了在一个单独的线程中加载和绘制图片,以避免阻塞 UI 线程。

效果展示

在这里插入图片描述

注:这个dmeo仅仅展示了SurfaceView 新开线程展示图片效果,学习SurfaceView渲染流程,无法应用实际开发,如果用于实际开发,如开发自定义控件,还需进一步完善自定义的类

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Apache httpd-vhosts.conf 配置详解(附Demo)
  • 【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十一)-无人机服务可用性用例需求
  • 不常用的第三方服务集成
  • [米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-22 TPG图像测试数据发生器设计
  • CSS实现从上往下过渡效果
  • 【算法基础】Dijkstra 算法
  • 乘积量化pq:将高维向量压缩 97%
  • SSM 整合(Spring + MyBatis;Spring + Spring MVC)
  • VUE中setup()
  • Python爬虫速成之路(3):下载图片
  • 【常见开源库的二次开发】基于openssl的加密与解密——Base的编解码(二进制转ascll)(二)
  • 1219:马走日
  • STM32 不同时钟频率有什么不同的影响
  • 云计算实训室的核心功能有哪些?
  • Xcode 16 beta3 真机调试找不到 Apple Watch 的尝试解决
  • [ JavaScript ] 数据结构与算法 —— 链表
  • [译]CSS 居中(Center)方法大合集
  • ES6之路之模块详解
  • github从入门到放弃(1)
  • javascript 总结(常用工具类的封装)
  • JS专题之继承
  • mongodb--安装和初步使用教程
  • Node + FFmpeg 实现Canvas动画导出视频
  • python3 使用 asyncio 代替线程
  • python大佬养成计划----difflib模块
  • Python语法速览与机器学习开发环境搭建
  • Shadow DOM 内部构造及如何构建独立组件
  • Wamp集成环境 添加PHP的新版本
  • 从零搭建Koa2 Server
  • 构建二叉树进行数值数组的去重及优化
  • 马上搞懂 GeoJSON
  • 码农张的Bug人生 - 见面之礼
  • 如何使用 JavaScript 解析 URL
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 双管齐下,VMware的容器新战略
  • 思否第一天
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 写代码的正确姿势
  • 学习笔记:对象,原型和继承(1)
  • # 计算机视觉入门
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (day18) leetcode 204.计数质数
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (转) 深度模型优化性能 调参
  • (转)EOS中账户、钱包和密钥的关系
  • (转)visual stdio 书签功能介绍
  • (转)一些感悟
  • .gitignore文件忽略的内容不生效问题解决
  • .htaccess配置常用技巧
  • .NET MVC第三章、三种传值方式
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉