今天无意当中发现在《Android开发学习之基于ZBar实现微信扫一扫》中的一部分代码可以用来以硬件方式实现一个照相机的功能,在《Android开发学习之调用系统相机完成拍照的实现》中我们是以Intent方式调用系统内置的相机来完成拍照的,今天呢,就让我们来以Camera类为核心来开发自己的相机应用吧。在Android的官方文档中,以硬件方式定制相机应用的步骤如下:
1. 检查和访问Camera
创建代码来检查Camera和所申请访问的存在性;
2. 创建一个预览类
继承SurfaceView来创建一个Camera的预览类,并实现SurfaceHolder接口。这个类用来预览来自Camera的实施图像。
3. 构建一个预览布局
一旦有了Camera预览类,就可以把这个预览类和你想要的用户界面控制结合在一起来创建一个视图布局。
4. 针对采集建立监听
把监听器与响应用户动作(如按下按钮)的界面控制连接到一起来启动图像或视频的采集。
5. 采集和保存文件
针对真正采集的图片或视频,以及输出的保存来编写代码。
6. 释放Camera
使用Camera之后,你的应用程序必须释放Camera,以便其他应用程序能够使用。
Camera硬件是一个必须要认真管理的共享资源,因此你的应用程序在使用它时,不能跟其他应用程序发生冲突。下文将讨论如何检查Camera硬件、如何申请对Camera的访问,如何采集图片和视频,以及在应用使用完成后如何释放Camera。
警告:在应用程序使用完Camera时,要记住通过调用Camera.release()方法来释放Camera对象。如果你的应用程序没有正确的释放Camera,所有的后续的视图对Camera的访问,包括你自己的应用程序,都会失败,并可能到你的或其他的应用程序关闭。
下面我们就来按照上面的步骤来一步步的制作一个属于自己的相机应用吧!
第一步:检查和访问相机
/** 官方建议的安全地访问摄像头的方法 **/ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); } catch (Exception e){ Log.d("TAG", "Error is "+e.getMessage()); } return c; } /** 检查设备是否支持摄像头 **/ private boolean CheckCameraHardware(Context mContext) { if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // 摄像头存在 return true; } else { // 摄像头不存在 return false; } }
package com.android.OpenCamera; import java.io.IOException; import android.annotation.SuppressLint; import android.content.Context; import android.hardware.Camera; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; /** 一个基本的相机预览界面类 **/ @SuppressLint("ViewConstructor") public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { /** Camera **/ private Camera mCamera; /** SurfaceHolder **/ private SurfaceHolder mHolder; /** CamreaPreview构造函数 **/ @SuppressWarnings("deprecation") public CameraPreview(Context mContext,Camera mCamera) { super(mContext); this.mCamera=mCamera; // 安装一个SurfaceHolder.Callback, // 这样创建和销毁底层surface时能够获得通知。 mHolder = getHolder(); mHolder.addCallback(this); // 已过期的设置,但版本低于3.0的Android还需要 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } @Override public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); mCamera.setDisplayOrientation(90); } catch (IOException e) { Log.d("TAG", "Error is "+e.getMessage()); } } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // 如果预览无法更改或旋转,注意此处的事件 // 确保在缩放或重排时停止预览 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()); } } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <FrameLayout android:id="@+id/PreviewView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.8" > </FrameLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="0.2" android:background="@drawable/main_bg"> <Button android:id="@+id/BtnCapture" android:layout_width="60dp" android:layout_height="60dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@drawable/camera"/> <ImageView android:id="@+id/ThumbsView" android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_margin="15dp" android:contentDescription="@string/Description" /> </RelativeLayout> </LinearLayout>
/** 拍照回调接口 **/ private PictureCallback mPicureCallback=new PictureCallback() { @Override 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 ShutterCallback mShutterCallback=new ShutterCallback() { @Override public void onShutter() { mPlayer=new MediaPlayer(); mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter); try { mPlayer.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mPlayer.start(); } };
package com.android.OpenCamera; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.net.Uri; import android.os.Environment; @SuppressLint("SimpleDateFormat") 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) { return Uri.fromFile(mFile); } public static File getOutputFile(int mType) { File mMediaFileDir=new File(Environment.getExternalStorageDirectory(),"OpenCamera"); if(!mMediaFileDir.exists()) { if(!mMediaFileDir.mkdir()) { return null; } } File mMediaFile=null; /** 创建文件名 **/ String mFileName=new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); switch(mType) { case MEDIA_TYPE_IMAGE: mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"IMG_"+mFileName+".jpg"); break; case MEDIA_TYPE_VIDEO: mMediaFile=new File(mMediaFileDir.getPath()+File.separator+"VID_"+mFileName+".mp4"); break; default: mMediaFile=null; } return mMediaFile; } }
if (mCamera != null){ mCamera.release(); mCamera = null; }
package com.android.OpenCamera; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toast; public class MainActivity extends Activity { /** 相机 **/ private Camera mCamera; /** 预览界面 **/ private CameraPreview mPreview; /** 缩略图 **/ ImageView ThumbsView; /** 当前缩略图Uri **/ private Uri mUri; /** MediaPlayer **/ private MediaPlayer mPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /** 隐藏标题栏 **/ requestWindowFeature(Window.FEATURE_NO_TITLE); /** 隐藏状态栏 **/ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); /** 禁用锁屏,因为再次唤醒屏幕程序就会终止,暂时不知道怎么解决 **/ getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_main); /** 硬件检查 **/ if(CheckCameraHardware(this)==false) { Toast.makeText(this, "很抱歉,您的设备可能不支持摄像头功能!", Toast.LENGTH_SHORT).show(); return; } /** 获取相机 **/ mCamera=getCameraInstance(); /** 获取预览界面 **/ mPreview = new CameraPreview(this, mCamera); FrameLayout mFrameLayout = (FrameLayout)findViewById(R.id.PreviewView); mFrameLayout.addView(mPreview); mCamera.startPreview(); /** 拍照按钮 **/ Button BtnCapture = (Button) findViewById(R.id.BtnCapture); BtnCapture.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** 使用takePicture()方法完成拍照 **/ mCamera.autoFocus(new AutoFocusCallback() { /** 自动聚焦聚焦后完成拍照 **/ @Override public void onAutoFocus(boolean isSuccess, Camera camera) { if(isSuccess&&camera!=null) { mCamera.takePicture(mShutterCallback, null, mPicureCallback); } } }); } }); /** 相机缩略图 **/ ThumbsView = (ImageView)findViewById(R.id.ThumbsView); ThumbsView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** 使用Uri访问当前缩略图 **/ Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(mUri, "image/*"); startActivity(intent); } }); } /** 快门回调接口 **/ private ShutterCallback mShutterCallback=new ShutterCallback() { @Override public void onShutter() { mPlayer=new MediaPlayer(); mPlayer=MediaPlayer.create(MainActivity.this,R.raw.shutter); try { mPlayer.prepare(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } mPlayer.start(); } }; /** 拍照回调接口 **/ private PictureCallback mPicureCallback=new PictureCallback() { @Override 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(); } } }; /** 官方建议的安全地访问摄像头的方法 **/ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); } catch (Exception e){ Log.d("TAG", "Error is "+e.getMessage()); } return c; } /** 检查设备是否支持摄像头 **/ private boolean CheckCameraHardware(Context mContext) { if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { // 摄像头存在 return true; } else { // 摄像头不存在 return false; } } @Override protected void onPause() { super.onPause(); if (mCamera != null){ mCamera.release(); mCamera = null; } } @Override protected void onResume() { super.onResume(); if(mCamera == null) { mCamera = getCameraInstance(); mCamera.startPreview(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
晚上看到Camera360开放了SDK的消息。主推拍照和滤镜API,其实开源的滤镜有很多啦,结合我们今天的例子,我们完全可以做出一个不错的相机应用来,作为长期受益于开源社区的回报,我决定在整合滤镜以后把全部的程序开源,希望大家支持我哦。唉,明天要考试了,我竟然还能如此充满激情的编程,其实这就是编程的魅力啦,在编程的世界里,我可以完全按照自己的意愿去做自己喜欢的,而这就够了,当老师和同学们都觉得我在抱怨专业课的时候,其实我只是想让自己的内心感到快乐而已。好了,晚安,各位!