您的位置:首页 > 其它

关于安卓调用相机有关知识(含6.0新增权限使用)

2016-08-29 10:42 495 查看
一、调用系统相机拍照

拍照和相册的功能在实际开发中是最常见的功能,这里记录下。

准备工作

权限
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.
WRITE_EXTERNAL_STORAGE" />
<!--请求访问使用照相设备-->
<uses-permission android:name="android.permission.CAMERA" />


常量
public final static int ALBUM_REQUEST_CODE = 1;
public final static int CROP_REQUEST = 2;
public final static int CAMERA_REQUEST_CODE = 3;
// 拍照路径
public static String SAVED_IMAGE_DIR_PATH =
Environment.getExternalStorageDirectory().getPath()
+ "/AppName/camera/";
String cameraPath;

相机
// 指定相机拍摄照片保存地址
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
cameraPath = SAVED_IMAGE_DIR_PATH +
System.currentTimeMillis() + ".png";
Intent intent = new Intent();
// 指定开启系统相机的Action
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
String out_file_path = SAVED_IMAGE_DIR_PATH;
File dir = new File(out_file_path);    if (!dir.exists()) {
dir.mkdirs();
}
// 把文件地址转换成Uri格式
Uri uri = Uri.fromFile(new File(cameraPath));
// 设置系统相机拍摄照片完成后图片文件的存放地址
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, CAMERA_REQUEST_CODE);
} else {
Toast.makeText(getApplicationContext(), "请确认已经插入SD卡",
Toast.LENGTH_LONG).show();
}


onActivityResult

拿到cameraPath,就随便你搞了。
@Override

public void onActivityResult(int requestCode,
int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CAMERA_REQUEST_CODE) {
LogUtil.d("path=" + cameraPath);
}
}

相册
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, ALBUM_REQUEST_CODE);


onActivityResult

调用系统相册,然后通过Uri拿到图片的绝对地址。
@Override
public void onActivityResult(int requestCode,
int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == AppConstants.ALBUM_REQUEST_CODE) {
try {

Uri uri = data.getData();
final String absolutePath=
getAbsolutePath(mActivity, uri);

ogUtil.d("path=" + absolutePath);

} catch (Exception e) {
e.printStackTrace();

}
}

}
}


getAbsolutePath方法
public String getAbsolutePath(final Context context,
final Uri uri) {
if (null == uri) return null;
final String scheme = uri.getScheme();
String data = null;
if (scheme == null)
data = uri.getPath();
else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = context.getContentResolver().query(uri,
new String[]{MediaStore.Images.ImageColumns.DATA},
null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(
MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}     return data;
}


二、调用第三方相机进行拍照

1、需要添加调用相机和外部存储的权限

[html] view
plain copy

<span style="font-size:14px;"><uses-permission android:name="android.permission.CAMERA"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/></span>

需要注意的是安卓6.0后使用相机等涉及隐私的权限需要在代码内在申明,可通过以下方法:

[java] view
plain copy

<span style="font-size:14px;">if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, CAMERA_JAVA_REQUEST_CODE);

}</span>

2、使用Intent启动第三方相机

可以通过startActivityForResult获取到拍完照片的结果信息。

[java] view
plain copy

Intent intent = new Intent();

intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, getUri());

startActivityForResult(intent, CAMERA_REQUEST_CODE);

其中需要的到给定一个图片保存的Uri

[java] view
plain copy

private Uri getUri() {

return Uri.fromFile(getFile());

}

/**

* 该方法用于获取指定路径 和 名字 的file

* @return

*/

private File getFile() {

File filePath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "CameraDemo");

if (!filePath.exists()) {

filePath.mkdirs();

}

//将图片保存的名字设置为当前拍照的时间

SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");

String name = format.format(new Date());

File file = new File(filePath.getPath() + File.separator + name + ".jpg");

return file;

}

3、重写onActivityResult获取拍照后返回的信息

[java] view
plain copy

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (requestCode == CAMERA_REQUEST_CODE) {

if (resultCode == RESULT_OK) {

Log.d("print", "拍照成功");

}

}

}

