15

I have a ListView that uses a custom adaper (that extends BaseAdapter). How do I add the swipe to delete gesture?

I want use the same functionality the gmail application uses.

Dave Thomas
  • 3,667
  • 2
  • 33
  • 41
Fra87
  • 297
  • 2
  • 5
  • 16

5 Answers5

26

The easiest way to do this is to move your ListView over to a RecyclerView and use a GridLayoutManager with a single column. It will look the same, but allows you to swipe to dismiss using the ItemTouchHelper.

     recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
     recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 1));

     recyclerView.setAdapter(adapter);


    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
            // Remove item from backing list here
            adapter.notifyDataSetChanged();
        }
    });

    itemTouchHelper.attachToRecyclerView(recyclerView);
Gary Bak
  • 4,746
  • 4
  • 22
  • 39
  • Yes, you will have to change the ListView to a RecyclerView, change the adapter base class type and then create one or more Handlers for the data. – Gary Bak Oct 17 '16 at 23:38
  • I have do it, but now i have scroll in recycleview, i don't want scroll i have already scrollview is possible remove scroll? – Fra87 Oct 18 '16 at 08:48
  • I have solved problem of scroll, my Adapter is RecyclerView.Adapter, if i want method that return item from position How i do? – Fra87 Oct 18 '16 at 09:38
  • i want to create my custom method delete in RecyclerView.Adapter but i have a dubt, after i have delete item from ArrayList then i call notifyDataSetChanged() or i call notifyItemRemoved() ? – Fra87 Oct 18 '16 at 09:53
  • Removed is probably the better choice, then the adapter knows which positions it has to redraw. The `notifyDataSetChanged` will force it to redraw whatever is on the screen. – Gary Bak Oct 18 '16 at 10:56
  • Last question: If i want add listner on item how i can? – Fra87 Oct 18 '16 at 14:42
  • From adapter is possible call onCreate activity? – Fra87 Oct 18 '16 at 16:10
  • Shall also implement public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {return false;} Upvoted! – Giorgio Barchiesi Nov 07 '19 at 11:52
1

You can use the lib ::

SwipeMenuListView

Anil Prajapati
  • 457
  • 3
  • 10
1

Accepted answer translated to Kotlin:

val itemTouchHelper =  ItemTouchHelper(object: ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        TODO("Not yet implemented")
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
            todoListAdapter.notifyDataSetChanged()
    }

})
this.runOnUiThread {
    itemTouchHelper.attachToRecyclerView(recyclerView)
}
TruckerCat
  • 1,437
  • 2
  • 16
  • 39
1

A Kotlin version using a ListAdapter

all credit goes to this blog post: https://theeasiestwayy.medium.com/listadapter-and-swipe-to-delete-741e32e5f824 (which as a couple of typos in it's code examples)

abstract class ListAdapterSwipeable <T, VH: RecyclerView.ViewHolder>(
    diffCallback: DiffUtil.ItemCallback<T>
): ListAdapter<T, VH>(diffCallback) {

    private val removedItems: MutableList<T> = mutableListOf()

    fun removeItem(position: Int): T? {
        if (position >= itemCount) return null
        val item = currentList[position]
        removedItems.add(item)
        val actualList = currentList - removedItems
        if (actualList.isEmpty()) removedItems.clear()
        submit(actualList, true)
        return item
    }

    private fun submit(list: List<T>?, isLocalSubmit: Boolean) {
        if (!isLocalSubmit) removedItems.clear()
        super.submitList(list)
    }

    @CallSuper
    override fun submitList(list: List<T>?) {
        submit(list, false)
    }
}
class ItemSwipeHandler<T>(
    private val adapter: ListAdapterSwipeable<T, *>,
    private val onItemRemoved: ((item: T) -> Unit)? = null
) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
//        val position = viewHolder.adapterPosition  DEPRECATED!!
        val position = viewHolder.bindingAdapterPosition
        val item = adapter.removeItem(position) ?: return
        onItemRemoved?.invoke(item)
    }

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

More about it's usage see the blog article from above

CyclingSir
  • 143
  • 1
  • 9
-4

The simplest way I have found is to use a ListView.builder in a StatefullWidget and wrap the children in a Dismissible component:

ListView.builder(
  itemCount: sampleList.length, //number of items on the list
  itemBuilder: (BuildContext context, int index) {
      return Dismissible(
        key: Key(sampleList[index]), //unique key string for each element (in this case each string is unique)
        onDismissed: (direction) {
          setState(() {
            sampleList.removeAt(index); //remove list element at given index and repaint
          });
        },
        child: child: Text('${sampleList[index]}'), //what to display form the list
      );
  },
);

you can check/copy the code bellow as a working example, and pass it as a child to a component:

import 'package:flutter/material.dart';

class DismissibleList extends StatefulWidget {
  @override
  _DismissibleListState createState() => _DismissibleListState();
}

class _DismissibleListState extends State<DismissibleList> {
  @override
  Widget build(BuildContext context) {
    List<String> sampleList = ['aa', 'bb', 'cc']; // the list of elements
    return ListView.builder(
        itemCount: sampleList.length, //number of items on the list
        itemBuilder: (BuildContext context, int index) {
            return Dismissible(
              key: Key(sampleList[index]), //unique key string for each element (in this case each string is unique)
              onDismissed: (direction) {
                setState(() {
                  sampleList.removeAt(index); //remove list element at given index and repaint
                });
              },
              child: child: Padding(
                padding: EdgeInsets.all(16.0), // just to help the visual
                child: Text('${sampleList[index]}'),
              ) //what to display form the list
            );
        },
      );
  }
}