【问题标题】:Can't communication between DialogFragment and Activity using Observer pattern?无法使用观察者模式在 DialogFragment 和 Activity 之间进行通信?
【发布时间】:2019-12-25 10:36:49
【问题描述】:

当你按下按钮打开一个单独的输入窗口时,有一个显示结果toast的功能。

class MainActivity : AppCompatActivity() {

val disposable = CompositeDisposable()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    button.setOnClickListener {
        val f = TestPopup()
        usingRxJava(f)
        //usingLiveData(f)
    }
}

private fun usingRxJava(f: TestPopup) {
    val subject = SingleSubject.create<String>()
    f.show(supportFragmentManager, "TAG")
    button.post {
        f.dialog.setOnDismissListener {
            val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
            subject.onSuccess(str)
        }
    }
    subject.subscribe({
        Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
    }, {

    }).addTo(disposable)
}

private fun usingLiveData(f: TestPopup) {
    val liveData = MutableLiveData<String>()
    f.show(supportFragmentManager, "TAG")
    button.post {
        f.dialog.setOnDismissListener {
            val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
            liveData.postValue(str)
        }
    }
    liveData.observe(this, Observer {
        Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
    })
}

override fun onDestroy() {
    disposable.dispose()
    super.onDestroy()
}
}

对话框片段

class TestPopup : DialogFragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.dialog_test, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    button_test.setOnClickListener {
        val arg = Bundle()
        arg.putString(TEST_KEY, edit_test.text.toString())
        arguments = arg
        dismiss()
    }
}

companion object {
    const val TEST_KEY = "KEY"
}
}

(示例项目网址:https://github.com/heukhyeon/DialogObserverPattern)

此示例代码在正常情况下有效。但是,经过以下过程,吐司不会浮动。

  1. 开发者选项 - 不要启用活动
  2. 打开 TestPopup,然后输入您的文本。 (不要按确定按钮)
  3. 按主页按钮将应用移至后台
  4. 应用被系统杀死。
  5. 重新激活应用(就像点击应用列表中的应用一样)

在这种情况下,我输入的文本仍保留在屏幕上,但当我按下 OK 按钮时没有任何反应。

我当然知道会发生这种情况,因为在活动结束时,活动和对话框之间的观察关系结束了。

大部分代码使用Activity中那个Dialog的回调接口的实现来处理这种情况。

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    button_test.setOnClickListener {
        val input = edit_test.text.toString()
        (activity as MyListener).inputComplete(input)
        dismiss()
    }
}

class MainActivity : AppCompatActivity(), TestPopup.MyListener {

override fun inputComplete(input: String) {
    Toast.makeText(this, "Accept : $input", Toast.LENGTH_SHORT).show()
}
}

但我认为这是一种不符合观察者模式的方式,我想尽可能使用观察者模式来实现它。

我正在考虑从 FragmentManager 获取 Fragment 并在 onCreate 再次订阅,但我认为有更好的方法。

有人可以帮我吗?

【问题讨论】:

    标签: dialog rx-java android-dialogfragment observer-pattern android-livedata


    【解决方案1】:

    您对问题的理解是正确的,除了任何配置更改都会出现问题,包括屏幕旋转。您可以在不使用开发人员模式的情况下重现问题。试试这个例子:

    1. 打开 TestPopup,然后输入您的文本。 (不要按确定按钮)
    2. 旋转屏幕
    3. 查看 toast 消息未弹出。

    另请注意,您的“观察者模式”实现不是正确的观察者模式。观察者模式有一个主题和一个观察者。在您的实现中,活动同时充当 主题 和 观察者。对话框不参与此观察者模式,使用.setOnDismissListener 只是另一种形式的侦听器模式。

    为了在 Fragment(主体)和 Activity(观察者)之间实现观察者模式,Activity 需要按照您的建议使用 FragmentManager 获取 Fragment 的引用。我建议使用视图模型,并在视图层和视图模型层之间建立观察者模式。

    RxJava 示例:

    //MainViewModel.kt
    class MainViewModel: ViewModel() {
    
        val dialogText = PublishProcessor.create<String>()
    
        fun postNewDialogText(text: String) {
            dialogText.onNext(text)
        }
    }
    
    // Activity
    val disposable = CompositeDisposable()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    
        val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    
        viewModel.dialogText.subscribe {
            Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
        }.addTo(disposable)
    
        button.setOnClickListener {
            TestPopup().show(supportFragmentManager, "TAG")
            // usingRxJava(f)
            // usingLiveData(f)
        }
    }
    
    override fun onDestroy() {
        disposable.dispose()
        super.onDestroy()
    }
    
    // Dialog Fragment
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    
        // Important!! use activity when getting the viewmodel.
        val viewModel = ViewModelProviders.of(requireActivity()).get(MainViewModel::class.java)
    
        button_test.setOnClickListener {
            viewModel.postNewDialogText(edit_test.text.toString())
            dismiss()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      • 2021-05-08
      • 2021-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多