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

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

 package com.lixu.SwipeRefreshLayoutyongfa;

 import java.util.ArrayList;
import java.util.HashMap; import com.lixu.SwipeRefreshLayoutyongfa.SwipeDismissListViewTouchListener.DismissCallbacks;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView; public class MainActivity extends Activity {
private ArrayAdapter<String> adapter;
private ArrayList<HashMap<String, Object>> data;
private int count = 0;
SwipeRefreshLayout srl;
private int[] image = { R.drawable.beijing, R.drawable.chengdu, R.drawable.guangzhou, R.drawable.hangzhou,
R.drawable.wuhan, R.drawable.xian, R.drawable.shenzhen }; private String[] city = { "北京", "成都", "广州", "杭州", "武汉", "西安", "深圳" };
private String KEY_IMAGE = "image";
private String KEY_TXT = "txt"; private int i = 0;
private int j = 0; ListView lv; SwipeDismissListViewTouchListener touchlistener; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); data = new ArrayList<HashMap<String, Object>>(); lv = (ListView) findViewById(R.id.lv); srl = (SwipeRefreshLayout) findViewById(R.id.srl);
// 设置刷新动画的颜色.
srl.setColorSchemeResources(android.R.color.holo_green_light, android.R.color.holo_blue_bright,
android.R.color.holo_red_light); srl.setOnRefreshListener(new OnRefreshListener() {
// SwipeRefreshLayout接管其包裹的ListView下拉事件。
// 每一次对ListView的下拉动作,将触发SwipeRefreshLayout的onRefresh()。
@SuppressWarnings("unchecked")
@Override
public void onRefresh() { new MyAsyncTask().execute(); }
});
adapter = new MyAdapter(this, -1); lv.setAdapter(adapter);
addSwipeDismissListView();
// 往listview里面添加触摸事件
lv.setOnTouchListener(touchlistener);
} private void addSwipeDismissListView() {
touchlistener = new SwipeDismissListViewTouchListener(lv, new DismissCallbacks() { @Override
public void onDismiss(ListView listView, int[] reverseSortedPositions) {
for (int pos : reverseSortedPositions)
// 获取滑动的下标行后删除这一行
data.remove(pos);
// 每次要刷新适配器
adapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "删除成功!", 0).show();
} @Override
public boolean canDismiss(int position) {
return true;
}
});
} private class MyAdapter extends ArrayAdapter {
private LayoutInflater flater; public MyAdapter(Context context, int resource) {
super(context, resource);
flater = LayoutInflater.from(context);
} @Override
public int getCount() {
return data.size();
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = flater.inflate(R.layout.item, null); TextView tv = (TextView) convertView.findViewById(R.id.tv);
tv.setText(data.get(position).get(KEY_TXT) + ""); ImageView iv = (ImageView) convertView.findViewById(R.id.iv);
iv.setImageResource((Integer) data.get(position).get(KEY_IMAGE)); return convertView;
}
} @SuppressWarnings("rawtypes")
private class MyAsyncTask extends AsyncTask {
private HashMap<String, Object> map; @Override
protected void onPreExecute() {
// 刷新开始
srl.setRefreshing(true);
map = new HashMap<String, Object>();
} @Override
protected Object doInBackground(Object... params) {
// 防止下标越界,到达数组长度后返回开始
if (i == image.length)
i = 0;
if (j == city.length)
j = 0; map.put(KEY_IMAGE, image[i++]);
map.put(KEY_TXT, city[j++]); // 处理一些耗时的事件
return map;
} @SuppressWarnings("unchecked")
@Override
protected void onPostExecute(Object result) {
// 每次从头部添加
data.add(0, (HashMap<String, Object>) result);
// 刷新适配器
adapter.notifyDataSetChanged();
// 刷新完毕
srl.setRefreshing(false);
} } }

