一、调用系统相机拍照
拍照和相册的功能在实际开发中是最常见的功能,这里记录下。
权限
<!-- 往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、需要添加调用相机和外部存储的权限
需要注意的是安卓6.0后使用相机等涉及隐私的权限需要在代码内在申明,可通过以下方法:
2、使用Intent启动第三方相机
可以通过startActivityForResult获取到拍完照片的结果信息。
其中需要的到给定一个图片保存的Uri
3、重写 onActivityResult 获取拍照后返回的信息
二、使用camera进行拍照
1、需要借助SurfaceView来显示,进行拍照预览。在布局文件中添加SurfaceView。
2、对相应权限进行设置,同调用第三方相机。
3、为SurfaceView设置回调
其中打开相机的方法
3、设置屏幕的分辨率
不同机型的分辨率不同,我们这取最大分辨率进行相机预览
4、让相机点击屏幕可以进行聚焦,需要为SurfaceView设置触摸监听
5、预览完成,可以进行拍照
调用相机的takePicture的方法进行拍照,拍照返回数据为一个byte数组,可转为bitmap并进行保存。
6、进行图片保存
7、设置一个boolean值记录目前相机是为前置或后置
8、得到系统相机的数量和相机信息,进行相机的切换
在相机切换时需要释放前一个相机
这样就可以进行相机的拍照和摄像头的切换了
三、常见问题
由于国内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