《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 adapter = null;
- private ArrayList 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[email protected]View.OnTouchListener} that makes the list items in a
- *[email protected]ListView} dismissable.[email protected]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.
- *
- *
- * After creating the listener, the caller should also call
- *
- [email protected]ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
- * in the scroll listener returned by[email protected]#makeScrollListener()}. If a scroll
- * listener is already assigned, the caller should still pass scroll changes
- * through to this listener. This will ensure that this
- *[email protected]SwipeDismissListViewTouchListener} is paused during list view
- * scrolling.
- *
- *
- *
- * Example usage:
- *
- *
- *
- * 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());
- *
- *
- *
- * This class Requires API level 12 or later due to use of
- *
- [email protected]ViewPropertyAnimator}.
- *
- *
- *
- * For a generalized
- [email protected]View.OnTouchListener} that makes any view
- * dismissable, see[email protected]SwipeDismissTouchListener}.
- *
- *
- * @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 mPendingDismisses = new ArrayList();
- 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 [email protected]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[email protected]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[email protected]AbsListView.OnScrollListener} to be added to the
- *[email protected]ListView} using
- *[email protected]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
- *[email protected]SwipeDismissListViewTouchListener} is paused during list view
- * scrolling.
- *
- * @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();
- }
- }
文件夹结构:
来源: http://www.bubuko.com/infodetail-2086843.html