Timmy1e
Timmy1e

Reputation: 11

Android RecyclerView submitList not updating data in LiveData observer

I am trying to update my nested RecyclerView list using submitList in a LiveData Observer. The list is submitted, but the UI only updates when you touch the screen. The issue arose when I added nested RecyclerViews in my master RecyclerView. So I think it has something to do with it.

I want it to update after the submitList is called. What am I doing wrong?

Outer ListAdapter:

class ReservationAdapter(
        private val changeState: (visit: Visit, newState: Visit.State) -> MutableLiveData<Visit?>
) : ListAdapter<Reservation, ReservationViewHolder>(ReservationDiffCallback()) {
    private val recycledViewPool = RecyclerView.RecycledViewPool()

    /// Filters \\\
    // Building
    var filterBuildingId = ""
        set(value) {
            field = value
            filteredVisits = visits.filter(Visit.filter(value, filterSearchString))
        }

    // Search string
    var filterSearchString = ""
    set(value) {
        field = value
        filteredVisits = visits.filter(Visit.filter(filterBuildingId, value))
    }

    /// Filtering chain \\\
    // Visits come in and get filtered
    var visits = arrayListOf<Visit>()
        set(value) {
            field = value
            filteredVisits = value.filter(Visit.filter(filterBuildingId, filterSearchString))
        }

    // Filtered visits come in, a reservation map is made
    var filteredVisits = listOf<Visit>()
        set(value) {
            field = value
            reservationVisits = Reservation.extractMapFromVisits(value)
        }

    // Reservation map comes in, and submits a list of reservations
    private var reservationVisits = hashMapOf<Reservation, ArrayList<Visit>>()
        set(value) {
            field = value
            _reservationVisits.value = value
            submitList(value.keys.toMutableList())
            notifyDataSetChanged()
        }
    private val _reservationVisits = MutableLiveData<HashMap<Reservation, ArrayList<Visit>>>()


    /**
     * Inflate items
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReservationViewHolder {
        return ReservationViewHolder(
                LayoutInflater.from(parent.context).inflate(
                        R.layout.list_reservation,
                        parent,
                        false
                ),
                recycledViewPool,
                changeState,
                _reservationVisits
        )
    }

    /**
     * Bind items
     */
    override fun onBindViewHolder(holder: ReservationViewHolder, position: Int) {
        val reservation = getItem(position)
        holder.bind(reservation, reservationVisits[reservation])
    }
}

Outer ViewHolder:

class ReservationViewHolder(
        private val view: View,
        private val recycledViewPool: RecyclerView.RecycledViewPool,
        changeState: (visit: Visit, newState: Visit.State) -> MutableLiveData<Visit?>,
        reservationVisits: MutableLiveData<HashMap<Reservation, ArrayList<Visit>>>
) : RecyclerView.ViewHolder(view) {
    private val adapter = VisitAdapter(changeState)
    private var _reservation: Reservation? = null

    init {
/*
THIS IS WHERE I SETS THE VALUE, BUT DOESN'T UPDATE THE UI.
*/
        reservationVisits.observe(view.context as LifecycleOwner) {
            if (_reservation != null) {
                (view.context as MainActivity).runOnUiThread {
                    adapter.submitList(null)
                    adapter.notifyDataSetChanged()
                    adapter.submitList(it[_reservation!!]?.toMutableList())
                    adapter.notifyDataSetChanged()
                    view.refreshDrawableState()
                }
            }
        }
    }

    fun bind(reservation: Reservation, visits: ArrayList<Visit>?): View = with(view) {
        println("3: ${visits?.size}")
        _reservation = reservation
        // Initialize values
        txtSubject.text = reservation.subject
        txtTime.text = "TIME"
        // Recycler view for visits
        rcvVisits.apply {
            (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
            layoutManager = LinearLayoutManager(context)
            adapter = [email protected]
            setRecycledViewPool([email protected])
        }

        // Add visits to adapter
        adapter.submitList(visits)

        // Return view
        view
    }
}

Inner ListAdapter:

class VisitAdapter(
        private val changeState: (visit: Visit, newState: Visit.State) -> MutableLiveData<Visit?>
) : ListAdapter<Visit, VisitViewHolder>(VisitDiffCallback()) {
    // Sorter
    private val _visitSorter = compareBy<Visit>({ it.state }, { it.expectedArrival }, { it.visitor?.name })

    // ListAdapter create
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VisitViewHolder {
        return VisitViewHolder(
                LayoutInflater.from(parent.context).inflate(
                        R.layout.list_visitor,
                        parent,
                        false
                ),
                changeState
        )
    }

    // ListAdapter bind
    override fun onBindViewHolder(holder: VisitViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    // ListAdapter submit
    override fun submitList(list: List<Visit>?) {
        super.submitList(list?.sortedWith(_visitSorter) ?: listOf())
    }
}

Upvotes: 1

Views: 2290

Answers (2)

Timmy1e
Timmy1e

Reputation: 11

I solved it. The issue was with the com.mikepenz:aboutlibraries:7.0.4@aar library.

After I removed it and commented out any code that used it, it started working.

Upvotes: 0

Ehsan Msz
Ehsan Msz

Reputation: 2164

I checked your code these are what I found:

you created an instance of the adapter and an observer in view holder class.

view holder will instantiate multiple times, so you shouldn't instantiate the adapter in it. check this link

and if you want to pass data to your view holder, you should do it in the constructor, never observe for data change in view holder class.

so you can observe data in activity and pass data to your adapter.

and there is no need to call runOnUiThread in an observer. its already on main Thread (UI Thread).

The events are dispatched on the main thread. If LiveData already has data set, it will be delivered to the observer. LiveData

Upvotes: 1

Related Questions