自定义RecyclerView 支持 横向纵向,滑动删除Item

项目源码已经放上去:https://github.com/xufeifandj/SwipeRecyclerView

本项目直接 继承于RecyclerView 本身就支持 横向纵向的 列表。

具体代码请看代码。

package com.ferris.holeswipeview;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.Scroller;

/**
 * http://blog.csdn.net/xufeifandj www.github.com/xufeifandj
 *
 * @ferris [email protected]
 *
 */
public class CusomSwipeView extends RecyclerView {
	private Orientation orientation = Orientation.HORIZONTAL;
	/**
	 * 当前滑动的ListView position
	 */
	private int slidePosition;
	/**
	 * 手指按下X的坐标
	 */
	private int downY;
	/**
	 * 手指按下Y的坐标
	 */
	private int downX;
	/**
	 * 屏幕宽度
	 */
	private int screenWidth;
	/**
	 * ListView的item
	 */
	private View itemView;
	/**
	 * 滑动类
	 */
	private Scroller scroller;
	private static final int SNAP_VELOCITY = 600;
	/**
	 * 速度追踪对象
	 */
	private VelocityTracker velocityTracker;
	/**
	 * 是否响应滑动,默认为不响应
	 */
	private boolean isSlide = false;
	/**
	 * 认为是用户滑动的最小距离
	 */
	private int mTouchSlop;
	/**
	 * 移除item后的回调接口
	 */
	private RemoveListener mRemoveListener;
	/**
	 * 用来指示item滑出屏幕的方向,向左或者向右,用一个枚举值来标记
	 */
	private RemoveDirection removeDirection;

	// 滑动删除方向的枚举值
	public enum RemoveDirection {
		RIGHT, LEFT;
	}

