原文連結:https://juejin.im/post/6869045188963074061
前言
我們都知道在Activity之間傳遞資料很繁瑣,為了簡化,很多人都是使用過EventBus,可EventBus在追蹤問題時反而束手無策,反而增加調試時間,那我們能不能找一個折中的方案,又能簡單的實作,又能容易追查問題呢?下面請允許我介紹一下最新的方式在實作資料的Result。
先來看看以前我們都是如何做的
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQUEST_PERMISSION -> {
// Do something if success / failed
}
REQUEST_MULTIPLE_PERMISSION -> {
// Do something if success / failed
}
REQUEST_TO_POST -> {
// Parse result and do something
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
companion object {
const val REQUEST_PERMISSION = 1001
const val REQUEST_MULTIPLE_PERMISSION = 1002
const val REQUEST_TO_POST = 1003
}
複制代碼
以前,我們都是這樣處理Result,分别定義不同的RequestCode,然後根據這個值來比對資料。我們再來看看新的方式,然後做個比較
Activity Result新方式
step one
加入依賴
implementation 'androidx.activity:activity-ktx:1.2.0-alpha08'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha08'
複制代碼
step two
編碼
class PostActivityContract(val postId: Int) : ActivityResultContract<Int, String?>() {
override fun createIntent(context: Context, input: Int): Intent {
return Intent(context, ResultActivity::class.java).apply {
putExtra(MainActivity.ID, postId)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): String? {
val data = intent?.getStringExtra(ResultActivity.TITLE)
return if (resultCode == Activity.RESULT_OK && data != null) data
else null
}
}
複制代碼
繼承ActivityResultContract,并覆寫createIntent和parseResult函數,這倆函數很好了解,一個負責建立Intent,一個負責解析Result,那接下來如何使用呢?
class MainActivity : AppCompatActivity() {
private val openPostActivityCustom =
registerForActivityResult(PostActivityContract(2)) { result ->
Log.d("MainActivity", "Result : $result")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
openPostActivityCustom.launch(1)
}
companion object {
val ID = "id"
}
}
複制代碼
隻需要調用registerForActivityResult函數,然後在點選事件中,用其傳回值調用launch函數即可
class ResultActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_result)
Log.d("ResultActivity", intent.getIntExtra(MainActivity.ID,0).toString())
}
companion object{
val TITLE = "title"
}
fun result(view: View) {
setResult(Activity.RESULT_OK, Intent().apply {
putExtra(TITLE,"title")
})
finish()
}
}
複制代碼
代碼運作效果
這麼實作有什麼優點呢?
- 一方面針對ResultActivity的啟動傳參數,更加的具體,啟動該Activity,不必要關心postId的key是什麼,這樣在其他頁面需要調用的時候,也隻是傳入參數,這樣對于以後key的重構就不會影響其他部分
- 另一方面不再需要定義RequestCode,也不錯哦
- 其實它還有個優勢哦,看過源碼才知道,其實它是結合了lifecycle,在頁面銷毀的時候,會自動将生成的RequestCode給remove掉
Fragment Result新方式
一張圖看清整個通訊的過程
Fragment B 使用 FragmentManager 将資料發送到 Fragment A
在生成結果的 Fragment B 中,必須使用相同的 requestKey 在同一 FragmentManager 上設定結果。您可以使用 setFragmentResult() API 來完成此操作:
button.setOnClickListener {
val result = "result"
// Use the Kotlin extension in the fragment-ktx artifact
setResult("requestKey", bundleOf("bundleKey" to result))
}
複制代碼
在Fragment A中注冊回調結果監聽
FragmentManager.setFragmentResultListener(
requestKey,
lifecycleOwner,
FragmentResultListener { requestKey: String, result: Bundle ->
// 處理結果
})
複制代碼
這種情況需要注意的就是requestKey,在出去和傳回的時候都要保持一緻。還有一種情況是,如果有多個資料傳遞,隻會接收到最新的值,更Activity一樣,在DESTROYED時,會自動取消listener,以上是同級Fragment的傳遞,如果是父子級呢?
如圖所示,需要在Parent Fragment 調用 childFragmentManager來注冊listener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// We set the listener on the child fragmentManager
childFragmentManager.setResultListener("requestKey") { key, bundle ->
val result = bundle.getString("bundleKey")
// Do something with the result..
}
}
複制代碼
這麼實作有什麼優點呢?
- 完全解耦,無需依賴對方
- 當ON_DESTROY時自動登出
總結
經過這期,主要了解到,最新的Activity和Fragment之間如何用新的方式傳遞資料,且都是kotlin版本的擴充,java也能用,但kotlin更加的簡潔好用,也希望能幫助到你,記得點個贊哦。