插播:
新的AppCompatActivity以及ComponentActivity,支持構(gòu)造函數(shù)中傳入layotuId了,不用再setContentView()了
//fragment
implementation 'androidx.fragment:fragment-ktx:1.3.2'
//activity
implementation 'androidx.activity:activity-ktx:1.3.0-alpha05'
位于 ComponentActivity 或 Fragment 中時,Activity Result API 會提供 registerForActivityResult() API,用于注冊結(jié)果回調(diào)。registerForActivityResult() 接受 ActivityResultContract 和 ActivityResultCallback 作為參數(shù),并返回 ActivityResultLauncher,供您用來啟動另一個 activity。
- 跳轉(zhuǎn)頁面新的寫法
//跳轉(zhuǎn)手機默認Intent
class StartResultActivity : ComponentActivity(R.layout.activity_start_result) {
private val TAG = "StartResultActivity"
//接受回傳結(jié)果
val result = registerForActivityResult(ActivityResultContracts.GetContent()) {
Log.d(TAG, "onCreate: ${it}")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
text.setOnClickListener {
//啟動跳轉(zhuǎn)
result.launch("image/*")
}
}
}
//跳轉(zhuǎn)指定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()的替代,簡化了數(shù)據(jù)回調(diào)的寫法
最基本最簡單最常用的寫法
//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:獲取返回的數(shù)據(jù),
//ActivityResultContracts.StartActivityForResult 是官方提供用來處理回調(diào)數(shù)據(jù)的ActivityResultContract類
//跳轉(zhuǎn)到BActivity后,調(diào)用setResult()方法傳遞數(shù)據(jù),這部分和以前一樣
使用registerForActivityResult方法,可以簡化許多相關(guān)操作,舉幾個例子
調(diào)用聯(lián)系人列表,獲取聯(lián)系人
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("聯(lián)系人姓名:$name")
if(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) == "1"){
//該聯(lián)系人名下存在手機號,查詢方法自行實現(xiàn)
}
}
}
}
}.launch(null)
調(diào)用相機拍照
//需要WRITE_EXTERNAL_STORAGE權(quán)限
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)
調(diào)用文件選擇器選擇文件
調(diào)用文件選擇器,獲取指定類型的文件,可在launch()方法里使用mimetype指定調(diào)用文件類型,這里以調(diào)用文本文檔為例
registerForActivityResult(ActivityResultContracts.GetContent()){
}.launch("text/plain")
如果需要選擇多種文件類型,可以使用OpenDocument
registerForActivityResult(ActivityResultContracts.OpenDocument()){
Glide.with(this).load(it).into(binding.imageView)
}.launch(arrayOf("image/*","text/plain"))
獲取敏感權(quán)限
使用registerForActivityResult獲取權(quán)限,也會變得非常簡單
獲取一個權(quán)限
registerForActivityResult(ActivityResultContracts.RequestPermission()){
if(it){
//用戶同意了該權(quán)限
}else{
//用戶拒絕了該權(quán)限
}
}.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
獲取多個權(quán)限
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()){it->
//通過的權(quán)限
val grantedList = it.filterValues { it }.mapNotNull { it.key }
//是否所有權(quán)限都通過
val allGranted = grantedList.size == it.size
val list = (it - grantedList).map { it.key }
//未通過的權(quán)限
val deniedList = list.filter { ActivityCompat.shouldShowRequestPermissionRationale(this, it) }
//拒絕并且點了“不再詢問”權(quán)限
val alwaysDeniedList = list - deniedList
}.launch(arrayOf("權(quán)限1","權(quán)限2","權(quán)限3"))
自定義ActivityResultContract<I,O>
ActivityResultContract<I,O> 官方提供的,通過輸入類型I構(gòu)建意圖并將回調(diào)數(shù)據(jù)轉(zhuǎn)換成輸入類型O的契約類,官方總共給我們提供了14個ActivityResultContract實現(xiàn)類(截自本文發(fā)布時間),通過ActivityResultContracts構(gòu)建,可以非常輕松地調(diào)用文件,聯(lián)系人,敏感權(quán)限等等,另外,我們也可以實現(xiàn)自己的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
//構(gòu)建意圖
override fun createIntent(context: Context, input: CropImageResult): Intent {
//把CropImageResult轉(zhuǎn)換成裁剪圖片的意圖
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
}
//接收意圖并處理數(shù)據(jù)
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()里先注冊,然后在需要調(diào)用的地方調(diào)用launch方法,以裁剪圖片為例,在activity方法里需要這樣寫:
class Activity1: ComponentActivity(){
val cropImage:ActivityResultLauncher<CropRequest> =
registerForActivityResult(CropImage()) {
//獲取到裁剪的圖片
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Button(this).setOnClickListener {
cropImage.launch(…)
}
}
}