Android - Activity Result API

本文介绍Android中ActivityResult API的使用方法,包括启动Activity、请求权限等场景,并提供自定义Contract的示例。

一、概念

当 appcompat ≥ 1.3.0 的时候,startActivityForResult() 和 requestPermissions() 都已经被废弃,转而推荐更方便简洁的 Activity Result API。

registerForActivityResult( )

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

注册一个Activity结果的监听,参数一contract类型对应不同的需求,参数二是一个Lambda当有回调的时候会显示在这里。

Contract名称及作用
StartActivityForResult()开启Activity
StartIntentSenderForResult()
RequestPermission()申请权限。
RequestMultiplePermissions()申请多个权限。
TakePicturePreview()调用手机自带相机拍照,拍摄后有确认和重拍选项,确认后得到这张照片的Bitmap对象。
TakePicture()调用手机自带相机拍照,拍摄后有确认和重拍选项,确认后保存到指定的 Uri,返回true表示保存成功。
TakeVideo()调用系统自带相机录像,保存到指定的Uri,返回一张缩略图。
PickContact()从通讯录APP中获取联系人。
GetContent()选择一个内容,返回Uri。
GetMultipleContents()选择多个内容,返回多个Uri。
OpenDocument()选择一个文件,返回Uri。
OpenMultipleDocuments()选择多个文件,返回多个Uri。
OpenDocumentTree()选择一个目录(文件夹),返回Uri。
CreateDocument()创建一个文件,返回Uir。
PickVisualMedia()通过系统媒体选择器选择单张图片/视频(Android 13新增,11(API33)及以上可用)
PickMultipleVisualMedia()通过系统媒体选择器选择多张图片/视频(Android 13新增,11(API33)及以上可用)

二、使用

2.1 Activity之间传递数据 StartActivityForResult()

2.1.1 A→B 原来写法

class FirstActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val intent = Intent(this, SecondActivity::class.java)
        startActivityForResult(intent, 1)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> {
                if (resultCode == RESULT_OK) {
                    val data = data?.getStringExtra("data")
                    // Handle data from SecondActivity
                }
            }
        }
    }
}

2.1.2 A→B 现在写法

class FirstActivity : AppCompatActivity() {
    private val requestDataLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            val data = result.data?.getStringExtra("data")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        val intent = Intent(this, SecondActivity::class.java)
        requestDataLauncher.launch(intent)
    }
}

2.1.3 B→A 写法没变

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val intent = Intent()
        setResult(RESULT_OK, intent)
        finish()
    }
}

2.2 请求权限

建议使用 PermissionX

2.2.1 单个权限 RequestPermission()

class MyActivity : AppCompatActivity() {
    private val permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            takePic()
        } else {
            Toast.makeText(APP.context, "相机权限被拒绝", Toast.LENGTH_SHORT).show()
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        button.setOnClickListener {
            permissionLauncher.launch(Manifest.permission.CAMERA)
        }
        //判断是否有权限
        if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {}
        //是否显示权限说明(只有第一次点拒绝后才会返回true)
        if ( shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) ) {}
    }
}

2.2.2 多个权限 RequestMultiplePermissions()

class MainActivity : AppCompatActivity() {
	private val requestMultiplePermissions = registerForActivityResult(
		ActivityResultContracts.RequestMultiplePermissions()
	) { permissions ->  //类型是 Map<String, Boolean>
		permissions.entries.forEach {
			//TODO...
		}
	}
	override fun onCreate(savedInstanceState: Bundle?) {
		requestMultiplePermissions.launch(
			arrayOf(
				Manifest.permission.BLUETOOTH,
				Manifest.permission.NFC,
			)
		)
	}
}

2.3 文件操作

2.3.1 获取文件 GetContent()

class MainActivity : AppCompatActivity() {
    private val requestUriLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
        if(uri != null) {
            //将uri转为输入流来读取文件
            val inputStream = contentResolver.openInputStream(uri)
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        requestUriLauncher.launch("image/*")    //只显示图片
    }
}

2.3.2 写入文件 CreateDocument()

class MainActivity : AppCompatActivity() {
    private val createDocument = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri ->
        if (uri != null) {
            //将uri转为输出流来写入文件
            val outputStream = contentResolver.openOutputStream(uri)
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        createDocument.launch("demo.txt")
    }
}

2.3.3 选择目录(选择文件夹) OpenDocumentTree()

class TestActivity : AppCompatActivity() {
    private val openTree = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
        //uri转File后使用
        uri?.path?.let {
            val directory = File(it)
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        openTree.launch(null)   //可传入默认目录uri
    }
}

2.4 拍照并获取Bitmap TakePicturePreview()

class MainActivity : AppCompatActivity() {
    private val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicturePreview()) { bitmap ->
        //TODO...
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        takePictureLauncher.launch(null)
    }
}

2.5 通过系统媒体选择器选取图片/视频

Android 13 之前还是通过应用内置的图片选择器来实现业务功能,13 之后的版本仅使用系统内置的图片选择器,完全弃用 READ_EXTERNAL_STORAGE 权限。

private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
    if (uri != null) {
        //TODO
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_result)

    //选择图片或视频
    pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo))

    //仅选择图片
    pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))

    //仅选择视频
    pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly))

    //仅选择 Gif 图片
    pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.SingleMimeType("image/gif")))

}

三、自定义 Contract

继承 ActivityResultContract 类,指定输入参数和输出参数的泛型,重写 createIntent() 用于创建 Intent 发起动作,重写 parseResult() 用于解析响应的结果并作为输出参数返回到 Lambda 中。

class GetDataFromSecondActivity : ActivityResultContract<Void, String?>() {
    override fun createIntent(context: Context, input: Void?): Intent {
        return Intent(context, SecondActivity::class.java)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        if (resultCode == Activity.RESULT_OK) {
            if (intent != null) {
                return intent.getStringExtra("data")
            }
        }
        return null
    }
}
class MainActivity : AppCompatActivity() {
    private val getDataLauncher = registerForActivityResult(GetDataFromSecondActivity()) { data ->
        //TODO...
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        getDataLauncher.launch(null)
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值