为在应用程序中拍摄照片,最简单的方法就是使用 MediaStore.ACTION_IMAGE_CAPTURE动作触发一个Intent:
//请求码
public static final int TAKE_PICTURE = 100;
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),TAKE_PICTURE);
用户对拍摄的照片满意后,该照片就会通过onActivityResult处理程序收到的Intent返回给应用程序
默认情况下,拍摄的照片将会作为一个缩略图返回,通过返回的Inetnt的data extra 可以访问原始位图
要获得完整图像,必须指定一个用于存储该图像的目标文件,该文件将会被编码为一个URI,并在启动Inent中使用MediaStore.EXTRA_OUTPUT extra 传入该URI
//目标输出文件URI
private Uri outputFileUri ;
public void getOriginCapture() {
//获取输出文件
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
Log.e("output_file", file.toString());
outputFileUri = Uri.fromFile(file);
//生成Intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
//启动摄像头拍照
startActivityForResult(intent, TAKE_PICTURE);
}
代码:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == TAKE_PICTURE) {
//检查结果是否包含缩略图
if (data != null) {
if (data.hasExtra("data")) {
Bitmap thumbnail = data.getParcelableExtra("data");
iv.setImageBitmap(thumbnail);
}
} else {//如果不是缩略图的话,接受的Intent 即data == null,说明目标存储在目标输出URI中
//计算待填充的view的大小。
int width = iv.getWidth();
int height = iv.getHeight();
BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = true;//只加载基本图片的信息。
Bitmap boundBitmap = BitmapFactory.decodeFile(outputFileUri.getPath(), ops);
int xRate = ops.outWidth / width;
int yRate = ops.outHeight / height;
//获取到最总缩放倍数 r 越大, 图片越小
// 一般是2的倍数 r = 2 ,缩小到原图的1/4.-->依次类推
int r = Math.min(xRate, yRate);
ops.inSampleSize = r;
ops.inJustDecodeBounds = false;
ops.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(outputFileUri.getPath(), ops);
iv.setImageBitmap(bitmap);
}
}
}
我们可以将图片保存到媒体库中,方便其他应用程序调用。
//从外部存储读取的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
//向外部存储写入的权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
//为了直接访问摄像头,需要在应用程序的manifest文件中添加CAMERA权限
<uses-permission android:name="android.permission.CAMERA"/>
Camera
类的静态方法open
方法。 import android.hardware.Camera:
//打开并初始化摄像头。
//此时可以修改配置,配置预览Surface和拍摄照片
Camera camera = Camera.open();
release
来释放camera资源。 //释放摄像头资源
camera.release();
2.2.1:摄像头属性
通过调用Camera对象的
getParameters
方法可以得到CamearParameter对象,然后可以使用该对象存储摄像头设置。
/**
* 初始化摄像头。
*/
private void initCamera() {
//打开摄像头并初始化,获取一个实例。
camera = Camera.open();
//获取摄像头属性集
Camera.Parameters parameters = camera.getParameters();
//使用摄像头可以获取摄像头的许多属性和对焦的场景。
//android 2.2(API level 8)中引入的getFocalLength 和 get[Horizontal/Vertical]ViewAngle方法
//分别可以得到焦距和相关的水平或垂直视角
float focalLength = parameters.getFocalLength();
float horizontalViewAngle = parameters.getHorizontalViewAngle();
float verticalViewAngle = parameters.getVerticalViewAngle();
//android 2.3(API level 9) 中引入 getFocusDistances()方法,用于估算镜头和当前被对焦的物体之间的距离。
//这个方法并不返回值,而是填充一个与近、远和最佳距离对应的浮点数组。
float[] focusDistances = new float[3];
parameters.getFocusDistances(focusDistances);
//近
float near = focusDistances[Camera.Parameters.FOCUS_DISTANCE_NEAR_INDEX];
//最佳
float optimal = focusDistances[Camera.Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX];
//远
float far = focusDistances[Camera.Parameters.FOCUS_DISTANCE_FAR_INDEX];
Log.e("printer", sb.toString());
}
结果图
2.2.2. 摄像头设置和图像参数
要更改摄像头设置,需要使用set*方法来修改Parameters参数。Android 2.0 引入了大量的摄像头参数,每个参数都有对应的setter和getter方法。在修改任何摄像头的参数之前,确定设备商的摄像头实现支持这种修改该十分重要。
camera.setParameters(parameters);
如果要替换原生Camera应用程序,下面的大部分参数都很有用。
Camera.Parameters 方法名【set/get】 | 方法说明 |
---|---|
SceneMode | 使用一个SCENE_MODE_*静态常量返回或设置所拍摄的场景的类型。每个场景模式都为特定的场景类型(聚会、海滩、落日等)优化了摄像头参数的配置。 |
FLASHMODE | 使用FLASH_MODE_*静态常量返回或设置当前的闪光模式(通常为“打开”、“关闭”、“红眼消除”、“闪光灯”模式) |
WhiteBalance | 使用WHITE_BALANCE_*静态常量返回或设置白平衡矫正来矫正场景。在设置白平衡之前,用【getSupportedWhiteBalance】方法来确认那些设置可用。 |
AutoWhiteBalanceLock | android 4.0引入。当使用自动白平衡时,启用自动白平衡锁会暂停颜色矫正算法,从而确保连续拍摄多张照片使用相同的颜色平衡设置。当拍摄全景照片或者为高动态光照渲染图象使用包围曝光时,这种方法特别有用。使用【isAutoWhiteBalanceLockSupported】方法可以确认设备是否支持这种功能。 |
ColorEffect | 使用一个Effect_*静态常量返回或设置应用到图像的特殊颜色效果。可用的颜色效果(包括 深褐色调、多色调分色印或者黑板效果)随设备和平台而异。 |
FocusMode | 使用 FOCUS_MODE_*静态常量或者设置摄像头尝试对焦的方式。(连续自动对焦在android 4.0引入)使用【getSupportedFocusModes】方法可以找出可用的模式 |
Antibanding | 使用一个ANTIBANDING_*静态常量或设置用来降低条带效果的屏幕刷新频率。使用【getSupprotedAntibanding】方法可以找出可用的频率。 |
Camera.Parameters 方法名【set/get】 | 方法说明 |
---|---|
JPEG和缩略图质量 | 使用【setJpegQuality】和【setJpegThumbnailQuality】方法,并传入0~100之间的整数,其中100表示最佳质量。 |
图像、预览和缩略图大小 | 分别使用【setPictureSize】,【setPreviewSize】,【setJpegThumbnailSize】参数指定图像‘预览和缩略图的高度和宽度。对于每种情况,分别使用【getSupportedPictureSizes】,【getSupportedPreviewSizes】,【getSupportedJpegThumbnailSizes】方法来确定有效的值。每个方法返回一个指定了有效高度/宽度的Camera.Size对象的列表。 |
图像和预览像素格式 | 使用PixelFormat类中的要给静态常量调用【setPictureFormat】和【setPreviewFormat】可以设置图像的格式。在使用这两个setter方法之前,可以使用【getSupportedPictureFormats】和【getSupportedPreviewFormats】方法返回支持格式的列表。 |
预览帧速率 | 【setPreviewFpsRange】方法取代了已在Android 2.3(API level 9)中启用的setPreviceFrameRate方法。它可以用来指定预览的首选帧率范围。使用【getSupportedPreviewFpsRange】可以找出所支持的最低和最高帧率。【这两个方法都采用一个整数乘以1000的形式表示帧率,所以帧率的范围24~30 表示为 24000 ~ 30000】 |
2.2.3:控制自动对焦、对焦区域和测光区域
如果设备的摄像头支持自动对焦,那么可以通过调用【setFocusMode】方法并传入一个Camera.Parameters.FOCUS_MODE_*常量来指定对焦模式。可用的对焦模式取决于具体的硬件的能力以及硬件上运行的android平台。使用【getSupportedFocusModes】方法可以找出可用的对焦模式。
autofocus
方法启动自动对焦,并指定AutoFocusCallback实现。public void method_demo{
Camera.Parameters parameters = camera.getParameters();
if(parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
//设置自动对焦监听回调。
camera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
Log.e(TAG, "对焦成功:" + (success ? "Successed" : "Failed"));
}
});
}
Android 4.0(API level 14)引入了另外两个对焦API,用于在对焦图像或者确定场景的白平衡和亮度时指定对焦区域和测光区域。
并不是所有的设备都支持定义对焦区域,因此需要使用Camera.Parameters的 getMaxNumFocusAreas 方法来确定设备是否支持该功能
int focusAreaCount = camera.getParameters().getMaxNumFocusAreas();
setFocusAreas
方法,并传入一个Camera.Area对象的列表。每一个Camera Area都有一个矩形,该矩形定义了相对于当前可见场景的对焦区域的边界(在-1000~1000)之间,从左上角开始测量。以及该聚焦区域的重要性的权值所组成。当尝试对焦场景时,摄像头驱动程序会把每个对焦区域的面积与其权值相乘以计算出每个区域的相对权重。setMeteringAreas
方法以同样的方式设置测光区域。与对焦区域类似,并不是所有的设备都支持测光区域–使用getMaxNumMeteringAreas
来确定摄像头是否支持一个或更多个测光区域。 //获取最大对焦区域数
List list = null;
int focusAreaCount = camera.getParameters().getMaxNumFocusAreas();
//获取最大测光区域数
int maxNumMeteringAreas = camera.getParameters().getMaxNumMeteringAreas();
if (focusAreaCount != 0 || maxNumMeteringAreas != 0) {
list = new ArrayList<>();
Rect rect = new Rect(0, 0, 100, 100);
Rect rect1 = new Rect(100, 100, 200, 200);
Camera.Area area = new Camera.Area(rect, 1);
Camera.Area area1 = new Camera.Area(rect1, 2);
list.add(area);
list.add(area1);
}
if (focusAreaCount != 0)
camera.getParameters().setFocusAreas(list);//设置对焦区域
if (maxNumMeteringAreas != 0)
camera.getParameters().setMeteringAreas(list);//设置测光区域
2.2.4: 使用摄像头预览
在实现自己的摄像头时,需要显示摄像头捕获的内容的一个预览,以便用户可以选择拍摄什么样的照片。不先使用一个预览,是无法使用Camera对象拍摄照片的
startPreview
将开始流式传输,调用stopPreview
将结束流式传输。//SurfaceHolder.Callback的接口方法定义
/**
* A client may implement this interface to receive information about
* changes to the surface. When used with a {@link SurfaceView}, the
* Surface being held is only available between calls to
* {@link #surfaceCreated(SurfaceHolder)} and
* {@link #surfaceDestroyed(SurfaceHolder)}. The Callback is set with
* {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.
*/
public interface Callback {
/**
* This is called immediately after the surface is first created.
* Implementations of this should start up whatever rendering code
* they desire. Note that only one thread can ever draw into
* a {@link Surface}, so you should not draw into the Surface here
* if your normal rendering will be in another thread.
*
* @param holder The SurfaceHolder whose surface is being created.
*/
public void surfaceCreated(SurfaceHolder holder);
/**
* This is called immediately after any structural changes (format or
* size) have been made to the surface. You should at this point update
* the imagery in the surface. This method is always called at least
* once, after {@link #surfaceCreated}.
*
* @param holder The SurfaceHolder whose surface has changed.
* @param format The new PixelFormat of the surface.
* @param width The new width of the surface.
* @param height The new height of the surface.
*/
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height);
/**
* This is called immediately before a surface is being destroyed. After
* returning from this call, you should no longer try to access this
* surface. If you have a rendering thread that directly accesses
* the surface, you must ensure that thread is no longer touching the
* Surface before returning from this function.
*
* @param holder The SurfaceHolder whose surface is being destroyed.
*/
public void surfaceDestroyed(SurfaceHolder holder);
}
CameraPreviewActivity.java
“`
package com.aaron.anew;
import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
/**
* Created by pc on 2016/8/29.
* 摄像头预览
*/
public class CameraPreviewActivity extends Activity implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreviewActivity";
private Camera camera;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preview);
init();
}
private void init() {
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface_view);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.setFixedSize(400, 300);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
camera.stopPreview();
}
@Override
protected void onResume() {
super.onResume();
if (camera == null)
camera = Camera.open();
}
@Override
protected void onPause() {
super.onPause();
if (camera != null)
camera.release();
}
}
未完