二、使用camera进行拍照

1、需要借助SurfaceView来显示,进行拍照预览。在布局文件中添加SurfaceView。

2、对相应权限进行设置,同调用第三方相机。

3、为SurfaceView设置回调

[java] view
plain copy

sf_camera.getHolder().addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

/**

* 请求权限是一个异步任务 不是立即请求就能得到结果 在结果回调中返回

*/

requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);

} else {

//打开相机 前置摄像头

openCamera(front_camera);

}

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

//设置相机自动对焦

mcamera.autoFocus(null);

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

//释放相机资源

if (mcamera != null) {

mcamera.release();

mcamera = null;

}

}

});

其中打开相机的方法

[java] view
plain copy

private void openCamera(int inDex) {

mcamera = Camera.open(inDex);

try {

mcamera.setPreviewDisplay(sf_camera.getHolder());//关联surfaceview,成为相机的预览界面

setCamra();

mcamera.startPreview();//开启预览

mcamera.autoFocus(null);

} catch (IOException e) {

e.printStackTrace();

}

}

3、设置屏幕的分辨率

不同机型的分辨率不同,我们这取最大分辨率进行相机预览

[java] view
plain copy

private void setCamra() {

Camera.Parameters parameters = mcamera.getParameters();

List<Camera.Size> sizeList = parameters.getSupportedPictureSizes();//得到手机支持的图片的尺寸的集合

int size = sizeList.size();

Camera.Size size1 = sizeList.get(0);

Log.d("qf", size1.height + "*" + size1.width + "=" + size1.width * size1.height);

Camera.Size size2 = sizeList.get(size - 1);

Log.d("qf", size2.height + "*" + size2.width + "=" + size2.width * size2.height);

//判断 去最大的尺寸为相机的分辨率

if (size1.height * size1.width > size2.width * size2.height) {

parameters.setPictureSize(size1.height, size1.width);

Log.d("qf", size1.height + "*" + size1.width + "=" + size1.width * size1.height);

} else {

parameters.setPictureSize(size2.height, size2.width);

Log.d("qf", size2.height + "*" + size2.width + "=" + size2.width * size2.height);

}

mcamera.setParameters(parameters);

mcamera.setDisplayOrientation(90);//预览结果屏幕飞正常显示 需要旋转屏幕

}

4、让相机点击屏幕可以进行聚焦,需要为SurfaceView设置触摸监听

[java] view
plain copy

sf_camera.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

mcamera.autoFocus(null);

return true;

}

}

5、预览完成,可以进行拍照

调用相机的takePicture的方法进行拍照,拍照返回数据为一个byte数组,可转为bitmap并进行保存。

[java] view
plain copy

private void takePhotos() {

mcamera.takePicture(null, null, new Camera.PictureCallback() {

@Override

public void onPictureTaken(byte[] data, Camera camera) {

Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

Bitmap btmap = rotateBitmap(bitmap);//旋转原bitmap得到新的bitmap

savePic(btmap);

bitmap.recycle();//回收原来的bitmap

mcamera.startPreview();

}

});

}

//对bitmap进行旋转

private Bitmap rotateBitmap(Bitmap bitmap) {

Matrix matrix = new Matrix();

matrix.reset();

if (isBack) {

matrix.postRotate(90);//后置摄像头需要进行90度旋转,前置进行-90度旋转

} else {

matrix.postRotate(-90);

}

return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);

}

6、进行图片保存

[java] view
plain copy

private void savePic(Bitmap bitmap) {

File filePath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "CameraDemo");

SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");

String name = format.format(new Date());

File file = new File(filePath.getPath() + File.separator + name + ".jpg");

BufferedOutputStream os;

try {

os = new BufferedOutputStream(new FileOutputStream(file));

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);//进行图片压缩 保存在file路径下

os.flush();

os.close();

} catch (Exception e) {

e.printStackTrace();

}

Toast.makeText(Main2Activity.this, "拍照成功,图片存至" + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();

}

7、设置一个boolean值记录目前相机是为前置或后置

