前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(五十六)摄像头拍照

Android开发笔记(五十六)摄像头拍照

作者头像
aqi00
发布2019-01-18 11:09:14
1.8K0
发布2019-01-18 11:09:14
举报
文章被收录于专栏:老欧说安卓

相机Camera

Camera是直接操作摄像头硬件的工具类。常用的方法如下: getNumberOfCameras : 获取本机的摄像头数目 open : 打开摄像头,默认打开后置摄像头。如果有多个摄像头,那么open(0)表示打开后置摄像头,open(1)表示打开前置摄像头。 以上两个方法是静态方法。 getParameters : 获取摄像头的拍照参数。 setParameters : 设置摄像头的拍照参数。 --setPreviewSize : 设置预览界面的尺寸。 --setPictureSize : 设置保存图片的尺寸。 --setPictureFormat : 设置图片格式。一般使用ImageFormat.JPEG表示jpg格式, --setFocusMode : 设置对焦模式。一般使用FOCUS_MODE_AUTO表示自动对焦。 以上是Camera.Parameters的参数设置方法。 setPreviewDisplay : 设置预览界面,参数为SurfaceHolder类型。 startPreview : 开始预览。该方法在setPreviewDisplay之后调用。 stopPreview : 停止预览 unlock : 录像时需要对摄像头解锁,这样摄像头才能持续录像。该方法在startPreview之后调用。 lock : 录像完毕对摄像头加锁。该方法在stopPreview之后调用。 setDisplayOrientation : 设置预览的角度。因为Android的0角度都在三点钟位置,而手机画面都是六点钟的垂直位置,所以从三点钟到六点钟需要旋转90度。 autoFocus : 设置对焦事件,参数为AutoFocusCallback类型。比如说在对焦成功时显示一个图片提示用户可以拍照了。 takePicture : 拍照。第一个参数ShutterCallback用来控制按下快门时的事件,我们可在此播放拍照声音,默认就是咔嚓一声;后面的几个回调接口PictureCallback分别对应原始图像、缩放和压缩图像和JPG图像,图像数据可以在接口中的onPictureTaken方法中获得,通常我们只关心最后一个的JPG图像数据,所以前面的接口参数可以直接传null。 release : 释放摄像头。每次退出拍照都要释放,因为摄像头不能重复打开,要么就是把Camera对象做成单例模式。

预览视图SurfaceView/预览持有者SurfaceHolder

SurfaceView是Android中的一种特殊视图,它拥有独立的绘图表面,即它不与其宿主页面共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的界面就可以在一个独立的线程中进行绘制,我们称之为渲染线程。因为它不占用主线程资源,所以一方面可以实现复杂而高效的UI,另一方面也会及时响应用户输入。鉴于SurfaceView具备如上特性,故而它可用于拍照以及录像的预览界面,也可用于游戏的画面。 不过SurfaceView自身主要完成绘图功能,其他功能设置以及事件处理还有待于SurfaceHolder来操作。SurfaceView的getHolder方法把二者关联了起来,可获取预览界面当前干活的操纵者。SurfaceHolder应与SurfaceView配合使用,下面是SurfaceHolder的常用方法: addCallback : 添加回调接口 removeCallback : 移除回调接口 isCreating : 判断预览界面是否有效 setFormat : 设置预览格式。PixelFormat.TRANSPARENT表示透明,PixelFormat.TRANSLUCENT表示半透明,PixelFormat.OPAQUE表示不透明。 setFixedSize : 设置预览界面的尺寸 setSizeFromLayout : 设置预览界面的尺寸为布局文件中的配置 getSurfaceFrame : 获取预览界面的尺寸 getSurface : 获取预览视图的对象。主要用于播放视频。

拍照的相关事件

