0

This is my first time posting here. Please forgive me for any formatting issues <3

I'm trying to implement two-way data binding to record the current BPM number. When I click the add BPM or sub BPM button, I can observe on the back-end that the bpm field in the View Model is getting updated, but the UI is not. I have my fragment, view model, and layout shown below. I appreciate any and all help I receive!

Fragment Layout

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="practiceSegViewModel"
            type="com.example.drumpracticeorganizer.practiceseg.PracticeSegViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/practice_seg_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ececec">

        <EditText
            android:id="@+id/edit_text_bpm"
            style="@style/fragment_add_practice_label"
            android:hint="@string/bpm"
            app:layout_constraintEnd_toEndOf="@+id/edit_text_type"
            app:layout_constraintStart_toStartOf="@+id/edit_text_type"
            app:layout_constraintTop_toBottomOf="@+id/spinner_practice_sub_category" />

        <Button
            android:id="@+id/button_sub_bpm"
            style="@style/fragment_add_practice_button_bpm"
            android:onClick="@{() -> practiceSegViewModel.onDecrementBpm()}"
            android:text="@string/minus_sign"
            app:layout_constraintEnd_toStartOf="@+id/edit_text_bpm_input"
            app:layout_constraintTop_toBottomOf="@+id/edit_text_bpm" />

        <Button
            android:id="@+id/button_add_bpm"
            style="@style/fragment_add_practice_button_bpm"
            android:onClick="@{() -> practiceSegViewModel.onIncrementBpm()}"
            android:text="@string/plus_sign"
            app:layout_constraintStart_toEndOf="@+id/edit_text_bpm_input"
            app:layout_constraintTop_toBottomOf="@+id/edit_text_bpm" />

        <EditText
            android:id="@+id/edit_text_bpm_input"
            style="@style/fragment_add_practice_input"
            android:layout_width="100dp"
            android:gravity="center_horizontal"
            android:inputType="number"
            android:maxLength="3"
            android:text="@={`` + practiceSegViewModel.practice.bpm}"
            android:textAlignment="center"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/edit_text_bpm" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Fragment Class

class PracticeSegFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        setHasOptionsMenu(true)

        val application = requireNotNull(this.activity).application
        val dataSource = PracticeDatabase.getInstance(application).practiceDatabaseDao
        val viewModelFactory = PracticeSegViewModelFactory(dataSource, application)
        val practiceSegViewModel =
            ViewModelProvider(this, viewModelFactory).get(PracticeSegViewModel::class.java)

        val binding = DataBindingUtil.inflate<FragmentPracticeSegBinding>(
            inflater,
            R.layout.fragment_practice_seg,
            container,
            false
        ).apply {
            this.practiceSegViewModel = practiceSegViewModel
            this.lifecycleOwner = viewLifecycleOwner
        }

        return binding.root
    }
}

View Model

class PracticeSegViewModel(datasource: PracticeDatabaseDao, application: Application) :
    AndroidViewModel(application) {
 
    private val _practice = MutableLiveData<PracticeEntity>().apply {
        value = PracticeEntity(subCategory = "", category = "", bpm = 0, duration = "0")
    }
    val practice: LiveData<PracticeEntity>
        get() = _practice

    val dao: PracticeDatabaseDao = datasource

    fun onSave() {
        savePractice()
    }

    private fun savePractice() {
        viewModelScope.launch { _practice.value?.let { dao.insert(it) } }
    }

    fun onIncrementBpm() {
        _practice.value?.bpm?.let {
            if (it < 300) {
                _practice.value?.bpm = it.plus(1)
                println(_practice.value?.bpm.toString())
            }
        }
    }

    fun onDecrementBpm() {
        _practice.value?.bpm?.let {
            if (it > 0) {
                _practice.value?.bpm = it.minus(1)
                println(_practice.value?.bpm.toString())
            }
        }
    }
}
  • setting value to `_practice.value?.bpm` won't work to update UI in two-way binding in LiveData case because you are manipulating the variable inside an object of livedata, try updating value as `_practice.value = PracticeEntity(...)` this might help you. – Amir Raza Feb 13 '21 at 22:04
  • Thanks for your reply Amir! What if I captured the individual variable of bpm and incremented or decremented it, then create the practice entity object when I save? Then I wouldn't be manipulating bpm inside of the live data object. Let me know if that makes sense... – fruitlessartery Feb 13 '21 at 22:37
  • Please disregard my previous comment. Your solution works! Thanks so much for your help! – fruitlessartery Feb 14 '21 at 00:16
  • I had the same problem before. You can see my question and self answer and also see comments here https://stackoverflow.com/questions/56741250/update-object-type-livedata-with-databinding – Amir Raza Feb 15 '21 at 13:53

0 Answers0