需要用的SwipeDismissListViewTouchListener类

 package com.lixu.SwipeRefreshLayoutyongfa;

 /*
* 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();
}
}

xml文件:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lixu.SwipeRefreshLayoutyongfa.MainActivity" > <android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
android:layout_width="match_parent"
android:layout_height="match_parent" > <ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout> </RelativeLayout>
 <?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="match_parent"
android:orientation="vertical" > <ImageView
android:id="@+id/iv"
android:layout_width="50dp"
android:layout_height="50dp" /> <TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp" /> </LinearLayout>

运行效果图:

下拉刷新列表添加SwipeDismissListViewTouchListener实现滑动删除某一列。的更多相关文章

  1. 你必须了解的RecyclerView的五大开源项目-解决上拉加载、下拉刷新和添加Header、Footer等问题

    前段时间做项目由于采用的MD设计,所以必须要使用RecyclerView全面代替ListView.但是开发中遇到了需要实现RecyclerView上拉加载.下拉刷新和添加Header以及Footer等 ...

  2. 第三方开源框架的下拉刷新列表(QQ比较常用的)。

    PullToRefreshListView是第三方开源框架下拉刷新列表,比较流行的QQ 微信等上面都在用. 下载地址(此开源框架于2013年后不再更新) 点此下载 package com.lixu.k ...

  3. ionic3.0 中带顶部导航的下拉刷新列表的实现

    1.最终实现效果 2.html代码布局: 3.css样式控制(注:下面这两个css类名需在浏览器解析后才可看到)

  4. Android Demo 下拉刷新+加载更多+滑动删除

    小伙伴们在逛淘宝或者是各种app上,都可以看到这样的功能,下拉刷新和加载更多以及滑动删除,刷新,指刷洗之后使之变新,比喻突破旧的而创造出新的,比如在手机上浏览新闻的时候,使用下拉刷新的功能,我们可以第 ...

  5. 安卓listView实现下拉刷新上拉加载滑动仿QQ的删除功能

    大家对这些功能都是看的多了,然后对上拉刷新和下拉加载的原理都是非常清楚的,所以实现这功能其实也就是为了让大家能够从众多的同行们来进行比较学习而已,虽然即使是这样,但是面试的时候面试官还是会问你上拉和下 ...

  6. mui实现列表的下拉刷新上拉加载

    1.引入mui控件的js文件和css样式文件 <link rel="stylesheet" href="css/mui.min.css"> < ...

  7. Windows phone应用开发[18]-下拉刷新

    在windows phone 中采用数据列表时为了保证用户体验常遇到加载数据的问题.这个问题普遍到只要你用到数据列表就要早晚面对这个问题. 很多人会说这个问题已经有解决方案. 其实真正问题并不在于如何 ...

  8. RecyclerViewLoadMoreDemo【封装上拉加载功能的RecyclerView,搭配SwipeRefreshLayout实现下拉刷新】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 封装含有上拉加载功能的RecyclerView,然后搭配SwipeRefreshLayout实现下拉刷新.上拉加载功能. 在项目中将 ...

  9. ListView实现下拉刷新功能

    很久没有写博客了,感觉都懒惰了,今天说一下一个自定义的空间,就是ListView下拉列表可以刷新的功能,相信很多同学都看到过这种功能,最典型的就是新浪微博的下拉刷新列表了. 废话不多说,首先呢,下拉刷 ...

随机推荐

  1. PL/SQL 编写规范

    大小写风格 所有数据库关键字和保留字均大写; 缩进风格 使用空格, 缩进2格(前边空两个格, 后边要与SQL语句的一致, 比如下面:) SELECT * FROM TABLE 空格及换行 1行只写1条 ...

  2. php提示:Call to undefined function curl_init

    我要利用curl函数进行数据采集时发现提示Call to undefined function curl_init错误了,后来从官网了解到因为curl默认不是php开启的函数,我们需要手工打开哦,下面 ...

  3. c++中的类的对象与类的指针

    以上内容来自:http://wenku.baidu.com/link?url=haeRBhswlEcqddk48uW8YVMsdFNWsllimn_dzUYchb6G9NdT4pqgluCpnLQId ...

  4. Object Pascal 面向对象的特性

    2 面向对象的特性 在软件系统开发过程中,结构分析技术和结构设计技术具有很多优点,但同时也存在着许多难以克服的缺点.因为结构分析技术和结构设计技术是围绕着实现处理功能来构造系统的,而在系统维护和软件升 ...

  5. Android网络编程系列 一 TCP/IP协议族

    在学习和使用Android网路编程时,我们接触的仅仅是上层协议和接口如Apache的httpclient或者Android自带的httpURlconnection等等.对于这些接口的底层实现我们也有必 ...

  6. hdu 1588(Fibonacci矩阵求和)

    题目的大意就是求等差数列对应的Fibonacci数值的和,容易知道Fibonacci对应的矩阵为[1,1,1,0],因为题目中f[0]=0,f[1]=1,所以推出最后结果f[n]=(A^n-1).a, ...

  7. 转:C/C++基本数据类型所占字节数

    参考:http://blog.csdn.net/vast_sea/article/details/8076934 关于这个基本的问题,很早以前就很清楚了,C标准中并没有具体给出规定那个基本类型应该是多 ...

  8. Mvc4_@RenderBody()和@RenderSection()

    @RenderBody():呈现子页的主体内容 @RenderSection():呈现特别的节部分. HelperResult RenderSection(string name, bool requ ...

  9. iOS开发 判断用户是否开启了定位

    - (BOOL)achiveUserLocationStart { CLAuthorizationStatus status = [CLLocationManager authorizationSta ...

  10. easyui-datebox 和easyui-datetimebox 设置默认时间当前时间

    //显示当前日期 formatterDate = function (date) { var day = date.getDate() > 9 ? date.getDate() : " ...