美文网首页我爱编程
自定义带取景框的camera

自定义带取景框的camera

作者: ruancw | 来源:发表于2018-04-12 14:26 被阅读205次

前言:公司项目需求,在图像信息采集时只采集肩部以上部位的图片(和我们平时的一寸证件照很像),首先想到的是用第三方的图片选择器,他们都自带裁剪功能,不过每次拍完照后的手动裁剪,结果老大说简化业务人员的操作,不过这也难不倒无所不能的程序猿,没有咱们可以new一个(女朋友)。言归正传,开启我们的自定义带取景框的camera...
转载链接:https://blog.csdn.net/ruancw/article/details/79907677
效果图:

image

技术实现:(Activity中实现)

1.SurfaceView

2.Camera

3.自定义矩形取景框view

SurfaceView

介绍:从API中可以看出SurfaceView属于View的子类,它的功能很强大,它支持OpenGL ES库,2D和3D的效果,可以制作游戏、视频等,这里我们用surfaceview和camera实现相机的拍照取景功能。首先,让我们创建了SurfaceView的类实现SurfaceHolder的CallBack接口,重写CallBack的3个方法用于监听SurfaceView的创建、改变、销毁状态。

//SurfaceHolder的callback接口 public interface Callback {
    //对surfaceView的创建状态的监听
  void surfaceCreated(SurfaceHolder var1);
   //对SurfaceView的状态改变进行监听
  void surfaceChanged(SurfaceHolder var1, int var2, int var3, int var4);
   //对SurfaceView的销毁进行监听
  void surfaceDestroyed(SurfaceHolder var1); }

1.创建SurfaceView
这里我们使用Api自带的SurfaceView进行相机的预览(当然你也可以自定义surfaceView),在Activity的onCreate方法中进行初始化SurfaceView。

mCameraSurfaceView = (SurfaceView) findViewById(R.id.cameraSurfaceView);

2.使用SurfaceView

//根据layoutParams设置surfaceView的大小 
mCameraSurfaceView.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height));

3.获取SrfaceView的SurfaceHoler

mHolder = mCameraSurfaceView.getHolder(); 
//添加监听
 mHolder.addCallback(this); 
//设置类型 
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

然后就是在三个监听方法中进行camera的相关的操作。

自定义view(矩形取景框)

刚才已经介绍了显示camera预览的surfaceView,那么surfaceView上的取景框该如何实现呢?canvas and paint,没错,今天我们就用paint和canvas画出效果图的矩形取景框,接下来我们来自定义view。

模块实现:

a.顶部文字

b.阴影区域

c.矩形取景框

d.四角红色短线

1.自定义View,继承自imageView

构造方法:

public OverLayerTopView(Context context) {
    this(context,null,0); 
}

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

public OverLayerTopView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  init(context); 
}

2.初始化view,实现四个模块

(1)顶部文字实现

a.初始化画笔

//顶部文字提示信息 
wordPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
wordPaint.setColor(Color.WHITE);//字体颜色 
wordPaint.setTextAlign(Paint.Align.CENTER);//居中显示 
wordPaint.setStrokeWidth(3f);//画笔的宽度 
wordPaint.setTextSize(45);//字体大小

b.onDraw方法中绘画

注:必须在super.onDraw之前调用

/**  
* 画文字提示 
* @param canvas 画布  
*/ 
private void drawTipText(Canvas canvas) {
    canvas.drawText(TIPS, mCenterRect.centerX(), mCenterRect.top-50, wordPaint); 
}

(2)矩形取景框实现

a.初始化画笔

//中间矩形取景框的边界 
mRectBorderPaint=new Paint(Paint.ANTI_ALIAS_FLAG); 
mRectBorderPaint.setColor(Color.RED); 
mRectBorderPaint.setStyle(Paint.Style.STROKE);
mRectBorderPaint.setStrokeWidth(5f); 
mRectBorderPaint.setAlpha(0);//透明度

