本文参考google官方api写得例子, 然后也做了一些优化, 参考地址大家请看官方camera api即可.
Google官方入门实例
1. 引入权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.xm.camerasimple"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
2. 打开相机
CameraActivity.java中添加
// 判断相机是否支持
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
return true;
} else {
return false;
}
}
// 获取相机
public static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
e.printStackTrace();
}
return c;
}
3. 创建preview
CameraPreview .java
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null){
return;
}
try {
mCamera.stopPreview();
} catch (Exception e){
e.printStackTrace();
}
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
e.printStackTrace();
}
}
}
4.创建相机视图
activity_camera.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/button_capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:text="拍照" />
</RelativeLayout>
5.在Activity中引用视图
CameraActivity.java
public class CameraActivity extends Activity {
public static final String TAG = "CameraSimple";
private Camera mCamera;
private CameraPreview mPreview;
private FrameLayout mCameralayout;
private Button mTakePictureBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
if (!checkCameraHardware(this)) {
Toast.makeText(CameraActivity.this, "相机不支持", Toast.LENGTH_SHORT)
.show();
} else {
mCamera = getCameraInstance();
mPreview = new CameraPreview(CameraActivity.this, mCamera);
mCameralayout = (FrameLayout) findViewById(R.id.camera_preview);
mCameralayout.addView(mPreview);
}
}
6.添加拍照功能
Android中拍照可通过回调来完成, camera拍照回调如下:
// 拍照回调
private PictureCallback mPictureCallback = new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (pictureDir == null) {
Log.d(TAG,
"Error creating media file, check storage permissions!");
return;
}
try {
String pictureName = new DateFormat().format("yyyyMMddHHmmss",
new Date()).toString()
+ ".png";
FileOutputStream fos = new FileOutputStream(pictureDir
+ File.separator + pictureName);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
添加按钮事件:
mTakePictureBtn = (Button) findViewById(R.id.button_capture);
mTakePictureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.takePicture(null, null, mPictureCallback);
}
});
完成到这里, 就已经实现了最基本的功能, 可以运行你的自定义相机程序看看效果
但是上面的相机功能很明显远远达不到我们想要的效果, 所以我们来尝试一些改进.
添加功能
1. 将相机设置成竖屏
// 设置相机横竖屏
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
在第一次初始化时可在onCreate() 调用.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置无标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_camera);
if (!checkCameraHardware(this)) {
Toast.makeText(CameraActivity.this, "相机不支持", Toast.LENGTH_SHORT)
.show();
} else {
openCamera();
mTakePictureBtn = (Button) findViewById(R.id.button_capture);
mTakePictureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.takePicture(null, null, mPictureCallback);
}
});
setCameraDisplayOrientation(this, mCameraId, mCamera);
}
}
注意我这里添加了全屏选项, 避免相机画面, 注意这两句一定要添加在 setContentView() 之前
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
然后我把打开相机和操作抽取出来, 写成了openCamera() 方法:
// 获取相机实例
public Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(mCameraId);
} catch (Exception e) {
e.printStackTrace();
}
return c;
}
// 开始预览相机
public void openCamera(){
if(mCamera == null){
mCamera = getCameraInstance();
mPreview = new CameraPreview(CameraActivity.this, mCamera);
mCameralayout = (FrameLayout) findViewById(R.id.camera_preview);
mCameralayout.addView(mPreview);
}
}
// 释放相机
public void releaseCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
2. 修改图片保存方向
刚刚的例子中, 点击拍照后就停在那儿了, 如果需要继续预览, 需要在拍照结束后再次调用 startPreview() 方法.
现在拍的照片, 保存到图库中都是横向的, 如果我们要根据当前相机的方向来保存图片怎么办呢? 修改拍照回调的保存图片方法.
// 旋转图片
public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
Bitmap returnBm = null;
Matrix matrix = new Matrix();
matrix.postRotate(degree);
try {
returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(),
bm.getHeight(), matrix, true);
} catch (OutOfMemoryError e) {
}
if (returnBm == null) {
returnBm = bm;
}
if (bm != returnBm) {
bm.recycle();
}
return returnBm;
}
修改拍照回调, 注意这里将保存图片放到了线程中处理, 避免卡顿:
// 拍照回调
private PictureCallback mPictureCallback = new PictureCallback() {
@Override
public void onPictureTaken(final byte[] data, Camera camera) {
File pictureDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
final String picturePath = pictureDir
+ File.separator
+ new DateFormat().format("yyyyMMddHHmmss", new Date())
.toString() + ".jpg";
new Thread(new Runnable() {
@Override
public void run() {
File file = new File(picturePath);
try {
// 获取当前旋转角度, 并旋转图片
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
bitmap = rotateBitmapByDegree(bitmap, 90);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.flush();
bos.close();
bitmap.recycle();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
mCamera.startPreview();
}
};
前置摄像头和后置摄像头的切换
// 切换前置和后置摄像头
public void switchCamera() {
CameraInfo cameraInfo = new CameraInfo();
Camera.getCameraInfo(mCameraId, cameraInfo);
if(cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK){
mCameraId = CameraInfo.CAMERA_FACING_FRONT;
}
else{
mCameraId = CameraInfo.CAMERA_FACING_BACK;
}
mCameralayout.removeView(mPreview);
releaseCamera();
openCamera();
setCameraDisplayOrientation(CameraActivity.this, mCameraId, mCamera);
}
mCameraId
是一个成员变量, 记录当前是前置还是后置摄像头, 程序启动默认初始化为后置摄像头: CameraInfo.CAMERA_FACING_BACK
.
添加了前置摄像头后会发现,我们拍的照片成倒立的了,这与我们前面选择图片有关系,如果是前置摄像头的话, 旋转应该是 -90度. 所以需要添加保存图片时的前置和后置判断:
if (cameraid == CameraInfo.CAMERA_FACING_BACK) {
bitmap = rotateBitmapByDegree(bitmap, 90);
} else {
bitmap = rotateBitmapByDegree(bitmap, -90);
}
为相机添加聚焦功能
android相机中已经提供了聚焦的功能, 使用 camera.autoFocus(null) 方法即可, 如果需要得到聚焦成功或失败的回调,需要自行传入.
我们这里添加两个聚焦, 一个是点击相机画面时只聚焦不拍照, 另外一个是点击拍照按钮, 先聚焦, 聚焦成功后再拍照, 代码如下:
为 mPreview添加聚焦功能
// 开始预览相机
public void openCamera() {
if (mCamera == null) {
mCamera = getCameraInstance();
mPreview = new CameraPreview(CameraActivity.this, mCamera);
mPreview.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
mCamera.autoFocus(null);
return false;
}
});
mCameralayout = (FrameLayout) findViewById(R.id.camera_preview);
mCameralayout.addView(mPreview);
mCamera.startPreview();
}
}
为拍照按钮添加聚焦, 将原来的拍照放到聚焦成功后再调用:
mTakePictureBtn = (ImageView) findViewById(R.id.btn_capture);
mTakePictureBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.autoFocus(mAutoFocusCallback);
}
});
// 聚焦回调
private AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
mCamera.takePicture(null, null, mPictureCallback);
}
}
};
这篇感觉也够长了, 下篇继续, 下面贴以下代码下载地址:
http://download.csdn.net/detail/u013647382/9562136