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

一、调用系统相机拍照

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


准备工作


权限

<!-- 往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
  1. <span style="font-size:14px;"><uses-permission android:name="android.permission.CAMERA"/>  
  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/></span>  

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

[java]  view plain  copy
  1. <span style="font-size:14px;">if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {  
  2.             requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, CAMERA_JAVA_REQUEST_CODE);  
  3.         }</span>  

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

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

[java]  view plain  copy
  1. Intent intent = new Intent();  
  2. intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);  
  3. intent.putExtra(MediaStore.EXTRA_OUTPUT, getUri());  
  4. startActivityForResult(intent, CAMERA_REQUEST_CODE);  
其中需要的到给定一个图片保存的Uri

[java]  view plain  copy
  1. private Uri getUri() {  
  2.   
  3.         return Uri.fromFile(getFile());  
  4.     }  
  5.   
  6.     /** 
  7.      * 该方法用于获取指定路径 和 名字 的file 
  8.      * @return 
  9.      */  
  10.     private File getFile() {  
  11.         File filePath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "CameraDemo");  
  12.         if (!filePath.exists()) {  
  13.             filePath.mkdirs();  
  14.         }  
  15.         //将图片保存的名字设置为当前拍照的时间  
  16.         SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");  
  17.         String name = format.format(new Date());  
  18.         File file = new File(filePath.getPath() + File.separator + name + ".jpg");  
  19.         return file;  
  20.   
  21.     }  
3、重写 onActivityResult 获取拍照后返回的信息


[java]  view plain  copy
  1. @Override  
  2.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.         super.onActivityResult(requestCode, resultCode, data);  
  4.         if (requestCode == CAMERA_REQUEST_CODE) {  
  5.             if (resultCode == RESULT_OK) {  
  6.                 Log.d("print""拍照成功");  
  7.             }  
  8.         }  
  9.     }  

二、使用camera进行拍照

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

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

3、为SurfaceView设置回调

[java]  view plain  copy
  1. sf_camera.getHolder().addCallback(new SurfaceHolder.Callback() {  
  2.     @Override  
  3.     public void surfaceCreated(SurfaceHolder holder) {  
  4.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {  
  5.             /** 
  6.              * 请求权限是一个异步任务  不是立即请求就能得到结果 在结果回调中返回 
  7.              */  
  8.             requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);  
  9.         } else {  
  10.             //打开相机 前置摄像头  
  11.             openCamera(front_camera);  
  12.         }  
  13.     }  
  14.   
  15.     @Override  
  16.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  17.         //设置相机自动对焦  
  18.         mcamera.autoFocus(null);  
  19.     }  
  20.   
  21.     @Override  
  22.     public void surfaceDestroyed(SurfaceHolder holder) {  
  23.         //释放相机资源  
  24.         if (mcamera != null) {  
  25.             mcamera.release();  
  26.             mcamera = null;  
  27.         }  
  28.     }  
  29. });  
其中打开相机的方法

