Как функция ViewModel может получить доступ к экземпляру Place, возвращенному PlaceSelectionListener из AutocompleteSupportFragment? - PullRequest
0 голосов
/ 04 апреля 2020

Я создаю приложение Android с использованием Kotlin.

У меня есть макет, содержащий create_button . Когда пользователь нажимает кнопку, onClick должен вызвать onCreateJourney() в моей ViewModel. Для этой функции требуется параметр selectedPlaceId, чтобы обновить сущность Journey, указав ее значение.

В своем фрагменте я использую AutocompleteSupportFragment, чтобы пользователь мог выполнить поиск и выбрать нужное место. Это хорошо работает, так как onPlaceSelected() возвращает правильное место в ответ на выбор пользователя. Но я не могу понять, как использовать возвращенное значение идентификатора места (p0.id) в моей ViewModel onCreateJourney().

На этом этапе компилятор выдает эту ошибку:

не удается найти метод onCreateJourney () в классе com.example.traveljournal.journey.NewJourneyViewModel

Пожалуйста, не могли бы вы пролить свет на эту проблему для меня?

Мой пользовательский интерфейс ( xml макет фрагмента):

<?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>

Моя 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()
    }
}

Мой фрагмент

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)
    }
}

Ответы [ 2 ]

0 голосов
/ 05 апреля 2020

В NewJourneyViewModel я установил selectedPlaceId как MutableLiveData.

val selectedPlaceId = MutableLiveData<String>()

В NewJourneyFragment я использовал lateinit для создания поля для NewJourneyViewModel с именем newJourneyViewModel и инициализировал его в onCreateView().

private lateinit var newJourneyViewModel : NewJourneyViewModel

Теперь, имея доступ к моей ViewModel за пределами onCreateView(), в моем методе Fragment onPlaceSelected() я могу установить значение selectedPlaceId в p0.id.

newJourneyViewModel.selectedPlaceId.value = p0.id

Если кто-нибудь сможет придумать лучший и безопасный подход к этому, он будет высоко оценен.

0 голосов
/ 04 апреля 2020

Вы должны передать поле selectedPlaceId в качестве аргумента в xml:

android:onClick="@{() -> newJourneyViewModel.onCreateJourney(newJourneyViewModel.selectedPlaceId)}"

Как вы получите модель selectedPlaceId в представлении, зависит от ваших программных логик c.

В целях тестирования вы можете жестко закодировать значение.

...