Reputation: 1
I am building an Android app using Kotlin.
I have a layout that contains a create_button. When the user clicks the button, the onClick should call onCreateJourney()
in my ViewModel. This function requires the selectedPlaceId
parameter to update a Journey entity with its value.
In my Fragment, I use the AutocompleteSupportFragment for the user to search and choose the desired place. This works well, as onPlaceSelected()
returns the correct place in response to the user's selection. But I can't figure out how to use the returned place id value (p0.id) in my ViewModel onCreateJourney()
.
At this stage, the compiler throws this error:
cannot find method onCreateJourney() in class com.example.traveljournal.journey.NewJourneyViewModel
Please, can you shed some light on this issue for me?
My UI (.xml fragment layout):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".journey.NewJourneyFragment">
<data>
<variable
name="newJourneyViewModel"
type="com.example.traveljournal.journey.NewJourneyViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/whereQuestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="10dp"
android:text="@string/whereQuestion"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.027"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/journeyBckgImageView" />
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent">
</androidx.cardview.widget.CardView>
<fragment
android:id="@+id/autocomplete_fragment"
android:name="com.google.android.libraries.places.widget.AutocompleteSupportFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/whereQuestion" />
<Button
android:id="@+id/create_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:onClick="@{() -> newJourneyViewModel.onCreateJourney()}"
android:text="@string/createJourneyButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
My ViewModel
class NewJourneyViewModel (
private val journeyKey: Long = 0L,
val database: TravelDatabaseDao) : ViewModel() {
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val _navigateToJourneys = MutableLiveData<Boolean?>()
val navigateToJourneys: LiveData<Boolean?>
get() = _navigateToJourneys
fun doneNavigating() {
_navigateToJourneys.value = null
}
fun onCreateJourney(selectedPlaceId: String) {
uiScope.launch {
withContext(Dispatchers.IO) {
val journey = database.getJourney(journeyKey) ?: return@withContext
journey.placeId = selectedPlaceId
database.updateJourney(journey)
}
_navigateToJourneys.value = true
}
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
My Fragment
class NewJourneyFragment : Fragment(), PlaceSelectionListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.createJourney)
val binding: FragmentNewJourneyBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_new_journey, container, false
)
val application = requireNotNull(this.activity).application
val arguments = NewJourneyFragmentArgs.fromBundle(arguments!!)
val dataSource = TravelDatabase.getInstance(application).travelDatabaseDao
val viewModelFactory = NewJourneyViewModelFactory(arguments.journeyKey, dataSource)
val newJourneyViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(NewJourneyViewModel::class.java)
binding.newJourneyViewModel = newJourneyViewModel
newJourneyViewModel.navigateToJourneys.observe(this, Observer {
if(it == true) {
this.findNavController().navigate(
NewJourneyFragmentDirections.actionNewJourneyDestinationToJourneysDestination())
newJourneyViewModel.doneNavigating()
}
})
if (!Places.isInitialized()) {
this.context?.let { Places.initialize(it, getString(R.string.apiKey), Locale.US) }
}
val autocompleteFragment = childFragmentManager.findFragmentById(R.id.autocomplete_fragment)
as? AutocompleteSupportFragment
autocompleteFragment?.setOnPlaceSelectedListener(this)
autocompleteFragment!!.setHint(getString(R.string.destinationExample))
autocompleteFragment!!.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME))
return binding.root
}
@ExperimentalStdlibApi
override fun onPlaceSelected(p0: Place) {
Log.i("PLACE", "Place: " + p0.name + ", " + p0.id)
}
override fun onError(status: Status) {
Toast.makeText(this.context,""+status.toString(),Toast.LENGTH_LONG).show()
Log.i("ERROR", "An error occurred: " + status)
}
}
Upvotes: 0
Views: 192
Reputation: 1
In NewJourneyViewModel, I set up selectedPlaceId
as MutableLiveData.
val selectedPlaceId = MutableLiveData<String>()
In NewJourneyFragment, I used lateinit
to create a field for NewJourneyViewModel called newJourneyViewModel
and initialized it in onCreateView()
.
private lateinit var newJourneyViewModel : NewJourneyViewModel
Now having access to my ViewModel outside of onCreateView()
, in my Fragment onPlaceSelected()
method I can set the value of selectedPlaceId
to p0.id
.
newJourneyViewModel.selectedPlaceId.value = p0.id
If anybody can come up with a better and safer approach on this, it will be highly appreciated.
Upvotes: 0
Reputation: 5595
You have to pass the field selectedPlaceId
as an argument in the xml :
android:onClick="@{() -> newJourneyViewModel.onCreateJourney(newJourneyViewModel.selectedPlaceId)}"
How will you get the selectedPlaceId in view model depends on your programming logic.
For testing purposes, you can hardcode the value.
Upvotes: 0