注:setAlpha(int value),value的值越小,透明度越高

b.onDraw方法中绘画

注:必须在super.onDraw之前调用

//判断取景框矩形是否为空 
if (mCenterRect==null) return;
//绘制中间矩形取景框 
canvas.drawRect(mCenterRect,mRectBorderPaint);

(3)阴影区域实现

a.初始化画笔

//阴影区域的画笔 
mShadePaint=new Paint(Paint.ANTI_ALIAS_FLAG); 
mShadePaint.setColor(Color.GRAY); 
mShadePaint.setStyle(Paint.Style.FILL); 
mShadePaint.setAlpha(100);

b.onDraw方法中绘画

注:必须在super.onDraw之前调用

canvas.drawRect(0,0,screenWidth,mCenterRect.top-2,mShadePaint);//顶部 
canvas.drawRect(0,mCenterRect.bottom+2,screenWidth,screenHeight,mShadePaint);//左侧 
canvas.drawRect(0,mCenterRect.top-2,mCenterRect.left-2,mCenterRect.bottom+2,mShadePaint);//下部 
canvas.drawRect(mCenterRect.right+2,mCenterRect.top-2,screenWidth,mCenterRect.bottom+2,mShadePaint);//右侧

(4)四角红色短线实现

a.初始化画笔

//矩形四角的短线 
mLinePaint=new Paint(); 
mLinePaint.setColor(Color.RED); 
mLinePaint.setAlpha(150);

b.onDraw方法中绘画

注:必须在super.onDraw之前调用

//左下 
canvas.drawRect(mCenterRect.left-2,mCenterRect.bottom,mCenterRect.left+50,mCenterRect.bottom+2,mLinePaint);//底部 
canvas.drawRect(mCenterRect.left-2,mCenterRect.bottom-50,mCenterRect.left,mCenterRect.bottom,mLinePaint);//左侧 
//左上 
canvas.drawRect(mCenterRect.left-2,mCenterRect.top-2,mCenterRect.left+50,mCenterRect.top,mLinePaint);//顶部 
canvas.drawRect(mCenterRect.left-2,mCenterRect.top,mCenterRect.left,mCenterRect.top+50,mLinePaint);//左侧 
//右上 
canvas.drawRect(mCenterRect.right-50,mCenterRect.top-2,mCenterRect.right+2,mCenterRect.top,mLinePaint);//顶部
canvas.drawRect(mCenterRect.right,mCenterRect.top,mCenterRect.right+2,mCenterRect.top+50,mLinePaint);//右侧 
//右下 
canvas.drawRect(mCenterRect.right-50,mCenterRect.bottom,mCenterRect.right+2,mCenterRect.bottom+2,mLinePaint);//右侧
canvas.drawRect(mCenterRect.right,mCenterRect.bottom-50,mCenterRect.right+2,mCenterRect.bottom,mLinePaint);//底部

3.设置矩形框的大小

/**  
* 设置取景框的矩形区域大小 
* @param mCenterRect 取景框矩形  
*/ 
public void setCenterRect(Rect mCenterRect){
     this.mCenterRect=mCenterRect;
  //postInvalidate(); 
}

4.OverLayerTopView的使用(Activity中)

// 设置取景框的margin; 距 左 、上 、右、下的 距离 单位是dp 
mCenterRect = DisplayUtils.createCenterRect(this, new Rect(120, 180, 120, 300));
mOverLayerView.setCenterRect(mCenterRect);

这样我们就实现了取景框的绘制,并能设置你想要的取景框的大小。

!!!重点就是下面的camera的实现了,让我们继续吧

Camera

