Swipe ListView item From right to left show delete button

AndroidAndroid ListviewSwipe Gesture

Android Problem Overview


I have a custom ListView showing the list of words selecting from database. When I swipe this listview item i want to show Delete button like image below. And when I press that button it is deleted from Database and refresh the listview. mSwipe Listview item

I already look in this sample code here. But it still does not work.

Android Solutions


Solution 1 - Android

EDIT: between other options there's a nice library that could solve your issue: https://github.com/daimajia/AndroidSwipeLayout

Solution 2 - Android

I used to have the same problem finding a good library to do that. Eventually, I created a library which can do that: SwipeRevealLayout

In gradle file:

dependencies {
    compile 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.0'
}

In your xml file:

<com.chauthai.swipereveallayout.SwipeRevealLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:mode="same_level"
    app:dragEdge="left">

    <!-- Your secondary layout here -->
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />

    <!-- Your main layout here -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.chauthai.swipereveallayout.SwipeRevealLayout>

Then in your adapter file:

public class Adapter extends RecyclerView.Adapter {
  // This object helps you save/restore the open/close state of each view
  private final ViewBinderHelper viewBinderHelper = new ViewBinderHelper();

  @Override
  public void onBindViewHolder(ViewHolder holder, int position) {
    // get your data object first.
    YourDataObject dataObject = mDataSet.get(position); 

    // Save/restore the open/close state.
    // You need to provide a String id which uniquely defines the data object.
    viewBinderHelper.bind(holder.swipeRevealLayout, dataObject.getId()); 

    // do your regular binding stuff here
  }
}

Solution 3 - Android

i've searched google a lot and find the best suited project is the swipmenulistview <https://github.com/baoyongzhang/SwipeMenuListView> on github.

Solution 4 - Android

I had created a demo on my github which includes on swiping from right to left a delete button will appear and you can then delete your item from the ListView and update your ListView.

Solution 5 - Android

I just got his working using the ViewSwitcher in a ListItem.

list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<ViewSwitcher
    android:id="@+id/list_switcher"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:inAnimation="@android:anim/slide_in_left"
    android:outAnimation="@android:anim/slide_out_right"
    android:measureAllChildren="false" >
    <TextView
        android:id="@+id/tv_item_name"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="center_vertical"
        android:maxHeight="50dp"
        android:paddingLeft="10dp" />
    
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:clickable="false"
        android:gravity="center"
        >

    <Button
        android:id="@+id/b_edit_in_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Edit"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        
         />
    <Button
        android:id="@+id/b_delete_in_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Delete"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:background="@android:color/holo_red_dark"
        />
    </LinearLayout>
</ViewSwitcher>

In the ListAdapter: Implement OnclickListeners for the Edit and Delete button in the getView() method. The catch here is to get the position of the ListItem clicked inside the onClick methods. setTag() and getTag() methods are used for this.

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
	// TODO Auto-generated method stub
	final ViewHolder viewHolder;
	if (convertView == null) {
		viewHolder = new ViewHolder();
		convertView = mInflater.inflate(R.layout.list_item, null);
		viewHolder.viewSwitcher=(ViewSwitcher)convertView.findViewById(R.id.list_switcher);
		viewHolder.itemName = (TextView) convertView
				.findViewById(R.id.tv_item_name);
		viewHolder.deleteitem=(Button)convertView.findViewById(R.id.b_delete_in_list);
		viewHolder.deleteItem.setTag(position);
		viewHolder.editItem=(Button)convertView.findViewById(R.id.b_edit_in_list);
		viewHolder.editItem.setTag(position);
		viewHolder.deleteItem.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				fragment.deleteItemList((Integer)v.getTag());
			}
		});
		
		viewHolder.editItem.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				fragment.editItemList(position);
			}
		});
		convertView.setTag(viewHolder);
} else {
		viewHolder = (ViewHolder) convertView.getTag();
	}
	
	viewHolder.itemName.setText(itemlist[position]);
return convertView;  
}

In the Fragment, Add a Gesture Listener to detect the Fling Gesture:

public class MyGestureListener extends SimpleOnGestureListener {
	private ListView list;

	public MyGestureListener(ListView list) {
		this.list = list;
	}

	// CONDITIONS ARE TYPICALLY VELOCITY OR DISTANCE

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		// if (INSERT_CONDITIONS_HERE)
		
