700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android 调用 系统选择器 选择 图片 或 文件(ACTION_PICK ACTION_GET_CONTENT)

Android 调用 系统选择器 选择 图片 或 文件(ACTION_PICK ACTION_GET_CONTENT)

时间:2019-09-11 17:23:30

相关推荐

Android 调用 系统选择器 选择 图片 或 文件(ACTION_PICK ACTION_GET_CONTENT)

本文链接: /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_CONTENTACTION_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/123val 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中添加读取外部存储器的权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><!--Android 6.0+ 需要动态获取存储权限。一般系统 APP 的内容提供者可以通过 content.getContentResolver()不需要权限直接读取选择的 Uri 内容。有些则需要权限才能读取,保险起见,读取内容前建议先申请相关权限。-->

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

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/btn_choose_image"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Choose Image"/><ImageViewandroid:id="@+id/image_view"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>

(3)Activity 代码:MainActivity.kt

package com.xiets.demoimport android.content.Intentimport android.database.Cursorimport android.graphics.BitmapFactoryimport .Uriimport android.os.Bundleimport android.provider.MediaStoreimport android.util.Logimport android.view.Viewimport android.widget.ImageViewimport androidx.appcompat.app.AppCompatActivityimport java.io.Closeableimport java.io.InputStreamimport 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<View>(R.id.btn_choose_image).setOnClickListener {// 点击按钮: 使用 ACTION_PICK 选择图片,启动 Activity IntentopenSystemImageChooser(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/123Log.d(TAG, "选取的图片: $uri")// 如果需要使用图片的数据(如解析为 Bitmap 或 上传至服务端),// 可以使用 ContentResolver 直接打开输入流var imageInputStream: InputStream? = nulltry {// 打开 Uri 的输入流imageInputStream = contentResolver.openInputStream(uri)// 把输入流解析为 Bitmapval bitmap = BitmapFactory.decodeStream(imageInputStream)// 显示 Bitmap 到 ImageViewfindViewById<ImageView>(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) 打开的 Uriintent.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?.clipDataif (clipData != null && clipData.itemCount > 0) {for (i in 0 until clipData.itemCount) {val item = clipData.getItemAt(i)val uri = item.uri ?: continuehandleSelectedFile(uri)}}// 单选的情况val uri = data?.data ?: returnhandleSelectedFile(uri)}}private fun handleSelectedFile(uri: Uri) {// 获取选取返回的文件资源, 结果为 "content://" 开头的 Uri 格式的资源,// Uri 格式参考: content://com.android.providers.media.documents/document/document%3A145val uri = data?.data ?: return// 获取文件的数据, 可以使用 ContentResolver 直接打开输入流var fileInputStream = contentResolver.openInputStream(uri)// 查询文件的详细信息val cursor = contentResolver.query(uri, null, null, null, null)...}

2.2 详细代码示例

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

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

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

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:id="@+id/btn_get_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Choose Files"/></LinearLayout>

(3)Activity 代码:MainActivity.kt

package com.xiets.demoimport android.content.Intentimport android.database.Cursorimport .Uriimport android.os.Bundleimport android.provider.DocumentsContractimport android.util.Logimport android.view.Viewimport androidx.appcompat.app.AppCompatActivityimport java.io.Closeableimport java.io.InputStreamimport 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<View>(R.id.btn_get_content).setOnClickListener {// 点击按钮: 使用 ACTION_GET_CONTENT 选择文件,启动 Activity IntentopenSystemFilesChooser(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) 打开的 Uriintent.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?.clipDataif (clipData != null && clipData.itemCount > 0) {for (i in 0 until clipData.itemCount) {val item = clipData.getItemAt(i)val uri = item.uri ?: continuehandleSelectedFile(uri)}}// 单选的情况val uri = data?.data ?: returnhandleSelectedFile(uri)}}private fun handleSelectedFile(uri: Uri) {// URI 格式参考: content://com.android.providers.media.documents/document/document%3A145Log.d(TAG, "选取的文件: $uri")// 如果需要使用文件的数据, 可以使用 ContentResolver 直接打开输入流var fileInputStream: InputStream? = nulltry {// 打开 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?.clipDataif (clipData != null && clipData.itemCount > 0) {for (i in 0 until clipData.itemCount) {val item = clipData.getItemAt(i)val uri = item.uri ?: continuehandleSelectedFile(uri)}}// 单选的情况val uri = data?.data ?: returnhandleSelectedFile(uri)}}private fun handleSelectedFile(uri: Uri) {// 获取选取返回的文件资源, 结果为 "content://" 开头的 Uri 格式的资源,// Uri 格式参考: content://com.android.providers.media.documents/document/document%3A145val uri = data?.data ?: return// 获取文件的数据, 可以使用 ContentResolver 直接打开输入流var fileInputStream = contentResolver.openInputStream(uri)// 查询文件的详细信息val cursor = contentResolver.query(uri, null, null, null, null)...}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。