简体   繁体   中英

Add swipe right to delete ListView item

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.

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);

You can use the lib ::

SwipeMenuListView

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)
}

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

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
            );
        },
      );
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM