之前实现了 Android 拍照获取图片,现在需要再加上从相册选择照片。
交互
其实有多种交互方式:
- 界面上直接放两个按钮:一个是拍照,一个是从相册选取。简单直接。
- 点击拍照按钮,弹出一个选择框,提供两个选项,一拍照,二从相册选择
对于界面空间富裕的场景,我觉得第一种方式就非常方便了。当然,我也懒得搞那么复杂。。。
可是微信里面那个相机为啥那么方便呢?是否是自定义实现的相机,而不是调用的默认的相机应用。
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
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式