利用 SwipeDismissListViewTouchListener 实现左右侧滑删除条目 item

Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeToDismiss )。该开源项目旨在:使得一个ListView的item在用户的手指在屏幕上左滑或者右滑时候,删除当前的这个ListView Item。如果只是打算简单的只是支持一个ListView列表中item左滑/右滑删除,那么仅仅只导入SwipeDismissListViewTouchListener.java这一个库文件即可。效果如下:




package com.example.swipetodismiss;

import java.util.ArrayList;

import com.example.swipetodismiss.SwipeDismissListViewTouchListener.DismissCallbacks;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends ListActivity {

	private ArrayAdapter<String> adapter = null;
	private ArrayList<String> items = null;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		ListView listView = getListView();

		items = new ArrayList<String>();
		for (int i = 0; i < 50; i++) {
			items.add("测试数据:" + i);
		}

		adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, items);

		listView.setAdapter(adapter);

		addSwipe(listView);
	}

	private void addSwipe(ListView listView) {

		SwipeDismissListViewTouchListener touchListener = 
				new SwipeDismissListViewTouchListener(listView,
				new DismissCallbacks() {

					@Override
					public boolean canDismiss(int position) {
						return true;
					}

					@Override
					public void onDismiss(ListView listView, int[] reverseSortedPositions) {

						for (int index : reverseSortedPositions) {
							items.remove(index);
						}

						adapter.notifyDataSetChanged();
					}
				});
		listView.setOnTouchListener(touchListener);
	}
}


SwipeDismissListViewTouchListener.java :

package com.example.swipetodismiss;

/*
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.widget.AbsListView;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * A {@link View.OnTouchListener} that makes the list items in a
 * {@link ListView} dismissable. {@link ListView} is given special treatment
 * because by default it handles touches for its list items... i.e. it's in
 * charge of drawing the pressed state (the list selector), handling list item
 * clicks, etc.
 *
 * <p>
 * After creating the listener, the caller should also call
 * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
 * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll
 * listener is already assigned, the caller should still pass scroll changes
 * through to this listener. This will ensure that this
 * {@link SwipeDismissListViewTouchListener} is paused during list view
 * scrolling.
 * </p>
 *
 * <p>
 * Example usage:
 * </p>
 *
 * <pre>
 * SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(
 * 		listView, new SwipeDismissListViewTouchListener.OnDismissCallback() {
 * 			public void onDismiss(ListView listView,
 * 					int[] reverseSortedPositions) {
 * 				for (int position : reverseSortedPositions) {
 * 					adapter.remove(adapter.getItem(position));
 * 				}
 * 				adapter.notifyDataSetChanged();
 * 			}
 * 		});
 * listView.setOnTouchListener(touchListener);
 * listView.setOnScrollListener(touchListener.makeScrollListener());
 * </pre>
 *
 * <p>
 * This class Requires API level 12 or later due to use of
 * {@link ViewPropertyAnimator}.
 * </p>
 *
 * <p>
 * For a generalized {@link View.OnTouchListener} that makes any view
 * dismissable, see {@link SwipeDismissTouchListener}.
 * </p>
 *
 * @see SwipeDismissTouchListener
 */