介绍:android framework包括对设备上可用的各种相机及相机功能的支持,在应用中实现拍照和录制视频,不过,API 21(Android5.0)中将原来的camera API弃用转而推荐使用新增的camera 2 API,这是google对camera架构的一个大动作,因为API换了新架构,让开发者用起来有些难度,本文不对camera 2进行研究。这里使用的还是之前的camera API进行相机拍照实现。接下来我们将在surfaceView的接口方法中对camera进行初始化、设置参数以及资源释放等。

1.camera的初始化(surfaceCreated的方法中)

@Override 
public void surfaceCreated(SurfaceHolder holder) {
    openCamera(); 
}

openCamera方法

@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private void openCamera() {
    if (!isFrontCamera) {//是否是后置摄像头
 //打开相机  mCamera = Camera.open();
 try {
            //摄像头画面显示在Surface上
  mCamera.setPreviewDisplay(mHolder);
  } catch (IOException e) {
            e.printStackTrace();
  }
    } else {//前置摄像头的操作
 //获取摄像头信息  Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
 //遍历所有摄像头信息,查找前置摄像头
  for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            Camera.getCameraInfo(i, cameraInfo);
  {
                //判断是否是前置摄像头
  if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    //打开前置摄像头
  mCamera = Camera.open(i);
  isFrontCamera = true;
  }
            }
        }
    }
}

2.camera参数设置(surfaceChanged方法中)

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

initCamera方法

/**  
* 照相机参数设置 
*/ 
public void initCamera() {
    if (mCamera != null && !isPreview) {
        //获取相机参数
  Camera.Parameters parameters = mCamera.getParameters();
  // 设置闪光灯为自动 前置摄像头时 不能设置
  if (!isFrontCamera) {
      parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
  }
      //设置相机预览及图片参数
  setCameraParams(mPoint.x, mPoint.y);
  //开启预览
  mCamera.startPreview();
  isPreview = true;
  }
}

setCameraparams方法

/**  
* 设置预览图片和裁剪图片的大小 
* @param width 屏幕宽度  
* @param height 屏幕高度  
*/ 
private void setCameraParams(int width, int height) {
  Log.i(TAG, "setCameraParams  width=" + width + "  height=" + height);
  Camera.Parameters parameters = mCamera.getParameters();
  // 获取摄像头支持的PictureSize列表
  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 = getPreviewSize(pictureSizeList, ((float) height / width));
  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);
  //根据layoutParams设置surfaceView的大小
  mCameraSurfaceView.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height)); 
  // 获取摄像头支持的PreviewSize列表
  List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
  //获取预览图片的大小
  Camera.Size preSize = getPreviewSize(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();
  // 设置PreviewDisplay的方向,效果就是将捕获的画面旋转多少度显示
  mCamera.setDisplayOrientation(90);
  //设置参数(不设置不会有效果)
  mCamera.setParameters(parameters);   }

getPreviewSize方法:

/**  
* 从列表中选取合适的分辨率 
* 默认w:h = 4:3 
*/ 
private Camera.Size getPreviewSize(List<Camera.Size> pictureSizeList, float 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; 
}

3.释放camera(surfaceDestoryed方法中)

@Override 
public void surfaceDestroyed(SurfaceHolder holder) {
    // 当holder被回收时 释放硬件
    releaseCamera(); 
}

releaseCamera方法:

/**  
* 释放camera资源 
*/ 
private void releaseCamera() {
    if (mCamera != null) {
        if (isPreview) {
            //停止camera的预览
            mCamera.stopPreview();
        }
        //释放相机资源
        mCamera.release();
        //相机设置为null
        mCamera = null;
    }
    isPreview = false; }</pre>

重点来啦,让我们来拍照吧

4.camera拍照

(1)设置camera的回调接口:Camera.pictureCallBack

