Android应用开发(35)SufaceView基本用法

Android应用开发学习笔记——目录索引

 参考Android官网:https://developer.android.com/reference/android/view/SurfaceView

一、SurfaceView简介

SurfaceView派生自View,提供嵌入视图层次结构内部的专用绘图表面,SurfaceView可以在主线程之外的线程中向屏幕绘图,这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。

通过 SurfaceHolder 接口提供对Surface的访问,调用getHolder()方法获取SurfaceHolder。

实现SurfaceHolder.Callback回调接口,回调是通过 SurfaceHolder.addCallback方法设置。

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

SurfaceHolder.Callback#surfaceCreated  // 当首次创建surface后立即调用。
SurfaceHolder.Callback#surfaceChanged  //当surface进行任何更改立即调用此方法。
​SurfaceHolder.Callback#surfaceDestroyed  //当surface即将被破坏之前调用的。

//当surface对象创建后,该方法就会被立即调用。 
public voidsurfaceCreated(SurfaceHolder holder)   {  }  //当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, intarg3) {  }  //当surface对象在将要销毁前,该方法会被立即调用。   
public voidsurfaceDestroyed(SurfaceHolder holder)  {  }  

什么是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 的 Public 方法

voidapplyTransactionToFrame(SurfaceControl.Transaction transaction)

添加一个事务,该事务将与显示 SurfaceView 的下一帧同步应用。

voiddraw(Canvas canvas)

手动将此视图(及其所有子视图)渲染到给定的画布。

booleangatherTransparentRegion(Region region)

当视图层次结构包含一个或多个 SurfaceView 时,ViewRoot 使用它来执行优化。

SurfaceHoldergetHolder()

返回 SurfaceHolder,提供对此 SurfaceView 底层表面的访问和控制。

IBindergetHostToken()

用于构造 的令牌SurfaceControlViewHost

intgetImportantForAccessibility()

获取用于确定此 View 对于可访问性是否重要的​​模式。

SurfaceControlgetSurfaceControl()

返回一个 SurfaceControl,可用于将 Surface 设为此 SurfaceView 的父级。

booleanhasOverlappingRendering()

返回此视图是否有重叠的内容。

voidsetAlpha(float alpha)

将视图的不透明度设置为 0 到 1 之间的值,其中 0 表示视图完全透明,1 表示视图完全不透明。

voidsetChildSurfacePackage(SurfaceControlViewHost.SurfacePackage p)

显示嵌入此 SurfaceView 中的视图层次结构SurfaceControlViewHost.SurfacePackage 。

voidsetClipBounds(Rect clipBounds)

在此视图上设置一个矩形区域,绘制视图时将剪裁到该区域。

voidsetSecure(boolean isSecure)

控制是否应将表面视图的内容视为安全,以防止其出现在屏幕截图中或在非安全显示器上查看。

voidsetSurfaceLifecycle(int lifecycleStrategy)

控制此 SurfaceView 拥有的 Surface 的生命周期。

voidsetVisibility(int visibility)

设置此视图的可见性状态。

voidsetZOrderMediaOverlay(boolean isMediaOverlay)

控制表面视图的表面是否放置在窗口中另一个常规表面视图的顶部(但仍在窗口本身的后面)。

voidsetZOrderOnTop(boolean onTop)

控制表面视图的表面是否放置在其窗口的顶部。

SurfaceView 的 Protected 方法

voiddispatchDraw(Canvas canvas)

由draw调用来绘制子视图。

voidonAttachedToWindow()

当视图附加到窗口时调用此方法。

voidonDetachedFromWindow()

当视图与窗口分离时调用此方法。

voidonFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)

当该视图的焦点状态发生变化时,由视图系统调用。

voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

测量视图及其内容以确定测量的宽度和测量的高度。

booleanonSetAlpha(int alpha)

如果存在涉及 alpha 的 Transform,则调用。

voidonWindowVisibilityChanged(int visibility)

当包含的窗口的可见性发生更改(在GONEINVISIBLE和 之间VISIBLE)时调用。

二、SurfaceView和View的不同之处

Android提供了View来进行绘图处理,在大部分情况下View都能满足绘图需求。大家都知道View是通过刷新来重绘视图,Android系统通过发出VSYNC信号来进行屏幕的重绘,刷新的间隔时间为16ms。如果在16ms内View完成了你所需要执行的所有操作,那么用户在视觉上,就不会产生卡顿的感觉;反之,如果操作的逻辑过多时,处理时间超过了一帧的时间周期,就会掉帧,从而使得用户感觉到卡顿。特别的需要频繁刷新的界面上,如游戏(60FPS以上)、高帧率视频等,就会不断阻塞主线程,从而导致界面卡顿。基于此Android提供SurfaceView来解决这种情况。

