Android 使用 ActivityResult 处理 Activity 之间的数据通信及调起拍照实例

在 Android 中,我们如果想在 Activity 之间双向传递数据,需要使用 startActivityForResult 启动,然后在 onActivityResult 中处理返回,另外申请权限也是类似的步骤。
但是这样的处理方式会让我们的代码变得非常复杂,并且也无法保证在 Activity 发送或接收数据时参数的类型安全。
ActivityResult 是 Jetpack 提供的一个功能,可以简化 Activity 直接的数据传递 (包括权限申请)。它通过提供类型安全的 contract (协定) 来简化处理来自 Activity 的数据。这些协定为一些常见操作 (比如: 拍照或请求权限) 定义了预期的输入和输出类型,除此之外您还能够自定义协定来满足不同场景的需求。
ActivityResult API 提供了一些组件用于注册 Activity 的处理结果、发起请求以及在系统返回结果后立即进行相应处理。您也可以在启动 Activity 的地方使用一个独立的类接收返回结果,这样依然能够保证类型安全。

一、ActivityResult 使用

使用 ActivityResult 先添加依赖:

dependencies {
  // 在 https://developer.android.google.cn/jetpack/androidx/releases/activity 获得最新版本号
  def activity_version = "1.2.0"
  // 在 https://developer.android.google.cn/jetpack/androidx/releases/fragment 获得最新版本号
  def fragment_version = "1.3.0"

  implementation "androidx.activity:activity:$activity_version"
  implementation "androidx.fragment:fragment:$fragment_version”
}

然后先看看最简单的使用方式,比如打开系统文件管理器选择一个图片,代码如下:

val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
    // 处理返回的 Uri
}

getContent.launch("image/*") //过滤图片

这里涉及几个重要的类和函数:

(1) registerForActivityResult: 是 ComponentActivity 的一个函数,注意这里的 ComponentActivity 是 androidx.activity.ComponentActivity 而不是 androidx.core.app.ComponentActivity,androidx.core 中的对应类 (截止 1.3.0) 还不支持这项功能。

public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback)

可以看到这个函数接收两个参数,分别是 ActivityResultContract 和回调 ActivityResultCallback,ActivityResultContract 是封装启动所需要的各项参数 (组成 Intent,后面会细说)。函数返回 ActivityResultLauncher,可以看到后面通过他的 launch 函数就可以启动 activity。

(2) GetContent: ActivityResultContracts.GetContent 类是一个继承 ActivityResultContract 的具体实现类,封装了调用系统文件管理器的功能。Jetpack 提供了一些常用的 ActivityResultContract,比如选取图片,拍照等等,如果我们需要拉起自己的 Activity,就需要自定义一个 ActivityResultContract。

(3) launch: ActivityResultLauncher 的函数,启动 activity,代替了之前的 startActivity。

二、在 Jetpack 提供的已封装好的 ActivityResultContract 有 (都是 ActivityResultContracts 的子类):

(1) StartActivityForResult:

最简单的,相当于传统方式的 startActivityForResult,只不过将 onActivityResult 的几个参数封装成一个 ActivityResult。

(2) StartIntentSenderForResult:

相当于 Activity.startIntentSender(IntentSender, Intent, int, int, int),与 PendingIntent 配合使用。

(3) RequestMultiplePermissions:

用于批量申请权限。以 Map 形式返回每个权限的情况。

(4) RequestPermission

申请单个权限,通过这两个来申请权限就可以很方便的进行后续处理。

(5) TakePicturePreview

拉起拍照预览,直接返回 bitmap 数据。(跟传统方式一样,这个 bitmap 只是一个图片预览,因为 intent 中不能传输过大的数据) 。
注意虽然输入是 Void,但是执行 ActivityResultLauncher 的 lanch 函数是还需要传入一个 null 才行。

(6) TakePicture

拉起拍照,输入图片要保存的位置 uri

(7) TakeVideo

录制视频,输入视频要保存的位置 uri,返回视频的缩略图

(8) PickContact

选取联系人

(9) GetContent

获取单个文件,输入过滤类型,返回文件 uri

