Android拍照或从系统相册获取图片

概述

在做Android开发中还是会经常选择照片然后做上传操作的。但是其中选择照片系统的有两种方式,第一种是拍照、第二种是从相册中选择。这里分别介绍下。

其中拍照有两种方法,从系统相册选择有两种方法,会分别介绍和分析。

拍照获取照片的方法

刚才说过会介绍两种方法,其实无论几种方法原理都是一个。就是通过intent发出隐式意图调用系统的照相机,然后在获取到从相机返回的图片,这里的两种主要是返回方式有两种。

1、直接返回图片。

2、提前创建好存放图片的Uri然后拍照返回后存储起来。

第一种拍照方法:

 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent, TAKE_PHOTO_REQUEST);

没错,简单的两行代码就可以调取摄像头进行拍照了,这时候我们是通过Intent指定activion: MediaStore.ACTION_IMAGE_CAPTURE去查找符合条件的程序。相机里面会对这个action做处理,这一步属于intent的操作了,这里不再赘述。

case TAKE_PHOTO_REQUEST:
                if (resultCode == RESULT_CANCELED) {
                    Toast.makeText(MainActivity.this, "取消了拍照", Toast.LENGTH_LONG).show();
                    return;
                }
                Bitmap  photo = data.getParcelableExtra("data");
                iv_image.setImageBitmap(photo);

                break;

上面的代码是onActivityResult中的处理,判断request后做拍照返回处理,其中data直接返回Bitmap,不过这里要注意一点就是,这个Bitmap会经过系统压缩。所以有时候可能看起来照片并没有那么清晰。也正是由于是系统压缩的原因,这个图片基本不会很大,基本不会OOM。

第二种拍照获取照片方法:

第二种方法其实也是一样的,只不过我们事先定义好uri,然后图片会存储到这个uri中,然后我们可以通过这个uri在本地找到具体的图片,然后做处理,展示。

  private static Uri createImageUri(Context context) {
        String name = "takePhoto" + System.currentTimeMillis();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.Images.Media.TITLE, name);
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, name + ".jpeg");
        contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        Uri uri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        return uri;
    }

上述代码是创建一个uri用来存储拍照后的照片。

public static void delteImageUri(Context context, Uri uri) {
        context.getContentResolver().delete(uri, null, null);

    }

上述代码是用来删除一个本地uri

   btn_take_photo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                imageUri = createImageUri(MainActivity.this);
                Intent intent = new Intent();
                intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//如果不设置EXTRA_OUTPUT getData()  获取的是bitmap数据  是压缩后的
                startActivityForResult(intent, TAKE_PHOTO_REQUEST_ONE);


            }
        });

然后通过上述代码创建imageUri然后发起拍照,方式同样用Intent,可参第一种方法。

   if (resultCode == RESULT_CANCELED) {
            delteImageUri(MainActivity.this,imageUri);
                    return;
                }
  case TAKE_PHOTO_REQUEST_ONE:
                 iv_image.setImageURI(imageUri);
                    break;

最后就是获取拍照的照片做处理或者显示。

其中如果取消的话就删除创建的rui。

Bitmap bitmap=MediaStore.Images.Media.getBitmap(getContentResolver(),imageUri);
iv_image.setImageBitmap(bitmap);

还可以直接通过MediaStore获取bitmap进行设置。

以上方法经测试在可以正常获取照片。

但是这样还会有个问题,就是如果图片过大的情况下,会有异常。

W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (3120x4208, max=4096x4096)

如上代码所示,会直接报bitmap的过大而无法显示图片。

第一种处理方法就是对图片进行处理。
这里介绍第二种处理方式,就是用不同的方式去创建存储图片的文件。

public class TakePhotoUtils {