ViewSurfaceView
共享Surface独立Surface
在主线程中进行画面更新通常通过一个子线程来进行画面更新

SurfaceView和View一大不同:View通过invalidate方法通知系统来主动刷新界面的,但View的刷新是依赖于系统的VSYSC信号的,而且因为UI线程中的其他一些操作会导致掉帧卡顿。而对于SurfaceView而言,SurfaceView是在子线程中绘制图形,根据这一特性即可控制其显示帧率,通过简单地设置休眠时间,即可,并且由于在子线程中,一般不会引起UI卡顿。

Thread.sleep(50);即可以控制1s内刷新20次

三、SurfaceView的基本操作

1. 测试程序一:下面通过画圆来介绍SurfaceView的基本操作

public class MainActivity extends AppCompatActivity implements View.OnClickListener, SurfaceHolder.Callback {private static final String TAG = "SurfaceViewTest";private Button mButton;private SurfaceView mSurfaceView;private SurfaceHolder mSurfaceHolder;private Paint mPaint = new Paint();private int mCircleRadius = 10;private boolean isRunning = false;private boolean isStart = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);mSurfaceHolder = mSurfaceView.getHolder();mSurfaceHolder.addCallback(this);mButton = (Button) findViewById(R.id.button);mButton.setOnClickListener(this);mButton.setText("启动");}@Overridepublic void onClick(View v) {if (v.getId() == R.id.button) {isStart = !isStart;if (isStart) {mButton.setText("停止");start();} else {mButton.setText("启动");stop();}}}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {Log.d(TAG, "surfaceCreated...");mPaint.setAntiAlias(true); // 设置画笔为无锯齿mPaint.setColor(Color.RED); // 设置画笔的颜色mPaint.setStrokeWidth(10); // 设置画笔的线宽mPaint.setStyle(Paint.Style.FILL); // 设置画笔的类型。STROK表示空心,FILL表示实心mPaint.setTextSize(30);}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {Log.d(TAG, "surfaceChanged...");}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {Log.d(TAG, "surfaceDestroyed...");}// 开始绘制public void start() {isRunning = true;new Thread() {@Overridepublic void run() {while (isRunning) {drawCircle();try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}// 停止绘制public void stop() {isRunning = false;}// 绘制图形private void drawCircle() {long now = System.currentTimeMillis();synchronized (mSurfaceHolder) {Canvas canvas = mSurfaceHolder.lockCanvas();if (canvas != null) {// 设置画布为灰色背景色canvas.drawARGB(255, 55, 55, 55);// 画圆canvas.drawCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, mCircleRadius, mPaint);if (mCircleRadius < canvas.getWidth() / 2) {mCircleRadius++;} else {mCircleRadius = 10;}mSurfaceHolder.unlockCanvasAndPost(canvas);}}}
}

layout XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="350dp"android:layout_height="350dp"android:layout_marginTop="8dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:text="启动"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/surfaceView" />
</androidx.constraintlayout.widget.ConstraintLayout>

2. 测试程序二:下面通过touch划线来介绍SurfaceView的基本操作

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {private final static String TAG = "lzl-test";private SurfaceView mSurfaceView;//SurfaceHolderprivate SurfaceHolder mHolder;//用于绘图的Canvasprivate Canvas mCanvas;//子线程标志位private boolean mIsDrawing;//画笔private Paint mPaint;//路径private Path mPath;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "onCreate...");setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); //设置屏幕不随手机旋转setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //设置屏幕直向显示mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);mHolder = mSurfaceView.getHolder();mHolder.addCallback(this);/* 清屏 */findViewById(R.id.button_clear).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (v.getId() == R.id.button_clear) {mPath.reset();}}});}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {Log.d(TAG, "surfaceCreated...");if (mHolder == null) {mHolder = mSurfaceView.getHolder();mHolder.addCallback(this);}mPath = new Path();//初始化画笔mPaint = new Paint();mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(6);mPaint.setAntiAlias(true);mPaint.setColor(Color.RED);mSurfaceView.setFocusable(true);mSurfaceView.setFocusableInTouchMode(true);mSurfaceView.setKeepScreenOn(true);mIsDrawing = true;new Thread(runnable).start();}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {Log.d(TAG, "surfaceChanged...");}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {Log.d(TAG, "surfaceDestroyed...");mIsDrawing = false;mHolder = null;}private void draw() {if (mHolder != null) {Canvas canvas = null;try{//用于绘图的Canvas, 锁定画布并返回画布对象canvas = mHolder.lockCanvas();//接下去就是在画布上进行一下drawcanvas.drawColor(Color.WHITE);canvas.drawPath(mPath,mPaint);} catch (Exception e){} finally {//当画布内容不为空时,才post,避免出现黑屏的情况。if(canvas !=null && mHolder != null)mHolder.unlockCanvasAndPost(canvas);}}}private  Runnable runnable = new Runnable() {@Overridepublic void run() {long start =System.currentTimeMillis();while(mIsDrawing){draw();long end = System.currentTimeMillis();if(end-start<100){try{Thread.sleep(100-end+start);} catch (InterruptedException e) {e.printStackTrace();}}}}};@Overridepublic boolean onTouchEvent(MotionEvent event) {int x=(int) event.getX();int y= (int) event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG, "onTouchEvent: down");mPath.moveTo(x,y);break;case MotionEvent.ACTION_MOVE:Log.d(TAG, "onTouchEvent: move");mPath.lineTo(x,y);break;case MotionEvent.ACTION_UP:Log.d(TAG, "onTouchEvent: up");break;}return true;}
}