	public CusomSwipeView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
		init(context);
	}

	public CusomSwipeView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		init(context);
	}

	public CusomSwipeView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
		init(context);
	}

	public void init(Context context) {

		if (orientation == Orientation.VERTICAL) {
			screenWidth = ((WindowManager) context
					.getSystemService(Context.WINDOW_SERVICE))
					.getDefaultDisplay().getWidth();
		} else {
			screenWidth = ((WindowManager) context
					.getSystemService(Context.WINDOW_SERVICE))
					.getDefaultDisplay().getHeight();
		}
		scroller = new Scroller(context);
		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	}

	/**
	 * 设置滑动删除的回调接口
	 *
	 * @param removeListener
	 */
	public void setRemoveListener(RemoveListener removeListener) {
		this.mRemoveListener = removeListener;
	}

	/**
	 * 分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件
	 */
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN: {
			addVelocityTracker(event);

			// 假如scroller滚动还没有结束,我们直接返回
			if (!scroller.isFinished()) {
				return super.dispatchTouchEvent(event);
			}
			downX = (int) event.getX();
			downY = (int) event.getY();

			itemView = findChildViewUnder(downX, downY);
			if (itemView == null) {
				return super.dispatchTouchEvent(event);
			}

			slidePosition = getChildPosition(itemView);
			// 无效的position, 不做任何处理
			if (slidePosition == AdapterView.INVALID_POSITION) {
				return super.dispatchTouchEvent(event);
			}

			break;
		}
		case MotionEvent.ACTION_MOVE: {
			if (orientation == Orientation.VERTICAL) {// 如果
														// 左右滑动的距离大于最大的滑动距离,并且没有上下滑动,就代表
														// 右滑删除或者左滑删除
				if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY
						|| (Math.abs(event.getX() - downX) > mTouchSlop && Math
								.abs(event.getY() - downY) < mTouchSlop)) {
					isSlide = true;

				}
			} else {
				// 如果 上下滑动的距离大于最大的滑动距离,并且没有左右滑动,就代表上滑删除或者下滑删除
				if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY
						|| (Math.abs(event.getY() - downY) > mTouchSlop && Math
								.abs(event.getX() - downX) < mTouchSlop)) {
					isSlide = true;

				}

			}
			break;
		}
		case MotionEvent.ACTION_UP:
			recycleVelocityTracker();
			break;
		}

		return super.dispatchTouchEvent(event);
	}

	/**
	 * 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值
	 */
	private void scrollRight() {
		if (orientation == Orientation.VERTICAL) {// 往右滑动
			removeDirection = RemoveDirection.RIGHT;
			final int delta = (screenWidth + itemView.getScrollX());
			// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
			scroller.startScroll(itemView.getScrollX(), 0, -delta, 0,
					Math.abs(delta));
			postInvalidate(); // 刷新itemView
		} else {// 往上滑动
			removeDirection = RemoveDirection.RIGHT;
			final int delta = (screenWidth + itemView.getScrollY());
			// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
			scroller.startScroll(0, itemView.getScrollY(), -delta, 0,
					Math.abs(delta));
			postInvalidate(); // 刷新itemView
		}
	}

	/**
	 * 向左滑动,根据上面我们知道向左滑动为正值
	 */
	private void scrollLeft() {
		if (orientation == Orientation.VERTICAL) {// 往左滑动
			removeDirection = RemoveDirection.LEFT;
			final int delta = (screenWidth - itemView.getScrollX());
			// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
			scroller.startScroll(itemView.getScrollX(), 0, delta, 0,
					Math.abs(delta));
			postInvalidate(); // 刷新itemView
		} else {
			removeDirection = RemoveDirection.LEFT;
			final int delta = (screenWidth - itemView.getScrollY());
			// 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
			scroller.startScroll(0, itemView.getScrollY(), delta, 0,
					Math.abs(delta));
			postInvalidate(); // 刷新itemView
		}
	}

	/**
	 * 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动
	 */
	private void scrollByDistanceX() {
		// 如果向左滚动的距离大于屏幕的二分之一,就让其删除

		if (orientation == Orientation.VERTICAL) {
			if (itemView.getScrollX() >= screenWidth / 2) {
				scrollLeft();
			} else if (itemView.getScrollX() <= -screenWidth / 2) {
				scrollRight();
			} else {
				// 滚回到原始位置,为了偷下懒这里是直接调用scrollTo滚动
				itemView.scrollTo(0, 0);
			}
		} else {
			if (itemView.getScrollY() >= screenWidth / 2) {
				scrollLeft();
			} else if (itemView.getScrollY() <= -screenWidth / 2) {
				scrollRight();
			} else {
				// 滚回到原始位置,为了偷下懒这里是直接调用scrollTo滚动
				itemView.scrollTo(0, 0);
			}
		}

	}

	/**
	 * 处理我们拖动ListView item的逻辑
	 */
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (isSlide && slidePosition != AdapterView.INVALID_POSITION
				&& itemView != null) {
			requestDisallowInterceptTouchEvent(true);
			addVelocityTracker(ev);
			final int action = ev.getAction();
			int x = (int) ev.getX();
			int y = (int) ev.getY();
			switch (action) {
			case MotionEvent.ACTION_DOWN:
				break;
			case MotionEvent.ACTION_MOVE:

				MotionEvent cancelEvent = MotionEvent.obtain(ev);
				cancelEvent
						.setAction(MotionEvent.ACTION_CANCEL
								| (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
				onTouchEvent(cancelEvent);

				if (orientation == Orientation.VERTICAL) {
					int deltaX = downX - x;
					downX = x;
					// 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚
					itemView.scrollBy(deltaX, 0);
				} else {
					int deltaY = downY - y;
					downY = y;
					itemView.scrollBy(0, deltaY);
				}

				return true; // 拖动的时候ListView不滚动
			case MotionEvent.ACTION_UP:
				int velocityX = getScrollVelocity();
				if (velocityX > SNAP_VELOCITY) {
					scrollRight();
				} else if (velocityX < -SNAP_VELOCITY) {
					scrollLeft();
				} else {
					scrollByDistanceX();
				}

				recycleVelocityTracker();
				// 手指离开的时候就不响应左右滚动
				isSlide = false;
				break;
			}
		}

		// 否则直接交给ListView来处理onTouchEvent事件
		return super.onTouchEvent(ev);
	}

	@Override
	public void computeScroll() {
		// 调用startScroll的时候scroller.computeScrollOffset()返回true,
		if (scroller.computeScrollOffset()) {
			// 让ListView item根据当前的滚动偏移量进行滚动
			itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY());

			postInvalidate();

			// 滚动动画结束的时候调用回调接口
			if (scroller.isFinished()) {
				if (mRemoveListener == null) {
					throw new NullPointerException(
							"RemoveListener is null, we should called setRemoveListener()");
				}

				itemView.scrollTo(0, 0);
				mRemoveListener.removeItem(removeDirection, slidePosition);
			}
		}
	}

	/**
	 * 添加用户的速度跟踪器
	 *
	 * @param event
	 */
	private void addVelocityTracker(MotionEvent event) {
		if (velocityTracker == null) {
			velocityTracker = VelocityTracker.obtain();
		}

		velocityTracker.addMovement(event);
	}

	/**
	 * 移除用户速度跟踪器
	 */
	private void recycleVelocityTracker() {
		if (velocityTracker != null) {
			velocityTracker.recycle();
			velocityTracker = null;
		}
	}

	/**
	 * 获取X方向的滑动速度,大于0向右滑动,反之向左
	 *
	 * @return
	 */
	private int getScrollVelocity() {
		if (orientation == Orientation.VERTICAL) {
			velocityTracker.computeCurrentVelocity(1000);
			int velocity = (int) velocityTracker.getXVelocity();
			return velocity;
		}else{
			velocityTracker.computeCurrentVelocity(1000);
			int velocity = (int) velocityTracker.getYVelocity();
			return velocity;
		}

	}

	/**
	 *
	 * 当ListView item滑出屏幕,回调这个接口 我们需要在回调方法removeItem()中移除该Item,然后刷新ListView
	 *
	 *
	 */
	public interface RemoveListener {
		public void removeItem(RemoveDirection direction, int position);
	}

	public static enum Orientation {
		HORIZONTAL(0), VERTICAL(1);

		private int value;

		private Orientation(int i) {
			value = i;
		}

		public int value() {
			return value;
		}

		public static Orientation valueOf(int i) {
			switch (i) {
			case 0:
				return HORIZONTAL;
			case 1:
				return VERTICAL;
			default:
				throw new RuntimeException("[0->HORIZONTAL, 1->VERTICAL]");
			}
		}
	}
}
时间: 2024-10-23 01:04:51

