原文地址:Jetpack架構元件學習(3)——Activity Results API使用 - Stars-One的雜貨小窩
技術與時俱進,頁面跳轉傳值一直使用的是startActivityForResult
方法,如今有了新的API實現方式,學習並稍微總結下 ?
startActivityForResult複習
MainActivity程式碼:
Main2Activity程式碼:
效果:
上面的程式碼應該是比較基礎的程式碼,這裡我就不再贅述了
主要說些缺點
所有邏輯都在onActivityResult()
方法裡進行判斷,根據requestCode
和resultCode
進行判斷
如果單個還好說,但是如果有多個的,就會看見onActivityResult()
裡一堆的if邏輯,閱讀起來就十分繁瑣,且維護困難
谷歌官方也是考慮到了這個,於是便是在新版本推出了個Activity Results API去替代了上面所述的方式,下面就介紹下如何使用
簡單使用
1.引入依賴
首先,需要我們引入依賴:
implementation 'androidx.appcompat:appcompat:1.3.1'
PS: 請使用1.3.1以上版本,低版本沒有這個列舉類
ActivityResultContracts
我們先以上面的例子,使用Activity Results API
Main2Acitivity
程式碼不用動,我們只需要調整MainActivity
檔案裡程式碼,如下所示:
2.建立契約
val contract = ActivityResultContracts.StartActivityForResult()
contract變數的物件類名為ActivityResultContract
ActivityResultContracts
相當於一個列舉類,是谷歌官方貼心封裝的,裡面提供了一些常用的ActivityResultContract
類物件供我們使用
像拍照,申請許可權的等操作,從程式碼提示就可以看到了,如下圖所示:
這裡我們選用StartActivityForResult()
,字面意思應該很好理解,就是應用在就是頁面跳轉並返回資料的情景
PS:根據我們選用的ActivityResultContracts,會影響第4步中的傳參型別
下面補充下對應的選擇說明:
StartActivityForResult
: 通用的Contract,不做任何轉換,Intent作為輸入,ActivityResult作為輸出,這也是最常用的一個協定。CreateDocument
: 提示使用者選擇一個文件,返回一個(file:/http:/content:)開頭的Uri。GetContent
: 提示用選擇一條內容,返回一個通過ContentResolver#openInputStream(Uri)訪問原生資料的Uri地址(content://形式) 。預設情況下,它增加了 Intent#CATEGORY_OPENABLE, 返回可以表示流的內容。GetMultipleContents
:獲取多條內容OpenDocument
: 提示使用者選擇指定型別檔案(輸入引數為mimeType),返回使用者所選檔案UriOpenDocumentTree
: 提示使用者選擇一個目錄,並返回使用者選擇的作為一個Uri返回,應用程式可以完全管理返回目錄中的文件。OpenMultipleDocuments
: 提示使用者選擇文件(可以選擇多個),分別返回它們的Uri,以List的形式。PickContact
: 從通訊錄APP獲取聯絡人RequestMultiplePermissions
:用於請求一組許可權RequestPermission
: 用於請求單個許可權TakePicturePreview
: 呼叫MediaStore.ACTION_IMAGE_CAPTURE
拍照,返回值為Bitmap圖片TakePicture
: 呼叫MediaStore.ACTION_IMAGE_CAPTURE
拍照,並將圖片儲存到給定的Uri地址,返回true表示儲存成功。TakeVideo
: 呼叫MediaStore.ACTION_VIDEO_CAPTURE
拍攝視訊,儲存到給定的Uri地址,返回一張縮圖。
具體引數和說明可以使用的時候檢視文件哦~
實際上,如果上面所列還不能滿足我們的需求,那麼我們也可以自定義契約操作,在文章下面再進行補充說明,這裡就先不進行擴充套件了
3.建立契約(註冊Contact)
//註冊ActivityResultContract
val myLauncher = registerForActivityResult(contract){
if (it.resultCode==2) {
val data = it.data
if (data != null) {
val resultData = data.getStringExtra("mydata")
Toast.makeText(this, resultData, Toast.LENGTH_SHORT).show()
}
}
}
使用Activity類中registerForActivityResult()
方法,進行契約的註冊,實際上就是相當於註冊了一個監聽,之後從Main2Activity頁面返回MainActivity頁面,會回撥這個裡面的方法
注意: 此
registerForActivityResult
方法需要在Activity的生命週期的onStart()
之前前進行呼叫!!
4.發起頁面跳轉
val intent = Intent(this, Main2Activity::class.java)
myLauncher.launch(intent)
呼叫myLauncher物件的launch()
方法,將intent物件傳遞即可實現頁面跳轉的操作
PS: 這裡還是在按鈕的點選事件裡,方便閱讀就省略了
之後從Main2Activity頁面返回之後,會回撥第二步中的操作,效果與上面的動圖演示一致,這裡就不再重新貼個圖了
自定義ActivityResultContract
ActivityResultContract
實際上還包含兩個泛型,完整應該是這樣ActivityResultContract<I,O>
I
為input的意思,意為輸入引數型別O
為output的意思,意為輸出引數型別
ActivityResultContract<I,O>
是個抽象類,我們想要實現自定義,那麼就直接繼承它
class MyContract: ActivityResultContract<String, String>() {
override fun createIntent(context: Context, input: String?): Intent {
//這裡input的型別,就是上文說到的I
}
override fun parseResult(resultCode: Int, intent: Intent?): String {
//這裡方法返回的結果型別,就是上文說到的O
}
}
繼承發現,需要我們實現兩個方法,createIntent()
和parseResult()
一眼過去其實很好理解,createIntent()
就是建立一個intent物件,呼叫launch()
方法的時候(上面使用的第4步操作),裡面就會根據此intent進行頁面的跳轉操作
而parseResult()
方法,則是建立契約那步,裡面回撥的資料型別
我們以上面的例子,發現我們還得宣告一個Intent物件傳遞,以及回傳的時候還得通過intent物件去獲取資料,有些繁瑣,有些程式碼可以封裝成通用的
照著這個想法,我們可以實現一個自定義ActivityResultContract,傳遞頁面引數即可拿到Main2Activity返回的資料,程式碼如下所示:
class MyContract: ActivityResultContract<KClass<out Activity>, String>() {
override fun parseResult(resultCode: Int, intent: Intent?): String? {
if (resultCode == 2 && intent!=null) {
return intent.getStringExtra("mydata")
}
return null
}
override fun createIntent(context: Context, input: KClass<out Activity>?): Intent {
val intent = Intent(context,input?.java)
return intent
}
}
使用:
//1.建立契約
val contract = MyContract()
//2.註冊ActivityResultContract
val myLauncher = registerForActivityResult(contract){
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
btnGo.setOnClickListener {
//2.發起頁面跳轉
myLauncher.launch(Main2Activity::class)
}
當然,這裡還可以優化,如我們讓MyContract多個建構函式,這樣取值的key也可以通過此進行定義
class MyContract(val key: String) : ActivityResultContract<KClass<out Activity>, String>() {
override fun parseResult(resultCode: Int, intent: Intent?): String? {
if (resultCode == 2 && intent != null) {
return intent.getStringExtra(key)
}
return null
}
override fun createIntent(context: Context, input: KClass<out Activity>?): Intent {
val intent = Intent(context, input?.java)
return intent
}
}
使用:
//建立契約裡傳參即可
val contract = MyContract("mydata")