下面是几个拍照用到的回调事件接口: 预览变化事件  监听器类名 : SurfaceHolder.Callback 设置监听器的方法 :  Camera.setPreviewDisplay : 添加预览持有者SurfaceHolder。该方法用于关联Camera和SurfaceHolder SurfaceHolder.addCallback : 添加回调接口Callback。该方法用于关联SurfaceView和SurfaceHolder,它与Camera.setPreviewDisplay最终联合完成SurfaceView与Camera的关联,即摄像头的画面展示在预览界面上。 SurfaceHolder.removeCallback : 移除回调接口Callback 监听器需要重写的方法 :  surfaceCreated : 预览创建。不管是拍照还是录像,通常在该方法中设置拍照预览Camera.setPreviewDisplay。 surfaceChanged : 预览变化 surfaceDestroyed : 预览结束。注意SurfaceView的渲染线程只在surfaceCreated和surfaceDestroyed之间有效,所以如果在别处操作SurfaceView画面,得判断当前预览界面是否有效,也就是调用SurfaceHolder.isCreating方法来判断。 自动对焦事件  监听器类名 : Camera.AutoFocusCallback 设置监听器的方法 : Camera.autoFocus 监听器需要重写的方法 : onAutoFocus 快门按下事件  监听器类名 : Camera.ShutterCallback 设置监听器的方法 : Camera.takePicture 监听器需要重写的方法 : onShutter 拍照事件  监听器类名 : Camera.PictureCallback 设置监听器的方法 : Camera.takePicture 监听器需要重写的方法 : onPictureTaken 变焦事件  监听器类名 : Camera.OnZoomChangeListener 设置监听器的方法 : Camera.setZoomChangeListener 监听器需要重写的方法 : onZoomChange

扫描二维码

这个功能最有名的应用就是微信里的“扫一扫”了,通过摄像头拍照从二维码中获取相关信息,然后再进行相应操作(比如说添加好友、下载文件、访问页面等等)。Android中的二维码扫描可用Google的zxing开源库,再结合zxing的使用框架MipcaActivityCapture。 下面是zxing+MipcaActivityCapture框架的代码集成例子: 1、给工程加入zxing3.2.1.jar; 2、把MipcaActivityCapture源码(com.app.zxing)加入到工程; 3、编写MipcaActivityCapture的布局文件activity_capture.xml,主要是加入SurfaceView和com.app.zxing.view.ViewfinderView两个视图,前一个视图是预览界面,后一个是扫码界面; 4、如果需要调整扫描界面的UI,则修改ViewfinderView的onDraw方法,可加入新的元素或者调整尺寸。 5、对扫码结果的处理见MipcaActivityCapture的handleDecode方法,视情况做相应处理,如添加好友、下载文件、访问页面等等。

代码示例

下面是相机视图CameraView的代码示例:

代码语言:javascript
复制
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
	private static final String TAG = "CameraView";
	private Context mContext;
	private Camera mCamera;
    private Bitmap mBitmap = null;
	private SurfaceHolder mHolder = null;
	private boolean isPreviewing = false;
	private Point mCameraSize;
	private int mCameraType = CAMERA_BEHIND;
	public static int CAMERA_BEHIND = 0;
	public static int CAMERA_FRONT = 1;

	public CameraView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		mHolder = getHolder();
		mHolder.setFormat(PixelFormat.TRANSPARENT);//translucent半透明 transparent透明
		//mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		mHolder.addCallback(this);
	}

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

	public int getCameraType() {
		return mCameraType;
	}
	
	public void setCameraType(int CameraType) {
		mCameraType = CameraType;
	}

	public Bitmap getPhoto() {
		if (mBitmap != null) {
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
		} else {
			Log.d(TAG, "mBitmap is null.");
		}
		return mBitmap;
	}

	public void doTakePicture() {
		if(isPreviewing && (mCamera != null)) {
			mCamera.takePicture(mShutterCallback, null, mPictureCallback);
		}
	}

	//快门按下的回调,在这里我们可以设置类似播放“咔嚓”声之类的操作。默认的就是咔嚓。
	ShutterCallback mShutterCallback = new ShutterCallback() {
		public void onShutter() {
			Log.d(TAG, "myShutterCallback:onShutter...");
		}
	};
	
	PictureCallback mPictureCallback = new PictureCallback() {
		public void onPictureTaken(byte[] data, Camera camera) {
			Log.d(TAG, "mPictureCallback:onPictureTaken...");
			Bitmap b = null;
			if(null != data) {
				b = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据,将其解析成位图
				mCamera.stopPreview();
				isPreviewing = false;
			}
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				mBitmap = BitmapUtil.getRotateBitmap(b, 90);
			} else {
				mBitmap = BitmapUtil.getRotateBitmap(b, -90);
			}
			Log.d(TAG, "mBitmap.size="+(mBitmap.getByteCount()/1024)+"K");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//再次进入预览
			mCamera.startPreview();
			isPreviewing = true;
		}
	};

	@Override
	public void surfaceCreated(SurfaceHolder mHolder) {
		// 当预览视图创建的时候开启相机
		mCamera = Camera.open(mCameraType);
		try {
			// 设置预览
			mCamera.setPreviewDisplay(mHolder);
			mCameraSize = MetricsUtil.getCameraSize(mCamera.getParameters(), MetricsUtil.getSize(mContext));
			Log.d(TAG, "width="+mCameraSize.x+", height="+mCameraSize.y);
		    Camera.Parameters parameters = mCamera.getParameters();
			// 设置预览大小
		    parameters.setPreviewSize(mCameraSize.x, mCameraSize.y);
			// 设置图片保存时的分辨率大小
			parameters.setPictureSize(mCameraSize.x, mCameraSize.y);
			// 设置格式
			parameters.setPictureFormat(ImageFormat.JPEG);
			// 设置自动对焦。前置摄像头似乎无法自动对焦
			if (mCameraType == CameraView.CAMERA_BEHIND) {
				parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
			}
		    mCamera.setParameters(parameters);
		} catch (Exception e) {
			Log.d(TAG, "setPreviewDisplay error: "+e.getMessage());
			// 释放相机资源并置空
			mCamera.release();
			mCamera = null;
		}
		return;
	}

	@Override
	public void surfaceChanged(SurfaceHolder mHolder, int format, int width, int height) {
		Log.d(TAG, "surfaceChanged");
		mCamera.setDisplayOrientation(90);
		// 开始预览
		mCamera.startPreview();
		isPreviewing = true;
		mCamera.autoFocus(null);
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder mHolder) {
		Log.d(TAG, "surfaceDestroyed");
		mCamera.stopPreview();
		mCamera.release();
		mCamera = null;
	}

}