    /**
     * 拍照
     */
    public static Uri takePhoto(Activity mActivity, int flag) throws IOException {
        //指定拍照intent
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri imageUri = null;
        if (takePictureIntent.resolveActivity(mActivity.getPackageManager()) != null) {
            String sdcardState = Environment.getExternalStorageState();
            File outputImage = null;
            if (Environment.MEDIA_MOUNTED.equals(sdcardState)) {
                outputImage = createImageFile(mActivity);
            } else {
                Toast.makeText(mActivity.getApplicationContext(), "内存异常", Toast.LENGTH_SHORT).show();
            }
            try {
                if (outputImage.exists()) {
                    outputImage.delete();
                }
                outputImage.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (outputImage != null) {
                imageUri = Uri.fromFile(outputImage);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                mActivity.startActivityForResult(takePictureIntent, flag);
            }
        }

        return imageUri;
    }





    public static  File createImageFile(Activity mActivity) throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp;//创建以时间命名的文件名称
        File storageDir = getOwnCacheDirectory(mActivity, "takephoto");//创建保存的路径
        File image = new File(storageDir.getPath(), imageFileName + ".jpg");
        if (!image.exists()) {
            try {
                //在指定的文件夹中创建文件
                image.createNewFile();
            } catch (Exception e) {
            }
        }

        return image;
    }


    /**
     * 根据目录创建文件夹
     * @param context
     * @param cacheDir
     * @return
     */
    public static File getOwnCacheDirectory(Context context, String cacheDir) {
        File appCacheDir = null;
        //判断sd卡正常挂载并且拥有权限的时候创建文件
        if ( Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
            appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
        }
        if (appCacheDir == null || !appCacheDir.exists() && !appCacheDir.mkdirs()) {
            appCacheDir = context.getCacheDir();
        }
        return appCacheDir;
    }


    /**
     * 检查是否有权限
     * @param context
     * @return
     */
    private static boolean hasExternalStoragePermission(Context context) {
        int perm = context.checkCallingOrSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE");
        return perm == 0;
    }


}

代码没什么难点,简单写了一个工具类,里面封装了一个拍照的方法,并返回一个存储拍照后的路径。
路径是自己制定文件夹后创建一个文件,用于存储照片。文件名是根据时间命名的,以免重复。

    if (resultCode == RESULT_CANCELED) {
                    Toast.makeText(MainActivity.this, "点击取消从相册选择", Toast.LENGTH_LONG).show();
                    return;
                }


                Bitmap bitmap = BitmapFactory.decodeFile(imageUri.getPath(), getOptions(imageUri.getPath()));
                iv_image.setImageBitmap(bitmap);

然后在onActivityResult的方法中进行处理,这里也最好对图片进行下压缩处理。然后就可以正常显示拍照后的图片了。


    /**
     * 获取压缩图片的options
     *
     * @return
     */
    public static BitmapFactory.Options getOptions(String path) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inSampleSize = 4;      //此项参数可以根据需求进行计算  
        options.inJustDecodeBounds = false;

        return options;
    }

这里的只是简单的处理方法,按照指定参数压缩下,这里的inSapleSizes是需要根据自己需求进行算法的。

这样基本就可以通过拍照来获取照片了

从相册选择照片来展示

其实拍照主要也是通过intent来调用系统相册,然后通过返回数据在onActivityResult中进行处理。


    public void pickImageFromAlbum() {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        startActivityForResult(intent, 111);

    }

    public void pickImageFromAlbum2() {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_PICK);
        intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, 222);

    }

如图两种方式均可以调用系统相册进行选择照片。

  if (resultCode == RESULT_CANCELED) {
                    Toast.makeText(MainActivity.this, "点击取消从相册选择", Toast.LENGTH_LONG).show();
                    return;
                }

                try {
                    Uri imageUri = data.getData();
                    Log.e("TAG", imageUri.toString());
                    iv_image.setImageURI(imageUri);
                } catch (Exception e) {
                    e.printStackTrace();
                }
    

然后返回的处理方式基本是一样的 拿到uri后进行对图片处理就好了。这里说明下如果图片过大可能也需要进行二次处理。

如本篇有错误欢迎大家留言指正。

demo源码地址:https://github.com/xuanguofeng/TakeAndChoosePhoto

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容