Кинжал 2 Внедрить класс DialogUtils в действие - PullRequest
0 голосов
/ 13 октября 2019

Я пытаюсь внедрить мой DialogUtils класс, которому требуется контекст активности в его конструкторе, в мой LoginActivity. Код ниже работает, но я просто инициализирую DialogUtils и не внедряю его.

DialogUtils.kt

class DialogUtils constructor(context: Context) {

    private val dialog: AlertDialog

    init {
        dialog = AlertDialog.Builder(context)
            .setPositiveButton(R.string.ok) { dialog, _ ->
                dialog.dismiss()
            }
            .create()
    }

    fun showDialog(title: String, msg: String) {
        dialog.setTitle(title)
        dialog.setMessage(msg)
        dialog.show()
    }

    fun clear() {
        if (dialog.isShowing) {
            dialog.dismiss()
        }
    }

}

LoginActivity.kt

class LoginActivity : DaggerAppCompatActivity() {

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private val loginViewModel by lazy {
        ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel::class.java)
    }
    private lateinit var dialogUtils: DialogUtils

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        dialogUtils = DialogUtils(this)
    }

    override fun onPause() {
        super.onPause()
        dialogUtils.clear()
    }

}

Что яхочу сделать это @Inject dialogUtils: DialogUtils в моем LoginActivity, но для этого мне понадобится контекст активности. Как мне достичь этого, помня, что DialogUtils используется во многих видах деятельности.

Ответы [ 2 ]

1 голос
/ 13 октября 2019

В вашем дизайне есть недостаток, который не имеет ничего общего даже с DI или Dagger: ваш класс DialogUtils будет использовать простые AlertDialog s вместо DialogFragment s. Это означает, что диалоги, сгенерированные вашим классом, не являются постоянными на экране (например, во время перезапуска активности, например, когда устройство вращается), что дает плохой пользовательский опыт.

Так что вместо этого я бы написал пользовательскийAlertDialogFragment реализация, которая взаимодействует через обратные вызовы со своим хостом (который может быть Activity или Fragment), что означает, что ваша реализация фрагмента диалога ищет в определенных местах, если она находит хост, который реализует свой обратный вызов и передает ответные действия пользователя(например, нажатия кнопок и т. д.). Это некоторый служебный код, который я обычно использую в своих проектах для этой цели:


inline fun <reified T> Fragment.requireCallback(): T =
    findCallback() ?: error("No parent / target found or parent / target does not implement " + T::class.java)

inline fun <reified T> Fragment.findCallback(): T? {
    val callback: Any? = parentFragment ?: targetFragment ?: context
    return callback as? T
}

Это может войти в вашу реализацию AlertDialogFragment следующим образом:

class AlertDialogFragment : DialogFragment() {

    private lateinit var listener: Listener

    interface Listener {
        fun onDialogResult(requestCode: Int, result: AlertDialogResult)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        listener = requireCallback()
    }
    ...
}

Теперь, когда вы создаете свой экземплярв диалоге вашего родителя Activity или Fragment, вам просто нужно позволить этому родителю реализовать интерфейс AlertDialogFragment.Listener, и вы перенаправите его результаты на ваш ViewModel - и это хорошо: это будет работать при перезапуске активности!

Вы можете задаться вопросом, хотите ли вы требовать, чтобы обратный вызов был реализован на хосте для каждого диалога;Мое личное правило: для однокнопочных диалогов, которые в основном подтверждают действия (например, диалоговые окна с сообщениями об ошибках), я не требую наличия обратного вызова (т. е. используйте простой findCallback()), в то время как для специальных реализаций DecisionDialogFragment, которые имеют болееДля нажатия одной кнопки я обычно выполняю обратный вызов, поэтому мое приложение на самом деле падает, если обратный вызов не реализован на хосте, так как это обычно означает отсутствие фрагмента кода на моей стороне.

0 голосов
/ 13 октября 2019

Вы можете связать LoginActivity (и все остальные) как Context в модуле, а затем добавить этот модуль к компоненту, который вы используете для внедрения своей деятельности. Затем ваши утилиты могут без проблем внедрить / использовать Context.

, например, в вашем LoginModule

@Module
interface LoginModule {
  @Binds
  bindActivityContext(activity: Context) : Context
}

... затем добавить модуль в ваш компонент ...

@(Sub)Component(modules=[..., LoginModule::class])
interface LoginComponent {
  // ...
}

Если вы уже связали контекст, вы можете связать другой, используя @Named(..) или любую другую аннотацию квалификатора. Вы также можете связать его как Activity напрямую.

...