Android 调用 系统选择器 选择 图片 或 文件(ACTION

您所在的位置:网站首页 选择文件方法 Android 调用 系统选择器 选择 图片 或 文件(ACTION

Android 调用 系统选择器 选择 图片 或 文件(ACTION

2024-02-12 01:26| 来源: 网络整理| 查看: 265

本文链接: https://blog.csdn.net/xietansheng/article/details/115763279

打开系统 APP 的资源选择器选取资源(图片/文件),通常可以使用以下 3 个 Action:

Intent.ACTION_PICKIntent.ACTION_GET_CONTENTIntent.ACTION_OPEN_DOCUMENT

一般 Android 系统内置的相关 APP 中均有实现了这 3 个 Action(如: 相册、文件管理),三的均能打开系统 APP 的资源选择器选择资源(图片、视频、文件、通讯录等)并返回,但三者的使用并不完全相同。有些第三方 APP 实现了这 3 个 Action 的,也可以用于选取相应的资源。

一般使用 ACTION_PICK 选择图片,使用 ACTION_GET_CONTENT 或 ACTION_OPEN_DOCUMENT 选择文件。

1. 使用 ACTION_PICK 选择图片

从数据中选择一个项目(不支持多选),并返回选择的内容,从返回的 intent.getData() 中获取资源,资源类型为 "content://" 开头的 Uri 资源,可通过 context.getContentResolver() 获取资源的内容和相关信息。

Intent.ACTION_PICK 的值为: "android.intent.action.PICK"

1.1 简单示例

启动的 Intent:

val intent = Intent(Intent.ACTION_PICK) intent.type = "image/*" activity.startActivityForResult(intent, REQUEST_CODE_PICK) // 选择视频: intent.type = "video/*"; // 选择所有类型的资源: intent.type = "*/*"

在 Activity.onActivityResult(...) 中接收选取返回的图片资源:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK && requestCode == REQUEST_CODE_PICK) { // 获取选取返回的图片资源, 结果为 "content://" 开头的 Uri 格式的资源, // Uri 格式参考: content://media/external/images/media/123 val uri = data?.data ?: return // 获取图片的数据, 可以使用 ContentResolver 直接打开输入流 var imageInputStream = contentResolver.openInputStream(uri) // 查询图片的详细信息 val cursor = contentResolver.query(uri, null, null, null, null) ... } }

获取到的 uri 资源一般只能在当前 Activity 实例没有被销毁前被访问,如果当前 Activity 实例已 onDestroy(),访问该 uri 可能会报无权限访问 Uri 资源的错误。

1.2 详细代码示例

(1)先在 AndroidManifest.xml 中添加读取外部存储器的权限:

(2)布局文件: res/layout/activity_main.xml

(3)Activity 代码: MainActivity.kt

package com.xiets.demo import android.content.Intent import android.database.Cursor import android.graphics.BitmapFactory import android.net.Uri import android.os.Bundle import android.provider.MediaStore import android.util.Log import android.view.View import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import java.io.Closeable import java.io.InputStream import java.util.* class MainActivity : AppCompatActivity() { companion object { private const val TAG = "MainActivity" private const val REQUEST_CODE_PICK = 1000 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById(R.id.btn_choose_image).setOnClickListener { // 点击按钮: 使用 ACTION_PICK 选择图片,启动 Activity Intent openSystemImageChooser(REQUEST_CODE_PICK) } } /** * 使用 ACTION_PICK 选择图片,启动 Activity Intent */ private fun openSystemImageChooser(requestCode: Int) { val intent = Intent(Intent.ACTION_PICK) intent.type = "image/*" startActivityForResult(intent, requestCode) // 选择视频: intent.type = "video/*"; // 选择所有类型的资源: intent.type = "*/*" } /** * 在返回的 onActivityResult 中接收选取返回的图片资源 */ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK) { Log.d(TAG, "onActivityResult not ok.") return } if (requestCode == REQUEST_CODE_PICK) { // 获取选取返回的图片资源, Uri 格式 val uri = data?.data ?: return // URI 格式参考: content://media/external/images/media/123 Log.d(TAG, "选取的图片: $uri") // 如果需要使用图片的数据(如解析为 Bitmap 或 上传至服务端), // 可以使用 ContentResolver 直接打开输入流 var imageInputStream: InputStream? = null try { // 打开 Uri 的输入流 imageInputStream = contentResolver.openInputStream(uri) // 把输入流解析为 Bitmap val bitmap = BitmapFactory.decodeStream(imageInputStream) // 显示 Bitmap 到 ImageView findViewById(R.id.image_view).setImageBitmap(bitmap) } catch (e: Exception) { e.printStackTrace() } finally { closeStream(imageInputStream) } // 查询图片的详细信息 queryUriDetail(uri) } } private fun queryUriDetail(uri: Uri) { // 如果需要选取的图片的详细信息(图片大小、路径、所在相册名称、修改时间、MIME、宽高、文件名等), // 则需要通过 content.getContentResolver().query(uri, ...) 查询(直接查询所有字段) val cursor = contentResolver.query(uri, null, null, null, null) // 一般查询出来的只有一条记录 if (cursor?.moveToFirst() == true) { // 查看查询结果数据的的所有列, 不同系统版本列名数量和类型可能不相同, 参考: // [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, // description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, // mini_thumb_magic, bucket_id, bucket_display_name, width, height] Log.d(TAG, "columnNames: " + Arrays.toString(cursor.columnNames)) // 获取图片的 大小、文件名、路径 // val size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns.SIZE)) // val filename = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DISPLAY_NAME)) // val path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)) // 输出所有列对应的值 for (column in cursor.columnNames) { val index = cursor.getColumnIndex(column) val valueDesc = when (cursor.getType(index)) { Cursor.FIELD_TYPE_NULL -> "$column: NULL" Cursor.FIELD_TYPE_INTEGER -> "$column: " + cursor.getInt(index) Cursor.FIELD_TYPE_FLOAT -> "$column: " + cursor.getFloat(index) Cursor.FIELD_TYPE_STRING -> "$column: " + cursor.getString(index) Cursor.FIELD_TYPE_BLOB -> "$column: BLOB" else -> "$column: Unknown" } Log.d(TAG, valueDesc) } } cursor?.close() } private fun closeStream(c: Closeable?) { try { c?.close() } catch (e: Exception) { e.printStackTrace() } } } 2. 使用 ACTION_GET_CONTENT 选择文件

ACTION_GET_CONTENT 允许用户选择一种特定类型的数据并返回(支持多选)。这与 ACTION_PICK 不同,因为这里我们只说需要哪种数据,而不是用户可以选择的现有数据的 URI。ACTION_GET_CONTENT 可以允许用户在数据运行时创建数据(例如拍照或录制声音),让他们浏览Web 并下载所需的数据,等等。获取的资源类型为 "content://" 开头的 Uri 资源,可通过 context.getContentResolver() 获取资源的内容和相关信息。

ACTION_GET_CONTENT 不单只支持文件/图片,还支持选择通讯录、录音、音频、视频等内容。

Intent.ACTION_GET_CONTENT 的值为: "android.intent.action.GET_CONTENT"

2.1 简单示例

启动的 Intent:

val intent = Intent(Intent.ACTION_GET_CONTENT) intent.type = "*/*" // 只选择图片: intent.type = "image/*" // 只选择视频: intent.type = "video/*" // 支持多选(长按多选) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 用于表示 Intent 仅希望查询能使用 ContentResolver.openFileDescriptor(Uri, String) 打开的 Uri intent.addCategory(Intent.CATEGORY_OPENABLE) activity.startActivityForResult(intent, REQUEST_CODE_GET_CONTENT) // 可以包装 Intent // activity.startActivityForResult(Intent.createChooser(intent, "选择文件"), REQUEST_CODE_GET_CONTENT)

在 Activity.onActivityResult(...) 中接收选取返回的文件资源:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK && requestCode == REQUEST_CODE_GET_CONTENT) { // 多选的情况 val clipData = data?.clipData if (clipData != null && clipData.itemCount > 0) { for (i in 0 until clipData.itemCount) { val item = clipData.getItemAt(i) val uri = item.uri ?: continue handleSelectedFile(uri) } } // 单选的情况 val uri = data?.data ?: return handleSelectedFile(uri) } } private fun handleSelectedFile(uri: Uri) { // 获取选取返回的文件资源, 结果为 "content://" 开头的 Uri 格式的资源, // Uri 格式参考: content://com.android.providers.media.documents/document/document%3A145 val uri = data?.data ?: return // 获取文件的数据, 可以使用 ContentResolver 直接打开输入流 var fileInputStream = contentResolver.openInputStream(uri) // 查询文件的详细信息 val cursor = contentResolver.query(uri, null, null, null, null) ... } 2.2 详细代码示例

(1)先在 AndroidManifest.xml 中添加读取外部存储器的权限:

(2)布局文件: res/layout/activity_main.xml

(3)Activity 代码: MainActivity.kt

package com.xiets.demo import android.content.Intent import android.database.Cursor import android.net.Uri import android.os.Bundle import android.provider.DocumentsContract import android.util.Log import android.view.View import androidx.appcompat.app.AppCompatActivity import java.io.Closeable import java.io.InputStream import java.util.* class MainActivity : AppCompatActivity() { companion object { private const val TAG = "MainActivity" private const val REQUEST_CODE_GET_CONTENT = 1001 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById(R.id.btn_get_content).setOnClickListener { // 点击按钮: 使用 ACTION_GET_CONTENT 选择文件,启动 Activity Intent openSystemFilesChooser(REQUEST_CODE_GET_CONTENT) } } /** * 使用 ACTION_GET_CONTENT 选择文件,启动 Activity Intent */ private fun openSystemFilesChooser(requestCode: Int) { val intent = Intent(Intent.ACTION_GET_CONTENT) intent.type = "*/*" // 只选择图片: intent.type = "image/*" // 只选择视频: intent.type = "video/*" // 支持多选(长按多选) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 用于表示 Intent 仅希望查询能使用 ContentResolver.openFileDescriptor(Uri, String) 打开的 Uri intent.addCategory(Intent.CATEGORY_OPENABLE) startActivityForResult(intent, requestCode) // 可以包装 Intent // startActivityForResult(Intent.createChooser(intent, "选择文件"), requestCode) } /** * 在返回的 onActivityResult 中接收选取返回的文件资源 */ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK) { Log.d(TAG, "onActivityResult not ok.") return } if (requestCode == REQUEST_CODE_GET_CONTENT) { // 多选的情况 val clipData = data?.clipData if (clipData != null && clipData.itemCount > 0) { for (i in 0 until clipData.itemCount) { val item = clipData.getItemAt(i) val uri = item.uri ?: continue handleSelectedFile(uri) } } // 单选的情况 val uri = data?.data ?: return handleSelectedFile(uri) } } private fun handleSelectedFile(uri: Uri) { // URI 格式参考: content://com.android.providers.media.documents/document/document%3A145 Log.d(TAG, "选取的文件: $uri") // 如果需要使用文件的数据, 可以使用 ContentResolver 直接打开输入流 var fileInputStream: InputStream? = null try { // 打开 Uri 的输入流 fileInputStream = contentResolver.openInputStream(uri) // ... } catch (e: Exception) { e.printStackTrace() } finally { closeStream(fileInputStream) } // 查询文件的详细信息 queryUriDetail(uri) } private fun queryUriDetail(uri: Uri) { // 如果需要选取的文件的详细信息(MIME、文件名、修改时间、大小等), // 则需要通过 content.getContentResolver().query(uri, ...) 查询(直接查询所有字段) val cursor = contentResolver.query(uri, null, null, null, null) // 一般查询出来的只有一条记录 if (cursor?.moveToFirst() == true) { // 查看查询结果数据的的所有列, 不同系统版本列名数量可能不相同, 参考: // [document_id, mime_type, _display_name, last_modified, flags, _size], 这里没有路径字段 Log.d(TAG, "columnNames: " + Arrays.toString(cursor.columnNames)) // 获取文件的 大小、文件名, 列名常量值参考: DocumentsContract.Document.COLUMN_XXX // val size = cursor.getLong(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE)) // val filename = cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)) // 输出所有列对应的值 for (column in cursor.columnNames) { val index = cursor.getColumnIndex(column) val valueDesc = when (cursor.getType(index)) { Cursor.FIELD_TYPE_NULL -> "$column: NULL" Cursor.FIELD_TYPE_INTEGER -> "$column: " + cursor.getInt(index) Cursor.FIELD_TYPE_FLOAT -> "$column: " + cursor.getFloat(index) Cursor.FIELD_TYPE_STRING -> "$column: " + cursor.getString(index) Cursor.FIELD_TYPE_BLOB -> "$column: BLOB" else -> "$column: Unknown" } Log.d(TAG, valueDesc) } } cursor?.close() } private fun closeStream(c: Closeable?) { try { c?.close() } catch (e: Exception) { e.printStackTrace() } } } 3. 使用 ACTION_OPEN_DOCUMENT 选择文件

ACTION_OPEN_DOCUMENT 允许用户选择并返回一个或多个现有文档。调用时,系统显示实现了 DocumentsProvider 实例的文件选取器(APP),让用户以交互方式浏览他们。支持的文档包括本地媒体(例如照片和视频)以及已安装的云存储提供商提供的文档。

选取的每个文档都表示为由 DocumentsProvider 提供的 "content://" 开头的 Uri 资源,可以通过 ContentResolver.openInputStream(Uri) 将该 Uri 作为流打开,也可以通过 ContentResolver.openFileDescriptor(Uri, String) 查询 DocumentsContract.Document 元数据。

ACTION_OPEN_DOCUMENT 的用法与 ACTION_GET_CONTENT 基本相似,参考之。

与 ACTION_GET_CONTENT 不同的是,ACTION_OPEN_DOCUMENT 只支持选择文档(即图片、音视频、文件 等)。

Intent.ACTION_OPEN_DOCUMENT 的值为: "android.intent.action.OPEN_DOCUMENT"

简单实例:

启动的 Intent:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.type = "*/*" // 只选择图片: intent.type = "image/*" // 只选择视频: intent.type = "video/*" // 支持多选(长按多选) intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) activity.startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT)

在 Activity.onActivityResult(...) 中接收选取返回的文件资源:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != RESULT_OK && requestCode == REQUEST_CODE_OPEN_DOCUMENT) { // 多选的情况 val clipData = data?.clipData if (clipData != null && clipData.itemCount > 0) { for (i in 0 until clipData.itemCount) { val item = clipData.getItemAt(i) val uri = item.uri ?: continue handleSelectedFile(uri) } } // 单选的情况 val uri = data?.data ?: return handleSelectedFile(uri) } } private fun handleSelectedFile(uri: Uri) { // 获取选取返回的文件资源, 结果为 "content://" 开头的 Uri 格式的资源, // Uri 格式参考: content://com.android.providers.media.documents/document/document%3A145 val uri = data?.data ?: return // 获取文件的数据, 可以使用 ContentResolver 直接打开输入流 var fileInputStream = contentResolver.openInputStream(uri) // 查询文件的详细信息 val cursor = contentResolver.query(uri, null, null, null, null) ... }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3