Jetpack架構元件學習(3)——Activity Results API使用

one發表於2022-06-11

原文地址:Jetpack架構元件學習(3)——Activity Results API使用 - Stars-One的雜貨小窩

技術與時俱進,頁面跳轉傳值一直使用的是startActivityForResult方法,如今有了新的API實現方式,學習並稍微總結下 ?

startActivityForResult複習

MainActivity程式碼:

Main2Activity程式碼:

效果:

上面的程式碼應該是比較基礎的程式碼,這裡我就不再贅述了

主要說些缺點

所有邏輯都在onActivityResult()方法裡進行判斷,根據requestCoderesultCode進行判斷

如果單個還好說,但是如果有多個的,就會看見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),返回使用者所選檔案Uri
  • OpenDocumentTree: 提示使用者選擇一個目錄,並返回使用者選擇的作為一個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")

參考

相關文章