Android SwipeToDismiss:左滑/右滑删除ListView条目Item



《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;@Overrideprotected 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() {@Overridepublic boolean canDismiss(int position) {return true;}// 此处将执行删除,记得要notifyDataSetChanged()。@Overridepublic 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 * * * * 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 valuesprivate int mSlop;private int mMinFlingVelocity;private int mMaxFlingVelocity;private long mAnimationTime;// Fixed propertiesprivate ListView mListView;private DismissCallbacks mCallbacks;private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero// Transient propertiesprivate 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() {@Overridepublic void onScrollStateChanged(AbsListView absListView,int scrollState) {setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);}@Overridepublic void onScroll(AbsListView absListView, int i, int i1, int i2) {}};}@Overridepublic 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) {// cancelmDownView.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 draggingdismiss = (velocityX < 0) == (deltaX < 0);dismissRight = mVelocityTracker.getXVelocity() > 0;}if (dismiss && mDownPosition != ListView.INVALID_POSITION) {// dismissfinal View downView = mDownView; // mDownView gets null'd before// animation endsfinal int downPosition = mDownPosition;++mDismissAnimationRefCount;mDownView.animate().translationX(dismissRight ? mViewWidth : -mViewWidth).alpha(0).setDuration(mAnimationTime).setListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {performDismiss(downView, downPosition);}});} else {// cancelmDownView.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;}@Overridepublic int compareTo(PendingDismissData other) {// Sort by descending positionreturn 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() {@Overridepublic void onAnimationEnd(Animator animation) {–mDismissAnimationRefCount;if (mDismissAnimationRefCount == 0) {// No active animations, process all pending dismisses.// Sort by descending positionCollections.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 positionmDownPosition = ListView.INVALID_POSITION;ViewGroup.LayoutParams lp;for (PendingDismissData pendingDismiss : mPendingDismisses) {// Reset view presentationpendingDismiss.view.setAlpha(1f);pendingDismiss.view.setTranslationX(0);lp = pendingDismiss.view.getLayoutParams();lp.height = originalHeight;pendingDismiss.view.setLayoutParams(lp);}// Send a cancel eventlong 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() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {lp.height = (Integer) valueAnimator.getAnimatedValue();dismissView.setLayoutParams(lp);}});mPendingDismisses.add(new PendingDismissData(dismissPosition,dismissView));animator.start();}}

目录结构:

华贵的衣服穿在心肠污浊的人身上,显得更丑恶。

Android SwipeToDismiss:左滑/右滑删除ListView条目Item

相关文章:

你感兴趣的文章:

标签云: