相机的用法
参考:
http://www.2cto.com/kf/201401/272099.html
侵立删。
最近在学习Android硬件Camera的调用,所以结合书本和网上博客练练手。
需要注意
- 此API 21(Lollipop android 5.0)就开始弃用
建议学习最新的camera2,不然使用as总是弹出deprecate(弃用)。 - 调试时,不能用API 23 (marshmallow android 6.0)调试,因为此版本的camera的permission为动态的,静态的permission不可用。
- 下面有使用方法,禁止as 弹通知。
步骤:
- 检查和访问摄像头存在与数目
由于camera为单进程占用硬件,使用之前需要检查是否被占用,否则会与其他程序冲突,无法使用。同理,使用完一定要释放camera资源,以便留给其他进程使用。
// 官方建议的安全地访问摄像头的方法
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
//返回一个新建Camera对象
} catch (Exception e) {
Log.d("Tag", "Error is" + e.getMessage());
}
return c;
}
检查设备是否支持摄像头
// 检查设备是否支持摄像头
public boolean CheckCameraHardware(Context mContext) {
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// 摄像头存在
return true;
} else {
// 摄像头不存在
return false;
}
}
摄像头数量
int cameraNum = Camera.getNumberOfCameras();
//一般有两个,0为后置,1为前置
mCamera = getCameraInstance();
if (mCamera != null) {
mCamera.startPreview();
}
Log.d("Tag77", "CameraNum:" + cameraNum);
释放相机资源。
if (mCamera != null) {
// 释放相机资源
mCamera.release();
mCamera = null;
}
- 创建预览类
//CameraView.java
/**
* Created by ss on 2016/10/29.
*基本的相机预览类
*/
//小写传参,禁止deprecation 弹出
@SuppressWarnings("deprecation")
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
//相机类
private Camera mCamera;
private SurfaceHolder mHolder;
public CameraView(Context mContext,Camera mCamera) {
super(mContext);
this.mCamera=mCamera;
//创建和销毁底层surface时可以获得通知
mHolder =getHolder();
//添加SurfaceHolder.Callback
mHolder.addCallback(this);
//已经在API 11就失效了
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try{
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
//mCamera.setPreviewDisplay(holder);
mCamera.setDisplayOrientation(90);
//clockwise顺时针旋转90
}
catch (IOException e) {
Log.d("Tag","Error is"+e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 如果预览无法更改或旋转,注意此处的事件
// 确保在缩放或重排时停止预览
if (mHolder.getSurface() == null){
// 预览surface不存在
return;
}
// 更改时停止预览
try {
mCamera.stopPreview();
} catch (Exception e){
// 忽略:试图停止不存在的预览
}
// 在此进行缩放、旋转和重新组织格式
// 以新的设置启动预
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
} catch (Exception e){
Log.d("TAG", "Error is " + e.getMessage());
}
}
}
- 创建相机UI
UI为一个照相并保存的按钮,一个退出按钮,一个图片显示器(ImageView)。
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_weight="0.2"
android:layout_height="0dp">
<Button
android:id="@+id/BtnCapture"
android:text="@string/button_name"
android:layout_width="60dp"
android:layout_height="30dp" />
<ImageView
android:id="@+id/thumbsView"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@string/content_description"
android:layout_width="60dp"
android:layout_height="60dp"/>
<Button
android:id="@+id/BtnExit"
android:text="@string/button_exit"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/BtnCapture"
android:layout_toEndOf="@+id/BtnCapture"
android:layout_marginLeft="60dp"
android:layout_marginStart="30dp" />
</RelativeLayout>
</LinearLayout>
- 采集图片与Uri
主要重写两个函数,PictureCallback和ShutterCallback
拍照回调接口
// 拍照回调接口
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] mData, Camera camera) {
File mPictureFile = StorageHelper.getOutputFile(StorageHelper.MEDIA_TYPE_IMAGE);
if (mPictureFile == null) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(mPictureFile);
fos.write(mData);
// 输出文件流后要关闭
fos.close();
Bitmap mBitmap = BitmapFactory.decodeByteArray(mData, 0, mData.length);
thumbsView.setImageBitmap(mBitmap);
// 获取缩略图Uri
mUri = StorageHelper.getOutputUri(mPictureFile);
// 停止预览
mCamera.stopPreview();
mCamera.startPreview();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
};
快门回调接口
// 快门回调接口
private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
public void onShutter() {
mediaPlayer = new MediaPlayer();
mediaPlayer = mediaPlayer.create(MainActivity.this, R.raw.shutter);
try {
mediaPlayer.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.start();
Log.d("Tag66", "mediaPlayer 66");
}
};
- 存储的辅助类
/**
* Created by ss on 2016/10/29.
* 保存文件的辅助类
*/
public final class StorageHelper {
public static final int MEDIA_TYPE_IMAGE=0;
public static final int MEDIA_TYPE_VIDEO=1;
public static Uri getOutputUri(File mFile){
//返回URi 路径
return Uri.fromFile(mFile);
}
public static File getOutputFile(int mType){
//Environment:Return the primary external storage directory
File mMediaFileDir = new File(Environment.getExternalStorageDirectory(),"OpenCamera");
if (!mMediaFileDir.exists()){
if(!mMediaFileDir.mkdir()){
//mkdir:Creates the directory named by this file, assuming its parents exist
return null;
}
}
File mMediaFile = null;
//创建文件名
String mFileName = new SimpleDateFormat("yyyMMddHHmmss").format(new Date());
switch (mType){
case MEDIA_TYPE_IMAGE:
mMediaFile = new File(mMediaFile.getPath()+File.separator+"IMG_"+mFileName+".jpg");
break;
case MEDIA_TYPE_VIDEO:
mMediaFile = new File(mMediaFile.getPath()+File.separator+"VID_"+mFileName+".mp4");
default:mMediaFile=null;
}
return mMediaFile;
}
}
6.下面是主活动的代码逻辑
public class MainActivity extends AppCompatActivity {
private Camera mCamera;
// 预览界面
private CameraView mCameraView;
// 缩略图
private ImageView thumbsView;
// 当前缩略图Uri
private Uri mUri;
private MediaPlayer mediaPlayer;
private GoogleApiClient client;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 要求全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
// 检查硬件设备Camera
if (!CheckCameraHardware(this)) {
Toast.makeText(this, "sorry no camera!", Toast.LENGTH_SHORT).show();
return;
}
int cameraNum = Camera.getNumberOfCameras();
//mCamera = getCameraInstance();
mCamera = Camera.open();
if (mCamera != null) {
mCamera.startPreview();
}
Log.d("Tag77", "CameraNum:" + cameraNum);
// 获取预览画面
mCameraView = new CameraView(this, mCamera);
FrameLayout mFrameLayout = (FrameLayout) findViewById(R.id.frameLayout);
mFrameLayout.addView(mCameraView);
mCamera.startPreview();
// 拍照按钮
Button BtnCapture = (Button) findViewById(R.id.BtnCapture);
BtnCapture.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// 使用takePicture完成拍照
mCamera.autoFocus(new Camera.AutoFocusCallback() {
// 自动对焦完成拍照
public void onAutoFocus(boolean isSuccess, Camera camera) {
if (isSuccess && camera != null) {
mCamera.takePicture(mShutterCallback, null, mPictureCallback);
}
}
});
}
});
//退出按钮
Button BtnExit =(Button) findViewById(R.id.BtnExit);
BtnExit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
thumbsView = (ImageView) findViewById(R.id.thumbsView);
thumbsView.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// 使用URI访问当前缩略图
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(mUri, "image/*");
startActivity(intent);
}
});
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}
//下面省略四个函数
//拍照回调接口
//快门回调接口
//官方建议的安全地访问摄像头的方法
//检查设备是否支持摄像头
7.声明权限
在AndroidManifest.xml声明权限,获得许可使用硬件camera。主要在Android 6.0 以上无效,至今没有找到方法解决。
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />