音视频开发系列(4)——全面了解Android Surfaceview

SurfaceView和普通的View有什么区别?

SurfaceView和普通的View在实现原理和使用场景上有较大的区别。

更多音视频知识请关注公众号:进击的代码家

实现原理
SurfaceView实际上是一个包含了Surface的View,它可以在非UI线程中单独进行绘制操作,而且可以获得更高的绘制效率。而普通的View则是在UI线程中进行绘制操作的,因此如果绘制过程过于复杂,会导致UI卡顿的情况出现。

使用场景
普通的View适用于静态的UI展示,例如TextView、Button等控件。而SurfaceView则适用于需要实时绘制的场景,例如视频播放、游戏开发等。由于SurfaceView可以获得更高的绘制效率,因此在实现这些应用时,使用SurfaceView可以提供更好的用户体验。

SurfaceView的特点:

1)可以在非UI线程中进行绘制操作,避免占用主线程资源导致UI卡顿;
2)支持直接操作底层的Surface对象,能够实现更加复杂和高效的绘制操作;
3)支持透明背景,可以实现更加丰富的UI效果;
4)支持双缓冲机制,可以避免画面闪烁和撕裂的现象。

需要注意的是,由于SurfaceView的绘制操作是在子线程中进行的,因此在进行UI交互操作时需要使用Handler或其他线程通信方式来进行跨线程通信。

SurfaceView Api介绍

1.SurfaceHolder
SurfaceHolder是SurfaceView的内部类,它可以获取到SurfaceView的Surface对象,提供了对Surface的操作方法。一般在SurfaceView的回调函数中使用。

1.1 getHolder()方法

获取SurfaceHolder对象。

SurfaceHolder holder = surfaceView.getHolder();

1.2 addCallback()方法
注册SurfaceHolder.Callback回调函数。

holder.addCallback(callback);

1.3 setFixedSize()方法

扫描二维码关注公众号,回复: 14954656 查看本文章

设置Surface的大小。

holder.setFixedSize(width, height);

2.SurfaceHolder.Callback
SurfaceHolder.Callback是一个回调函数接口,提供了SurfaceView的生命周期回调方法。

2.1 surfaceCreated()方法

当Surface被创建时调用。

@Override
public void surfaceCreated(SurfaceHolder holder) {
    // 初始化MediaPlayer等对象
}

2.2 surfaceChanged()方法

当Surface的大小或格式改变时调用。

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // 更新MediaPlayer等对象的参数
}

2.3 surfaceDestroyed()方法

当Surface被销毁时调用。

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // 释放MediaPlayer等对象
}

3.Canvas
Canvas是用于绘制SurfaceView的对象,可以在SurfaceHolder.Callback回调函数中使用。

3.1 lockCanvas()方法

获取Canvas对象,开始绘制操作。

Canvas canvas = holder.lockCanvas();

3.2 drawXXX()方法

进行绘制操作,例如绘制文字、图形等。

canvas.drawText("Hello, world!", x, y, paint);

3.3 unlockCanvasAndPost()方法

结束绘制操作,提交绘制结果。

holder.unlockCanvasAndPost(canvas);

4.Surface
Surface是SurfaceView内部的一个底层对象,可以直接操作Surface进行绘制操作。

4.1 getSurface()方法

获取Surface对象。

Surface surface = holder.getSurface();

4.2 setFormat()方法

设置Surface的像素格式。

surface.setFormat(PixelFormat.RGBA_8888);

4.3 setFixedSize()方法

设置Surface的大小。

surface.setFixedSize(width, height);

5.SetZOrderXXX
setZOrderOnTop和setZOrderMediaOverlay方法可以用于设置SurfaceView的Z-order,控制SurfaceView在其他View上的显示顺序。

