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渲染流程,无法应用实际开发,如果用于实际开发,如开发自定义控件,还需进一步完善自定义的类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/47608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HiFi-GAN——基于 GAN 的声码器,能在单 GPU 上生成 22 KHz 音频

拟议的 HiFiGAN 可从中间表征生成原始波形 源码地址&#xff1a;https://github.com/NVIDIA/DeepLearningExamples 论文地址&#xff1a;https://arxiv.org/pdf/2010.05646.pdf 研究要点包括 **挑战&#xff1a;**基于 GAN 的语音波形生成方法在质量上不及自回归模型和基于流…

纯前端小游戏,4096小游戏,有音效,Html5,可学习使用

// 游戏开始运行create: function(){this.fieldArray [];this.fieldGroup this.add.group();this.score 0;//4096 增加得分this.bestScore localStorage.getItem(gameOptions.localStorageName) null ? 0 : localStorage.getItem(gameOptions.localStorageName);for(var …

vscode及pycharm配置Python文件模板

一、vscode配置方法 第一步&#xff0c;依次点击“File”->“preference”->“Configure User Snippets”&#xff0c;在弹出的框中输入Python&#xff0c;打开python.json 文件 第二步&#xff0c;python.json 文件中输入以下内容&#xff1a; {"Python Template…

QtC++ 设计模式(五)——状态模式

状态模式 序言理解源码 序言 设计模式只是一个抽象的设计模式方法&#xff0c;并不是一个固定使用的搭配&#xff0c;就算是普通switch语句&#xff0c;Map&#xff0c;乃至状态机都是状态模式的其中一种实现方法 状态模式看起来好像和策略模式差不多&#xff0c;主要是其的侧…

快速上手绿联私有云UGOS Pro系统Docker

要快速上手使用绿联私有云UGOS Pro系统上的Docker&#xff0c;可以按照以下步骤进行操作&#xff1a; 1. 设置绿联私有云UGOS Pro 确保你已经成功设置并连接了绿联私有云设备。完成基本的网络配置和用户设置。 2. 访问UGOS Pro系统 通过浏览器访问你的绿联私有云管理界面。…

git记住账号和密码

git记住账号和密码 一、git记住账号和密码1. 全局记住2. 取消全局记住 一、git记住账号和密码 在使用 git 使用 https推送的时候一直需要输入密码。就可以试试下面的方法 1. 全局记住 windows 在git bash 中执行以下命令 linux 直接在bash中执行 git config --global crede…

深入探讨:Node.js、Vue、SSH服务与SSH免密登录

在这篇博客中&#xff0c;我们将深入探讨如何在项目中使用Node.js和Vue&#xff0c;并配置SSH服务以及实现SSH免密登录。我们会一步步地进行讲解&#xff0c;并提供代码示例&#xff0c;确保你能轻松上手。 一、Node.js 与 Vue 的结合 1.1 Node.js 简介 Node.js 是一个基于 …

Java记事本工具Notepad++

常见的高级记事本 Editplus、Notepad、Sublime Notepad软件的安装和使用 安装&#xff1a;傻瓜式安装 1、选择中文-->【OK】 2、点击【下一步】 3、协议点击【我接受】 4、选择安装路径-->【下一步】 5、点击【下一步】 6、最后点击【安装】 7、将运行取消-->点击…

戴尔电脑开机出现no boot device found错误提示原因分析及解决方法

戴尔电脑是一款不的品牌,戴尔电脑一直以来都是以IT直销享誉全球的。而旗下的戴尔笔记本&#xff0c;更是深受用户们的追捧和喜爱。最近有网友反馈戴尔电脑开机出现no boot device found错误提示是怎么回事&#xff1f;后来发现有很多网友将引导模式改成legacymbr后发现启动时出…

Python 中的属性和方法

在面向对象编程&#xff08;OOP&#xff09;中&#xff0c;类是代码的基本构造块。类通过属性和方法来定义对象的状态和行为。在 Python 中&#xff0c;属性和方法是定义类时的重要组成部分。本文将深入探讨 Python 中的属性和方法&#xff0c;帮助你更好地理解它们的用法和意义…

2024-07-18 Unity插件 Odin Inspector8 —— Type Specific Attributes

