Android 从相册选择照片,或者拍照

更新日期: 2022-04-05 阅读次数: 5820 字数: 880 分类: Android

之前实现了 Android 拍照获取图片,现在需要再加上从相册选择照片。

交互

其实有多种交互方式:

  1. 界面上直接放两个按钮:一个是拍照,一个是从相册选取。简单直接。
  2. 点击拍照按钮,弹出一个选择框,提供两个选项,一拍照,二从相册选择

对于界面空间富裕的场景,我觉得第一种方式就非常方便了。当然,我也懒得搞那么复杂。。。

可是微信里面那个相机为啥那么方便呢?是否是自定义实现的相机,而不是调用的默认的相机应用。

kotlin registerForActivityResult 实现,目前推荐

registerForActivityResult 并没有 startActivityForResult 的 requestCode 来判定从哪个 activity 返回的, 但是可以通过定义多个 launch 来区分,以使用不同的回调处理函数。

目前只是猜测,需要看看官方是否有使用说明,以及 github 上的开源代码来做参考。

https://stackoverflow.com/questions/62387789/capture-image-from-camera-gallery-and-display-in-activity-fragment-using-kotlin

看,确实跟我猜测的一样。

startActivityForResult 配合 onActivityResult,需要 requestCode 是因为没法指定回调函数, 只能使用固定的 onActivityResult 来处理,所以必须配一个 requestCode。 而 registerForActivityResult 则可以自由地生成 N 个对应的处理回调,那么确实就不需要 requestCode 了。

最终实现:

val pickLauncher = registerForActivityResult(ActivityResultContracts.GetContent()){ uri: Uri? ->
    uri?.let { it ->
        Log.d("tag", it.toString())
        val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            ImageDecoder.decodeBitmap(ImageDecoder.createSource(this.contentResolver, it))
        } else {
            MediaStore.Images.Media.getBitmap(this.contentResolver, it)
        }
        handleCameraImage(bitmap)
    }
}

val btnPickPicture = findViewById<Button>(R.id.btnPickPicture)
btnPickPicture.setOnClickListener {
    pickLauncher.launch("image/*")
}

registerForActivityResult 的实现非常贴心,之前是用通用的方法ActivityResultContracts.StartActivityForResult(),这里用的是更简化的 ActivityResultContracts.GetContent(),可以直接获取到 Uri。

旧的代码实现 startActivityForResult, 已废弃

可以参考这里的做法:

https://stackoverflow.com/questions/10165302/dialog-to-pick-image-from-gallery-or-from-camera

Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePicture, 0);//zero can be replaced with any action code (called requestCode)


Intent pickPhoto = new Intent(Intent.ACTION_PICK,
           android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto , 1);//one can be replaced with any action code


protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) { 
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent); 
    switch(requestCode) {
    case 0:
        if(resultCode == RESULT_OK){  
            Uri selectedImage = imageReturnedIntent.getData();
            imageview.setImageURI(selectedImage);
        }

    break; 
    case 1:
        if(resultCode == RESULT_OK){  
            Uri selectedImage = imageReturnedIntent.getData();
            imageview.setImageURI(selectedImage);
        }
    break;
    }
}

registerForActivityResult 的介绍

Android 官方虽然有介绍,但是太简陋了,没有更多的细节和使用教程。

https://developer.android.com/training/basics/intents/result

各种 Intent 的区别

Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

弹出选择框,相册,或是文件管理。而文件管理中,没有文件。

ACTION_GET_CONTENT 与 ACTION_OPEN_DOCUMENT 的区别

https://stackoverflow.com/questions/36182134/what-is-the-real-difference-between-action-get-content-and-action-open-document

《第一行代码》里是用的 ACTION_OPEN_DOCUMENT 配合 intent type image 来实现的相册图片选择。

ACTION_OPEN_DOCUMENT is not intended to be a replacement for ACTION_GET_CONTENT. The one you should use depends on the needs of your app:

  • Use ACTION_GET_CONTENT if you want your app to simply read/import data. With this approach, the app imports a copy of the data, such as an image file.
  • Use ACTION_OPEN_DOCUMENT if you want your app to have long term, persistent access to documents owned by a document provider. An example would be a photo-editing app that lets users edit images stored in a document provider.

暂时没有必要纠结,可以参考网上怎么实现的选取图片并上传,来判定该使用哪个 action。

uri 的格式

content://com.android.providers.media.documents/document/image%3A213678
content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FScreenshots%2FScreenshot_2022-04-03-10-57-43-787_com.tencent.mm.jpg

会有两种格式:

  • 第一种是直接在弹出的选择框里选择图片
  • 第二种是进入相册,选择的图片

通过 Uri 初始化一个 Bitmap

Java 代码:

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

Kotlin 代码:

val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri)

但是 getBitmap 又废弃了。。。

'getBitmap(ContentResolver!, Uri!): Bitmap!' is deprecated. Deprecated in Java

拼凑了一个可以执行的代码:

val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
	ImageDecoder.decodeBitmap(ImageDecoder.createSource(this.contentResolver, imageUri))
} else {
	MediaStore.Images.Media.getBitmap(this.contentResolver, imageUri)
}

参考

https://stackoverflow.com/questions/3879992/how-to-get-bitmap-from-an-uri

tags: registerForActivityResult

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式