1

When using the itemTouchHelper onMove method. How can you while using a numbered list based on the position in the list.

For example, item 1, item 2, item 3, etc. all based on the actual position in the list the item is in. How can you then when moving the item by dragging it up or down. Update the rest of the items in the list to reflect the proper position in the list it’s in?

I'm using the below code:

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

            val fromPosition = viewHolder.adapterPosition
            val toPosition = target.adapterPosition

            Collections.swap(steps!!, fromPosition, toPosition)

            recyclerView.adapter!!.notifyItemMoved(fromPosition,toPosition)

            return true
        }

Snippet:

enter image description here

Video:

enter image description here

UPDATE (Adapter code):

val TAG:String = "StepAdapter"
var steps: MutableList<Step> = ArrayList()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StepViewHolder {
    return StepViewHolder(
        LayoutInflater.from(parent.context)
            .inflate(R.layout.recipe_property_step_list_item, parent, false))
}

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

override fun onBindViewHolder(holder: StepViewHolder, position: Int) {
    holder.bind(steps.get(position), position)
    Log.i(TAG,"onBindViewHolder - ${steps.get(position)}")
}

override fun onBindViewHolder(holder: StepViewHolder, position: Int, payloads: MutableList<Any>) {
    Log.i(TAG,"onBindViewHolder - Payloads - $payloads")
    val text:String = StringBuilder("${position + 1}.) ${steps[position].text}").toString()
    Log.i(TAG,"onBindViewHolder - Text - $text")
    holder.st_text.text = text
}

fun updateSteps(newSteps: MutableList<Step>){
    val diffResult = DiffUtil.calculateDiff(StepDiffCallBack(this.steps,newSteps))
    this.steps.clear()
    this.steps.addAll(newSteps)
    notifyDataSetChanged()
    diffResult.dispatchUpdatesTo(this)
}

LOGS:

2020-04-12 16:57:21.787 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Payloads - []
2020-04-12 16:57:21.787 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Text - 1.) Prepping the kitchen sink
2020-04-12 16:57:21.789 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Payloads - []
2020-04-12 16:57:21.789 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Text - 2.) prep the nuts and bolts
2020-04-12 16:57:21.791 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Payloads - []
2020-04-12 16:57:21.791 15808-15808/com.keep.recipeapp I/StepAdapter: onBindViewHolder - Text - 3.) Cook the kitchen sink

TEST DATA

wesley franks
  • 6,765
  • 4
  • 16
  • 29
  • Hi Wesley. Editors tend to remove commentary on possible duplicates for a couple of reasons, which I will enumerate here: (1) they tend to signify that the OP is not open to duplicates under any circumstances, which is a situation to be discouraged; (2) it does not sound very collaborative, and can earn downvotes or a lost of respondant interest; (3) future readers do not need to be distracted with unnecessary meta-commentary. In summary, it is not possible for a question author to be certain that no duplicates exist, so we remove it. – halfer Apr 08 '20 at 18:55
  • However, there _is_ a case where commentary on duplicates is encouraged - where a potential duplicate is proposed via the "Close" flag feature. In this case the Stack Overflow interface will encourage the question author to either accept the suggestion to close the question, or to add a post edit to explain why the author feels their post is different. This edit is much better, since it shall be related to a _specific_ proposed duplicate and not an unprovable "no dups exist" statement. – halfer Apr 08 '20 at 18:59

1 Answers1

1

The only way to achieve this is to rebind your ViewHolders so their TextViews display the changed positions. But calling notifyDataSetChanged() or notifyItemRangeChanged(0, pos) will get all your items fade out and fade in as they were all rebinded, I would not recommend this as its quite ugly (but still, it will work)

Have you ever used method onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>)? This allows to update only some Views inside your ViewHolder without redrawing the whole View, so the changes in your RecyclerView will be much smoother.

In your onMove method you call notifyItemRangeChanged providing range of the items that should be updated, and provide Any() as the payload, as no extra info is needed for rebinding the view, as you will have the position and data accessible in the adapter's method.

    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        val fromPosition = viewHolder.adapterPosition
        val toPosition = target.adapterPosition
        Collections.swap(steps!!, fromPosition, toPosition)

        recyclerView.adapter!!.notifyItemMoved(fromPosition,toPosition)
        recyclerView.adapter!!.notifyItemRangeChanged(0, max(fromPosition, toPosition), Any())
        // as you dont need to change items that are below the maximum positions
        // their positions will be correct
        return true
    }

Take in mind that you should call super method if the payloads are empty.

    override fun onBindViewHolder(holder: MyViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.isEmpty()) super.onBindViewHolder(holder, position, payloads)
        else {
            holder.theTextView.text = "$position.)${myList[position].data}"
        }
    }

update

Are you sure you call notifyItemRangeChanged(min(fromPos, toPos), max(fromPos,toPos)? This thing will call onBindViewHolder for every item in range. You should call it after you've called notifyItemMoved. When its working, try passing an object as a payload.

The logs don't show the behavior, it seems like its the output when the adapter binds views after initialization.

Teempy
  • 491
  • 4
  • 11
  • I remember looking into payload methods before and remember seeing how useful it could be. Thank you for mentioning this. I'll give it a try and get back to you with the results. – wesley franks Apr 06 '20 at 00:17
  • I tried it and it didn't work. I'm already doing that type of line of code in my viewholder. – wesley franks Apr 08 '20 at 02:46
  • It should work if everything done correctly. What behavior do you get? Put some logs in ```onBindViewHolder``` with payloads, does it try to update the textviews? And you can edit your answer with all the code of the adapter and onMove callback – Teempy Apr 08 '20 at 03:06
  • Yes, I can sorry for the late reply it's been busy with covid as my job is essential supporting IT for the VA across the country doing 12hr days. – wesley franks Apr 12 '20 at 20:28