几经折腾,综合了网上各大神的经验分享,终于可以顺利在手机上实例拍照功能了。
本人当前使用的 android studio 版本为 2.3.2
首先整理一下实现拍照功能的实现思路:
1、在 AndroidManifest.xml 开通必要的权限。
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.autofocus" android:required="true" />
2、因为在拍照之前,在手机上能预览到摄像头所获取到的景物,所以需要在主界面上增加一个景物预览的前端控件:
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3、要拍照,所以要有一个触发事件的控件。可以根据自己的喜好随便放(Button、View、TextView等等都可以)。
4、拍照的结果需要一个控件来展示。(ImageView、View都可以)
5、后端代码。
摄像头 Camera 对象这里采用的是 import android.hardware.Camera;
5.1、声明主要变量
private Camera camera;
private SurfaceView surfaceView;
private SurfaceHolder holder;
private SurfaceHolder holder1;
5.1.1 为 holder1 对象赋值:在 SurfaceCallBack 类的 surfaceCreated 方法里
注: holder 和 holder1 是两个不同的对象!
surfaceView = (SurfaceView) findViewById(R.id.surfaceView); //就是第2步里的 SurfaceView 控件 id
holder = surfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//holder.setFixedSize(176, 144);//设置分辨率
holder.setKeepScreenOn(true);//屏幕长亮
holder.addCallback(new SurfaceCallBack());为SurfaceView的句柄添加一个回调函数
class SurfaceCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
holder1 = holder;
openCamera();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters();//获取各项参数
Camera.Size previewSize = findFitPreResolution(parameters);
parameters.setPreviewSize(previewSize.width, previewSize.height);// 设置预览大小
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
parameters.setPictureFormat(PixelFormat.JPEG);//设置图片格式
//不能与setPreviewSize一起使用,否则setParamters会报错
//parameters.setPreviewFrameRate(5);//设置每秒显示4帧
parameters.setJpegQuality(80);// 设置照片质量
Camera.Size pictureSize = null;
if (equalRate(screenWidth, screenHeight, 1.33f)) {
pictureSize = findFitPicResolution(parameters, (float) 4 / 3);
} else {
pictureSize = findFitPicResolution(parameters, (float) 16 / 9);
}
parameters.setPictureSize(pictureSize.width, pictureSize.height);// 设置保存的图片尺寸
camera.setParameters(parameters);
camera.startPreview();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (camera != null) {
//当surfaceview关闭时,关闭预览并释放资源
camera.stopPreview();
camera.release();//释放相机
camera = null;
}
holder.getSurface().release();
}
private Camera.Size findFitPreResolution(Camera.Parameters cameraParameters) {
List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPreviewSizes();
Camera.Size resultSize = null;
for (Camera.Size size : supportedPicResolutions) {
if (size.width <= ALLOW_PIC_LEN) {
if (resultSize == null) {
resultSize = size;
} else if (size.width > resultSize.width) {
resultSize = size;
}
}
}
if (resultSize == null) {
return supportedPicResolutions.get(0);
}
return resultSize;
}
private Camera.Size findFitPicResolution(Camera.Parameters cameraParameters, float bl) {
List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes();
Camera.Size resultSize = null;
for (Camera.Size size : supportedPicResolutions) {
if ((float) size.width / size.height == bl && size.width <= ALLOW_PIC_LEN && size.height <= ALLOW_PIC_LEN) {
if (resultSize == null) {
resultSize = size;
} else if (size.width > resultSize.width) {
resultSize = size;
}
}
}
if (resultSize == null) {
return supportedPicResolutions.get(0);
}
return resultSize;
}
}
5.1.2 打开摄像头相关
private void openCamera(){
int cameraPosition = Camera.CameraInfo.CAMERA_FACING_BACK;
// 打开摄像头
camera = Camera.open(cameraPosition);
// 设置用于显示拍照影像的SurfaceHolder对象
camera.setPreviewDisplay(holder1);
camera.setDisplayOrientation(90);
camera.startPreview();
camera.autoFocus(autoFocusCallback);
}
5.1.3 autoFocusCallback 回调:
那么这时 autoFocusCallback = new MyAutoFocusCall();
class MyAutoFocusCall implements Camera.AutoFocusCallback {
@Override
public void onAutoFocus(boolean success, Camera camera) {
//to do something.
}
}
6 在触发拍照的事件里执行如下代码获取图片数据:
camera.takePicture(shutterCallback, null, pictureCallback);
6.1 shutterCallback
回调
shutterCallback
= new MyShutterCallback();
class MyShutterCallback implements Camera.ShutterCallback{
@Override
public void onShutter() {
//to do something.
}
}
6.2 pictureCallback
回调
pictureCallback
= new MyPictureCallback();
class MyPictureCallback implements Camera.PictureCallback{
@Override
public void onPictureTaken(byte[] data, Camera camera) {
if (null == data) return;
Bitmap img = BitmapFactory.decodeByteArray(data, 0, data.length);
//img 可以在 ImageView 里显示(imgView 对象自己用 findViewById 获取)
imgView.setImageBitmap(img);
}
}
7、到这步是可以显示拍照的图片了(如果不出意外,环境不出问题的话),但是这时会发现所展示的拍照后的结果图片角度不对,应该顺时针旋转90度才对。
private Bitmap RotatePic(Bitmap img) {
int width = img.getWidth();
int height = img.getHeight();
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap resultImg = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
//BitmapDrawable bitmapDrawable = new BitmapDrawable(resizedBitmap);
return resultImg;
}
这里的 resultImg 才是最终拍照后的图片。所以需要在显示图片前调用 RotatePic 方法旋转图片:
img = RotatePic(img);
imgView.setImageBitmap(img);