0

I have a BottomSheetDialogFragment containing a RecyclerView in my Android app. The data for the RecyclerView is being received one by one from a socket connection. I need to add these items to the RecyclerView dynamically as they come in and set a timer on each item using a ViewModel.

The main issue I'm encountering is that the RecyclerView items are not getting updated in the UI when new data is added. The changes only reflect when the state of the fragment or the RecyclerView is changed. I am using a ViewModel to manage the data and timer, and the socket is running asynchronously, but the UI updates are delayed or not showing until the state changes.

I've tried notifying the adapter using notifyItemInserted() and other common methods, but the problem persists.

Steps I've followed:

  • Data is received via the socket one by one.
  • Data is added to the list in the ViewModel.
  • The RecyclerView adapter is updated accordingly.
  • I attempt to set a timer for each item as it is added.

Any help or suggestions on how to ensure the RecyclerView updates in real time when new data is added would be appreciated.

This is my code:

BottomSheetDialogFragment

class RideRequestsBottomSheet(
    private val context: Context,
    private var viewModel: RideRequestViewModel,
    private val onRideRequestClickListener: OnRideRequestClickListener
) : BottomSheetDialogFragment() {

    private lateinit var binding: BottomSheetDialogCustomerDetailsBinding
    private lateinit var adapter: RideRequestAdapter

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        binding = BottomSheetDialogCustomerDetailsBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        adapter = RideRequestAdapter(context, onRideRequestClickListener, viewModel)
        binding.rvRideRequests.layoutManager = LinearLayoutManager(context)
        binding.rvRideRequests.adapter = adapter

        viewModel.rideRequests.observe(viewLifecycleOwner) { rideRequests ->
            adapter.updateData(rideRequests)
        }

        viewModel.timers.observe(viewLifecycleOwner) { timers ->
            adapter.updateTimers(timers)
        }
    }

This is how I am getting data from Socket and setting it up on BottomSheetDialogFragment

socketService.listenToRideRequestEvent(object : SocketCallback<ReceiveRideRequestResponse> {
            override fun onListen(data: ReceiveRideRequestResponse) {
                openBottomSheet()
                rideRequestViewModel.addRideRequest(data)
            }

            override fun onError(error: Throwable) {
                // Handle errors here if needed
            }
        })

RideRequestAdapter

class RideRequestAdapter(
    private val context: Context,
    private val onRideRequestClickListener: OnRideRequestClickListener,
    private val viewModel: RideRequestViewModel
) : RecyclerView.Adapter<RideRequestAdapter.RideRequestViewHolder>() {

    private var dataList: List<ReceiveRideRequestResponse> = emptyList()
    private val timerData: MutableMap<String, Long> = mutableMapOf()

    inner class RideRequestViewHolder(val binding: ItemRequestRidesBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RideRequestViewHolder {
        val binding = ItemRequestRidesBinding.inflate(LayoutInflater.from(context), parent, false)
        return RideRequestViewHolder(binding)
    }

    override fun onBindViewHolder(holder: RideRequestViewHolder, position: Int) {
        val binding = holder.binding
        val item = dataList[position]

        binding.tvFare.text = context.getString(R.string.price, item.fare.toString())

        // Update timer from ViewModel
        val remainingTime = timerData[item.rideRequestId] ?: 0
        binding.tvTimer.text = "${remainingTime / 1000}"

        // Handle accept/reject buttons
        binding.mbAcceptRide.setOnClickListener {
            onRideRequestClickListener.onAccept(item)
            viewModel.removeRideRequest(item.rideRequestId!!)
        }

        binding.mbRejectRide.setOnClickListener {
            onRideRequestClickListener.onReject(item)
            viewModel.removeRideRequest(item.rideRequestId!!)
        }
    }

    override fun getItemCount(): Int = dataList.size

    fun updateData(newData: List<ReceiveRideRequestResponse>) {
        this.dataList = newData
        notifyDataSetChanged()
    }

    fun updateTimers(newTimers: Map<String, Long>) {
        this.timerData.clear()
        this.timerData.putAll(newTimers)
        notifyDataSetChanged()
    }
}

ViewModel


class RideRequestViewModel : ViewModel() {
    private val _rideRequests = MutableLiveData<MutableList<ReceiveRideRequestResponse>>()
    val rideRequests: LiveData<MutableList<ReceiveRideRequestResponse>> = _rideRequests

    private val _timers = MutableLiveData<Map<String, Long>>() 
    val timers: LiveData<Map<String, Long>> = _timers

    private val timerJobs = mutableMapOf<String, Job>()

    init {
        _rideRequests.value = mutableListOf()
        _timers.value = emptyMap()
    }

    fun addRideRequest(request: ReceiveRideRequestResponse) {
        _rideRequests.value?.let {
            it.add(request)
            _rideRequests.value = it
        }
        startTimer(request.rideRequestId!!)
    }

    // Start a timer for a specific rideRequestId
    private fun startTimer(rideRequestId: String) {
        val timerDuration = 25000L // 25 seconds
        timerJobs[rideRequestId] = viewModelScope.launch {
            var remainingTime = timerDuration
            while (remainingTime > 0) {
                delay(100L)
                remainingTime -= 100L
                _timers.value = _timers.value.orEmpty().toMutableMap().apply {
                    this[rideRequestId] = remainingTime
                }
            }
            removeRideRequest(rideRequestId)
        }
    }

    fun removeRideRequest(rideRequestId: String) {
        timerJobs[rideRequestId]?.cancel()
        timerJobs.remove(rideRequestId)

        _rideRequests.value?.let {
            val updatedList = it.filter { request -> request.rideRequestId != rideRequestId }
            _rideRequests.value = updatedList.toMutableList()
        }
    }
}

1 Answer 1

0

You can update the list using the below way also, I think in your case the list item are not copying in this line this.dataList = newData You can try with addAll method this way, I hope the data will update

// Initial mutable list
var myList: MutableList<String> = mutableListOf("Apple", "Banana", "Orange")

// Method to update the list
fun updateList() {
    // Clear the existing list and add new elements
    myList.clear()
    myList.addAll(listOf("Grapes", "Mango", "Pineapple"))
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.