		ltor=(e2.getX()-e1.getX()>DELTA_X);
		if (showDeleteButton(e1))
		{
			return true;
		}
		return super.onFling(e1, e2, velocityX, velocityY);
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2,
			float distanceX, float distanceY) {
		// TODO Auto-generated method stub
		return super.onScroll(e1, e2, distanceX, distanceY);
	}

	private boolean showDeleteButton(MotionEvent e1) {
		int pos = list.pointToPosition((int) e1.getX(), (int) e1.getY());
		return showDeleteButton(pos);
	}

	private boolean showDeleteButton(int pos) {
		
		View child = list.getChildAt(pos);
		if (child != null) {
			Button delete = (Button) child
					.findViewById(R.id.b_edit_in_list);
			ViewSwitcher viewSwitcher = (ViewSwitcher) child
					.findViewById(R.id.host_list_switcher);
			TextView hostName = (TextView) child
					.findViewById(R.id.tv_host_name);
			if (delete != null) {
					viewSwitcher.setInAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_left));
					viewSwitcher.setOutAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_out_right));
				}
			
				viewSwitcher.showNext();
				// frameLayout.setVisibility(View.VISIBLE);
			}
			return true;
		}
		return false;
	}
}

In the onCreateView method of the Fragment,

GestureDetector gestureDetector = new GestureDetector(getActivity(),
			new MyGestureListener(hostList));
	hostList.setOnTouchListener(new View.OnTouchListener() {

		@Override
		public boolean onTouch(View v, MotionEvent event) {
			// TODO Auto-generated method stub
			if (gestureDetector.onTouchEvent(event)) {
				return true;
			} else {
				return false;
			}
			
		}
	});

This worked for me. Should refine it more.

Solution 6 - Android

see there link was very nice and simple. its working fine... u don't want any library its working fine. click here

OnTouchListener gestureListener = new View.OnTouchListener() {
    private int padding = 0;
    private int initialx = 0;
    private int currentx = 0;
    private  ViewHolder viewHolder;

    public boolean onTouch(View v, MotionEvent event) {
        if ( event.getAction() == MotionEvent.ACTION_DOWN) {
            padding = 0;
            initialx = (int) event.getX();
            currentx = (int) event.getX();
            viewHolder = ((ViewHolder) v.getTag());
        }
        if ( event.getAction() == MotionEvent.ACTION_MOVE) {
            currentx = (int) event.getX();
            padding = currentx - initialx;
        }       
        if ( event.getAction() == MotionEvent.ACTION_UP || 
                     event.getAction() == MotionEvent.ACTION_CANCEL) {
            padding = 0;
            initialx = 0;
            currentx = 0;
        }
        if(viewHolder != null) {
            if(padding == 0) {
                v.setBackgroundColor(0xFF000000 );  
                if(viewHolder.running)
                    v.setBackgroundColor(0xFF058805);
            }
            if(padding > 75) {
                viewHolder.running = true;
                v.setBackgroundColor(0xFF00FF00 );  
                viewHolder.icon.setImageResource(R.drawable.clock_running);
            }
            if(padding < -75) {
                viewHolder.running = false;
                v.setBackgroundColor(0xFFFF0000 );  
            }

            v.setPadding(padding, 0,0, 0);
        }

        return true;
    }
};

Solution 7 - Android

An app is available that demonstrates a listview that combines both swiping-to-delete and dragging to reorder items. The code is based on Chet Haase's code for swiping-to-delete and Daniel Olshansky's code for dragging-to-reorder.

Chet's code deletes an item immediately. I improved on this by making it function more like Gmail where swiping reveals a bottom view that indicates that the item is deleted but provides an Undo button where the user has the possibility to undo the deletion. Chet's code also has a bug in it. If you have less items in the listview than the height of the listview is and you delete the last item, the last item appears to not be deleted. This was fixed in my code.

Daniel's code requires pressing long on an item. Many users find this unintuitive as it tends to be a hidden function. Instead, I modified the code to allow for a "Move" button. You simply press on the button and drag the item. This is more in line with the way the Google News app works when you reorder news topics.

The source code along with a demo app is available at: https://github.com/JohannBlake/ListViewOrderAndSwipe

Chet and Daniel are both from Google.

Chet's video on deleting items can be viewed at: https://www.youtube.com/watch?v=YCHNAi9kJI4

Daniel's video on reordering items can be viewed at: https://www.youtube.com/watch?v=_BZIvjMgH-Q

A considerable amount of work went into gluing all this together to provide a seemless UI experience, so I'd appreciate a Like or Up Vote. Please also star the project in Github.

Solution 8 - Android

I have gone through tons of third party libraries to try to achieve this. But none of them exhibits smoothness and usability experience which i wanted. Then i decided to write it myself. And the result was , well, i loved it. I will share the code here. Maybe i will write it as a library which can be embedded in any recycler view in future. But for now here is the code.

