SurfaceView、SurfaceHolder与Surface

本文详细介绍了SurfaceView、SurfaceHolder和Surface之间的关系,指出SurfaceView是拥有独立绘图层的特殊View,Surface是内存中的绘图缓冲区,而SurfaceHolder用于管理Surface。通过示例对比了普通View与SurfaceView的重绘差异,表明SurfaceView的刷新不会影响到Window的Surface,而是单独更新其所在区域。
相关文章

按照官方文档的说法,SurfaceView继承自View,并提供了一个独立的绘图层,你可以完全控制这个绘图层,比如说设定它的大小,所以SurfaceView可以嵌入到View结构树中,但是需要注意的是,由于SurfaceView直接将绘图表层绘制到屏幕上,所以和普通的View不同的地方就在与它不能执行Transition,Rotation,Scale等转换,也不能进行Alpha透明度运算。SurfaceView的Surface排在Window的Surface(也就是View树所在的绘图层)的下面,SurfaceView嵌入到Window的View结构树中就好像在Window的Surface上强行打了个洞让自己显示到屏幕上,而且SurfaceView另起一个线程对自己的Surface进行刷新。特别需要注意的是SurfaceHolder.Callback的所有回调方法都是在主线程中回调的。

SurfaceView、SurfaceHolder、Surface的关系可以概括为以下几点:
* SurfaceView是拥有独立绘图层的特殊View
* Surface就是指SurfaceView所拥有的那个绘图层,其实它就是内存中的一段绘图缓冲区。
* SurfaceView中具有两个Surface,也就是我们所说的双缓冲机制
* SurfaceHolder顾名思义就是Surface的持有者,SurfaceView就是通过过SurfaceHolder来对Surface进行管理控制的。并且SurfaceView.getHolder方法可以获取SurfaceView相应的SurfaceHolder。
* Surface是在SurfaceView所在的Window可见的时候创建的。我们可以使用SurfaceHolder.addCallback方法来监听Surface的创建与销毁的事件。

下面我们写一个Demo来对普通的View与SurfaceView进行区别。

普通的View

我们先写一个继承自View的DrawView,我们每过1秒进行一次重绘

public class DrawView extends View {
   
   
    private static final int DELAY = 1000;

    private Paint mPaint;