四、测试程序

完整源码

百度网盘链接:百度网盘 请输入提取码 提取码:test

SurfaceViewTest目录

运行效果

 

点此查看Android应用开发学习笔记的完整目录

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

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

相关文章

opencv基础60-用分水岭算法cv2.distanceTransform()实现图像分割与提取原理及示例

在图像处理的过程中&#xff0c;经常需要从图像中将前景对象作为目标图像分割或者提取出来。例如&#xff0c;在视频监控中&#xff0c;观测到的是固定背景下的视频内容&#xff0c;而我们对背景本身并无兴趣&#xff0c;感兴趣的是背景中出现的车辆、行人或者其他对象。我们希…

从安装 Seata 开始的分布式事务之旅 springboot集成seata

从安装 Seata 开始的分布式事务之旅 介绍什么是 Seata&#xff1f; 安装 Seata Server下载 Seata Server 发行版配置Seata解压文件配置Seata的yml文件把配置文件config.txt加载到nacos上修改config.txt文件加载到nacos上 启动Seata服务正常启动查看启动日志打开控制台页面 启动…

限流在不同场景的最佳实践

目录导读 限流在不同场景的最佳实践1. 前言2. 为什么要限流3. 有哪些限流场景3.1 限流场景分类3.2 限流与熔断降级之间的关系3.3 非业务限流3.4 业务限流 4. 有哪些限流算法4.1 计数器限流算法4.2 漏桶限流算法4.3 令牌桶限流算法4.4 滑动时间窗限流算法4.5 限流算法选型 5. 限…

NPM包的安装、更新、卸载

目录 1、下载安装全局包 2、解决全局安装包时的EACCES权限错误 2.1 重新安装NPM 2.2 手动更改npm的默认目录 3、更新从注册表下载的包 3.1 更新本地包 3.2 更新全局安装的软件包 3.3 确定哪些全局包需要更新 3.4 更新单个全局包 3.5 更新所有全局安装的软件包 4、在项…

VLAN监控及常见问题排查

局域网&#xff0c;我们通常称为LAN&#xff0c;是一种由基于同一地理位置的设备组成的网络&#xff0c;可实现它们之间的通信&#xff0c;局域网的虚拟对应物是虚拟局域网或 VLAN。VLAN 增强了 LAN&#xff0c;提供了进行更改的灵活性、更高的可扩展性和更好的安全性。 使用 …

使用 Gradio 构建生成式 AI 应用程序(一): 图片内容读取app

今天我们来学习DeepLearning.AI的在线课程&#xff1a;Building Generative AI Applications with Gradio&#xff0c;该课程主要讲述利用gradio来部署机器学习算法应用程序, 今天我们来学习第一课&#xff1a;Image captioning app&#xff0c;该课程主要讲述如何从图片中读取…

JavaScript 操作历史记录api怎样使用 JavaScript

JavaScript 操作历史记录api怎样使用 JavaScript History 是 window 对象中的一个 JavaScript 对象&#xff0c;它包含了关于浏览器会话历史的详细信息。你所访问过的 URL 列表将被像堆栈一样存储起来。浏览器上的返回和前进按钮使用的就是 history 的信息。 History 对象包含…

报错注入(主键重复)攻击原理

