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: ImageViewprivate lateinit var mImageView1: ImageViewprivate var mUri: Uri? = nullprivate var mTextView: TextView? = nulloverride 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、實現此實例還需要申請讀寫權限:
4、需要在AndroidManifest.xml中加入:
<providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.example.myapplication.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider>總結
以上是生活随笔為你收集整理的Android 使用 ActivityResult 处理 Activity 之间的数据通信及调起拍照实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 三方库EventBus的
- 下一篇: Kotlin与Java的几种单例模式