I'm new to Android development and making an Android map app. Right now, I have three fragments: one is the map, and two are recycler view lists of locations. All of them are in the nav host in my main activity. What I want to do is click the location in the list and it takes me to the map fragment and put a marker on the map.
My idea is to set the view model in my list fragment and get the view model in my map fragment.
My view model:
private val _location = MutableLiveData<Location>()
val location: LiveData<Location> = _location
fun setLocation(location: Location) {
_location.value = location
}
My click listener in location fragment:
private var clickListenerImpl = object : LocationsAdapter.OnClickListener {
override fun onNameClick(location: Location) {
viewModel.setLocation(location)
view?.findNavController()?.navigate(
LocationsFragmentDirections.actionLocationsToMap()
)
}
}
My on map ready callback:
viewModel.location.observe(viewLifecycleOwner, { location ->
googleMap.addMarker(
MarkerOptions()
.position(LatLng(location.lat, location.lng))
)
})
Am I using the view model right? I couldn't get my view model data in the map fragment, nor my other list fragment. I tried to use log in both location and map fragment. It seems it sets the view model in location fragment properly, but the map fragment still thinks it's null, so it won't update. Or is there a better way to do this instead of using the view model?
Thanks, Everybody. I really want to give all an upvote, but my reputation is too low to do that.
Although I really want to use ViewModel, I still couldn't get it to work in the activity scope. Maybe when I initiate it in a new fragment, it overwrites its old value? I don't know.
private val viewModel: LocationViewModel by activityViewModels {
LocationViewModelFactory((requireActivity().application as LocationApplication).repository)
}
My solution is changed to use SafeArgs. It requires an extra step to convert latitude and longitude to float type but gets the job done.
CodePudding user response:
Are you using an activity scoped ViewModel? You need to use an activity scoped ViewModel or nav scoped viewModel for this to work. If you use a fragment scoped ViewModel in your map and list fragments. The fragment would get different instances of your ViewModel. In my opinion, I'd simply just pass the location data as a fragment argument instead of sharing a ViewModel.
CodePudding user response:
The ViewModel
works like this:
Each
ViewModel
is (shold be) bound to a singleLifeCycleOwner
, which refers toFragment
orActivity
. In other words, EachViewModel
is bound to a singleFragment
orActivity
.For sharing (
ViewModel's
) data betweenFragments
, use SharedViewModel pattern, the idea is, thisViewModel
will be bound to the outer (parent)Activity
, and for each its childFragment
you can get thisViewModel
instance by using:ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
CodePudding user response:
Two ways of achieving sharing ViewModel data between fragments comes to mind.
- Use an activityViewModels(). This will keep the ViewModel alive as long as the parent Activity is alive. See Share Data Between Fragments Using ViewModels .
private val model: SharedViewModel by activityViewModels()
Edit: Using by activityViewModels with a ViewModelFactory
private val model: SharedViewModel by activityViewModels() {
ExampleViewModelFactory(
repository
)
}
- Second way is to instantiate a ViewModel in the Activity then in each Fragment grab that object to use. Almost like a Singleton.