Activity接收传值,请求权限

插播:
新的AppCompatActivity以及ComponentActivity,支持构造函数中传入layotuId了,不用再setContentView()了

//fragment
  implementation 'androidx.fragment:fragment-ktx:1.3.2'
//activity
  implementation 'androidx.activity:activity-ktx:1.3.0-alpha05'

位于 ComponentActivityFragment 中时,Activity Result API 会提供 registerForActivityResult() API,用于注册结果回调。registerForActivityResult() 接受 ActivityResultContractActivityResultCallback 作为参数,并返回 ActivityResultLauncher,供您用来启动另一个 activity。

  • 跳转页面新的写法
//跳转手机默认Intent
class StartResultActivity : ComponentActivity(R.layout.activity_start_result) {
    private val TAG = "StartResultActivity"
//接受回传结果
    val result = registerForActivityResult(ActivityResultContracts.GetContent()) {
        Log.d(TAG, "onCreate: ${it}")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        text.setOnClickListener {
           //启动跳转
            result.launch("image/*")
        }

    }
}
//跳转指定activity
 val result = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
        Log.d(TAG, "onCreate: ${it.data?.getStringExtra("value")}")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        text.setOnClickListener {
            result.launch(Intent(this,ForResultMainActivity::class.java))
        }

    }

class ForResultMainActivity : AppCompatActivity(R.layout.activity_for_result_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setResult(Activity.RESULT_OK,intent.putExtra("value","Angel"))
        finish()
    }
}

registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法
最基本最简单最常用的写法

        //java写法
        registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                Intent data = result.getData();
                int resultCode = result.getResultCode();
            }
        }).launch(new Intent(context,BActivity.class));
 
        //kotlin写法
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()
        ) { 
            val data = it.data
            val resultCode = it.resultCode
        }.launch(Intent(context,BActivity::class.java))

       //launch()方法,输入Intent,ActivityResultCallback:获取返回的数据,
       //ActivityResultContracts.StartActivityForResult 是官方提供用来处理回调数据的ActivityResultContract类
       //跳转到BActivity后,调用setResult()方法传递数据,这部分和以前一样
  

使用registerForActivityResult方法,可以简化许多相关操作,举几个例子

调用联系人列表,获取联系人

        registerForActivityResult(ActivityResultContracts.PickContact()){
            if(it != null){
                val cursor = contentResolver.query(it, null, null, null, null)
                cursor?.run {
                    if(cursor.moveToFirst()){
                        val name =
                            cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                        LogUtils.e("联系人姓名:$name")
                        if(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) == "1"){
                            //该联系人名下存在手机号,查询方法自行实现
                        }
                    }
 
                }
            }
        }.launch(null)

调用相机拍照

        //需要WRITE_EXTERNAL_STORAGE权限
        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val values = ContentValues()
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, "图片名称.jpg")
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
            contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }else{
            FileProvider.getUriForFile(this,BuildConfig.authorities,File(externalCacheDir!!.absolutePath+"图片名称.jpg"))
        }    
        registerForActivityResult(ActivityResultContracts.TakePicture()){
            if(it)
                Glide.with(this).load(uri).into(binding.imageView)
        }.launch(uri)
 
 
        //或者可以用更简单的方法
        registerForActivityResult(ActivityResultContracts.TakePicturePreview()){
            Glide.with(this).load(it).into(binding.imageView)
        }.launch(null)

调用文件选择器选择文件

调用文件选择器,获取指定类型的文件,可在launch()方法里使用mimetype指定调用文件类型,这里以调用文本文档为例

        registerForActivityResult(ActivityResultContracts.GetContent()){
 
        }.launch("text/plain")

如果需要选择多种文件类型,可以使用OpenDocument

        registerForActivityResult(ActivityResultContracts.OpenDocument()){
            Glide.with(this).load(it).into(binding.imageView)
        }.launch(arrayOf("image/*","text/plain"))

获取敏感权限
使用registerForActivityResult获取权限,也会变得非常简单

获取一个权限

            registerForActivityResult(ActivityResultContracts.RequestPermission()){
               if(it){
                   //用户同意了该权限
               }else{
                   //用户拒绝了该权限
               }
 
            }.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)

