1

The RecyclerView is supposed to show some users working hours and allow him to edit/delete them. To enable editing and deleting, I attached an ItemTouchHelper.SimpleCallback to the RecyclerView in order to have swipe-to-delete and swipe-to-edit. At first it all worked like a charm, but after making some changes to the networking layer it broke in a weird way.

Every time I swipe, the adapter used in callback has an empty item list, despite items being displayed. If I click on the item, everything works fine. I checked using the debugger and the adapter that is used by onSwiped is the exact same instance as the adapter used by the activity.

I tried placing field watcher on shifts, no luck. I tried debugging every method that accesses the same list, noone ever empties it.

Here is the Adapter, I ommited some of the methods because they aren't even reached:

class UserActivityRecyclerViewAdapter constructor(
    private val context: Context,
    private val listener: UserActivityListener
) : RecyclerView.Adapter<UserActivityRecyclerViewAdapter.ViewHolder>(), SwipeActions {

    private val shifts = arrayListOf<Shift>()
    private val recentlyDeleted: Queue<Shift> = ArrayDeque()
    private val recentlyDeletedPosition: Queue<Int> = ArrayDeque()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

        val context = parent.context
        val inflater = LayoutInflater.from(context)
        val recyclerViewItem = inflater.inflate(R.layout.item_user_activity, parent, false)
        return ViewHolder(recyclerViewItem)
    }

    override fun getItemCount(): Int {
        return shifts.size
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

        // setting view content
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position)
        } else {

            payloads.forEach { list ->

                (list as List<Any?>).forEach {
                 // check for specific changes in order to show a nicer animation than the default bubble
                }
            }
        }
    }

    override fun deleteItem(rootView: View, position: Int) {
        recentlyDeleted.add(shifts[position])
        recentlyDeletedPosition.add(position)
        shifts.removeAt(position)
        notifyItemRemoved(position)
        showUndoSnackbar(rootView)
    }

    private fun undoDelete() {
        val recDelPos = recentlyDeletedPosition.remove()
        shifts.add(recDelPos, recentlyDeleted.remove())
        notifyItemInserted(recDelPos)
    }

    override fun editItem(position: Int) {
        listener.onEdit(shifts[position], position)
        notifyItemChanged(position)
    }

    // this method is the only way for other classes to interact with content
    fun updateContent(newShifts: List<Shift>) {

        val sorted = newShifts.sortedByDescending { it.arrivalTime } // to be displayed
        val diffResult = DiffUtil.calculateDiff(ShiftsDiffCallback(this.shifts, sorted), true)

        shifts.clear()
        shifts.addAll(sorted)

        diffResult.dispatchUpdatesTo(this)
    }

    interface UserActivityListener {
        fun onDelete(shift: Shift, position: Int)
        fun onEdit(shift: Shift, position: Int)
    }

    class ViewHolder(itemView: View) :
        RecyclerView.ViewHolder(itemView)
}

And here is some of the callback, I ommited the onChildDraw because it has nothing to do with logic:

class SwipeActionCallback(
    private val adapter: UserActivityRecyclerViewAdapter,
    private var rootView: View
) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT) {

 override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }

    // this is the moment when adapter.itemCount = 0 for the first time
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        if (direction == ItemTouchHelper.RIGHT) {
            adapter.deleteItem(rootView, viewHolder.adapterPosition)
        } else {
            adapter.editItem(viewHolder.adapterPosition)
        }
    }
}

And here is how I initialize the RecyclerView:

private fun initRecyclerView(activity: FragmentActivity) {
        mRecyclerView = user_activity_recycler_view
        mRecyclerView?.isNestedScrollingEnabled = false
        mRecyclerView?.layoutManager = LinearLayoutManager(context)
        val recyclerViewAdapter = UserActivityRecyclerViewAdapter(
            context!!,
            object : UserActivityRecyclerViewAdapter.UserActivityListener {

                override fun onDelete(shift: Shift, position: Int) {
                    deleteShift(shift)
                }

                override fun onEdit(shift: Shift, position: Int) {
                    editShift(shift)
                }
            }
        )
        mRecyclerView?.adapter = recyclerViewAdapter

        updateRecyclerView()

        val itemTouchHelper =
            ItemTouchHelper(
                SwipeActionCallback(
                    mRecyclerView?.adapter as UserActivityRecyclerViewAdapter,
                    activity.window.decorView.findViewById(R.id.layout_home_activity)
                )
            )
        itemTouchHelper.attachToRecyclerView(mRecyclerView)
    }

And how I update it:

private fun updateRecyclerView() {
        (mRecyclerView?.adapter as UserActivityRecyclerViewAdapter)
            .updateContent(
                user!!.getUserActivity( // this returns a filtered list of shifts
                    timeManager.arrivalTime,
                    timeManager.exitTime
                )
            )
    }
Filip Pranklin
  • 345
  • 1
  • 3
  • 14

0 Answers0