(10) GetMultipleContents

文件多选,同上

(11) OpenDocument

打开单个文档 (拉起的是系统文档管理器)
对应 Intent.ACTION_OPEN_DOCUMENT,输入的是类型过滤 (如 image/*),输出 uri

(12) OpenMultipleDocuments

打开多个文档,与上面类似

(13) OpenDocumentTree

打开文档 tree,对应 Intent.ACTION_OPEN_DOCUMENT_TREE

(14) CreateDocument

新建一个文档,对应 Intent.ACTION_CREATE_DOCUMENT

可以看到 Android 已经将常用的功能都封装了,基本可以满足我们的开发使用。

三、使用TakePicture调起拍照实例

代码:MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var mImageView: ImageView
    private lateinit var mImageView1: ImageView
    private var mUri: Uri? = null
    private var mTextView: TextView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mImageView = findViewById(R.id.image)
        mImageView1 = findViewById(R.id.image1)
        mTextView = findViewById(R.id.tx)
        Glide.with(this).load("/storage/emulated/0/Android/data/com.example.myapplication/cache.jpg").into(mImageView1)
        mUri = FileProvider.getUriForFile(
            this, applicationContext.packageName + ".fileprovider",
            File(externalCacheDir!!.absolutePath + ".jpg")
        )
        findViewById<Button>(R.id.button).setOnClickListener {
            mTakePicture.launch(mUri)
        }
    }

    private val mTakePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) {
        if (it) {
            try {
                val bmp = MediaStore.Images.Media.getBitmap(
                    contentResolver,
                    Uri.fromFile(File(mUri?.path.toString().substring(5)))
                )
                mImageView.setImageBitmap(bmp)
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
}

代码说明:
1、代码中有一个mImageView和一个mImageView1,在mTakePicture代码块中,如果直接使用Glide.with(this).load方式加载图片至mImageView,没图片时会成功,有图片后,会出现图片不更新,不是刚所拍照片,是因为直接使用uri会有缓存的原因。故使用Bitmap方式去加载图片。
2、之所以mUri?.path需要substring(5),因为mUri?.path的路径为(/root/storage/emulated/0/Android/data/com.example.myapplication/cache.jpg),多了一个/root,导致读取不到照片,所以需要将其处理一下。
3、实现此实例还需要申请读写权限:

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

4、需要在AndroidManifest.xml中加入:

       <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.myapplication.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true"
            >
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,可以使用接口回调来实现两个 Activity 之间的数据传递和通信。下面是一个简单的示例: 首先,在定义接口时,创建一个名为 `DataListener` 的接口,用于定义数据传递的方法: ```java public interface DataListener { void onDataReceived(String data); } ``` 然后,在发送数据的 ActivityActivity A)中,需要持有一个 `DataListener` 类型的引用,并在某个操作时调用接口方法将数据传递给监听者: ```java public class ActivityA extends AppCompatActivity { private DataListener dataListener; public void setDataListener(DataListener listener) { this.dataListener = listener; } private void sendDataToActivityB(String data) { if (dataListener != null) { dataListener.onDataReceived(data); } } } ``` 在接收数据的 ActivityActivity B)中,需要实现 `DataListener` 接口,并在方法中处理接收到的数据: ```java public class ActivityB extends AppCompatActivity implements DataListener { // ... @Override public void onDataReceived(String data) { // 处理接收到的数据 // ... } } ``` 在 Activity B 中,可以通过以下方式将自身作为监听者注册到 Activity A 中: ```java ActivityA activityA = new ActivityA(); ActivityB activityB = new ActivityB(); activityA.setDataListener(activityB); ``` 当 Activity A 调用 `sendDataToActivityB()` 方法发送数据时,Activity B 实现的 `onDataReceived()` 方法将会被触发,并在该方法中进行相应的处理。 需要注意的是,在 Android 中,Activity 之间的启动和通信通常使用 `Intent` 或 `startActivityForResult()` 方法。接口回调通常用于 Activity 之间的松耦合通信,例如在两个独立的 Activity 中进行数据传递和通知。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值