Note: i have uses recycler view and ViewHolder. Some values are hardcoded so change them according to your requirement.

  • row_layout.xml

    http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="80dp" android:background="?android:attr/selectableItemBackground" android:id="@+id/chat_row_container">

     <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentRight="true"
         android:orientation="horizontal">
         <Button
             android:id="@+id/slide_button_2"
             android:text="Button2"
             android:layout_width="80dp"
             android:layout_height="80dp" />
         <Button
             android:id="@+id/slide_button_1"
             android:text="Button1"
             android:layout_width="80dp"
             android:layout_height="80dp" />
     </LinearLayout>
     <LinearLayout
         android:id="@+id/chat_row_cell"
         android:layout_width="match_parent"
         android:layout_height="80dp"
         android:orientation="horizontal"
         android:background="@color/white">
         <ImageView
             android:id="@+id/chat_image"
             android:layout_width="60dp"
             android:layout_height="60dp"
             android:layout_marginLeft="10dp"
             android:layout_marginTop="10dp"
             android:layout_marginBottom="10dp"
             android:layout_marginRight="10dp"
             android:layout_gravity="center"/>
         <LinearLayout
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
             android:gravity="center">
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
                 <TextView
                     android:id="@+id/chat_title"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:textColor="@color/md_grey_800"
                     android:textSize="18sp"/>
                 <TextView
                     android:id="@+id/chat_subtitle"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:textColor="@color/md_grey_600"/>
             </LinearLayout>
         </LinearLayout>
     </LinearLayout>
    

  • ChatAdaptor.java

public class ChatAdaptor extends RecyclerView.Adapter {

    List<MXGroupChatSession> sessions;
    Context context;
    ChatAdaptorInterface listener;

    public interface ChatAdaptorInterface{
        void cellClicked(MXGroupChatSession session);
        void utilityButton1Clicked(MXGroupChatSession session);
        void utilityButton2Clicked(MXGroupChatSession session);
    }

    public ChatAdaptor(List<MXGroupChatSession> sessions, ChatAdaptorInterface listener, Context context){
        this.sessions=sessions;
        this.context=context;
        this.listener=listener;
    }

    @Override
    public ChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view=(View)LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_row,null);
        ChatViewHolder chatViewHolder=new ChatViewHolder(view);
        return chatViewHolder;
    }

    @Override
    public void onBindViewHolder(ChatViewHolder holder, final int position) {
        MXGroupChatSession session=this.sessions.get(position);
        holder.selectedSession=session;
        holder.titleView.setText(session.getTopic());
        holder.subtitleView.setText(session.getLastFeedContent());
        Picasso.with(context).load(new File(session.getCoverImagePath())).transform(new CircleTransformPicasso()).into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return sessions.size();
    }

    public class ChatViewHolder extends RecyclerView.ViewHolder{
        ImageView imageView;
        TextView titleView;
        TextView subtitleView;
        ViewGroup cell;
        ViewGroup cellContainer;
        Button button1;
        Button button2;

        MXGroupChatSession selectedSession;
        private GestureDetectorCompat gestureDetector;

        float totalx;
        float buttonTotalWidth;

        Boolean open=false;
        Boolean isScrolling=false;


        public ChatViewHolder(View itemView) {
            super(itemView);
            cell=(ViewGroup) itemView.findViewById(R.id.chat_row_cell);
            cellContainer=(ViewGroup) itemView.findViewById(R.id.chat_row_container);
            button1=(Button) itemView.findViewById(R.id.slide_button_1);
            button2=(Button) itemView.findViewById(R.id.slide_button_2);

            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.utilityButton1Clicked(selectedSession);
                }
            });

            button2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.utilityButton2Clicked(selectedSession);
                }
            });

            ViewTreeObserver vto = cellContainer.getViewTreeObserver();
            vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    buttonTotalWidth = button1.getWidth()+button2.getWidth();
                }
            });

            this.titleView=(TextView)itemView.findViewById(R.id.chat_title);
            subtitleView=(TextView)itemView.findViewById(R.id.chat_subtitle);
            imageView=(ImageView)itemView.findViewById(R.id.chat_image);
            gestureDetector=new GestureDetectorCompat(context,new ChatRowGesture());

            cell.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if(gestureDetector.onTouchEvent(event)){
                        return true;
                    }

                    if(event.getAction() == MotionEvent.ACTION_UP) {
                        if(isScrolling ) {
                            isScrolling  = false;
                            handleScrollFinished();
                        };
                    }
                    else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                        if(isScrolling ) {
                            isScrolling  = false;
                            handleScrollFinished();
                        };
                    }
                    return false;
                }
            });
        }


        public class ChatRowGesture extends GestureDetector.SimpleOnGestureListener {

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                if (!open){
                    listener.cellClicked(selectedSession);
                }
                return true;
            }

            @Override
            public boolean onDown(MotionEvent e) {
                return true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    isScrolling=true;
                    totalx=totalx+distanceX;
                    freescroll(totalx);

                return true;
            }
        }

        void handleScrollFinished(){
            if (open){
                if (totalx>2*buttonTotalWidth/3){
                    slideLeft();
                    totalx=buttonTotalWidth;
                }else{
                    slideRight();
                    totalx=0;
                }
            }else{
                if (totalx>buttonTotalWidth/3){
                    slideLeft();
                    totalx=buttonTotalWidth;
                }else{
                    slideRight();
                    totalx=0;
                }
            }

        }

        void slideRight(){
            TransitionManager.beginDelayedTransition(cellContainer);
            ViewGroup.MarginLayoutParams params;
            params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
            params.setMargins(0,0,0,0);
            cell.setLayoutParams(params);
            open=false;
        }

        void slideLeft(){
            TransitionManager.beginDelayedTransition(cellContainer);
            ViewGroup.MarginLayoutParams params;
            params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
            params.setMargins(((int)buttonTotalWidth*-1),0,(int)buttonTotalWidth,0);
            cell.setLayoutParams(params);
            open=true;
        }
        void freescroll(float x){
            if (x<buttonTotalWidth && x>0){
                int xint=(int)x;
                ViewGroup.MarginLayoutParams params;
                params=(ViewGroup.MarginLayoutParams) cell.getLayoutParams();
                params.setMargins(params.leftMargin,0,xint,0);
                cell.setLayoutParams(params);
            }
        }
    }