下面是拍照页面CameraActivity的代码示例:

代码语言:javascript
复制
import java.text.SimpleDateFormat;
import java.util.Date;

import com.example.exmcamera.R;
import com.example.exmcamera.util.BitmapUtil;
import com.example.exmcamera.util.MetricsUtil;
import com.example.exmcamera.widget.CameraView;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageButton;

public class CameraActivity extends Activity implements OnClickListener {
	private static final String TAG = "CameraActivity";
	private CameraView cameraView;
	private ImageButton shutterBtn;
	private int mCameraType;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.item_camera);
		mCameraType = getIntent().getIntExtra("type", CameraView.CAMERA_BEHIND);
		cameraView = (CameraView)findViewById(R.id.camera_view);
		cameraView.setCameraType(mCameraType);
		shutterBtn = (ImageButton)findViewById(R.id.btn_shutter);
		shutterBtn.setOnClickListener(this);
		initViewParams();
	}

	private void initViewParams(){
		LayoutParams paramsLayout = cameraView.getLayoutParams();
		Point size = MetricsUtil.getSize(this);
		paramsLayout.width = size.x;
		paramsLayout.height = size.y;
		cameraView.setLayoutParams(paramsLayout);

		LayoutParams paramsButton = shutterBtn.getLayoutParams();
		paramsButton.width = MetricsUtil.dip2px(this, 60);
		paramsButton.height = MetricsUtil.dip2px(this, 60);
		shutterBtn.setLayoutParams(paramsButton);
	}

	@Override
	public void onBackPressed() {
        Intent intent = new Intent();
		Bundle bundle = new Bundle();
		Bitmap bitmap = cameraView.getPhoto();
		if (bitmap == null) {
			bundle.putString("is_null", "yes");
		} else {
			bundle.putString("is_null", "no");
			String path = String.format("%s%s.jpg", BitmapUtil.getCachePath(this),getNowDateTime());
			BitmapUtil.saveFile(cameraView.getPhoto(), path);
			bundle.putString("path", path);
		}
		intent.putExtras(bundle);
        setResult(Activity.RESULT_OK, intent);
        finish();
	}

	private String getNowDateTime() {
		SimpleDateFormat s_format = new SimpleDateFormat("yyyyMMddHHmmss");
		Date d_date = new Date();
		String s_date = "";
		s_date = s_format.format(d_date);
		return s_date;
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_shutter) {
			cameraView.doTakePicture();
		}
	}

}

点击下载本文用到的摄像头拍照与扫一扫的工程代码 点此查看Android开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年02月05日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 相机Camera
  • 预览视图SurfaceView/预览持有者SurfaceHolder
  • 拍照的相关事件
  • 扫描二维码
  • 代码示例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档