    private int mCount;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mCount++;
            invalidate();
            handler.sendEmptyMessageDelayed(0x0001, DELAY);
        }
    };

    public DrawView(Context context) {
        this(context, null);
    }

    public DrawView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DrawView(Context context, @Nullable AttributeSet attrs, 
<think>我们正在处理一个关于Android开发的问题:如何使用TextureView替代SurfaceViewSurfaceSurfaceHolder。根据用户提供的引用内容,我们需要总结关键点并给出具体实现步骤。 关键点总结: 1. TextureView是在Android 4.0(API level 14)引入的,因此它不能用于更早的版本。而SurfaceView从API 1就开始支持。 2. TextureView可以像普通View一样进行变换(如旋转、缩放、透明度等),因为它普通View在同一个窗口内。而SurfaceView由于在一个单独的窗口(独立于应用窗口)中,所以无法直接应用这些变换。 3. 在Android 7.0(API level 24)之后,SurfaceView也支持了动画,并且官方建议在7.0之后使用SurfaceView替代TextureView(因为性能更好,尤其是在视频播放等场景)。但是,如果应用需要支持4.0以上且需要视图变换,则TextureView仍然是一个选择。 4. 两者都可以在子线程中更新UI,但更新机制不同: - SurfaceView通过SurfaceHolder.addCallback回调,在子线程中通过SurfaceHolder.lockCanvas()和unlockCanvasAndPost()来更新画面。 - TextureView通过TextureView.setSurfaceTextureListener设置监听,在onSurfaceTextureAvailable回调中获取SurfaceTexture,然后创建Surface(用于渲染),同样可以在子线程中更新画面(例如通过Canvas绘制或OpenGL渲染)。 替换步骤: 由于TextureView不直接使用SurfaceHolder,我们需要调整代码结构,使用SurfaceTexture和Surface。 原SurfaceView的典型使用方式: 1. 在布局中定义SurfaceView。 2. 在Activity中获取SurfaceView,并添加SurfaceHolder.Callback。 3. 在surfaceCreated回调中启动绘制线程,在surfaceDestroyed中停止线程。 4. 在绘制线程中,通过SurfaceHolder.lockCanvas()获取Canvas,进行绘制,然后unlockCanvasAndPost()提交。 使用TextureView替代的步骤: 1. 在布局文件中使用TextureView替换SurfaceView。 2. 在Activity中获取TextureView,并设置SurfaceTextureListener。 3. 在onSurfaceTextureAvailable回调中,我们可以获取到SurfaceTexture,然后创建一个Surface对象(这个Surface将用于绘制)。同时,启动绘制线程。 4. 在onSurfaceTextureDestroyed回调中,停止绘制线程并释放资源(注意:需要返回true,表示由TextureView来释放SurfaceTexture)。 5. 在绘制线程中,我们可以通过Surface对象获取Canvas(通过lockCanvas()方法),然后进行绘制,最后通过unlockCanvasAndPost()提交。或者,也可以使用OpenGL在Surface上渲染。 注意:TextureView的绘制效率可能不如SurfaceView,因为它需要将内容渲染到View的图层中,然后由系统进行合成。而SurfaceView则可以直接由窗口管理器合成,节省了一个步骤。因此,在性能要求极高的场景(如游戏、高帧率视频)下,即使使用TextureView能够实现,也要注意性能问题。 代码示例: 布局文件(activity_main.xml): ```xml <TextureView android:id="@+id/texture_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` Activity代码(MainActivity.java): ```java public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener { private TextureView mTextureView; private Surface mSurface; private DrawingThread mDrawingThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextureView = findViewById(R.id.texture_view); mTextureView.setSurfaceTextureListener(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { // 创建Surface,后续绘制操作将在这个Surface上进行 mSurface = new Surface(surfaceTexture); // 启动绘制线程 mDrawingThread = new DrawingThread(mSurface); mDrawingThread.start(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // 当TextureView大小改变时,可以在这里调整绘制 } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { // 停止绘制线程 if (mDrawingThread != null) { mDrawingThread.quit(); try { mDrawingThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } mDrawingThread = null; } // 释放Surface mSurface.release(); mSurface = null; // 返回true,表示由TextureView来释放SurfaceTexture;返回false则需要手动释放 return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // 当SurfaceTexture更新时调用(例如每一帧更新) } // 绘制线程 private static class DrawingThread extends Thread { private final Surface mSurface; private volatile boolean mRunning = true; public DrawingThread(Surface surface) { mSurface = surface; } public void quit() { mRunning = false; } @Override public void run() { while (mRunning) { Canvas canvas = null; try { // 锁定Canvas进行绘制 canvas = mSurface.lockCanvas(null); synchronized (mSurface) { // 在Canvas上绘制内容,例如: canvas.drawColor(Color.WHITE); // 绘制其他内容... } } finally { if (canvas != null) { // 提交绘制 mSurface.unlockCanvasAndPost(canvas); } } // 控制帧率 try { Thread.sleep(16); // 约60fps } catch (InterruptedException e) { e.printStackTrace(); } } } } } ``` 注意事项: 1. 在绘制线程中,我们使用Surface的lockCanvas()和unlockCanvasAndPost()方法,这SurfaceHolder的用法类似。 2. 在onSurfaceTextureDestroyed方法中,我们停止线程并释放Surface(注意:SurfaceTexture的释放由TextureView处理,因为我们返回了true)。 3. 由于TextureView是在UI线程中创建和销毁的,所以我们在回调中处理线程的启动和停止。 另外,如果使用OpenGL ES进行渲染,那么需要创建EGL环境,并使用SurfaceTexture作为渲染目标。这里不展开。 引用说明: 根据引用[4],TextureView可以在子线程中更新UI,并且支持视图变换,但性能可能不如SurfaceView。在视频播放或相机应用等需要频繁更新画面的场景,如果不需要复杂的变换,建议使用SurfaceView;如果需要变换(如旋转、缩放)且API>=14,则使用TextureView。 根据引用[3],在Android7.0之后,SurfaceView也支持了动画,并且官方建议在7.0之后使用SurfaceView替代TextureView(因为性能更好)。所以,如果应用的最低API>=24,且不需要在4.0-7.0的设备上运行,那么可以考虑使用SurfaceView。 因此,在替换时,需要根据应用的目标API和功能需求来选择。 相关问题: 1. TextureView和SurfaceView在性能上有什么区别? 2. 在哪些场景下应该使用TextureView,哪些场景下应该使用SurfaceView? 3. 如何使用OpenGL ES在TextureView上进行渲染?</think>### 如何在 Android 中使用 TextureView 替代 SurfaceViewSurfaceSurfaceHolder TextureView 是 Android 4.0+ 引入的视图组件,它可以直接替代 SurfaceView 并简化渲染流程。以下是关键步骤和代码示例: #### 核心替换方案 1. **布局文件替换** 将 `SurfaceView` 改为 `TextureView`: ```xml <!-- 原 SurfaceView --> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 替换为 TextureView --> <TextureView android:id="@+id/texture_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. **回调机制替换** - **移除 `SurfaceHolder`**:不再使用 `SurfaceHolder.addCallback()` - **使用 `SurfaceTextureListener`**:通过 `TextureView.setSurfaceTextureListener()` 监听生命周期 3. **渲染流程调整** - 通过 `SurfaceTexture` 创建 `Surface` 对象(替代 `SurfaceHolder` 的 `Surface`) - 直接在 `Surface` 上绘制(支持 Canvas 或 OpenGL) #### 完整代码示例 ```java public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener { private TextureView textureView; private Surface renderSurface; // 替代 SurfaceHolderSurface private Thread renderThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textureView = findViewById(R.id.texture_view); textureView.setSurfaceTextureListener(this); // 设置监听 } @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { // 1. 创建 Surface(替代 SurfaceHolderSurface) renderSurface = new Surface(surfaceTexture); // 2. 启动渲染线程(替代 SurfaceView 的绘制线程) renderThread = new Thread(() -> { while (!Thread.interrupted()) { Canvas canvas = renderSurface.lockCanvas(null); // 在 Canvas 上绘制内容(例如:清屏 + 画圆) canvas.drawColor(Color.BLACK); Paint paint = new Paint(); paint.setColor(Color.RED); canvas.drawCircle(width/2, height/2, 100, paint); renderSurface.unlockCanvasAndPost(canvas); // 提交绘制 } }); renderThread.start(); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { // 3. 停止线程并释放资源 if (renderThread != null) { renderThread.interrupt(); renderThread = null; } renderSurface.release(); // 释放 Surface return true; // 由系统自动回收 SurfaceTexture } // 其他必要回调(空实现即可) @Override public void onSurfaceTextureSizeChanged(...) {} @Override public void onSurfaceTextureUpdated(...) {} } ``` #### 关键差异说明 | **组件** | **SurfaceView 方案** | **TextureView 替代方案** | |----------------|-----------------------------------|----------------------------------| | **视图容器** | `SurfaceView` + `SurfaceHolder` | `TextureView` | | **生命周期监听**| `SurfaceHolder.Callback` | `SurfaceTextureListener` | | **渲染表面** | `holder.getSurface()` | `new Surface(surfaceTexture)` | | **视图变换** | ❌ 不支持旋转/缩放/透明度 | ✅ 支持所有 View 变换 | | **线程模型** | 需在子线程绘制 | 同样需在子线程绘制 | | **版本兼容** | API 1+ | API 14+ (Android 4.0+) | #### 注意事项 1. **性能考量** - 对于高帧率场景(如 60FPS 视频),优先用 Android 7.0+ 的 `SurfaceView`(性能更优)[^3] - `TextureView` 的合成消耗更高,但支持视图动画 [^4] 2. **OpenGL 集成** 若使用 OpenGL,可通过 `SurfaceTexture` 创建 EGL 表面: ```java // 在 onSurfaceTextureAvailable 中 EGLSurface eglSurface = eglCreateWindowSurface( eglDisplay, eglConfig, surfaceTexture, // 关键参数 null ); ``` 3. **相机/视频特殊处理** 媒体播放可直接绑定 `MediaPlayer`: ```java mediaPlayer.setSurface(new Surface(textureView.getSurfaceTexture())); ``` #### 适用场景建议 - ✅ **需要视图变换**(旋转/透明度):用 `TextureView` - ✅ **API ≥ 14 且非高性能场景**:用 `TextureView` - ❌ **Android 4.0 以下设备**:必须用 `SurfaceView` - ❌ **高性能游戏/高帧率视频**:Android 7.0+ 用 `SurfaceView` [^3][^5] ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值