您的位置:首页 > 其它

拍照、相册及裁剪的终极实现(二)——相册选择及裁剪功能实现

2015-01-30 21:04 489 查看
前言,a马上要回家过年了,回家前争取把这篇文章写出来给大家,这三个月真是跟打仗似的,天天累的跟狗一样,不过看着自己做的东西被别人用,那真是爽,虽然俺还比较菜,也不是什么核心功能,能一毕业就能加入到这么大的项目中来也是很荣幸的,插一句,这几天阿里有七个工作日极速面试哦,有能力的同学拿简历来,嘿嘿

在上篇中,我们看到有关拍照和裁剪的方案,这里带大家看看如何从相册中选择文件并裁剪。

一、从相册选择图像并显示

先看看效果图:
如图一,有四个按钮,这里用第一个按钮:(相册选择——返回值),它的意思是通过intent.的data域来获取返回的图像数据;点击后转到图片选择页面,选择后,在底部将显示的图片显示出来。
(一) (二) (三)







在上篇中我们提到过启动相册的ACTION为:

public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
所以隐匿启动相册Intent的代码为:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
startActivityForResult(intent, RESULT_ALBUM_ONLY_THROUGH_DATA);
在开启相册之后,选择对应的图片,然后接收Result:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) {
        return;
    }
    switch (requestCode) {
        case RESULT_ALBUM_ONLY_THROUGH_DATA: {
            //照片的原始资源地址
            try {
                //使用ContentProvider通过URI获取原始图片
                Bitmap photo = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
                if (photo != null) {
                    Bitmap smallBmp = setScaleBitmap(photo, 2);
                    mImageView.setImageBitmap(smallBmp);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        break;
    }
}

这里要注意一下,这里没有将return-data设为false,也就是使用了默认值,即直接使用Intent中的data域来传递结果值,但问题在于Data域最大传递的值的大小为1M,所以图片的BITMAP当超过1M时就会失败。从而显示缩略图。
这里先演示这种方法的效果,至于从相册选择的终极方案,同样,我们会在讲过裁剪后给出。

二、从相册选取并裁剪(通过URI返回)

先看下效果:
点击“相册并截取——URI”(图一),转入相册页面(图二),选中一张图片,转到裁剪页面(图三),将裁剪后的图片显示在imageView中(图四)
(一) (二)





(三) (四)





还记得上篇中的裁剪用的N多参数么,这里再给大家贴一下

Exta Options Table for image/* crop:
附加选项数据类型描述
cropString发送裁剪信号
aspectXintX方向上的比例
aspectYintY方向上的比例
outputXint裁剪区的宽
outputYint裁剪区的高
scaleboolean是否保留比例
return-databoolean是否将数据保留在Bitmap中返回
dataParcelable相应的Bitmap数据
circleCropString圆形裁剪区域?
MediaStore.EXTRA_OUTPUT ("output")URI将URI指向相应的file:///...,详见代码示例
outputFormatString输出格式,一般设为Bitmap格式:Bitmap.CompressFormat.JPEG.toString()
noFaceDetectionboolean是否取消人脸识别功能
上篇我们说过这些参数可以随意匹配,那这里我们就用来裁剪从相册转过来的图片;首先,启动选择相册Intent,然后配置各个参数:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 1000);
intent.putExtra("outputY", 1000);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, RESULT_ALBUM_CROP_URI);
上面这段代码首先将action设为启动相册Intent的action:
public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
然后不让数据从Intent的data域返回,而是保存在我们指定的URI中;
然后在接收时:
直接从URI中获取图片,然后显示在imageView中;

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) {
        return;
    }
    switch (requestCode) {
        case RESULT_ALBUM_CROP_URI: {
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                if (bitmap != null) {
                    Bitmap smallBmp = setScaleBitmap(bitmap, 2);
                    mImageView.setImageBitmap(smallBmp);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        break;
    }
}
看着好像没什么事情,但问题来了:在有些手机上根本出不来裁剪页面,这是为什么呢?
还记得在上篇中拍照时也出现过出不来裁剪页面的情况,当时是由于Intent之间传递数据最大只能传1M左右,当超过这个数据时就会传递不成功,所以在上篇中我们为了传递拍照的结果,把它暂存到本地图片中,然后在重新调用裁剪Intent,把数据传进去,让它裁剪。通过绕过Intent之间直接数据传递,而使用间接传递数据的方法来完成较大数据的传递。那这里要怎么办呢?有没有一种方法能找到用户点击的图片的地址呢?如果能找到地址,那在用户点击一个图片后,再调用裁剪的Intent,从本地读取图片数据传进去裁剪Intent让它来裁剪,这样就绕过了直接通过Intent数据传递的大小限制。

三、从相册选取并裁剪(通过Path:终极方案)

找来找去,终于在开源项目中找到了一个方法,通过传进去Intent的data域返回的URI数据,找到图片地址,代码如下:
我没看懂,只能给大家贴代码了,非常抱歉。

// 解析获取图片库图片Uri物理路径
    public static String parsePicturePath(Context context, Uri uri) {

        if (null == context || uri == null)
            return null;

        boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
        // DocumentUri
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageDocumentsUri
            if (isExternalStorageDocumentsUri(uri)) {
                String docId = DocumentsContract.getDocumentId(uri);
                String[] splits = docId.split(":");
                String type = splits[0];
                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + File.separator + splits[1];
                }
            }
            // DownloadsDocumentsUri
            else if (isDownloadsDocumentsUri(uri)) {
                String docId = DocumentsContract.getDocumentId(uri);
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
                return getDataColumn(context, contentUri, null, null);
            }
            // MediaDocumentsUri
            else if (isMediaDocumentsUri(uri)) {
                String docId = DocumentsContract.getDocumentId(uri);
                String[] split = docId.split(":");
                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;
                }
                String selection = "_id=?";
                String[] selectionArgs = new String[] {split[1]};
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            if (isGooglePhotosContentUri(uri))
                return uri.getLastPathSegment();
            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;

    }
整体过程是这样的:
先调用相册Intent:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
startActivityForResult(intent, RESULT_ALBUM_CROP_PATH);
然后在接收时,找到图片路径,生成对应的URI,转给裁剪页面:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case RESULT_ALBUM_CROP_PATH:{
                String picPath = parsePicturePath(MyActivity.this,data.getData());
                File file = new File(picPath);
                Uri uri = Uri.fromFile(file);
                cropImg(uri);
            }
            break;
        }
    }
然后是裁剪图片的代码:cropImg(Uri uri)
public void cropImg(Uri uri) {
        File tempFile = getTempFile();
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 700);
        intent.putExtra("outputY", 700);
        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tempFile));
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);
        startActivityForResult(intent, RESULT_CAMERA_CROP_PATH_RESULT);
    }
在裁剪图片后,将结果保存到暂存的本地图片URI中,然后在接收Result时:
将保存的本地的图片取出,设置到ImageView中,为了停止显示不出来所以将其缩小到1/2

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case RESULT_CAMERA_CROP_PATH_RESULT: {
                Bundle extras = data.getExtras();
                if (extras != null) {
                    Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);
                    if (bitmap != null) {
                        Bitmap smallBmp = setScaleBitmap(bitmap, 2);
                        mImageView.setImageBitmap(smallBmp);
                    }
                }
            }
            break;
        }
    }

四、从相册选择图像并显示的终极方案

在第一部分,我们讲过,当图片过大时,会出现数据传递失败的情况,因为Intent的data域最大只能传1M的数据,当数据超过1M时就会出现传递失败,上面我们有了一个方法根本返回的data找到对应的路径。这里大家是不是有什么想法了,这里知道了路径,直接从路径取图片不就好了,所以通过路径的显示选择图片的终极方案就出来了。
首先启动相册Intent:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
startActivityForResult(intent, RESULT_ALBUM_ONLY_THROUGH_URI);
然后在接收时:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case RESULT_ALBUM_ONLY_THROUGH_URI: {
                try {
                    String picPath = parsePicturePath(MyActivity.this, data.getData());
                    File file = new File(picPath);
                    Uri uri = Uri.fromFile(file);
                    Bitmap photo = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                    if (photo != null) {
                        Bitmap smallBmp = setScaleBitmap(photo, 2);
                        mImageView.setImageBitmap(smallBmp);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            break;
        }
    }
直接从data域获取图片路径,然后从路径加载出Bitmap,这就不会因为图片太大显示不出来了。
好了,这篇文章到这里就结束了,工作太忙,一直没时间写,今天总算赶完给大家了。

更正:

在《三、从相册选取并裁剪(通过Path:终极方案)》中,在接收时:

上面我是这样写的:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case RESULT_CAMERA_CROP_PATH_RESULT: {
                Bundle extras = data.getExtras();
                if (extras != null) {
                    Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);
                    if (bitmap != null) {
                        Bitmap smallBmp = setScaleBitmap(bitmap, 2);
                        mImageView.setImageBitmap(smallBmp);
                    }
                }
            }
            break;
        }
    }
由于在cropImg(Uri uri)中,我们设置了MediaStore.EXTRA_OUTPUT参数,即我们已经将结果转成URI,输出到tempFile中去了,所以在Intent的Data域可能就没有值了;(极少个别机型会出现),所以正确的接收方式应该为:
即直接从TempFile中获取当前保存的图片:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        switch (requestCode) {
            case RESULT_CAMERA_CROP_PATH_RESULT: {
                if (getTempFile() != null) {
                    Bitmap bitmap = BitmapFactory.decodeFile(getTempFile().getAbsolutePath(), null);
                    if (bitmap != null) {
                        Bitmap smallBmp = setScaleBitmap(bitmap, 2);
                        mImageView.setImageBitmap(smallBmp);
                    }
                }
            }
            break;
        }
    }

注意,上面的更正在下面的源码中没有改过来,大家还需要在理解上基础上,自行更正。
源码有四个按钮:
1、相册选取——返回值:对应第一部分
2、相册选择——PATH(终极):对应第四部分
3、相册并截取——URI:对应第二部分
4、相册并截取——PATH(终极):对应第三部分

如果本文有帮到你,记得加关注哦。
源码下载地址:http://download.csdn.net/detail/harvic880925/8445281
请大家尊重原创者版权,转载请标明出处:/article/2594058.html,谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