setZOrderOnTop方法会使得SurfaceView在Z-order的最顶端显示,可以覆盖其他View,通常用于实现视频弹幕等效果;而setZOrderMediaOverlay方法会将SurfaceView显示在普通View之上,但在其他SurfaceView之下,通常用于实现UI控件和视频播放器的叠加。

这两个方法都需要传入一个布尔值参数,表示是否开启相应的Z-order,true表示开启,false表示关闭。例如:

surfaceView.setZOrderOnTop(true);

注意,这两个方法必须在SurfaceView创建之后调用,否则将无效。同时,在设置Z-order之后,可能需要更新SurfaceView的尺寸或者重绘View,以确保SurfaceView能够正确地覆盖在其他View上方或者下方。

需要注意的是,由于SurfaceView的特殊性,使用setZOrderOnTop或者setZOrderMediaOverlay方法会增加View的复杂度,可能会导致性能问题或者出现一些不可预知的问题,因此建议谨慎使用,并在具体场景下进行测试和优化。

总之,**SurfaceView的API使用相对较为简单,但需要注意在子线程中进行绘制操作,需要使用线程通信方式与UI线程进行交互。**同时,需要根据不同的应用场景进行参数的设置和优化,以提供更好的用户体验。

SurfaceView视频播放代码

public class VideoSurfaceView extends SurfaceView implements SurfaceHolder.Callback, MediaPlayer.OnPreparedListener, MediaPlayer.OnVideoSizeChangedListener {

    private SurfaceHolder mHolder;
    private MediaPlayer mMediaPlayer;

    public VideoSurfaceView(Context context) {
        super(context);
        initView();
    }

    public VideoSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public VideoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setDataSource("http://example.com/video.mp4");
            mMediaPlayer.setDisplay(mHolder);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnVideoSizeChangedListener(this);
            mMediaPlayer.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mMediaPlayer.stop();
        mMediaPlayer.release();
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
    }

    @Override
    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
        if (width != 0 && height != 0) {
            getHolder().setFixedSize(width, height);
        }
    }
}

在这个示例代码中,我们实现了SurfaceHolder.Callback、MediaPlayer.OnPreparedListener和MediaPlayer.OnVideoSizeChangedListener接口,用于监听SurfaceView的创建、销毁事件,以及视频的准备和大小变化事件。

在surfaceCreated方法中创建MediaPlayer对象,并设置数据源和显示器为SurfaceHolder,最后异步准备MediaPlayer。在onPrepared方法中开始播放视频。在surfaceDestroyed方法中停止播放器和释放资源。在onVideoSizeChanged方法中设置SurfaceView的尺寸,确保视频可以正常显示。

需要注意的是,在实际的项目中,需要对视频播放器进行更加完善的封装和处理,包括错误处理、播放控制、缓存控制等方面的处理。这里仅仅是一个简单的示例代码,不能满足实际需求。

SurfaceView在实际项目中使用场景

SurfaceView在实际开发中有很多使用场景,以下是一些常见的应用场景:

视频播放器:SurfaceView可以在一个独立的线程中绘制视频帧,可以提供更好的性能和流畅度。

相机预览:SurfaceView可以用于相机预览,可以在预览过程中进行实时的图像处理。

游戏开发:SurfaceView可以用于游戏的绘制,可以在一个独立的线程中进行游戏的逻辑处理和渲染。

自定义绘制控件:SurfaceView可以用于实现自定义的绘制控件,可以提供更加灵活的绘制方式。

网络传输:SurfaceView可以用于网络传输,可以在一个独立的线程中处理网络数据的接收和显示。

需要注意的是,由于SurfaceView是基于Surface的,所以在使用时需要注意Surface的生命周期和内存管理。如果没有正确管理Surface的生命周期和内存,可能会导致应用程序出现异常或崩溃。因此,在实际开发中需要认真考虑SurfaceView的使用场景和具体实现方式,确保应用程序的稳定性和性能。

猜你喜欢

转载自blog.csdn.net/u012124438/article/details/129335226