Android获取图片:拍照和从相册中选择

概述

在Android开发中获取图片主要包括如下两种方式:

  1. 打开相机拍照
  2. 从图库中选择图片

一、打开相机拍照

打开相机拍照主要包括如下几个部分:

  1. 权限申请
  2. 打开摄像头
  3. 拍照后传回数据处理

1. 权限申请

如果需要打开相机,则需要申请摄像头使用权限,在AndroidManifest.xml文件中添加如下代码:

<uses-permission android:name="android.permission.CAMERA" />

如果拍完照片后需要存储图片,则还需要文件读写的权限.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

注意权限申请在Android 6.0以上需要动态申请权限,在此不再赘述。

注意:

Android 10及以上,系统对文件读写的控制更加严格,如果想要粗暴的实现可读写效果,可以在AndroidManifest.xml<application>下配置如下代码达到效果:

android:requestLegacyExternalStorage="true"

2. 打开摄像头

打开摄像头可以通过设置intent来实现,示例如下:

    private fun openCamera() {
        if (PermissionUtils.instance.checkPermission(this, Manifest.permission.CAMERA)) {
        //为intent指定Action
            val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            startActivityForResult(intent, IntentUtil.instance.requestCameraPermissionCode)
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), IntentUtil.instance.requestCameraPermissionCode)
        }
    }

通过为intent指定摄像头Action:MediaStore.ACTION_IMAGE_CAPTURE,然后直接通过startActivityForResult的方式启动Intent即可启动摄像头。

3. 拍照后传回数据处理

在拍照结束后,我们可以对拍照结束后的回传的intent及携带的数据进行处理,例如可以将传回的图片以文件的形式保存起来或者直接加载在ImageView中,示例如下:

    override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                IntentUtil.instance.requestCameraPermissionCode -> {
                    var extra = data?.extras
                    var photo = extra?.get("data") as Bitmap?
                    if (photo != null) {
                        var path = FileUtils.instance.getAppDir()
                        if (path != null) {
                            ImageUtils.instance.saveBitmapToFile(photo, System.currentTimeMillis().toString() + ".png",
                                    "TestImg", this)
                        }
                        imgQRCode?.setImageBitmap(photo)
                    }
                }
                else -> {

                }
            }
        }
    }

通过在onActivityResult方法中获取到了intent对象,从其中获取到图片bitmap数据,然后进行对应的图片操作。

二、从相册中选取到图片

从相册中选取图片的实现主要包括如下几个步骤:

  1. 权限申请
  2. 打开相册选择图片
  3. 针对回传的图片uri地址去查询和获取到对应的文件的真实URI
  4. 根据文件URI获取到图片

1. 权限申请

从相册中选取图片所需要的权限主要包括文件读权限,在AndroidManifest.xml中添加如下代码:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Android 6.0及以上需要注意动态权限的申请,在此不再赘述。
Android 10及以上的处理同上。

2. 打开相册

和打开相机相似,打开相册也可以通过intent启动进行打开,示例代码如下:

    private fun openImageUtils() {
        val intent: Intent
        if (Build.VERSION.SDK_INT < 19) {
            intent = Intent(Intent.ACTION_GET_CONTENT)
            intent.type = "image/*"
        } else {
            intent = Intent(
                    Intent.ACTION_PICK,
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
        }
        startActivityForResult(intent, IntentUtil.instance.openImageGalleryCode)
    }

3. 针对回传数据进行处理

通过startActivityForResult启动intent,可以在onActivityResult中得到回传结果,因此可以通过回传的intent得到选择的图片的uri数据,示例代码如下:

override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                IntentUtil.instance.openImageGalleryCode -> {
                    var uri = data?.data
                    if (data != null && uri != null) {
                        var imgPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                            ImageUtils.instance.handleImageBeforeKitKat(data, this)
                        } else {
                            ImageUtils.instance.handleImageOnKitKat(data, this)
                        }
                        displayImage(imgPath)
                    }
                }
                else -> {

                }
            }
        }
    }

