我正在使用Kotlin,Coroutine,LiveData开发一个Android应用程序。
例如,当我导航到另一个片段时,导航到底部导航视图的第二个选项卡并返回到上一个片段,就永远不会调用onChanged
的{{1}}方法。
修改 我发现了问题。 当我持有velow之类的视图实例时会出现问题:\
Observer
但是如果我不保留它,就会出现问题,如下所示:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (rootView == null) {
rootView = inflater.inflate(layoutRes, container, false)
}
return rootView
}
SearchVM:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(layoutRes, container, false)
BaseFragment:
class SearchVM @Inject constructor(
private val docksRepository: DocksRepository,
app: Application
) : AndroidViewModel(app) {
val docks: LiveData<List<Dock>> = MutableLiveData()
val dockStart: LiveData<Dock> = MutableLiveData()
val dockEnd: LiveData<Dock> = MutableLiveData()
val dateStart: LiveData<Calendar> = MutableLiveData()
val dateEnd: LiveData<Calendar> = MutableLiveData()
init {
viewModelScope.launch(IO) {
try {
val response = docksRepository.getDocks()
docks as MutableLiveData<List<Dock>>
docks.postValue(response)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun setDockStart(dock: Dock) {
dockStart as MutableLiveData<Dock>
dockStart.postValue(dock)
}
fun setDockEnd(dock: Dock) {
dockEnd as MutableLiveData<Dock>
dockEnd.postValue(dock)
}
fun setDateStart(calendar: Calendar) {
dateStart as MutableLiveData<Calendar>
dateStart.postValue(calendar)
}
fun setDateEnd(calendar: Calendar) {
dateEnd as MutableLiveData<Calendar>
dateEnd.postValue(calendar)
}
}
SearchFragment:
@SuppressLint("Registered")
abstract class BaseFragment<T : AndroidViewModel>(
@LayoutRes private val layoutRes: Int,
private val viewModel: Class<T>
) : DaggerFragment() {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
var rootView: View? = null
lateinit var vm: T
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (rootView == null) {
rootView = inflater.inflate(layoutRes, container, false)
}
return rootView
}
override fun onAttach(context: Context) {
super.onAttach(context)
vm = ViewModelProviders.of(requireActivity(), viewModelFactory)[viewModel]
}
protected fun hideBottomNavigationView() {
(activity as MainActivity).hideBottomNav()
}
protected fun showBottomNavigationView() {
(activity as MainActivity).showBottomNav()
}
ViewModelModule.kt
class SearchFragment : BaseFragment<SearchVM>(R.layout.fragment_serach, SearchVM::class.java) {
private val now = Calendar.getInstance()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Picasso.get().load("https://imgurl.ir/uploads/t326248_.png").into(img_background)
vm.dateStart.observe(viewLifecycleOwner, Observer {
tv_startTime.text = it.getDisplayText()
})
vm.dateEnd.observe(viewLifecycleOwner, Observer {
tv_endTime.text = it.getDisplayText()
})
vm.dockStart.observe(viewLifecycleOwner, Observer {
tv_source.text = it.name
})
vm.dockEnd.observe(viewLifecycleOwner, Observer {
tv_destination.text = it.name
})
tv_source.setOnClickListener {
vm.docks.value?.let {
showDockPickerDialog(it, vm::setDockStart)
}
}
tv_destination.setOnClickListener {
vm.docks.value?.let {
showDockPickerDialog(it, vm::setDockEnd)
}
}
tv_startTime.setOnClickListener {
showDatePicker(vm.dateStart.value ?: now, vm::setDateStart)
}
tv_endTime.setOnClickListener {
showDatePicker(vm.dateEnd.value ?: now, vm::setDateEnd)
}
imageButton.setOnClickListener {
val dockStart = vm.dockStart.value
?: return@setOnClickListener toast(R.string.search_fragment_validation_error_fill_dock_start)
val dockEnd = vm.dockEnd.value
?: return@setOnClickListener toast(R.string.search_fragment_validation_error_fill_dock_end)
val dateStart = vm.dateStart.value
?: return@setOnClickListener toast(R.string.search_fragment_validation_error_fill_date_start)
val dateEnd = vm.dateEnd.value
?: return@setOnClickListener toast(R.string.search_fragment_validation_error_fill_date_end)
val action = HomeFragmentDirections.actionHomeFragmentToSearchResultFragment(
dockStart,
dockEnd,
dateStart,
dateEnd
)
view.findNavController()
.navigate(action)
}
}
private fun showDatePicker(now: Calendar, callBack: (date: Calendar) -> Unit) {
val datePickers = DatePickerDialog(
requireContext(),
DatePickerDialog.OnDateSetListener { view, year, month, dayOfMonth ->
val selectedDate = Calendar.getInstance()
selectedDate.set(Calendar.YEAR, year)
selectedDate.set(Calendar.MONTH, month)
selectedDate.set(Calendar.DAY_OF_MONTH, dayOfMonth)
callBack.invoke(selectedDate)
},
now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH)
)
datePickers.show()
}
private fun showDockPickerDialog(docks: List<Dock>, callback: (dock: Dock) -> Unit) {
val adapter = ArrayAdapter(requireActivity(), android.R.layout.simple_list_item_1, docks)
AlertDialog.Builder(requireContext())
.setAdapter(adapter) { DialogInterface, i ->
DialogInterface.dismiss()
callback.invoke(docks[i])
}.show()
}
}
AppComponent.kt
@Module
abstract class ViewModelModule {
// Bind ViewModels Here ...
@Binds
@IntoMap
@ViewModelKey(SearchVM::class)
internal abstract fun searchVM(viewModel: SearchVM): ViewModel
}
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
class ViewModelFactory @Inject constructor(
private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}
@Module
abstract class ViewModelFactoryModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
BaseActivity.kt
@Singleton
@Component(
modules = [
ViewModelModule::class,
ViewModelFactoryModule::class
]
)
interface AppComponent : AndroidInjector<App> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
如何解决此问题?