学而实习之 不亦乐乎

Android 打开相册获取相册图片

2023-09-25 06:37:46

系统:Android 11

在Android的开发过程中,需要从相册获取图片时,一般会读取手机里面的照片或者通过相机拍摄获取照片,这是两种常用的获取图片的方式。

一、获取图片的两种方法

方法一:从本地相册获取照片

一般方法如下

public void openAlbum(OpenAlbumEvent event){
    Intent intent = new Intent();
    intent.setType("image/*");  
    intent.setAction(Intent.ACTION_PICK);  
    this.startActivityForResult(intent,REQUEST_CODE_PICK_IMAGE);
}

ACTION_GET_CONTENT 返回的 uri 是类似于这样的:content://com.android.providers.media.documents/document/image%3A1666,与低版本不兼容!

KitKat in theory handles these URI's like this:

content://com.android.providers.media.documents/document/image:3951

KitKat in practice gives you permission to this URI:

content://com.android.providers.media.documents/document/image%A3951

So, if you were to decode the URI and try to access the actual image, you get a permissions error, because that's not what you were given permission to access. These permissions are per document and per intent, so just giving your app MANAGE_DOCUMENTS permission isn't going to fly either. This isn't going to be fixed any time soon, and I don't want broken Android behaviour to hold up our release.

方法二:从照相机获取照片

一般方法如下:

protected void getImageFromCamera() {  
    String state = Environment.getExternalStorageState();  
    if (state.equals(Environment.MEDIA_MOUNTED)) {  
        new Intent(MediaStore.ACTION_IMAGE_CAPTURE);    
        startActivityForResult(getImageByCamera, REQUEST_CODE_CAPTURE_CAMEIA);  
    } else {  
        Toast.makeText("请确认已经插入SD卡", Toast.LENGTH_LONG).show();  
    }  
}

二、接收获取的图片

在相册中选取图片或者拍照之后,还需要返回到 APP 后能够接收到,如下:

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

    if (requestCode == REQUEST_CODE_PICK_IMAGE) {
        Uri uri = data.getData();
        //to do find the path of pic
    } else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA ) {
        Uri uri = data.getData();  
        //to do find the path of pic  
    }
}

但是,有时候我们会发现用相机拍摄获取照片的时候,得到的 uri 是 null 的,这是因为 android 把拍摄的图片封装到 bundle 中传递回来,但是根据不同的机器获得相片的方式不太一样,可能有的相机能够通过  inten.getData() 获取到 uri, 然后再根据uri获取数据的路径,在封装成bitmap,但有时候有的相机获取到的是 null 的,这时候我们该怎么办呢? 其实这时候我们就应该从 bundle 中获取数据,通过 (Bitmap) bundle.get("data"); 获取到相机图片的bitmap数据。

为了能够同时适应上述两种情况,我们这时候就应该在获取图片时做判断了。我们可以在响应的时候做一个判断:

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

    if (requestCode == REQUEST_CODE_PICK_IMAGE) {            
        Uri uri = data.getData();  
        //to do find the path of pic by uri  
    } else if (requestCode == REQUEST_CODE_CAPTURE_CAMEIA ) {            

        Uri uri = data.getData();  
        if (uri == null){  
            //use bundle to get data  
            Bundle bundle = data.getExtras();    

            if (bundle != null) {                
                Bitmap bitmap = (Bitmap) bundle.get("data"); //get bitmap  
                //spath :生成图片取个名字和路径包含类型                              
                saveImage(bitmap, String spath);  
            } else {          
                Toast.makeText("Error", Toast.LENGTH_LONG).show();          
                return;
            }
        } else {  
            //to do find the path of pic by uri  
        }
    }
}

后面的过程就需要通过bitmap转化成相应的图片文件了。不过得到最终的图片是被压缩了的。

public static void saveImage(Bitmap photo, String spath) {  
    try {  
        BufferedOutputStream bos =new BufferedOutputStream(new FileOutputStream(spath, false));
        photo.compress(Bitmap.CompressFormat.PNG, 100, bos);
        bos.flush();  
        bos.close();  
    } catch (Exception e) {  
        e.printStackTrace();  
        return false;  
    }  
    return true;  
}

这样就能解决照相机取到的图片uri为空的状态了。但是在获取到 uri 为 null 情况下,如果想得到没有被压缩过的照片(也就是说得到的是直接从相机拍摄到的照片),怎么做呢?其实方式很简单,在Intent getImageByCamera = new Intent("android.media.action.IMAGE_CAPTURE");之后我们直接讲文件先保存到指定的路径 filepath,然后直接在onActivityResult(int requestCode, int resultCode, Intent data)中把filepath传递过去就行了。

private String capturePath = null;

//拍照
public void takePicture(){
    String state = Environment.getExternalStorageState();  

    if (state.equals(Environment.MEDIA_MOUNTED)) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File outDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);         if (!outDir.exists()) {  
            outDir.mkdirs();  
        }  

        File outFile =  new File(outDir, System.currentTimeMillis() + ".jpg");  
        capturePath = outFile.getAbsolutePath();
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outFile));  
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);  
        startActivityForResult(intent, REQUEST_CODE_CAPTURE_CAMEIA);  
    } else{
        AToast.show(R.string.ordergoods_refund_no_sdcard_tip);
    }
}

在onActivityResult(int requestCode, int resultCode, Intent data)中我们只要把路径filepath定义为全局的变量传送过来就行了。

这样得到的图片是直接从相机中拍摄得到的照片,不会被压缩了。

在做照相机图片相关操作的时候,由于android手机的适配原因,不同手机上出发的相机操作可能在细节上有很多不同,例如摄像头拍照的角度旋转了,使得获取到的图片也是旋转后的,再比如某些相机的图片像素太高了,对图片数据进行操作的时候造成内存不足等。

下面就对相机拍摄时的图像被旋转的问题进行一些实际的解决方案:

问题:由于摄像头拍照是竖屏,显示的时候需要旋转了 90 度。也就是说显示的是旋转 90 度后的预览图片?

有一种方法是在说在拍照的时候将内容显示设定为横屏显示,

<activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="landscape">

还有在 onCreate() 函数加入 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

这样拍出来的图片就不能被旋转了。说实话,这种方式不具有通用型,而且很不灵活,不能适配所有手机。所以不建议,但是可以作为一个想法。其实真正能解决这种问题的方法是找到相机在拍照后得到的原图和实际显示的图片的旋转角度,然后我们再通过 Matrix 对图片进行旋转就Ok了。

那么我们怎样获取相机拍摄的原图和实际显示图片的旋转角度呢?在网上找到了这种方法,经过验证,着实可行。

/**
 * 读取照片exif信息中的旋转角度<br>http://www.eoeandroid.com/thread-196978-1-1.html
 * @param path 照片路径
 * @return角度
 */
public static int readPictureDegree(String path) {  
    int degree  = 0;  
    try {  
        new ExifInterface(path);  
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 
        switch (orientation) {  
            case ExifInterface.ORIENTATION_ROTATE_90:  
                90;  
                break;  
            case ExifInterface.ORIENTATION_ROTATE_180:  
                180;  
                break;  
            case ExifInterface.ORIENTATION_ROTATE_270:  
                270;  
                break;  
        }  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  
    return degree;  
}

得到原图和实际显示的图片的旋转角度后,我们再通过对原图进行旋转degree就行了,这个旋转方法可以通过 Matrix实现。