文章目录 1 说明2 特定类型特性2.1 AssetList2.2 AssetSelector2.3 ChildGameObjectsOnly2.4 ColorPalette2.5 DisplayAsString2.6 EnumPaging2.7 EnumToggleButtons2.8 FilePath2.9 FolderPath2.10 HideInInlineEditors2.11 HideInTables2.12 HideMonoScript2.13 HideReferenc…

对消息队列进行深入学习

目录 1.什么是消息队列。1.1消息队列1.1.1同步的理解1.1.2异步的理解 1.2消息传递与消息队列 2. 消息队列应用场景2.1 异步处理2.2 流量削锋2.3 应用解耦2.4 日志处理2.5 消息通讯2.6 延时任务2.7 广播消费2.8 分布式事务 3. 主流消息队列3.1 RabbitMQ3.1.1 RabbitMQ工作原理3.…

河南萌新联赛2024第(一)场:河南农业大学(部分题解)

A 造数 题目链接 题意&#xff1a; 给一个整数n&#xff0c;求出最少的操作数使0转化为n 有三种操作方式&#xff1a; 12*2 解题思路&#xff1a; 我们可以将基础的1&#xff0c;2&#xff0c;3分别需要1&#xff0c;1&#xff0c;2次操作&#xff0c; 当n大于3时&#x…

仅两家!云原生向量数据库 PieCloudVector 全项通过信通院「可信数据库」评测

7月16日&#xff0c;2024 可信数据库发展大会在北京隆重举行。大会以“自主、创新、引领”为主题&#xff0c;近百位数据库领域的专家、学者齐聚一堂&#xff0c;带来高质量的数据库技术洞察与实战经验。 本次可信数据库发展大会中&#xff0c;中国信通院正式公布 2024 年上半年…

紫光展锐5G安卓核心板T760__国产手机芯片方案

展锐T760安卓核心板是具备续航和性能更加均衡的5G移动平台。其主要特点包括主流的6400万像素摄像头和高达120Hz的刷新率。 平台采用多模融合的创新架构和AI智能调节技术&#xff0c;从而在5G数据场景下降低了37%的整体功耗&#xff0c;在5G待机场景下降低了18%的整体功耗。 多…

gin框架 POST 请求参数绑定 JSON数据ShouldBind 使用注意事项 - 结构体必须定义json标签

gin框架中的请求数据绑定ShouldBind可将前端发送的数据直接绑定到自定义结构体&#xff0c; 但是在POST发送JSON 数据时 需要注意 因为gin框架在底层绑定数据时使用了json对参数进行了反序列化&#xff0c; 所以我们在自定义的结构体中&#xff0c;如果前端发送的JSON中的字段和…

直播架构如何设计核心节点和边缘节点

在直播架构中&#xff0c;核心节点和边缘节点的分工及主要服务是确保直播服务稳定、高效和可扩展的关键。以下是对这些节点的详细描述&#xff1a; 核心节点 核心节点通常位于数据中心&#xff0c;负责处理直播的主要逻辑和数据处理。其主要服务包括&#xff1a; 直播管理后…

Redis常见阻塞原因

1、命令阻塞 使用时间复杂度O&#xff08;n&#xff09;的不当命令造成全表扫描&#xff0c;导致阻塞 如 keys * 获取所有key 2、RDB持久化save阻塞 Redis提供两个命令来生成RDB快照文件&#xff1a; 1、save&#xff1a;主线程阻塞&#xff0c;写完RDB才放行 2、bgsave : …

qml 实现一个listview

主要通过qml实现listvie功能&#xff0c;主要包括右键菜单&#xff0c;滚动条&#xff0c;拖动改变内容等&#xff0c;c 与 qml之间的变量和函数的调用。 main.cpp #include <QQuickItem> #include <QQmlContext> #include "testlistmodel.h" int main…

js vue axios post 数组请求参数获取转换, 后端go参数解析(gin框架)全流程示例

今天介绍的是前后端分离系统中的请求参数 数组参数的生成&#xff0c;api请求发送&#xff0c;到后端请求参数接收的全过程示例。 为何会有这个文章&#xff1a;后端同一个API接口同时处理单条或者多条数据&#xff0c;这样就要求我们在前端发送请求参数的时候需要统一将请…