既然都已经学了如何打开Camera,那么就顺着官方API的引导,实现一下Camera拍照的功能吧!
百度了一下Android Camera拍照的功能实现,其中yanzi1225627是我认为写得比较好的,所以我也是照着他的思路实现的拍照功能实现,但是有个问题是,我按博主的代码实现后,SurfaceView中预览不到,其中的原因在我上一篇博文有讲解到,所以在这里,我对其代码进行了简单的修改后,就没有问题了!
首先是布局文件,我并没有作任何修改:
<RelativeLayout 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"
tools:context="org.hwm.app.photograph.PhotographActivity" >
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<org.hwm.app.photograph.PhotographSurface
android:id="@+id/surfaceview"
android:layout_width="0dip"
android:layout_height="0dip" />
</FrameLayout>
<ImageButton
android:id="@+id/btn_shutter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dip"/>
</RelativeLayout>
其次是AndroidManifest.xml文件,需要注意的是,在使用autofocus时,应该再加个android:required="false",不然事实证明,并不能聚焦,所以修改如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.hwm.app.photograph"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".PhotographActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
下面是Java代码的实现部分,先让我们来看一下工具类的实现,工具类还是一模一样的:
1. ImageUtil.java : 对得到的照片进行旋转处理。
package org.hwm.app.util;
import android.graphics.Bitmap;
import android.graphics.Matrix;
public class ImageUtil {
/**
* Rotate Bitmap
* @param b
* @param rotateDegree
* @return
*/
public static Bitmap getRotateBitmap(Bitmap b, float rotateDegree){
Matrix matrix = new Matrix();
matrix.postRotate((float)rotateDegree);
Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, false);
return rotaBitmap;
}
}
2. FileUtil.java:设置照片的保存路径。
package org.hwm.app.util;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.os.Environment;
import android.util.Log;
public class FileUtil {
private static final String TAG = "FileUtil";
private static final File parentPath = Environment.getExternalStorageDirectory();
private static String savePath = "";
private static final String SAVE_FOLDER_NAME = "Photograph";
/**Initiate savePath
* @return
*/
private static String initPath(){
if(savePath.equals("")){
savePath = parentPath.getAbsolutePath()+"/" + SAVE_FOLDER_NAME;
File f = new File(savePath);
if(!f.exists()){
f.mkdir();
}
}
return savePath;
}
/**save Bitmap to sdcard
* @param b
*/
public static void saveBitmap(Bitmap b){
String path = initPath();
long dataTake = System.currentTimeMillis();
String jpegName = path + "/" + dataTake +".jpg";
Log.i(TAG, "saveBitmap:jpegName = " + jpegName);
try {
FileOutputStream fos = new FileOutputStream(jpegName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
b.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
Log.i(TAG, "saveBitmap successed");
} catch (IOException e) {
// TODO Auto-generated catch block
Log.i(TAG, "saveBitmap failed");
e.printStackTrace();
}
}
}
3. DisplayUtil
package org.hwm.app.util;
import android.content.Context;
import android.graphics.Point;
import android.util.DisplayMetrics;
import android.util.Log;
public class DisplayUtil {
private static final String TAG = "DisplayUtil";
/**
* dip to px
* @param context
* @param dipValue
* @return
*/
public static int dip2px(Context context, float dipValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(dipValue * scale + 0.5f);
}
/**
* px to dip
* @param context
* @param pxValue
* @return
*/
public static int px2dip(Context context, float pxValue){
final float scale = context.getResources().getDisplayMetrics().density;
return (int)(pxValue / scale + 0.5f);
}
/**
* Obtain the width and height of screen
* @param context
* @return
*/
public static Point getScreenMetrics(Context context){
DisplayMetrics dm =context.getResources().getDisplayMetrics();
int w_screen = dm.widthPixels;
int h_screen = dm.heightPixels;
Log.i(TAG, "Screen---Width = " + w_screen + " Height = " + h_screen + " densityDpi = " + dm.densityDpi);
return new Point(w_screen, h_screen);
}
/**
* Obtain the rate of width and height
* @param context
* @return
*/
public static float getScreenRate(Context context){
Point P = getScreenMetrics(context);
float H = P.y;
float W = P.x;
return (H/W);
}
}
4. CamParamUtil.java :解析Camera的参数
package org.hwm.app.util;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
public class CamParaUtil {
private static final String TAG = "CamParaUtil";
private CameraSizeComparator sizeComparator = new CameraSizeComparator();
private static CamParaUtil myCamPara = null;
private CamParaUtil(){
}
public static CamParaUtil getInstance(){
if(myCamPara == null){
myCamPara = new CamParaUtil();
}
return myCamPara;
}
public Size getPropPreviewSize(List<Camera.Size> list, float th, int minWidth){
Collections.sort(list, sizeComparator);
int i = 0;
for(Size s:list){
if((s.width >= minWidth) && equalRate(s, th)){
Log.i(TAG, "PreviewSize:w = " + s.width + "h = " + s.height);
break;
}
i++;
}
if(i == list.size()){
i = 0;//set the minimum one
}
return list.get(i);
}
public Size getPropPictureSize(List<Camera.Size> list, float th, int minWidth){
Collections.sort(list, sizeComparator);
int i = 0;
for(Size s:list){
if((s.width >= minWidth) && equalRate(s, th)){
Log.i(TAG, "PictureSize : w = " + s.width + "h = " + s.height);
break;
}
i++;
}
if(i == list.size()){
i = 0;//set the minimum one
}
return list.get(i);
}
public boolean equalRate(Size s, float rate){
float r = (float)(s.width)/(float)(s.height);
if(Math.abs(r - rate) <= 0.03)
{
return true;
}
else{
return false;
}
}
public class CameraSizeComparator implements Comparator<Camera.Size>{
@Override
public int compare(Size lhs, Size rhs) {
if(lhs.width == rhs.width){
return 0;
}
else if(lhs.width > rhs.width){
return 1;
}
else{
return -1;
}
}
}
/**Print previewSizes
* @param params
*/
public void printSupportPreviewSize(Camera.Parameters params){
List<Size> previewSizes = params.getSupportedPreviewSizes();
for(int i=0; i< previewSizes.size(); i++){
Size size = previewSizes.get(i);
Log.i(TAG, "previewSizes:width = "+size.width+" height = "+size.height);
}
}
/**Print pictureSizes
* @param params
*/
public void printSupportPictureSize(Camera.Parameters params){
List<Size> pictureSizes = params.getSupportedPictureSizes();
for(int i=0; i< pictureSizes.size(); i++){
Size size = pictureSizes.get(i);
Log.i(TAG, "pictureSizes:width = "+ size.width
+" height = " + size.height);
}
}
/**Print supportFocusMode
* @param params
*/
public void printSupportFocusMode(Camera.Parameters params){
List<String> focusModes = params.getSupportedFocusModes();
for(String mode : focusModes){
Log.i(TAG, "focusModes--" + mode);
}
}
}
5. PhotographSurface.java :和原博主的SurfaceView不同,类中,我通过setCamera方法为PhotographSurface类传入Camera对象,并在surfaceCreated的方法中为传入的Camera设置Holder,这也是我和原博主的主要不同。
package org.hwm.app.photograph;
import android.content.Context;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class PhotographSurface extends SurfaceView implements SurfaceHolder.Callback{
private static final String TAG = "PhotographSurface";
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
public PhotographSurface(Context context) {
super(context);
init();
}
/** if you want to create a View in the layout, you have to implement the constructor with two parameters **/
public PhotographSurface(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PhotographSurface(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
//mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
}
public void setCamera(Camera camera) {
mCamera = camera;
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder arg0) {
try
{
if( mCamera != null )
{
mCamera.setPreviewDisplay( mSurfaceHolder );
}
}
catch( Exception exception )
{
Log.d( TAG, "SurfaceCreated failed" );
}
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
}
}
6. SingleCamera.java:Camera的单例模式,这里我去掉了Camera在open时的回调,主要是因为,我原博主在回调的方法中就开始startPreview了,而我需要在这之间为创建的PhotographSurface对象传入Camera,并设置Holder,这样才能在PhotographSurface中正常的预览。
package org.hwm.app.photograph;
import org.hwm.app.util.CamParaUtil;
import org.hwm.app.util.FileUtil;
import org.hwm.app.util.ImageUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.util.Log;
public class SingleCamera {
private static final String TAG = "SingleCamera";
private static SingleCamera cameraInstance;
private Camera mCamera;
private Camera.Parameters mCameraParams;
private boolean isPreviewing = false;
private SingleCamera(){};
public static SingleCamera getInstance(){
if (cameraInstance == null) {
synchronized(SingleCamera.class) {
cameraInstance = new SingleCamera();
}
}
return cameraInstance;
}
public void doOpenCamera(){
Log.i(TAG, "Camera open...");
mCamera = Camera.open();
}
/**
* start preview
*/
public void doStartPreview(float previewRate){
Log.i(TAG, "start preview...");
if (isPreviewing) {
mCamera.stopPreview();
return;
}
if (mCamera != null) {
// get Camera's parameters and reset it
mCameraParams = mCamera.getParameters();
mCameraParams.setPictureFormat(ImageFormat.JPEG);
Camera.Size pictureSize = CamParaUtil.getInstance().getPropPictureSize(mCameraParams.getSupportedPictureSizes(), previewRate, 800);
Camera.Size previewSize = CamParaUtil.getInstance().getPropPreviewSize(mCameraParams.getSupportedPreviewSizes(), previewRate, 800);
mCameraParams.setPictureSize(pictureSize.width, pictureSize.height);
mCameraParams.setPreviewSize(previewSize.width, previewSize.height);
mCamera.setParameters(mCameraParams);
// set display orientation
mCamera.setDisplayOrientation(90);
// start preview
mCamera.startPreview();
isPreviewing = true;
}
}
/**
* stop preview, and release Camera that other app can use it
*/
public void doStopCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
isPreviewing = false;
mCamera.release();
mCamera = null;
}
}
/**
* photoghraph
*/
public void doTakePicture() {
if (isPreviewing && (mCamera != null)) {
mCamera.takePicture(mShutterCallback, mPictureCallback, mJpegPictureCallback);
}
}
// the callback for image capture moment, or null
ShutterCallback mShutterCallback = new ShutterCallback() {
@Override
public void onShutter() {
Log.i(TAG, "ShutterCallback...");
}
};
// the callback for raw (uncompressed) image data, or null
PictureCallback mPictureCallback = new PictureCallback() {
@Override
public void onPictureTaken(byte[] arg0, Camera arg1) {
Log.i(TAG, "PictureCallback...");
}
};
// the callback for JPEG image data. set the path to save picture
PictureCallback mJpegPictureCallback = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.i(TAG, "JpegPictureCallback...");
Bitmap b = null;
// to take a picture, we have to stop preview
if (null != data) {
b = BitmapFactory.decodeByteArray(data, 0, data.length);
mCamera.stopPreview();
isPreviewing = false;
}
// save the picture to sdcard
if (null != b) {
Bitmap rotateBitmap = ImageUtil.getRotateBitmap(b, 90f);
FileUtil.saveBitmap(rotateBitmap);
}
// after saving picture, start preview
mCamera.startPreview();
isPreviewing = true;
}
};
public Camera getCamera() {
return this.mCamera;
}
}
7. PhotographActivity.java:在我的Activity中,大家可以看到我在执行doOpenCamera()和doStartPreview()方法之间,为SurfaceView传入了Camera。
package org.hwm.app.photograph;
import org.hwm.app.util.DisplayUtil;
import android.app.Activity;
import android.graphics.Point;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageButton;
public class PhotographActivity extends Activity {
private PhotographSurface mCameraSurface;
private ImageButton btn_shutter;
private float previewRate = -1f;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photograph);
initView();
initParams();
btn_shutter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_shutter :
SingleCamera.getInstance().doTakePicture();
break;
default :
break;
}
}
});
}
private void initView() {
mCameraSurface = (PhotographSurface) findViewById(R.id.surfaceview);
btn_shutter = (ImageButton) findViewById(R.id.btn_shutter);
}
private void initParams() {
LayoutParams params = mCameraSurface.getLayoutParams();
Point p = DisplayUtil.getScreenMetrics(this);
params.width = p.x;
params.height = p.y;
previewRate = DisplayUtil.getScreenRate(this);
mCameraSurface.setLayoutParams(params);
LayoutParams p2 = btn_shutter.getLayoutParams();
p2.width = DisplayUtil.dip2px(this, 80);
p2.height = DisplayUtil.dip2px(this, 80);;
btn_shutter.setLayoutParams(p2);
}
@Override
public void onResume() {
super.onResume();
SingleCamera.getInstance().doOpenCamera();
mCameraSurface.setCamera(SingleCamera.getInstance().getCamera());
SingleCamera.getInstance().doStartPreview(previewRate);
}
}
总之,实现Camera的拍照也是很简单的,但最好还是先看一下官方API给的流程。而对于原博主的代码,我不知道为什么原博主可以正常预览,而我的不行,所以在这里做了这些修改,以供以后查阅使用!