《Android SwipeToDismiss:左右滑动删除ListView条目Item》

Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeToDismiss )。该开源项目旨在:使得一个ListView的item在用户的手指在屏幕上左滑或者右滑时候,删除当前的这个ListView Item。

此种特效在新版的Android中应用不少。比方在Android 5.0及以上版本号中,通知栏下拉菜单中的条目。就是这样的操作效果,用户把通知栏下拉出来。手指轻触某一项。左滑或者右滑。删除(清除)此项。

 在  https://github.com/romannurik/Android-SwipeToDismiss 上下载到到库文件有两个(此说是基于截止2015年7月27日的版本号,兴许版本号或许会有所不同):SwipeDismissListViewTouchListener.java 和 SwipeDismissTouchListener.java
。假设仅仅是打算简单的仅仅是支持一个ListView列表中item左滑/右滑删除,那么仅仅仅仅导入SwipeDismissListViewTouchListener.java这一个库文件就可以。

如今就给出一个简单样例,说明怎样改造一个标准ListView。使其支持左右滑动某个item然后删除之。

測试用的MainActivity.java :

package zhangphil.listview;

import java.util.ArrayList;

import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
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 = this.getListView(); // 測试数据集。 items = new ArrayList<String>();
for (int i = 0; i < 50; i++) {
items.add("数据:" + i);
} adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, items);
listView.setAdapter(adapter); // 将ListView传递过来。 SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(
listView,
new SwipeDismissListViewTouchListener.DismissCallbacks() { @Override
public boolean canDismiss(int position) {
return true;
} // 此处将运行删除。记得要notifyDataSetChanged()。
@Override
public void onDismiss(ListView listView,
int[] reverseSortedPositions) {
for (int pos : reverseSortedPositions) {
items.remove(pos);
Log.i(this.getClass().getName(), "删除:" + pos);
} adapter.notifyDataSetChanged();
}
}); listView.setOnTouchListener(touchListener);
}
}

须要的库文件 SwipeDismissListViewTouchListener.java :

/*
* 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.
*/ package zhangphil.listview; 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();
}
}

文件夹结构:

Android SwipeToDismiss:左滑/右滑删除ListView条目Item的更多相关文章

  1. ViewPager 滑动一半的判断方法以及左滑右滑判断

    做项目的时候,会碰到用viewpager + fragments去实现多页滑动.有些时候需要完成:界面在滑动到一半或是一半以上的时候,需要把title之类的切换到下一个页面.这个时候仅仅依赖Viewp ...

  2. 左右滑动删除ListView条目Item--第三方开源--SwipeToDismiss

    Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeTo ...

  3. Android仿支付宝扣款顺序,动态改变ListView各Item次序

    前言:今天遇到个需求,需要让用户动态选择语音传输方式的次序,突然想起支付宝选择扣款顺序的功能,恰好能满足需要,就花了点时间写了个demo,在此权当学习记录 先上效果图 支付宝的效果 demo的效果 思 ...

  4. 移动端h5禁用浏览器左滑右滑的前进后退功能

    在项目运行过程中发现,用户在有左右滑动前进后退的功能的浏览器上签字时,偶然触发了前进后退会导致canvas像是重置了一样内容消失,所以需要在代码中处理这种情况. 基本原理就是在touchmove事件中 ...

  5. 【转】android: 长按删除listview的item

    原文网址:http://www.cnblogs.com/nuistlr/archive/2012/09/07/2675649.html 首先要继承OnItemLongClickListener pub ...

  6. android: 长按删除listview的item

    转自:http://www.cnblogs.com/nuistlr/archive/2012/09/07/2675649.html 首先要继承OnItemLongClickListener publi ...

  7. Taro UI开发小程序实现左滑喜欢右滑不喜欢效果

    前言:年后入职了一家新公司,与前同事交接完之后,发现公司有一个四端的项目(iOS,Android,H5,小程序),iOS和安卓都实现了左滑右滑的效果,而h5和小程序端没实现,询问得知前同事因网上没找到 ...

  8. App开发流程之右滑返回手势功能

    iOS7以后,导航控制器,自带了从屏幕左边缘右滑返回的手势功能. 但是,如果自定义了导航栏返回按钮,这项功能就失效了,需要自行实现.又如果需要修改手势触发范围,还是需要自行实现. 广泛应用的一种实现方 ...

  9. ListView条目的侧拉删除

    功能实现:类似QQ聊天记录中条目,左滑,可以删除该条目的功能:后续可以在自己的项目中添加该功能. 步骤一: (1)定义主布局,里面包含一个简单的ListView:命名id="lv" ...

随机推荐

  1. Javascript的一些技巧(《Javascript DOM编程艺术》、Javascript语言精粹)

    1.什么时候用布尔变量当变量 假设你需要一个这样的变量:我在睡觉——存为一个值:我没在睡觉——存为另一个值. 一般的做法: var stateOne="睡觉",stateTwo=& ...

  2. RabbitMQ 为什么需要信道?为什么不是TCP直接通信?

    交换器和队列的关系 1. 交换器是通过路由键和队列绑定在一起的,如果消息拥有的“路由键”跟队列和交换器的“路由键”匹配,那么消息就会被路由到该绑定的队列当中去 2. 也就是说,消息到队列的过程中,消息 ...

  3. Appium基于python unittest自动化测试并生成html测试报告

    本文基于python单元测试框架unittest完成appium自动化测试,生成基于html可视化测试报告 代码示例: #利用unittest并生成测试报告 class Appium_test(uni ...

  4. DatePickerDialog和TimePickerDialog(基于对话框显示时间和日期)

    public class MainActivity extends Activity implements android.view.View.OnClickListener{ private But ...

  5. Windows下压缩成tar.gz格式

    tar.gz 是linux和unix下面比较常用的格式,几个命令就可以把文件压缩打包成tar.gz格式,然而这种格式在windows并不多见,WinRAR.WinZip等主流压缩工具可以释放解开,却不 ...

  6. js技巧(三)

    1.检测浏览器,search的用法 if(window.navigator.userAgent.search(/firefox/i)!=-1){ alert('ff'); } else if(wind ...

  7. 【C++】智能指针简述(五):解决循环引用的weak_ptr

    总结一下前文内容: 1.智能指针通过RAII方法来管理指针:构造对象时,完成资源初始化;析构对象时,对资源进行清理及汕尾. 2.auto_ptr,通过“转移所有权”来防止析构一块内存多次.(如何转移? ...

  8. 【C++】智能指针简述(四):shared_ptr

    在开始本文内容之前,我们再来总结一下,前文内容: 1.智能指针采用RAII机制,在构造对象时进行资源的初始化,析构对象时进行资源的清理及汕尾. 2.auto_ptr防止拷贝后析构释放同一块内存,采用& ...

  9. 项目经验——Sql server 数据库的备份和还原____还原数据库提示“介质集有2个介质簇,但只提供了1个。必须提供所有成员” .

    在对数据库备份与还原的过程中,我遇到一个问题“介质集有2个介质簇,但只提供了1个.必须提供所有成员”,下面详细的介绍一下遇到问题的经过与问题解决的方法! 一.备份与还原遇到的问题描述与解决方法: 前两 ...

  10. R语言曲线拟合函数(绘图)

    曲线拟合:(线性回归方法:lm) 1.x排序 2.求线性回归方程并赋予一个新变量     z=lm(y~x+I(x^2)+...) 3.plot(x,y)    #做y对x的散点图 4.lines(x ...