文章目录
组件介绍
在 Android 开发中,SurfaceView
是一个非常特别的组件,它提供了一个专门的绘制表面,允许在主线程之外的线程上进行绘制操作。这使得 SurfaceView
非常适合需要高性能绘制和更新的场景,如视频播放和游戏渲染。这是因为它可以减少和避免 UI 线程的阻塞和延迟。
基本概念
SurfaceView
继承自 View
类,但与普通的 View
不同,它内部使用了一个独立的绘图表面(即 Surface
),这个 Surface
可以在主 UI 线程之外的线程上进行控制和绘制。这样做的好处是,即使绘制操作很复杂,也不会影响到 UI 线程的响应性和流畅性。
关键特性
-
独立的绘图表面:
SurfaceView
的Surface
是独立于应用窗口的其余部分,并且可以在单独的线程中进行更新和渲染。这意味着它不会受到其他视图层级更新的影响,从而提高了渲染效率。
-
线程安全:
- 由于
Surface
可以从任何线程进行访问和修改,因此SurfaceView
特别适用于后台线程渲染内容,如视频播放和动态图形。
- 由于
-
可见性管理:
SurfaceView
在屏幕上的可见性由 Android 的窗口管理器直接处理。当SurfaceView
变为不可见时,它的Surface
可能会被销毁,因此开发者需要在适当的生命周期回调中管理资源和绘制状态。
使用场景
- 视频播放 :
SurfaceView
常用于视频播放应用,因为视频解码和渲染可以在单独的线程上进行,避免 UI 线程阻塞。 - 实时游戏渲染 :游戏中的图形渲染需要高频率的更新和高性能,
SurfaceView
提供的独立绘图表面能够满足这些要求。 - 相机预览 :相机应用中,
SurfaceView
可用于显示相机的实时预览流。
SurfaceHolder介绍
在Android开发中,SurfaceHolder
是一个接口,用于控制和监视Surface
对象的状态。Surface
是一个特殊的对象,它承载了一个可以绘制的矩形区域。这个区域可以由你的应用程序或其他应用程序来绘制。SurfaceView
类就是围绕Surface
提供一个可视组件的实现,而SurfaceHolder
提供了对这个Surface
的控制和管理。
主要功能
1. 访问和控制Surface
:
SurfaceHolder
允许开发者直接访问Surface
对象,这意味着你可以管理和绘制到Surface
上的图形内容。通过SurfaceHolder
,你可以获取Surface
的Canvas
,并在这个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
打交道。
java
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);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
drawingThread = new DrawingThread(holder);
drawingThread.setRunning(true);
drawingThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Handle changes
}
@Override
public 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;
}
@Override
public 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 的工作机制和它与窗口系统之间的交互。
工作机制
-
双缓冲机制 :
SurfaceView
使用双缓冲机制,即前缓冲区和后缓冲区。前缓冲区显示在屏幕上,后缓冲区用于绘制新的内容。当后缓冲区绘制完成后,前后缓冲区交换,新的内容就会显示在屏幕上。这种机制可以减少屏幕闪烁和绘制延迟。 -
独立 Surface :
SurfaceView
创建了一个独立的 Surface,它与主 UI 线程分离,允许在另一个线程上进行绘制操作。这使得在 SurfaceView 上进行复杂的图形绘制时不会阻塞主 UI 线程,提高了绘制性能。 -
SurfaceHolder :
SurfaceView
通过SurfaceHolder
来管理其 Surface 的生命周期和绘制操作。SurfaceHolder
提供了一系列的回调方法,例如surfaceCreated
、surfaceChanged
和surfaceDestroyed
,用于监听 Surface 的创建、改变和销毁。
SurfaceView 挖洞的原理实际上是利用了 SurfaceView 绘制的特性,通过创建一个透明或空洞的区域,使得底层的内容可以透过 SurfaceView 显示出来。具体步骤如下:
-
设置透明背景 :将
SurfaceView
的背景设置为透明,使得 SurfaceView 背景区域不绘制任何内容,从而形成"挖洞"效果。xml<SurfaceView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" />
-
绘制透明区域 :在 SurfaceView 的 Canvas 上绘制一个透明区域,使得该区域不会绘制任何内容,从而露出底层视图。例如,可以使用
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
方法清空指定区域。 -
层次叠加 :利用视图的层次叠加关系,将
SurfaceView
放置在其他视图之上,并通过透明区域露出下层视图的内容。例如,可以在 SurfaceView 下方放置一个 ImageView 或其他自定义视图,通过透明区域显示底层视图的内容。
以下是一个简单的代码示例,演示了如何在 SurfaceView 中实现透明区域("挖洞"):
java
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); // 定义透明区域
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
draw();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public 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);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
//开启新线程 绘制图片
drawThread = new DrawThread(getHolder(), imageBitmap);
drawThread.setRunning(true);
drawThread.start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
//停止绘制线程
boolean retry = true;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//新开一个线程用来绘制图片到Canvas
private 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;
}
@Override
public 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 {
@Override
protected 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
类中的DrawThread
在surfaceDestroyed
方法被调用时应该被正确地停止,以避免可能的内存泄露或崩溃。- 这个例子展示了在一个单独的线程中加载和绘制图片,以避免阻塞 UI 线程。
效果展示
注:这个dmeo仅仅展示了SurfaceView 新开线程展示图片效果,学习SurfaceView渲染流程,无法应用实际开发,如果用于实际开发,如开发自定义控件,还需进一步完善自定义的类