自定义RecyclerView 支持 横向纵向,滑动删除Item的相关文章

本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18311877)Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果,之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就知道

Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果, 之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变 成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就

滑动删除Item,拖拽切换Item,你想了解的都在这儿

1. 概述 如果上两篇对RecyclerView介绍后,依然没有引起你的兴趣,那么下面关于RecyclerView的使用我相信一定会让你如获珍宝.直接看运行效果. 图-1 RecyclerView滑动删除 图-2 RecyclerView拖拽切换 用ListView或者GridView实现上图中的功能非常麻烦,而如果用RecyclerView来实现,则非常简单. 如果对RecyclerView还没有认识的同学,可以查看这两篇文章. RecyclerView使用一 RecyclerView使用二

ListView 实现带有Filpper效果的左右滑动删除 Item

ListView 实现带有Filpper效果的左右滑动删除 Item  的实现最主要的方法还是 对 Listview 的继承重写 .然后是在删除过程中添加 TranslateAnimation 滑动事件. <span style="font-size:14px;">public class FilpperActivity extends Activity { private FilpperListvew flipperListView; private MyAdapter

【转】Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

原文网址:http://blog.csdn.net/xiaanming/article/details/17539199 我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListVi

[转]Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该

Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该

android 下拉刷新上拉加载更多,高仿ios左滑动删除item,解决了众多手势问题

一.前言 老规矩,别的不说,这demo是找了很相关知识集合而成的,可以说对我这种小白来说是绞尽脑汁!程序员讲的是无图无真相!现在大家一睹为快! 二.比较关键的还是scroller这个类的 package com.icq.slideview.view; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; i

Android 自定义ListView控件,滑动删除

1.触摸事件 dispatchTouchEvent 判断是否处理触摸动作 onTouchEvent 处理触摸动作 2.Android对于控制和获取View在屏幕很强大 ListView: pointToPosition 根据触摸点获取item的位置 getChildAt 根据索引获取item的View,注意从第一个可视化的item算起 View: getLocationOnScreen获取View在屏幕的坐标 import android.content.Context; import andr