SurfaceView and TextureView are mainly used to preview camera images on the Android platform. So what’s the difference?

The SurfaceView can be drawn in a separate thread without affecting the main thread. It uses double buffering to make video playback smoother. The disadvantage is that the contents of the SurfaceView are no longer applied to the window, and its display is not controlled by View properties, so it cannot be panned, zoomed, or nested in other viewgroups.

TextureView supports moves, rotations, zooming, and other transformations. You can use View control features such as transparency. However, it must be used in hardware-accelerated Windows, takes up more memory than SurfaceView, and has a 1-3 second delay. Render in main thread before 5.0, separate render thread after 5.0.

Next, we use SurfaceView and TextureView for camera preview.

Use SurfaceView

Adding Camera Permissions

<uses-permission android:name="android.permission.CAMERA" />

Copy the code

Implement surfaceHolder. Callback, open the camera preview in the surfaceCreated method, and close the camera preview in the surfaceDestroy method. The Camera’s open method is a bit time-consuming, so to avoid blocking the UI thread, you can create child threads to open the Camera.

public class CameraSurfacePreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mSurfaceHolder;
    private Camera mCamera;
    private Activity mActivity;

    public CameraSurfacePreview(Context context) {
        super(context);
        init();
    }

    private void init() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        mActivity = (Activity) getContext();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        logger.debug("surfaceCreated");
        openCamera();
        startPreviewDisplay();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        logger.debug("surfaceChanged: format:{}, width:{}, height:{}", format, width, height);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        logger.debug("surfaceDestroyed"); releaseCamera(); } // Open the camera private voidopenCamera() {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        int number = Camera.getNumberOfCameras();
        for (int i = 0; i < number; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                try {
                    mCamera = Camera.open(i);
                    CameraUtils.setCameraDisplayOrientation(mActivity, i, mCamera);
                    Camera.Parameters params = mCamera.getParameters();
                    params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                } catch (Exception e) {
                    logger.error("openCamera error", e);
                    mActivity.onBackPressed();
                }
                break; }}} // Enable preview private voidstartPreviewDisplay() {
        if(mCamera ! = null) {try {/ / set preview callback, NV21 data, further processing mCamera. SetPreviewCallback (new Camera.PreviewCallback() {
                    @Override
                    public void onPreviewFrame(byte[] data, Camera camera) {

                    }
                });
                mCamera.setPreviewDisplay(mSurfaceHolder);
                mCamera.startPreview();
            } catch (IOException e) {
                logger.error("startPreviewDisplay error", e); }} // Close the camera private voidreleaseCamera() {
        if(mCamera ! = null) { try { mCamera.stopPreview(); mCamera.setPreviewDisplay(null); mCamera.release(); } catch (IOException e) { logger.error("releaseCamera error", e); } mCamera = null; }}}Copy the code

Using TextureView

Implement TextureView SurfaceTextureListener interface, when SurfaceTexture available, open the camera preview, use SurfaceTexture rendering pictures. When the SurfaceTexture is destroyed, the camera resource is freed. Textureview must be used in a hardware accelerated window, which is enabled by default.

public class CameraTexturePreview extends TextureView implements TextureView.SurfaceTextureListener {
    private Camera mCamera;
    private Activity mActivity;

    public CameraTexturePreview(Context context) {
        super(context);
        init();
    }

    private void init() {
        setSurfaceTextureListener(this);
        mActivity = (Activity) getContext();
    }

    @Override
    public void onSurfaceTextureAvailable(final SurfaceTexture surface, int width, int height) {
        logger.debug("onSurfaceTextureAvailable. width:{}, height:{}", width, height);
        openCamera();
        startPreviewDisplay(surface);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        logger.debug("onSurfaceTextureSizeChanged. width:{}, height:{}", width, height);
        // Ignored, Camera does all the work for us
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        logger.debug("onSurfaceTextureDestroyed");
        releaseCamera();
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // Invoked every time therePrivate void startPreviewDisplay(SurfaceTexture) {if (mCamera! = null) { try { mCamera.setPreviewTexture(surfaceTexture); mCamera.startPreview(); } catch (IOException e) { logger.error("startPreviewDisplay error", e); Private void releaseCamera() {if (mCamera! = null) { try { mCamera.stopPreview(); mCamera.setPreviewTexture(null); mCamera.release(); } catch (IOException e) { logger.error("releaseCamera error", e); } mCamera = null; }}} / / set the camera preview direction public static void setCameraDisplayOrientation (Activity to Activity, int cameraId, Camera camera) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; default: } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); }Copy the code

The detailed code is available on GitHub, and comments are welcome.