获取到URI之后便可以对URI进行处理并获取到图片,但是需要注意的是Android在4.4前后通过图库选择得到的图片的URI并不是一致的,因此需要分开处理。

Android在4.4之前获取到的URI是真实文件路径,因此不需要做过多的处理。

Android在4.4之后获取到URI并不是文件的真实路径,可能是类似于下面这种路径,因此需要重新对此URI进行解析以拿到真正的文件路径。

com.android.providers.media.documents.image%3A11111

解析代码示例如下:

    /**
     * 从相册中读取图片,在4.4之后
     * @param data:打开图片选择后返回的intent
     * @param context
     * @return
     */
    fun handleImageOnKitKat(data: Intent, context: Context?): String? {
        var imagePath: String? = null
        val uri = data.data
        if (DocumentsContract.isDocumentUri(context, uri)) {
            // 如果是document类型的Uri,则通过document id处理
            val docId = DocumentsContract.getDocumentId(uri)
            if ("com.android.providers.media.documents" == uri!!.authority) {
                val id = docId.split(":").toTypedArray()[1] // 解析出数字格式的id
                val selection = MediaStore.Images.Media._ID + "=" + id
                LogUtils.instance.getLogPrint("id=$id,selection=$selection")
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, context)
            } else if ("com.android.providers.downloads.documents" == uri.authority) {
                val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(docId))
                imagePath = getImagePath(contentUri, null, context)
            }
        } else if ("content".equals(uri!!.scheme, ignoreCase = true)) {
            // 如果是content类型的Uri,则使用普通方式处理
            imagePath = getImagePath(uri, null, context)
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
            // 如果是file类型的Uri,直接获取图片路径即可
            imagePath = uri.path
        }
        return imagePath
    }

    /**
     * 4.4版本以前,直接获取真实路径
     * @param data
     * @return
     */
    fun handleImageBeforeKitKat(data: Intent, context: Context?): String? {
        val uri = data.data
        return getImagePath(uri, null, context)
    }

    /**
     * 查询图库中是否存在有指定路径的图片
     * @param uri:路径URI
     * @param selection:筛选条件
     * @param context
     * @return
     */
    private fun getImagePath(uri: Uri?, selection: String?, context: Context?): String? {
        var path: String? = null
        // 通过Uri和selection来获取真实的图片路径
        val cursor: Cursor? = context?.contentResolver?.query(uri!!, null, selection, null, null)
        if (cursor != null) {
            LogUtils.instance.getLogPrint("cursor不为null  $selection")
            var i = 0
            while (i < cursor.columnCount) {
                var ss = cursor.getColumnName(i)
                LogUtils.instance.getLogPrint("$i   $ss")
                i++
            }
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA))
                LogUtils.instance.getLogPrint("get path= $path")
            }
            cursor.close()
        }
        return path
    }

最终通过cursor进行查询并获取到具体的图片文件地址。

4. 获取图片并处理

经过上步之后,我们可以获取到对应图片文件地址,可以通过过文件操作获取文件并添加到ImageView等操作,示例如下:

    private fun displayImage(imgPath: String?) {
        if (imgPath != null && FileUtils.instance.isFileExist(imgPath)) {
            var bitmap = BitmapFactory.decodeFile(imgPath)
            imgQRCode?.setImageBitmap(bitmap)
        }
    }

总结

获取图片的两种方式在实现上有相似的思路,都是经过如下几步:

  1. 申请权限
  2. 设置对应的intent Action,通过intent跳转到对应界面
  3. 在ActivityResults中处理返回的数据结果,获取到b图片对象。

需要主要的内容包括如下几点:

  1. 权限申请需要注意6.0以上的权限申请以及Android 10以上对于文件读写的控制。
  2. 打开图库选取照片要注意你当前从intent中获取的uri的格式,不仅仅需要考虑Android sdk版本的不同,同时对于不同的手机机型也有可能返回不同的uri路径,因此存在有单独适配的情况。
备注:

demo地址

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

推荐阅读更多精彩内容