[java] view
plain copy

boolean isBack = true;//true表示后置

8、得到系统相机的数量和相机信息,进行相机的切换

[java] view
plain copy

private void getCameraId() {

int numberOfCameras = Camera.getNumberOfCameras();

Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

for (int i = 0; i < numberOfCameras; i++) {

Camera.getCameraInfo(i, cameraInfo);

if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {

front_camera = i;

Log.d("print", front_camera + "前");

} else {

back_camera = i;

Log.d("print", back_camera + "后");

}

}

}

在相机切换时需要释放前一个相机

[java] view
plain copy

private void changeCamera() {

if (mcamera != null) {

mcamera.release();

mcamera = null;

}

if (isBack) {

openCamera(front_camera);

isBack = false;

} else {

openCamera(back_camera);

isBack = true;

}

}

这样就可以进行相机的拍照和摄像头的切换了

三、常见问题

由于国内Android机型繁多,各家都自己的rom,调用系统的还是会出现不少问题:

一、拍照照片被旋转

这种情况是使用Camera拍照以后,得到的照片会被自动旋转(90°、180°、270°)。解决方案:

1、读取图片的旋转属性

/**
* 读取图片的旋转的角度
*
* @param path
*            图片绝对路径
* @return 图片的旋转角度
*/

private int getBitmapDegree(String path) {
int degree = 0;
try {
// 从指定路径下读取图片,并获取其EXIF信息
ExifInterface exifInterface = new ExifInterface(path);
// 获取图片的旋转信息
int orientation = exifInterface.
getAttributeInt(ExifInterface.
TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}    return degree;
}


2、将图片按照某个角度进行旋转
/**
* 将图片按照某个角度进行旋转
*
* @param bm
*            需要旋转的图片
* @param degree
*            旋转角度
* @return 旋转后的图片
*/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;
}


详见博客:https://www.baidufe.com/item/4bb733d9999c53cb8fed.html

二、调用系统相册,拿不到绝对路径

上篇的方法,我在大部分手机测试是可行,但是一些机型还是拿不到,解决方案:
public class AbsolutePathUtil {
public static String getAbsolutePath(final Context context
, final Uri uri) {
// DocumentProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
&& DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract
.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract
.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];

Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images
.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video
.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio
.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri
, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context       The context.
* @param uri           The Uri to query.
* @param selection     (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context
, Uri uri, String selection, String[] selectionArgs) {

Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};        try {
cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs,null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor
.getColumnIndexOrThrow(column);
return cursor.getString(column_index);

}
} finally {
if (cursor != null)
cursor.close();
}        return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents"
.equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents"
.equals(uri.getAuthority());
}
}


详见stackoverflow:http://stackoverflow.com/questions/13209494/how-to-get-the-full-file-path-from-uri

三、裁剪

拿到图片的绝对路径,有可能需要对它进行裁剪,这里当然也可以调用系统的裁剪,不过我推荐UCrop,功能好强大,简单的使用方法:

/**
* 启动裁剪
*/
public static String startUCrop(Activity activity,
String sourceFilePath, int requestCode,
float aspectRatioX, float aspectRatioY) {
Uri sourceUri = Uri.fromFile(new File(sourceFilePath));
File outDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
if (!outDir.exists()) {
outDir.mkdirs();
}
File outFile = new File(outDir,
System.currentTimeMillis() + ".jpg");
String cameraScalePath = outFile.getAbsolutePath();
Uri destinationUri = Uri.fromFile(outFile);
UCrop uCrop = UCrop.of(sourceUri, destinationUri);
UCrop.Options options = new UCrop.Options();
options.setAllowedGestures(UCropActivity.SCALE,
UCropActivity.ROTATE, UCropActivity.ALL);
options.setHideBottomControls(true);
options.setFreeStyleCropEnabled(true);
uCrop.withOptions(options);
uCrop.withAspectRatio(aspectRatioX, aspectRatioY);
uCrop.start(activity, requestCode);
return cameraScalePath;
}


详见github:https://github.com/Yalantis/uCrop
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