Android自定义相机

Android自定义相机

最近由于项目需要要自定义一个相机,把今天学到的做一个小总结:
1.创建显示相机画面的布局,Android已经为我们选定好SurfaceView
2.通过SurfaceView#getHolder()获得链接Camera和SurfaceView的3.SurfaceHolder
4.Camame.open()打开相机
5.通过SurfaceHolder链接Camera和SurfaceView
下面直接上代码:

    public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {

    private static final String TAG = "CameraSurfaceView";

    private Context mContext;
    private SurfaceHolder holder;
    private Camera mCamera;

    private int mScreenWidth;
    private int mScreenHeight;

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

    public CameraSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        getScreenMetrix(context);
        initView();
    }

    private void getScreenMetrix(Context context) {
        WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        WM.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;
        mScreenHeight = outMetrics.heightPixels;
    }

    private void initView() {
        holder = getHolder();//获得surfaceHolder引用
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//设置类型
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated");
        if (mCamera == null) {
            mCamera = Camera.open();//开启相机
            try {
                mCamera.setPreviewDisplay(holder);//摄像头画面显示在Surface上
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i(TAG, "surfaceChanged");
        mCamera.startPreview();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "surfaceDestroyed");
        mCamera.stopPreview();//停止预览
        mCamera.release();//释放相机资源
        mCamera = null;
        holder = null;
    }

    @Override
    public void onAutoFocus(boolean success, Camera Camera) {
        if (success) {
            Log.i(TAG, "onAutoFocus success="+success);
        }
    }
}

添加相机和自动对焦权限

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />

将CameraSurfaceView放在布局文件中,这里建议最外层为FrameLayout,后面会用到。如此,我们便有了一个没有照相功能的相机。初次之外,仔细观察相机显示画面,图片是不是变形严重?那是因为我们还没有为相机设置各种参数。在预览前要设置摄像头的分辨率、预览分辨率和图片分辨率的宽高比保持一致。这样图片才不会变形。(原因下次写):

private void setCameraParams(Camera camera, int width, int height) {
    Log.i(TAG,"setCameraParams  width="+width+"  height="+height);
    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
    for (Camera.Size size : pictureSizeList) {
        Log.i(TAG, "pictureSizeList size.width=" + size.width + "  size.height=" + size.height);
    }

    Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
    if (null == picSize) {
        Log.i(TAG, "null == picSize");
        picSize = parameters.getPictureSize();
    }
    Log.i(TAG, "picSize.width=" + picSize.width + "  picSize.height=" + picSize.height);
     // 根据选出的PictureSize重新设置SurfaceView大小
    float w = picSize.width;
    float h = picSize.height;
    parameters.setPictureSize(picSize.width,picSize.height);
    this.setLayoutParams(new FrameLayout.LayoutParams((int) (height*(h/w)), height));

    // 获取摄像头支持的PreviewSize列表
    List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();

    for (Camera.Size size : previewSizeList) {
        Log.i(TAG, "previewSizeList size.width=" + size.width + "  size.height=" + size.height);
    }
    Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
    if (null != preSize) {
        Log.i(TAG, "preSize.width=" + preSize.width + "  preSize.height=" + preSize.height);
        parameters.setPreviewSize(preSize.width, preSize.height);
    }

    parameters.setJpegQuality(100); // 设置照片质量
    if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
        parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 连续对焦模式
    }

    mCamera.cancelAutoFocus();//自动对焦。
    mCamera.setDisplayOrientation(90);// 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
    mCamera.setParameters(parameters);

}
private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
    Log.i(TAG, "screenRatio=" + screenRatio);
    Camera.Size result = null;
    for (Camera.Size size : pictureSizeList) {
        float currentRatio = ((float) size.width) / size.height;
        if (currentRatio - screenRatio == 0) {
            result = size;
            break;
        }
    }

    if (null == result) {
        for (Camera.Size size : pictureSizeList) {
            float curRatio = ((float) size.width) / size.height;
            if (curRatio == 4f / 3) {// 默认w:h = 4:3
                result = size;
                break;
            }
        }
    }

    return result;
}

进去的是屏幕宽高,出来的是调整好了的参数。在surfaceChanged方法中执行mCamera.startPreview(); 前调用setCameraParams(mCamera, mScreenWidth, mScreenHeight); 就可以了。最后要在AndroidManifest.xml里设置activity的方向android:screenOrientation=”portrait”代码里有很多注释,其中也有我自己调试时候的Log,大家可以自己调试下,看看不同参数的效果。昨天调参数搞到一点多,都在折腾这个函数。唉,一把辛酸泪。
身为一个相机,居然不能照相?真是太丢脸了!下面给我们的相机添加上照相的功能。照相核心代码就一句:mCamera.takePicture(null, null, jpeg);
可以看到takePicture方法有三个参数,分别是ShutterCallback、PictureCallback和PictureCallback。这里我们只用了PictureCallback

    private Camera.ShutterCallback shutter = new Camera.ShutterCallback() {
    @Override
    public void onShutter() {
        Log.i(TAG,"shutter");
    }
};

// 获得没有压缩过的图片数据
private Camera.PictureCallback raw = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera Camera) {
        Log.i(TAG, "raw");

    }
};

//创建jpeg图片回调数据对象
private Camera.PictureCallback jpeg = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera Camera) {
    //保存图片
    }
};

在jpeg的onPictureTaken里。我们将存储照片信息的byte[] data解析成bitmap,然后转换成JPG格式的图片保存在SD卡中。注意finally中最后两句mCamera.stopPreview();// 关闭预览 mCamera.startPreview();// 开启预览 上文也提到:当调用camera.takePiture方法后,camera关闭了预览,这时需要调用startPreview()来重新开启预览。如果不再次开启预览,则会一直停留在拍摄照片画面。为了方便外部调用拍照。这里我暴露了一个方法供外部拍照。

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

至此,一个具有照相并保存拍摄图片功能的相机就做出来了,明天再写保存照片的代码!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值