[java]  view plain  copy
  1. private void openCamera(int inDex) {  
  2.         mcamera = Camera.open(inDex);  
  3.         try {  
  4.             mcamera.setPreviewDisplay(sf_camera.getHolder());//关联surfaceview,成为相机的预览界面  
  5.             setCamra();  
  6.             mcamera.startPreview();//开启预览  
  7.             mcamera.autoFocus(null);  
  8.         } catch (IOException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.     }  
3、设置屏幕的分辨率

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

[java]  view plain  copy
  1. private void setCamra() {  
  2.       Camera.Parameters parameters = mcamera.getParameters();  
  3.       List<Camera.Size> sizeList = parameters.getSupportedPictureSizes();//得到手机支持的图片的尺寸的集合  
  4.       int size = sizeList.size();  
  5.   
  6.       Camera.Size size1 = sizeList.get(0);  
  7.       Log.d("qf", size1.height + "*" + size1.width + "=" + size1.width * size1.height);  
  8.       Camera.Size size2 = sizeList.get(size - 1);  
  9.       Log.d("qf", size2.height + "*" + size2.width + "=" + size2.width * size2.height);  
  10.       //判断 去最大的尺寸为相机的分辨率  
  11.       if (size1.height * size1.width > size2.width * size2.height) {  
  12.           parameters.setPictureSize(size1.height, size1.width);  
  13.           Log.d("qf", size1.height + "*" + size1.width + "=" + size1.width * size1.height);  
  14.       } else {  
  15.           parameters.setPictureSize(size2.height, size2.width);  
  16.           Log.d("qf", size2.height + "*" + size2.width + "=" + size2.width * size2.height);  
  17.       }  
  18.   
  19.       mcamera.setParameters(parameters);  
  20.       mcamera.setDisplayOrientation(90);//预览结果屏幕飞正常显示 需要旋转屏幕   
  21.   }  


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

[java]  view plain  copy
  1. sf_camera.setOnTouchListener(new View.OnTouchListener() {  
  2.           @Override  
  3.           public boolean onTouch(View v, MotionEvent event) {  
  4.               mcamera.autoFocus(null);  
  5.               return true;  
  6.           }  
  7.       }  

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

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

[java]  view plain  copy
  1. private void takePhotos() {  
  2.         mcamera.takePicture(nullnullnew Camera.PictureCallback() {  
  3.             @Override  
  4.             public void onPictureTaken(byte[] data, Camera camera) {  
  5.                 Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);  
  6.                 Bitmap btmap = rotateBitmap(bitmap);//旋转原bitmap得到新的bitmap  
  7.                 savePic(btmap);  
  8.                 bitmap.recycle();//回收原来的bitmap  
  9.                 mcamera.startPreview();  
  10.             }  
  11.         });  
  12.     }  
  13.       
  14.     //对bitmap进行旋转  
  15.     private Bitmap rotateBitmap(Bitmap bitmap) {  
  16.         Matrix matrix = new Matrix();  
  17.         matrix.reset();  
  18.         if (isBack) {  
  19.             matrix.postRotate(90);//后置摄像头需要进行90度旋转,前置进行-90度旋转  
  20.         } else {  
  21.             matrix.postRotate(-90);  
  22.         }  
  23.         return Bitmap.createBitmap(bitmap, 00, bitmap.getWidth(), bitmap.getHeight(), matrix, false);  
  24.     }  
6、进行图片保存

[java]  view plain  copy
  1. private void savePic(Bitmap bitmap) {  
  2.      File filePath = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "CameraDemo");  
  3.      SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");  
  4.      String name = format.format(new Date());  
  5.      File file = new File(filePath.getPath() + File.separator + name + ".jpg");  
  6.      BufferedOutputStream os;  
  7.      try {  
  8.          os = new BufferedOutputStream(new FileOutputStream(file));  
  9.          bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);//进行图片压缩 保存在file路径下  
  10.          os.flush();  
  11.          os.close();  
  12.      } catch (Exception e) {  
  13.          e.printStackTrace();  
  14.      }  
  15.      Toast.makeText(Main2Activity.this"拍照成功,图片存至" + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();  
  16.  }  
7、设置一个boolean值记录目前相机是为前置或后置

[java]  view plain  copy
  1. boolean isBack = true;//true表示后置  
8、得到系统相机的数量和相机信息,进行相机的切换

[java]  view plain  copy
  1. private void getCameraId() {  
  2.         int numberOfCameras = Camera.getNumberOfCameras();  
  3.         Camera.CameraInfo cameraInfo = new Camera.CameraInfo();  
  4.         for (int i = 0; i < numberOfCameras; i++) {  
  5.             Camera.getCameraInfo(i, cameraInfo);  
  6.             if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {  
  7.                 front_camera = i;  
  8.                 Log.d("print", front_camera + "前");  
  9.             } else {  
  10.                 back_camera = i;  
  11.                 Log.d("print", back_camera + "后");  
  12.             }  
  13.         }  
  14.     }  
在相机切换时需要释放前一个相机
[java]  view plain  copy
  1. private void changeCamera() {  
  2.   
  3.        if (mcamera != null) {  
  4.            mcamera.release();  
  5.            mcamera = null;  
  6.        }  
  7.        if (isBack) {  
  8.            openCamera(front_camera);  
  9.            isBack = false;  
  10.        } else {  
  11.            openCamera(back_camera);  
  12.            isBack = true;  
  13.        }  
  14.    }  

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


三、常见问题

由于国内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


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值