获取多个权限

        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){it->
            //通过的权限
            val grantedList = it.filterValues { it }.mapNotNull { it.key }
            //是否所有权限都通过
            val allGranted = grantedList.size == it.size
            val list = (it - grantedList).map { it.key }
            //未通过的权限
            val deniedList = list.filter { ActivityCompat.shouldShowRequestPermissionRationale(this, it) }
            //拒绝并且点了“不再询问”权限
            val alwaysDeniedList = list - deniedList
        }.launch(arrayOf("权限1","权限2","权限3"))

自定义ActivityResultContract<I,O>
ActivityResultContract<I,O> 官方提供的,通过输入类型I构建意图并将回调数据转换成输入类型O的契约类,官方总共给我们提供了14个ActivityResultContract实现类(截自本文发布时间),通过ActivityResultContracts构建,可以非常轻松地调用文件,联系人,敏感权限等等,另外,我们也可以实现自己的ActivityResultContract来简化意图操作,这里写一个裁剪图片的ActivityResultContract为例

import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.webkit.MimeTypeMap
import androidx.activity.result.contract.ActivityResultContract
import java.io.File
 
class CropImage: ActivityResultContract<CropImageResult, Uri>(){
    var outUri:Uri? = null
    //构建意图
    override fun createIntent(context: Context, input: CropImageResult): Intent {
        //把CropImageResult转换成裁剪图片的意图
        val intent = Intent("com.android.camera.action.CROP")
        val mimeType = context.contentResolver.getType(input.uri)
        val imageName:String = "${System.currentTimeMillis()}.${MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)}"
        outUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            val values = ContentValues()
            values.put(MediaStore.MediaColumns.DISPLAY_NAME,imageName)
            values.put(MediaStore.MediaColumns.MIME_TYPE,mimeType)
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
            context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values)
        }else{
            Uri.fromFile(File(context.externalCacheDir!!.absolutePath, imageName))
        }
        context.grantUriPermission(context.getPackageName(),outUri, Intent.FLAG_GRANT_READ_URI_PERMISSION )
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.putExtra("noFaceDetection", true) //去除默认的人脸识别,否则和剪裁匡重叠
        intent.setDataAndType(input.uri, mimeType)
        intent.putExtra("crop", "true") // crop=true 有这句才能出来最后的裁剪页面.
        intent.putExtra("output", outUri)
        intent.putExtra("outputFormat", "JPEG") // 返回格式
        intent.putExtra("return-data", true)
 
 
        if (input.outputX != 0 && input.outputY != 0) {
            intent.putExtra("outputX", input.outputX)
            intent.putExtra("outputY", input.outputY)
        }
        if(input.aspectX != 0 && input.aspectY != 0){
            if(input.aspectY == input.aspectX && Build.MANUFACTURER == "HUAWEI"){
                intent.putExtra("aspectX", 9999)
                intent.putExtra("aspectY", 9998)
            }else{
                intent.putExtra("aspectX", input.aspectX)
                intent.putExtra("aspectY", input.aspectY)
            }
        }
 
        return intent
    }
 
    //接收意图并处理数据
    override fun parseResult(resultCode: Int, intent: Intent?): Uri {
        if(outUri != null)
            return outUri!!
        else
            return Uri.parse("")
    }
 
}
 
/**
 * uri:需要裁剪的图片
 * aspect:长宽比例
 * output:图片输出长宽
 * uri 要裁剪的图片
 * aspect 剪裁比例
 * output 输入图片长宽
 */
class CropImageResult(val uri: Uri,
                      val aspectX:Int = 0,
                      val aspectY:Int = 0,
                      @androidx.annotation.IntRange(from = 0 ,to = 1080)
                      val outputX:Int = 0,
                      @androidx.annotation.IntRange(from = 0 ,to = 1080)
                      val outputY:Int = 0)

使用:

    //裁剪图片
    private fun crop(uri: Uri) {
        registerForActivityResult(CropImage()){
            Glide.with(this).load(it.toFile).into(binding.ivImage)
        }.launch(CropImageResult(uri,1,1))
    }

registerForActivityResult()方法的使用还没做限制,新版本的ActivityResultLauncher必需在activity的onCreate()方法或fragment的onCreate()、onAttach()里先注册,然后在需要调用的地方调用launch方法,以裁剪图片为例,在activity方法里需要这样写:

class Activity1: ComponentActivity(){
    val cropImage:ActivityResultLauncher<CropRequest> =         
    registerForActivityResult(CropImage()) {
            //获取到裁剪的图片
        }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Button(this).setOnClickListener { 
            cropImage.launch(…)
        }
        
    }
}
 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容