基本原理 利用数据表中主键不能重复的特点&#xff0c;通过构造重复的主键&#xff0c;使得数据库报错&#xff0c;并将报错结果返回到前端。 SQL说明函数 以pet数据表为例进行说明 rond(): 返回[0,1)区间内的任意浮点数。 count(): 返回每个组的列行数。 如&#xff0…

IDEA新建类时自动设置类注释信息,署名和日期

IDEA设置路径 File --> Settings --> Editor --> File and Code Templates --> Include --> File Header 官方模板 这里 ${USER} 会读取计算机的用户名 ${DATE}是日期 ${TIME}是时间 /*** Author ${USER}* Date ${DATE} ${TIME}* Version 1.0*/

简单易懂的Transformer学习笔记

1. 整体概述 2. Encoder 2.1 Embedding 2.2 位置编码 2.2.1 为什么需要位置编码 2.2.2 位置编码公式 2.2.3 为什么位置编码可行 2.3 注意力机制 2.3.1 基本注意力机制 2.3.2 在Trm中是如何操作的 2.3.3 多头注意力机制 2.4 残差网络 2.5 Batch Normal & Layer Narmal 2.…

智安网络|网络安全:危机下的创新与合作

随着信息技术的迅猛发展和互联网的普及&#xff0c;我们进入了一个高度网络化的社会。网络在提供便利和连接的同时&#xff0c;也带来了许多安全隐患和挑战。 一、网络安全的危险 **1.数据泄露和隐私侵犯&#xff1a;**网络上的个人和机构数据存在遭受泄露和盗取的风险&#…

clickhouse 删除操作

OLAP 数据库设计的宗旨在于分析适合一次插入多次查询的业务场景&#xff0c;市面上成熟的 AP 数据库在更新和删除操作上支持的均不是很好&#xff0c;当然 clickhouse 也不例外。但是不友好不代表不支持&#xff0c;本文主要介绍在 clickhouse 中如何实现数据的删除&#xff0c…

go语言的database/sql结合squirrel工具sql生成器完成数据库操作

database/sql database/sql是go语言内置数据库引擎&#xff0c;使用sql查询数据库&#xff0c;配置datasource后使用其数据库操作方法对数据库操作&#xff0c;如下&#xff1a; package mainimport ("database/sql""fmt"_ "github.com/Masterminds…

Cesium 1.107+ 自定义类支持 readyPromise

由于cesium 1.107 的图元(Primitive) 已经不支持 readyPromise。 但是个人感觉比较好用,于是用了一个插件来实现。 用法: // 定义图元并添加,和之前一样 const boxGreen new BoxPrimitive({color: "#ff0000" }) viewer.scene.primitives.add(boxGreen.primitive)/…

【Spring专题】Bean的声明周期流程图

前言 我向来不主张【通过源码】理解业务&#xff0c;因为每个人的能力有限&#xff0c;甚至可能会因为阅读错误导致出现理解上的偏差&#xff0c;所以我决定&#xff0c;还是先帮大家【开天眼】&#xff0c;先整体看看流程图&#xff0c;好知道&#xff0c;Spring在写源码的过…

一文讲述什么是数字孪生?

当前世界正处于百年未有之大变局&#xff0c;数字经济在各国已成为经济发展的重点。数字经济也是我国社会经济发展的必经之路。 近些年&#xff0c;大数据、人工智能、数字孪生等技术的发展促使技术与国内各产业进一步融合&#xff0c;从而推动了各产业在智能化、数字化等方面…

程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解

Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句&#xff0c;但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…

10个AI绘图生成器让绘画更简单

AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中发挥着越来越大的作用&#xff0c;开创了AI绘画生成器新时代。在绘画领域当然也是如此&#xff0c;与传统的绘画工具不同&#xff0c;AI人工智能时代的绘画工具是全自动的、智能的&#xff0c;甚至可以说是“傻瓜式”…

Three.js阴影

目录 Three.js入门 Three.js光源 Three.js阴影 Three.js纹理贴图 使用灯光后&#xff0c;场景中就会产生阴影。物体的背面确实在黑暗中&#xff0c;这称为核心阴影&#xff08;core shadow&#xff09;。我们缺少的是落下的阴影&#xff08;drop shadow&#xff09;&#…

【ultralytics仓库使用自己的数据集训练RT-DETR】

ultralytics仓库使用自己的数据集训练RT-DETR RT-DETR由百度开发&#xff0c;是一款尖端的端到端物体检测器&#xff08;基于transformer架构&#xff09;&#xff0c;在提供实时性能的同时保持高精度。它利用视觉变换器&#xff08;ViT&#xff09;的力量&#xff0c;通过解耦…