在Android 一般拍照主要有两种方式,一种是调用系统相机,拍照之后获取路径显示。而另外一种则是调用Camera 然后去拍照。
Ok,那么今天讲的是后者.主要是自己编写代码去完成拍照功能。
首先我们需要建一个 CameraPreview 类 ,当然也需要继承 SurfaceView 并实现 SurfaceHolder.Callback接口,
那么该类主要是用作与显示拍照预览的。
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 相机图片预览类
*
* @author
*
*/
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback
{
public SurfaceHolder mHolder;
private Camera mCamera;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
public CameraPreview(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init();
}
public CameraPreview(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public CameraPreview(Context context)
{
super(context);
init();
}
/**
* 初始化工作
*
*/
private void init()
{
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
// 设置相机
public void setCamera(Camera camera)
{
mCamera = camera;
if (mCamera != null)
{
mSupportedPreviewSizes = mCamera.getParameters()
.getSupportedPreviewSizes();
requestLayout();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
// The Surface has been created, now tell the camera where to draw the
// preview.
try
{
if (null != mCamera)
{
mCamera.setPreviewDisplay(holder);
}
} catch (IOException e1)
{
e1.printStackTrace();
}
try
{
if (null != mCamera)
{
mCamera.startPreview();
}
} catch (Exception e)
{
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (null == mHolder.getSurface())
{
// preview surface does not exist
return;
}
// stop preview before making changes
try
{
if (null != mCamera)
{
mCamera.stopPreview();
}
} catch (Exception e)
{
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
if (null != mCamera)
{
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(90);
}
// 这里可以用来设置尺寸
// start preview with new settings
try
{
if (null != mCamera)
{
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
}
} catch (Exception e)
{
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
if (null != mCamera)
{
mCamera.stopPreview();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// We purposely disregard child measurements because act as a
// wrapper to a SurfaceView that centers the camera preview instead
// of stretching it.
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null)
{
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h)
{
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
if (sizes == null)
return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes)
{
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null)
{
minDiff = Double.MAX_VALUE;
for (Size size : sizes)
{
if (Math.abs(size.height - targetHeight) < minDiff)
{
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
完成该类之后,我们就需要编写如何打开相机,并如何拍照了。
首先你可以自己定义一个布局,然后搞上该类。但今天不写布局,直接用代码生成。
ok ,那么我们需要先定义一下需要用到的变量。
private Camera mCamera; // 相机
int mDefaultCameraId; // 默认相机ID
private CameraPreview mPreview; // 集成SurfaceView 的类
int mScreenWidth, mScreenHeight; // 屏幕宽和高
FrameLayout preview; // 布局
private File saveVideoFile; // 保存文件
然后我们开始生成布局文件,这里当然需要在onCreate方法中完成
// 无标题栏的窗口
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 选择支持半透明模式,在有surfaceview的activity中使用。
getWindow().setFormat(PixelFormat.TRANSLUCENT);
// 得到屏幕的大小
WindowManager wManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = wManager.getDefaultDisplay();
mScreenHeight = display.getHeight();
mScreenWidth = display.getWidth();
mPreview = new CameraPreview(this);
preview = new FrameLayout(getApplicationContext());
// 将相机预览图加入帧布局里面
preview.addView(mPreview, 0);
setContentView(preview);
<pre style="background-color:#ffffff;color:#000000;font-family:'宋体';font-size:9.0pt;"><span style="background-color:#ffe4ff;"> mDefaultCameraId</span> = CameraUtils.<span style="font-style:italic;">getDefaultCameraId</span>();
ok,生成这些之后,遵照面向对象的理念,我们将操作相机的方法进行封装 CameraUtils 类
import android.hardware.Camera;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by on 16-2-29.
*/
public class CameraUtils {
/**
* 获取默认的相机
* @return
*/
public static int getDefaultCameraId(){
int defaultId = -1;
// Find the total number of cameras available
int mNumberOfCameras = Camera.getNumberOfCameras(); // 得到摄像头个数
// Find the ID of the default camera
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < mNumberOfCameras; i++)
{
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
{
defaultId = i;
}
}
if (-1 == defaultId)
{
if (mNumberOfCameras > 0)
{
// 如果没有后向摄像头
defaultId = 0;
} else
{
//没有摄像头
return -1;
// 没有摄像头
}
}
return defaultId;
}
/**
* 打开相机
* @param cameraId 镜头ID 0位前置 1为后置
* @param exception 错误信息, 如果打开错误 则赋值给他
* @return 不为NULL 则打开成功
*/
public static Camera getCameraInstance(int cameraId,Exception exception)
{
Camera c = null;
try
{
c = Camera.open(cameraId); // attempt to get a Camera instance
if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
{
Camera.Parameters params = c.getParameters();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); // 设置聚焦方式
c.setParameters(params);
}
} catch (Exception e)
{
// Camera is not available (in use or does not exist)
e.printStackTrace();
exception = e;
return null;
}
return c; // returns null if camera is unavailable
}
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
/**
* 获取输出文件类型
* @param type 1 Image 2为VIDEO
* @param saveFilePath 保存文件路径 需要在尾加上/
* @return File 类 错误则为空
*/
public static File getOutputMediaFile(int type,String saveFilePath)
{
File mediaStorageDir = null;
try
{
mediaStorageDir = new File(saveFilePath);
} catch (Exception e)
{
e.printStackTrace();
}
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists())
{
if (!mediaStorageDir.mkdirs())
{
// 在SD卡上创建文件夹需要权限:
// <uses-permission
// android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE)
{
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO)
{
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");
} else
{
return null;
}
return mediaFile;
}
/**
* 释放相机
* @param mCamera 相机
* @throws Exception
*/
public static void releaseCamera(Camera mCamera) throws Exception {
if (mCamera != null)
{
mCamera.setPreviewCallback(null);
mCamera.stopPreview();// 停掉原来摄像头的预览
Camera.Parameters p = mCamera.getParameters();
if (p.getFlashMode().equals(Camera.Parameters.FLASH_MODE_TORCH))
p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
p.setZoom(0);
mCamera.setParameters(p);
mCamera.release();
mCamera = null;
}
}
}
ok,那么现在开始打开相机
mCamera = CameraUtils.getCameraInstance(mDefaultCameraId=0,mException);
if(mCamera == null){
Log.v(TAG,"open Camera Error");
return ;
}
mPreview.setCamera(mCamera);
mCamera.startPreview();
完成这步之后,大致上就可以预览相机了。那么开始拍照、
写一个内部类
/**
* 拍照
*/
private Camera.PictureCallback mPicture = new Camera.PictureCallback()
{
@Override
public void onPictureTaken(byte[] data, Camera camera)
{
File pictureFile = CameraUtils.getOutputMediaFile(CameraUtils.MEDIA_TYPE_IMAGE,saveFilePath);
if (pictureFile == null)
{
return;
}
try
{
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e)
{
Log.d(TAG, e.getMessage());
} catch (IOException e)
{
Log.d(TAG, e.getMessage());
}
// 拍照后重新开始预览
mCamera.stopPreview();
mCamera.startPreview();
}
};
那么拍照可以直接调用方法
mCamera.takePicture(null, null, mPicture);
那么到这里就完成一个拍照功能的应用了。