Android--surfaceview 使用详解

SurfaceView简介
surfaceView 是视图 (View) 的继承类,这个视图里内嵌了一个专门用于绘制的 surface,可以控制这个 surface 的格式和尺寸。surfaceview 控制这个 surface 的绘制位置。

SurfaceView分析

  1. surface 是纵深排序 (Z-ordered) 的,这表明它总在自己所在窗口的后面。
  2. surfaceview 提供了一个可见区域,只有在这个可见区域内 surface 部分内容才可见,可见区域外的部分不可见。
  3. surface 的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface 的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物 (overlays) (例如,文本和按钮等控件)。
  4. 注意,如果 surface 上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件之间的透明效果,这会影响性能。
  5. surface 类的目的在于提供一个可以使用绘图线程渲染到屏幕的 surface。
  6. 所有的 surface 和 surfaceHolder.Callback 方法都应该在主线程调用,所以对于绘图线程使用的变量应该做同步处理。

SurfaceHolder简介

SurfaceHolder是一个持有surface的抽象接口,可以控制surface的大小、格式、编辑、监听surface改变,一般通过SurfaceView实现。在SurfaceHolder的方法中,下面几个经常用到:

  1. abstract void addCallback(SurfaceHolder.Callback callback);// 给SurfaceView当前的持有者一个回调对象。
  2. abstract Canvas lockCanvas();// 锁定画布,一般在锁定后就可以通过其返回的画布对象进行画图操作。
  3. abstract Canvas lockCanvas(Rect dirty);// 锁定画布的某个区域进行画图等。因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
  4. abstract void unlockCanvasAndPost(Canvas canvas);// 结束锁定画图,并提交改变。

SuraceView搭配SurfaceHolder进行相机预览

在代码中,将操作 camera 相关的方法做成一个库,并提供一个对外的接口。

CameraInterface 主要提供操作 camera 相关的接口,主要方法为:
1. doOpenCamera 打开摄像头
2. doStopCamera 关闭摄像头
3. doStartPreview 开始预览

public class CameraInterface implements Camera.PreviewCallback {

    private static CameraInterface mInstance;
    private Camera mCamera;
    public int previewWidth, previewHeight;
    public int screenWidth, screenHeight;
    private int bufferSize;
    public byte gBuffer[];



    public interface CamOpenOverCallback{
        void onCameraHasOpened();
        void onCameraopenFailed();

    }
    public static CameraInterface getInstance(){
        if (mInstance == null) {
            synchronized (CameraInterface.class){
                if (mInstance == null) {
                    mInstance = new CameraInterface();
                }
            }
        }
        return mInstance;
    }

    public void doOpenCamera(CamOpenOverCallback callback){
        try {
            mCamera = Camera.open();
            Camera.Parameters parameters = mCamera.getParameters();
            List<Camera.Size> preSize = parameters.getSupportedPreviewSizes();
            previewWidth = preSize.get(0).width;
            previewHeight = preSize.get(0).height;
            for (int i = 1; i < preSize.size(); i++) {
                double similarity = Math.abs(((double) preSize.get(i).height / screenHeight)
                        - ((double) preSize.get(i).width / screenWidth));
                if (similarity < Math.abs(((double) previewHeight / screenHeight)
                        - ((double) previewWidth / screenWidth))) {
                    previewWidth = preSize.get(i).width;
                    previewHeight = preSize.get(i).height;
                }
            }
            parameters.setPreviewSize(previewWidth, previewHeight);
            mCamera.setParameters(parameters);
            bufferSize = previewWidth * previewHeight;
            bufferSize  = bufferSize * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8;
            gBuffer = new byte[bufferSize];
            if (callback != null) {
                callback.onCameraHasOpened();
            }
        } catch (Exception e) {
            e.printStackTrace();
            callback.onCameraopenFailed();
        }
    }

    public void doStopCamera(){
        if(null != mCamera)
        {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }


    public void doStartPreview(SurfaceHolder surfaceHolder){

        try {
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.addCallbackBuffer(gBuffer);
            mCamera.setPreviewCallbackWithBuffer(this);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        mCamera.addCallbackBuffer(gBuffer);
    }

CameraManager 提供对外的接口,与 CameraInterface 接口对应,但增加了一个打开摄像头成功与否的回调,因为有可能两个 app 同时打开摄像头,导致异常,在成功打开摄像头后再进行预览。同时,将打开设想头的操作放到子线程中。

public class CameraManager  {

    private static CameraManager mInstance;
    private Context mContext;
    private CameraHandler mCameraHandler;
    private CameraInterface mCameraInterface;
    private static final int MSG_OPEN_CAMERA = 1;
    private CameraInterface.CamOpenOverCallback mCamOpenOverCallback;
    public static CameraManager getInstance(Context c){
        if(mInstance==null){
            synchronized (CameraManager.class){
                if(mInstance==null)
                    mInstance = new CameraManager(c);
            }
        }
        return mInstance;
    }

    private CameraManager(Context c){
        mContext = c;
        HandlerThread handlerThread = new HandlerThread("camera");
        handlerThread.start();
        mCameraHandler = new CameraHandler(handlerThread.getLooper());
        mCameraInterface = CameraInterface.getInstance();

    }


    private class CameraHandler extends Handler  {
        public CameraHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {


            super.handleMessage(msg);
            switch (msg.what){
                case MSG_OPEN_CAMERA:
                    mCameraInterface.doOpenCamera(mCamOpenOverCallback);
                    break;
            }
        }


    }

    public void setCallback(CameraInterface.CamOpenOverCallback call){
        mCamOpenOverCallback = call;
    }

    public void openCamera(){
        mCameraHandler.sendEmptyMessage(MSG_OPEN_CAMERA);
    }

    public void startPreview(SurfaceHolder surfaceHolder){
        mCameraInterface.doStartPreview(surfaceHolder);
    }

    public void closeCamera(){
        mCameraInterface.doStopCamera();
    }

happy a nice day!

猜你喜欢

转载自blog.csdn.net/liqianwei1230/article/details/78648579