luizgrp / SectionedRecyclerViewAdapter

An Adapter that allows a RecyclerView to be split into Sections with headers and/or footers. Each Section can have its state controlled individually.
MIT License
1.68k stars 372 forks source link

swipe to delete... #192

Closed LilMoke closed 4 years ago

LilMoke commented 4 years ago

Hello,

I am trying to implement Swipe to delete on a sectioned recycler. I have created a fragment as follows:

class MyTestFragment : Fragment(), MyTestSection1.ClickListener
{
    private var sectionedAdapter: SectionedRecyclerViewAdapter? = null
    private var recyclerView: RecyclerView? = null

    var stringArrayList = ArrayList<String>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
    {
        stringArrayList.add("Item 1")
        stringArrayList.add("Item 2")
        stringArrayList.add("Item 3")
        stringArrayList.add("Item 4")
        stringArrayList.add("Item 5")

        val view: View = inflater.inflate(R.layout.fragment_my_test, container, false)
        this.sectionedAdapter = SectionedRecyclerViewAdapter()

        this.recyclerView?.setLayoutManager(LinearLayoutManager(context))
        this.recyclerView?.setAdapter(this.sectionedAdapter)

        this.addNewSectionToAdapter("TEST SECTION 1")
        this.addNewSectionToAdapter("TEST SECTION 2")

        view.findViewById<View>(R.id.fabAdd).setOnClickListener { view1: View? ->
        }

        return view
    }

Then I created a section as follows:

class MyTestSection1 internal constructor(private val title: String, clickListener: MyTestFragment) :
    Section(
        SectionParameters.builder()
            .itemResourceId(R.layout.section_item)
            .headerResourceId(R.layout.section_header)
            .build()
    )
{
    private val clickListener: ClickListener
    var stringArrayList = ArrayList<String>()

    override fun getContentItemsTotal(): Int
    {
        return stringArrayList.count()
    }

    override fun getItemViewHolder(view: View): RecyclerView.ViewHolder
    {
        return MyTestItemViewHolder(view)
    }

    override fun onBindItemViewHolder(holder: RecyclerView.ViewHolder, position: Int)
    {
        val itemHolder = holder as MyTestItemViewHolder
        itemHolder.tvItem.setText(stringArrayList[position])
        itemHolder.rootView.setOnClickListener { v ->
            clickListener.onItemRootViewClicked(this, itemHolder.adapterPosition)
        }

        val swipeHandler = object : SwipeToDeleteCallback() {
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {

Toast.makeText(context, String.format("ITEM: #%s, SECTION: %s", sectionedAdapter?.getPositionInSection(viewHolder.adapterPosition), "REFERENCES"), Toast.LENGTH_SHORT).show()

val adapter = sectionedAdapter?.getAdapterForSection(section)   // recyclerView!!.adapter as MyTestSection1
                adapter.remove(viewHolder.adapterPosition)

}
        }
        val itemTouchHelper = ItemTouchHelper(swipeHandler)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

The problem I am having is in my section in the Callback, how do I get the section so I know which section to remove the item from?

Any help is greatly appreciated. The Sample provided was great except it does not show a swipe to delete.

luizgrp commented 4 years ago

In the onSwiped method of SwipeToDeleteCallback you have access to a RecyclerView.ViewHolder (parameter viewHolder). You can get the adapter position of the item that was swiped calling the [getAdapterPosition](https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.ViewHolder#getAdapterPosition()) method.

With the adapter position you can call the method getSectionForPosition from SectionedRecyclerViewAdapter (variable sectionedAdapter in your code):

sectionedAdapter.getSectionForPosition(viewHolder.getAdapterPosition())
LilMoke commented 4 years ago

Thank you for this answer. One related question...

I have this code to add the swipe to delete in my fragment:

val swipeHandler = object : SwipeToDeleteCallback(context!!) {
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = sectionedAdapter?.getPositionInSection(viewHolder.adapterPosition) ?: -1
        val section = sectionedAdapter?.getSectionForPosition(viewHolder.getAdapterPosition()) as ProfileReferencesSection
        doDelete(section, position)
    }
}
val itemTouchHelper = ItemTouchHelper(swipeHandler)
itemTouchHelper.attachToRecyclerView(recyclerView)

The swiping works fine and your suggestion helped a lot.

One side effect of hat I am doing is that I am also able to swipe the section headers.

How can I prevent this?

NicolaiBure commented 3 years ago

Hi, on the SwipeToDeleteCallback function you can add this override:

 override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        if (viewHolder is HeaderViewHolder || viewHolder is FooterViewHolder) {
            return 0 // This prevent the swipe gesture
        }
        return super.getMovementFlags(recyclerView, viewHolder)
    }

Hope this help you