Hope this helps someone!!

Solution 9 - Android

Define a ViewPager in your layout .xml:

<android.support.v4.view.ViewPager
    android:id="@+id/example_pager"
    android:layout_width="fill_parent"
    android:layout_height="@dimen/abc_action_bar_default_height" />

And then, in your activity / fragment, set a custom pager adapter:

In an activity:

protected void onCreate(Bundle savedInstanceState) {
    PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
    ViewPager pager = (ViewPager) findViewById(R.id.example_pager);
    
    pager.setAdapter(adapter);
    // pager.setOnPageChangeListener(this); // You can set a page listener here
    pager.setCurrentItem(0);
}

In a fragment:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
	View view = inflater.inflate(R.layout.fragment_layout, container, false);

	if (view != null) {
		PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
		ViewPager pager = (ViewPager) view.findViewById(R.id.example_pager);

		pager.setAdapter(adapter);
		// pager.setOnPageChangeListener(this); // You can set a page listener here
		pager.setCurrentItem(0);
	}
	
	return view;
}

Create our custom pager class:

// setup your PagerAdapter which extends FragmentPagerAdapter
class PagerAdapter extends FragmentPagerAdapter {
    public static final int NUM_PAGES = 2;
    private CustomFragment[] mFragments = new CustomFragment[NUM_PAGES];
    public PagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }
    @ Override
    public int getCount() {
        return NUM_PAGES;
    }
    @ Override
    public Fragment getItem(int position) {
        if (mFragments[position] == null) {
               // this calls the newInstance from when you setup the ListFragment
            mFragments[position] = new CustomFragment();
        }
        return mFragments[position];
    }
}

Solution 10 - Android

It's a lose of time to implement from scratch this functionality. I implemented the library recommended by SalutonMondo and I am very satisfied. It is very simple to use and very quick. I improved the original library and I added a new click listener for item click. Also I added font awesome library (http://fortawesome.github.io/Font-Awesome/) and now you can simply add a new item title and specify the icon name from font awesome.

Here is the github link

Solution 11 - Android

you can use this code

holder.img_close.setOnClickListener(new View.OnClickListener() {
      @Override
        public void onClick(View view) {
            holder.swipeRevealLayout.close(true);
            list.remove(position);
            notifyDataSetChanged();
      }});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser3098538View Question on Stackoverflow
Solution 1 - AndroidLuciano RodríguezView Answer on Stackoverflow
Solution 2 - AndroidChau ThaiView Answer on Stackoverflow
Solution 3 - AndroidSalutonMondoView Answer on Stackoverflow
Solution 4 - AndroidLalit PoptaniView Answer on Stackoverflow
Solution 5 - Androiduser3316561View Answer on Stackoverflow
Solution 6 - AndroidArul PandianView Answer on Stackoverflow
Solution 7 - AndroidJohannView Answer on Stackoverflow
Solution 8 - AndroidGitesh AgarwalView Answer on Stackoverflow
Solution 9 - AndroidLesh_MView Answer on Stackoverflow
Solution 10 - AndroidPopa AndreiView Answer on Stackoverflow
Solution 11 - AndroidMohammad NoriView Answer on Stackoverflow