【问题标题】:My observer is always firing when I come back from one fragment to another当我从一个片段返回到另一个片段时,我的观察者总是在开火
【发布时间】:2020-06-10 19:38:32
【问题描述】:

我正在使用导航组件,我有 Fragment AFragment B,从 Fragment A 我发送一个对象到 带有安全参数的片段 B 并导航到它。

override fun onSelectableItemClick(position:Int,product:Product) {
        val action = StoreFragmentDirections.actionNavigationStoreToOptionsSelectFragment(product,position)
        findNavController().navigate(action)
    }

现在,在我的 Fragment B 中的一些逻辑之后,我想再次将该数据传递给 Fragment A,我使用它

  btn_add_to_cart.setOnClickListener {button ->     
 findNavController().previousBackStackEntry?.savedStateHandle?.set("optionList",Pair(result,product_position))
                findNavController().popBackStack()
            }

然后在Fragment A中,我用

赶上了这些数据
findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Pair<MutableList<ProductOptions>,Int>>("optionList")
            ?.observe(viewLifecycleOwner, Observer {
                storeAdapter.updateProductOptions(it.second,it.first)
            })

现在,这工作正常,但如果我从 Fragment A 转到 Fragment B 并按下后退按钮,上面的观察者会再次触发复制我当前的数据,当我只按下 Fragment B 中的btn_add_to_cart 按钮时,有没有办法触发这个观察者?

【问题讨论】:

标签: android android-fragments kotlin android-viewmodel android-architecture-navigation


【解决方案1】:

你使用这个扩展:

fun <T> Fragment.getResult(key: String = "key") =
    findNavController().currentBackStackEntry?.savedStateHandle?.get<T>(key)


fun <T> Fragment.getResultLiveData(key: String = "key"): MutableLiveData<T>? {

    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            findNavController().previousBackStackEntry?.savedStateHandle?.remove<T>(key)
        }
    })

    return findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)
}

fun <T> Fragment.setResult(key: String = "key", result: T) {
    findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
}

例子:

片段A -> 片段B

Fragment B需要设置TestModel.class的结果

ResultTestModel.class

data class ResultTestModel(val id:String?, val name:String?)

片段 A:

 override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    // ...

    getNavigationResultLiveData<PassengerFragmentResultNavigationModel>(
           "UNIQUE_KEY")?.observe(viewLifecycleOwner) { result ->
           Log.i("-->","${result.id} and ${result.name}")
    }

    //...
}

Fragment B:设置数据并调用popBackStack。

ResultTestModel(id = "xyz", name = "Rasoul")
setNavigationResult(key = "UNIQUE_KEY", result = resultNavigation)
findNavController().popBackStack()

【讨论】:

    【解决方案2】:

    从您的代码中并不清楚最后一段代码是在哪里调用的 - 您在哪里将 Observer 添加到 LiveData。我猜它在 onResume()onViewStateRestored() 方法之一或任何其他生命周期回调中,每当您返回 Fragment A 来自 片段 B。如果是这种情况,那么您将向 LiveData 添加一个新的观察者,并且 LiveData 的任何观察者都会收到当前值的即时更新。

    将那段代码移动到在片段的生命周期中只调用一次的回调方法之一。

    【讨论】:

      【解决方案3】:

      面临同样的问题

      通过从savedStateHandle 实时数据中删除旧数据来解决此问题

      片段 B 内部:

            button?.setOnClickListener {
      
              findNavController().previousBackStackEntry?.savedStateHandle?.set(key, data)
              findNavController().popBackStack()
      
           }
      

      内部片段 A:

      这是使用实时数据 remove 方法删除旧数据的关键,它应该在视图创建之后,如片段的 onViewCreated 方法

       override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
          
              findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>(key)?.observe(viewLifecycleOwner) {
                  result(it)
                  findNavController().currentBackStackEntry?.savedStateHandle?.remove<String>(key)
              }
              
          }
      

      更新:

      我为此创建了扩展程序以便更好地使用

         fun <T> Fragment.setBackStackData(key: String, data: T, doBack: Boolean = false) {
              findNavController().previousBackStackEntry?.savedStateHandle?.set(key, data)
              if (doBack)
                  findNavController().popBackStack()
          }
          
          fun <T> Fragment.getBackStackData(key: String, singleCall : Boolean= true , result: (T) -> (Unit)) {
              findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)
                  ?.observe(viewLifecycleOwner) {
                      result(it)
                      //if not removed then when click back without set data it will return previous data
                      if(singleCall) findNavController().currentBackStackEntry?.savedStateHandle?.remove<T>(key)
                  }
          }
      

      在片段内部调用就像

      在片段 B 中设置数据时

         var user : User = User(data) // Make sure this is parcelable or serializable   
         setBackStackData("key",user,true)
      

      在片段 A 中获取数据时

      getBackStackData<User>("key",true) { it ->
      
      }
      

      感谢This Guy

      【讨论】:

        猜你喜欢
        • 2022-11-19
        • 2020-05-07
        • 1970-01-01
        • 1970-01-01
        • 2015-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多