/**  
* 相机拍照的返回接口 
*/ 
private Camera.PictureCallback jpeg = new Camera.PictureCallback() {
    @Override
  public void onPictureTaken(byte[] data, Camera camera) {
        isTake = false;
        if (data == null) return;
        // 获取拍照回调的图片数据。
        Bitmap bitmap = BitmapFactory
                .decodeByteArray(data, 0, data.length, opt);
        Bitmap bm;
        //获取相机的方向(横向或纵向)
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            //矩阵转换
            Matrix matrix = new Matrix();
            matrix.setRotate(90, 0.1f, 0.1f);
            bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
            bitmap.getHeight(), matrix, false);
           if (isFrontCamera) {
                //前置摄像头旋转图片270度。
                matrix.setRotate(270);
                bm = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
           }
        } else {
            bm = bitmap;
        }
        //判断矩形取景框是否为空
        if (mCenterRect != null) {
            //获取取景框大小的bitmap
            bitmap = BitmapUtils.getRectBitmap(mCenterRect, bm, mPoint);
        }
        //图片缩放到341x481大小
        Bitmap scaleBitmap=Bitmap.createScaledBitmap(bitmap,341,481,false);
        //将以矩形取景框大小的图片保存到sd卡
        if (SdcardUtils.existSdcard()) {
            SdcardUtils.saveBitmap2SD(scaleBitmap, filesDir, imageName);
            //SdcardUtils.saveBitmap2SD(bitmap, filesDir, imageName);
        } else ToastUtil.showT(CameraActivity.this, "未检测到SD卡");
        //释放图片资源,防止OOM
        BitmapUtils.recycleBitmap(bm);
        //显示预览图
        ivPreview.setImageBitmap(bitmap);
        //释放相机资源
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.startPreview();
            isPreview = true;
        }
        //拍照成功返回
        setResult(-1);
        finish();
  }
};

(2)点击拍照

注:camera调用takePicture方法前要设置相机参数,不然拍照次数多了会出现闪退

// 设置相机参数 
setCameraParams(mPoint.x,mPoint.y); 
//拍照 
mCamera.takePicture(null, null, jpeg);

jpeg就是我们刚才设置的接口回调名称

源码地址:https://github.com/ruancw/CustomCamerDemo

完结!!!

相关文章

  • 自定义带取景框的camera

    前言:公司项目需求,在图像信息采集时只采集肩部以上部位的图片(和我们平时的一寸证件照很像),首先想到的是用第三方的...

  • Android 自定义View实现简单的相机预览取景框

    前言 最近遇到拍照时带取景框的需求,于是自己写了个简单的取景框配合相机预览界面实现拍照功能。本文涉及自定义view...

  • Android 多媒体 -- Camera拍照的使用

    Camera的使用。横竖屏,画面不变形 一、调用系统提供的拍照 二、自定义Camera 调用系统的Camera 实...

  • Android Camera实现毫秒级拍照

    我们知道自定义Camera需要以下几步 打开相机,即实例化Camera对象 设置Camera的相关参数 打开预览 ...

  • Camera相关知识

    App如何试图使用camera硬件特性的?-在manifest中声明使用camera权限-快速拍照还是自定义Cam...

  • Android相机详解

    内容概要 使用系统相机 自定义相机(Camera API)拍照录制视频 使用相机特性 Camera2 API介绍 ...

  • Android相机<第三篇>:Camera详解

    当我们自定义相机时,常常使用Camera对象完成拍照流程,本文的重点在于研究Camera 各个方法的作用。 [方法...

  • 使用CameraX 拍照&录制视频

    Android 自定义相机要考虑的东西还是非常多的,特别是兼容性问题,尽管Camera 2已经替换了Camera1...

  • 劫后余生 重新被掌握命运

    继续自定义view,关于Camera Camera 它类似一个摄像机镜头,可以照射三维的空间,把内容投影到画布上,...

  • Android 个人知识体系梳理

    [toc] 自定义View View的坐标系 Camera坐标系 Camera坐标系的旋转方向 第一章 第二章 第...

网友评论

    本文标题:自定义带取景框的camera

    本文链接:https://www.haomeiwen.com/subject/ikbxkftx.html