1

The issue that I have is not actually bug or big problem. And all works as it should, but nevertheless it annoys me.

In Fragment pbserver:

viewModel.pageNumbersPosition.observe(viewLifecycleOwner) {
        if (it!=null) {
            SharedPreferenceHelper.pagesNumber = viewModel.pageNumbersArray.value?.get(it)
            DLog.d("Set: ${viewModel.pageNumbersArray.value?.get(it)}}")
            //Log shows twice as start
        }
}

ViewModel:

class MenuViewModel : ViewModel() {

    var pageNumbersArray = MutableLiveData(getPageNumbering())
    var pageNumbersPosition = MutableLiveData(pageNumbersArray.value?.indexOf(SharedPreferenceHelper.pagesNumber))

    private fun getPageNumbering():Array<String> {
        val list = mutableListOf<String>()
        for (i in 1..25) {
            list.add(i.toString())
        }
        return list.toTypedArray()

    }
}

Spinner:

 <Spinner
                android:id="@+id/spinner"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:entries="@{viewModel.pageNumbersArray}"
                android:selectedItemPosition="@={viewModel.pageNumbersPosition}"/>

What happes is viewModel.pageNumbersPosition.observe triggered twice on start. Once from the initiation of the fragment and second time when the spinner sets. This is actually suppose to happen, but I don't like it when Shared Preference sets twice.

Sergio
  • 27,326
  • 8
  • 128
  • 149
Dim
  • 4,527
  • 15
  • 80
  • 139

2 Answers2

1

I came across a handy class SingleLiveEvent that we can use instead of LiveData in ViewModel class to send only new updates after subscription.

class SingleLiveEvent<T> : MutableLiveData<T>() {

    private val pending = AtomicBoolean(false)

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner, Observer<T> { t ->
            if (pending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    override fun setValue(t: T?) {
        pending.set(true)
        super.setValue(t)
    }

    fun call() {
        postValue(null)
    }
}

This LiveData extension only calls the observable if there's an explicit call to setValue() or call().

Update, primary constructor with parameter:

class SingleLiveEvent<T>(value: T) : MutableLiveData<T>(value) {...}
Sergio
  • 27,326
  • 8
  • 128
  • 149
  • How singleLiveEvent could be set with initial value without triggering setValue() or call() like: var pageNumbersPosition = MutableLiveData(pageNumbersArray.value?.indexOf(SharedPreferenceHelper.pagesNumber)) – Dim May 17 '22 at 20:11
  • You can try to add a primary constructor with parameter for `SingleLiveEvent`. Please see my updated answer. – Sergio May 17 '22 at 20:24
0

You can check if there is equal value in your shared to avoid the double set

if (it!=null) {
    viewModel.pageNumbersArray.value?.get(it).let{ value ->
        if (SharedPreferenceHelper.pagesNumber != value) 
             SharedPreferenceHelper.pagesNumber = value
}

}