public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
	// Cached ViewConfiguration and system-wide constant values
	private int mSlop;
	private int mMinFlingVelocity;
	private int mMaxFlingVelocity;
	private long mAnimationTime;

	// Fixed properties
	private ListView mListView;
	private DismissCallbacks mCallbacks;
	private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero

	// Transient properties
	private List<PendingDismissData> mPendingDismisses = new ArrayList<PendingDismissData>();
	private int mDismissAnimationRefCount = 0;
	private float mDownX;
	private float mDownY;
	private boolean mSwiping;
	private int mSwipingSlop;
	private VelocityTracker mVelocityTracker;
	private int mDownPosition;
	private View mDownView;
	private boolean mPaused;

	/**
	 * The callback interface used by {@link SwipeDismissListViewTouchListener}
	 * to inform its client about a successful dismissal of one or more list
	 * item positions.
	 */
	public interface DismissCallbacks {
		/**
		 * Called to determine whether the given position can be dismissed.
		 */
		boolean canDismiss(int position);

		/**
		 * Called when the user has indicated they she would like to dismiss one
		 * or more list item positions.
		 *
		 * @param listView
		 *            The originating {@link ListView}.
		 * @param reverseSortedPositions
		 *            An array of positions to dismiss, sorted in descending
		 *            order for convenience.
		 */
		void onDismiss(ListView listView, int[] reverseSortedPositions);
	}

	/**
	 * Constructs a new swipe-to-dismiss touch listener for the given list view.
	 *
	 * @param listView
	 *            The list view whose items should be dismissable.
	 * @param callbacks
	 *            The callback to trigger when the user has indicated that she
	 *            would like to dismiss one or more list items.
	 */
	public SwipeDismissListViewTouchListener(ListView listView,
			DismissCallbacks callbacks) {
		ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
		mSlop = vc.getScaledTouchSlop();
		mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
		mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
		mAnimationTime = listView.getContext().getResources()
				.getInteger(android.R.integer.config_shortAnimTime);
		mListView = listView;
		mCallbacks = callbacks;
	}

	/**
	 * Enables or disables (pauses or resumes) watching for swipe-to-dismiss
	 * gestures.
	 *
	 * @param enabled
	 *            Whether or not to watch for gestures.
	 */
	public void setEnabled(boolean enabled) {
		mPaused = !enabled;
	}

	/**
	 * Returns an {@link AbsListView.OnScrollListener} to be added to the
	 * {@link ListView} using
	 * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}. If a
	 * scroll listener is already assigned, the caller should still pass scroll
	 * changes through to this listener. This will ensure that this
	 * {@link SwipeDismissListViewTouchListener} is paused during list view
	 * scrolling.</p>
	 *
	 * @see SwipeDismissListViewTouchListener
	 */
	public AbsListView.OnScrollListener makeScrollListener() {
		return new AbsListView.OnScrollListener() {
			@Override
			public void onScrollStateChanged(AbsListView absListView,
					int scrollState) {
				setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
			}

			@Override
			public void onScroll(AbsListView absListView, int i, int i1, int i2) {
			}
		};
	}

	@Override
	public boolean onTouch(View view, MotionEvent motionEvent) {
		if (mViewWidth < 2) {
			mViewWidth = mListView.getWidth();
		}

		switch (motionEvent.getActionMasked()) {
		case MotionEvent.ACTION_DOWN: {
			if (mPaused) {
				return false;
			}

			// TODO: ensure this is a finger, and set a flag

			// Find the child view that was touched (perform a hit test)
			Rect rect = new Rect();
			int childCount = mListView.getChildCount();
			int[] listViewCoords = new int[2];
			mListView.getLocationOnScreen(listViewCoords);
			int x = (int) motionEvent.getRawX() - listViewCoords[0];
			int y = (int) motionEvent.getRawY() - listViewCoords[1];
			View child;
			for (int i = 0; i < childCount; i++) {
				child = mListView.getChildAt(i);
				child.getHitRect(rect);
				if (rect.contains(x, y)) {
					mDownView = child;
					break;
				}
			}

			if (mDownView != null) {
				mDownX = motionEvent.getRawX();
				mDownY = motionEvent.getRawY();
				mDownPosition = mListView.getPositionForView(mDownView);
				if (mCallbacks.canDismiss(mDownPosition)) {
					mVelocityTracker = VelocityTracker.obtain();
					mVelocityTracker.addMovement(motionEvent);
				} else {
					mDownView = null;
				}
			}
			return false;
		}

		case MotionEvent.ACTION_CANCEL: {
			if (mVelocityTracker == null) {
				break;
			}

			if (mDownView != null && mSwiping) {
				// cancel
				mDownView.animate().translationX(0).alpha(1)
						.setDuration(mAnimationTime).setListener(null);
			}
			mVelocityTracker.recycle();
			mVelocityTracker = null;
			mDownX = 0;
			mDownY = 0;
			mDownView = null;
			mDownPosition = ListView.INVALID_POSITION;
			mSwiping = false;
			break;
		}

		case MotionEvent.ACTION_UP: {
			if (mVelocityTracker == null) {
				break;
			}

			float deltaX = motionEvent.getRawX() - mDownX;
			mVelocityTracker.addMovement(motionEvent);
			mVelocityTracker.computeCurrentVelocity(1000);
			float velocityX = mVelocityTracker.getXVelocity();
			float absVelocityX = Math.abs(velocityX);
			float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
			boolean dismiss = false;
			boolean dismissRight = false;
			if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
				dismiss = true;
				dismissRight = deltaX > 0;
			} else if (mMinFlingVelocity <= absVelocityX
					&& absVelocityX <= mMaxFlingVelocity
					&& absVelocityY < absVelocityX && mSwiping) {
				// dismiss only if flinging in the same direction as dragging
				dismiss = (velocityX < 0) == (deltaX < 0);
				dismissRight = mVelocityTracker.getXVelocity() > 0;
			}
			if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
				// dismiss
				final View downView = mDownView; // mDownView gets null'd before
													// animation ends
				final int downPosition = mDownPosition;
				++mDismissAnimationRefCount;
				mDownView.animate()
						.translationX(dismissRight ? mViewWidth : -mViewWidth)
						.alpha(0).setDuration(mAnimationTime)
						.setListener(new AnimatorListenerAdapter() {
							@Override
							public void onAnimationEnd(Animator animation) {
								performDismiss(downView, downPosition);
							}
						});
			} else {
				// cancel
				mDownView.animate().translationX(0).alpha(1)
						.setDuration(mAnimationTime).setListener(null);
			}
			mVelocityTracker.recycle();
			mVelocityTracker = null;
			mDownX = 0;
			mDownY = 0;
			mDownView = null;
			mDownPosition = ListView.INVALID_POSITION;
			mSwiping = false;
			break;
		}

		case MotionEvent.ACTION_MOVE: {
			if (mVelocityTracker == null || mPaused) {
				break;
			}

			mVelocityTracker.addMovement(motionEvent);
			float deltaX = motionEvent.getRawX() - mDownX;
			float deltaY = motionEvent.getRawY() - mDownY;
			if (Math.abs(deltaX) > mSlop
					&& Math.abs(deltaY) < Math.abs(deltaX) / 2) {
				mSwiping = true;
				mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
				mListView.requestDisallowInterceptTouchEvent(true);

				// Cancel ListView's touch (un-highlighting the item)
				MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
				cancelEvent
						.setAction(MotionEvent.ACTION_CANCEL
								| (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
				mListView.onTouchEvent(cancelEvent);
				cancelEvent.recycle();
			}

			if (mSwiping) {
				mDownView.setTranslationX(deltaX - mSwipingSlop);
				mDownView.setAlpha(Math.max(0f,
						Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
				return true;
			}
			break;
		}
		}
		return false;
	}

	class PendingDismissData implements Comparable<PendingDismissData> {
		public int position;
		public View view;

		public PendingDismissData(int position, View view) {
			this.position = position;
			this.view = view;
		}

		@Override
		public int compareTo(PendingDismissData other) {
			// Sort by descending position
			return other.position - position;
		}
	}

	private void performDismiss(final View dismissView,
			final int dismissPosition) {
		// Animate the dismissed list item to zero-height and fire the dismiss
		// callback when
		// all dismissed list item animations have completed. This triggers
		// layout on each animation
		// frame; in the future we may want to do something smarter and more
		// performant.

		final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
		final int originalHeight = dismissView.getHeight();

		ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1)
				.setDuration(mAnimationTime);

		animator.addListener(new AnimatorListenerAdapter() {
			@Override
			public void onAnimationEnd(Animator animation) {
				--mDismissAnimationRefCount;
				if (mDismissAnimationRefCount == 0) {
					// No active animations, process all pending dismisses.
					// Sort by descending position
					Collections.sort(mPendingDismisses);

					int[] dismissPositions = new int[mPendingDismisses.size()];
					for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {
						dismissPositions[i] = mPendingDismisses.get(i).position;
					}
					mCallbacks.onDismiss(mListView, dismissPositions);

					// Reset mDownPosition to avoid MotionEvent.ACTION_UP trying
					// to start a dismiss
					// animation with a stale position
					mDownPosition = ListView.INVALID_POSITION;

					ViewGroup.LayoutParams lp;
					for (PendingDismissData pendingDismiss : mPendingDismisses) {
						// Reset view presentation
						pendingDismiss.view.setAlpha(1f);
						pendingDismiss.view.setTranslationX(0);
						lp = pendingDismiss.view.getLayoutParams();
						lp.height = originalHeight;
						pendingDismiss.view.setLayoutParams(lp);
					}

					// Send a cancel event
					long time = SystemClock.uptimeMillis();
					MotionEvent cancelEvent = MotionEvent.obtain(time, time,
							MotionEvent.ACTION_CANCEL, 0, 0, 0);
					mListView.dispatchTouchEvent(cancelEvent);

					mPendingDismisses.clear();
				}
			}
		});

		animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
			@Override
			public void onAnimationUpdate(ValueAnimator valueAnimator) {
				lp.height = (Integer) valueAnimator.getAnimatedValue();
				dismissView.setLayoutParams(lp);
			}
		});

		mPendingDismisses.add(new PendingDismissData(dismissPosition,
				dismissView));
		animator.start();
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个问题其实是一个编码问题。原本这个问句是“如何recyclerview侧删除item”,但是被编码转换了一下。如果要回答,应该是如下: 使用RecyclerView可以通过实现ItemTouchHelper.Callback类来实现删除item的效果。在实现ItemTouchHelper.Callback类中,可以通过onSwiped()方法实现删除item。在onSwiped()方法中,可以调用RecyclerView.Adapter的notifyItemRemoved()方法来通知RecyclerView移除该item。 ### 回答2: RecyclerView是Android上一个强大的列表控件。它提供了不同于ListView和GridView的Item视图回收机制以及更灵活的布局管理,同时也支持ItemTouchHelper类来实现方便的侧删除item的功能。 ItemTouchHelper是Android Support Library中为RecyclerView提供的一个辅助类,主要是用于支持RecyclerView中的拖拽和动事件。为了实现RecyclerView侧删除item的功能,我们需要使用ItemTouchHelper类,并实现ItemTouchHelper.Callback的各个方法(onMove、onSwiped等): 1. onMove():该方法用于处理RecyclerView中拖拽事件,通常用于实现Item的移动、排序等,我们可以在该方法中添加自己的代码,实现列表Item的移动。 2. onSwiped():该方法用于处理RecyclerView中的动事件,通常用于实现列表Item删除等功能,我们可以在该方法中添加自己的代码,实现Item删除的操作。 在我们实现删除Item的过程中,上述两个方法都需要实现。其中,onSwiped()方法中,我们通常会使用adapter的remove()方法将Item从列表中移除。 具体实现过程如下: 1.创建一个SwipeToDeleteCallback类,并继承自ItemTouchHelper.Callback类。 public class SwipeToDeleteCallback extends ItemTouchHelper.Callback { //实现 onMove() 方法 @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; } //实现 onSwiped() 方法 @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { //从数据源中移除 Item //adapter.remove(viewHolder.getAdapterPosition()) } //其他的实现方法... } 2.在onCreate()方法中创建SwipeToDeleteCallback实例,并将其绑定到RecyclerView上。 SwipeToDeleteCallback swipeHandler = new SwipeToDeleteCallback(this); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeHandler); itemTouchHelper.attachToRecyclerView(recyclerView); 3.在RecyclerView的Adapter中实现remove()方法,并在onBindViewHolder()方法中为每个Item添加GestureDetector。通过监听GestureDetector的动事件,判断当前Item是否向右动,并根据动的方向调用ItemTouchHelper.Callback的onSwiped()方法。 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { //数据源 List<String> mDataList = new ArrayList<>(); //添加Item操作 public void addItem(String item) { mDataList.add(item); notifyItemInserted(mDataList.size() - 1); } //移除 Item 操作 public void remove(int position) { mDataList.remove(position); notifyItemRemoved(position); } // 创建 ViewHolder @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { //创建 Item 布局 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false); return new ViewHolder(view); } // 绑定 ViewHolder @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { //设置 Item 数据 holder.itemText.setText(mDataList.get(position)); //添加 GestureDetector 监听 Item 动事件 GestureDetectorCompat gestureDetector = new GestureDetectorCompat(holder.itemView.getContext(), new MyGestureListener(holder)); holder.itemView.setOnTouchListener((view, motionEvent) -> { gestureDetector.onTouchEvent(motionEvent); return true; }); } //获取数据源大小 @Override public int getItemCount() { return mDataList.size(); } //创建 ViewHolder static class ViewHolder extends RecyclerView.ViewHolder { private TextView itemText; ViewHolder(View itemView) { super(itemView); itemText = itemView.findViewById(R.id.item_text); } } //GestureDetector 监听器,用于监听 Item动事件 class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private final ViewHolder mHolder; MyGestureListener(MyAdapter.ViewHolder holder) { mHolder = holder; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { final float xDiff = e2.getX() - e1.getX(); if (Math.abs(xDiff) > 100) { //向右动时调用 onSwiped()方法 if (xDiff > 0) { remove(mHolder.getAdapterPosition()); return true; } } return false; } } } 在上述功能实现过程中,我们继承了ItemTouchHelper.Callback来自定义每个Item的拖拽和动事件,使用ItemTouchHelper将这个Callback实例与RecyclerView绑定在一起,然后在Adapter中添加了remove()方法,以实现Item删除功能。最後,我们使用GestureDetector监听Item动事件,并根据动的方向调用ItemTouchHelper.Callback的onSwiped()方法。 在实现RecyclerView侧删除item的过程中,我们还可以对ItemView进行进一步的美化,例如在ItemView左右两侧添加删除和修改按钮等。总之,RecyclerView提供了足够灵活的机制,使得我们能够自由地扩展和定制列表控件的各种功能,为用户提供优秀的使用体验。 ### 回答3: RecyclerView是Android官方推出的一个列表控件,它比ListView具有更为灵活和高效的特性,支持数据动态更新、多种布局管理、 ItemTouchHelper操作等,而其支持的动操作也极大地方便了用户操作列表数据。针对RecyclerView侧删除item,可以通过以下方式实现。 1. ItemTouchHelper ItemTouchHelper是RecyclerView的内置工具类,可实现删除、拖曳排序等操作。通过ItemTouchHelper.onSwiped()回调将删除数据源,再通过适配器的notifyItemRemoved()通知RecyclerView删除某个条目。 2. 自定义ItemDecoration 我们可以通过RecyclerView的ItemDecoration来实现删除操作,通过自定义一个类继承ItemDecoration并在其onDraw()方法中绘制删除按钮,再通过onTouchEvent()方法实现删除操作,即可实现删除item。 3. 第三方库 目前市面上也有一些优秀的RecyclerView侧删除第三方库,如SwipeMenuRecyclerView、SwipeRecyclerView等。这些库都提供了美观且易于使用的侧删除控件,大大降低了开发者的工作量。 总之,RecyclerView的侧删除功能符合用户的使用习惯,提高了用户的操作效率,是不可或缺的一个功能,希望开发者在设计列表页面时能够注重用户体验,添加RecyclerView的侧删除功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值