自定义View之列表滑动删除DEMO

列表左右滑动删除demo.

list_item.xml
<?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="horizontal" >
     
 	<TextView 
 	    android:id="@+id/text1"
 	    android:layout_width="fill_parent"
 	    android:layout_height="60dip"
 	    android:gravity="center"
 	    android:textColor="#ff000000"
 	    android:background="#ff00ff00"
 	    />
 </LinearLayout>

activity_main.xml

<LinearLayout 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"
     android:orientation="vertical" >
 
     <TextView
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="@string/hello_world"
         tools:context=".MainActivity" />
 
     <com.android.listviewtest.TestListView
         android:id="@+id/lv"
         android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:divider="@null"
             android:stackFromBottom="true"
             android:fadingEdge="vertical"
             android:scrollbars="none"
             android:fadingEdgeLength="20dip"
             android:layout_gravity="bottom|left"
             android:clipToPadding="false"
             android:clipChildren="false"
         >
 
         <LinearLayout
             android:id="@+id/linear_layout"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:clipChildren="false"
             android:clipToPadding="false"
             android:orientation="vertical" >
         </LinearLayout>
     </com.android.listviewtest.TestListView>
 
 </LinearLayout>

SwipeHelper.java

package com.android.listviewtest;
 
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.graphics.RectF;
 import android.util.Log;
 import android.view.animation.LinearInterpolator;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 
 public class SwipeHelper {
     static final String TAG = "com.android.systemui.SwipeHelper";
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
     private static final boolean CONSTRAIN_SWIPE = true;
     private static final boolean FADE_OUT_DURING_SWIPE = true;
     private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
 
     public static final int X = 0;
     public static final int Y = 1;
 
     private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
 
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
     private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
     private int MAX_DISMISS_VELOCITY = 2000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
 
     public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
                                                  // where fade starts
     static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which alpha->0
 
     private float mPagingTouchSlop;
     private Callback mCallback;
     private int mSwipeDirection;
     private VelocityTracker mVelocityTracker;
 
     private float mInitialTouchPos;
     private boolean mDragging;
     private View mCurrView;
     private View mCurrAnimView;
     private boolean mCanCurrViewBeDimissed;
     private float mDensityScale;
 
     public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
             float pagingTouchSlop) {
         mCallback = callback;
         mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
         mDensityScale = densityScale;
         mPagingTouchSlop = pagingTouchSlop;
     }
 
     public void setDensityScale(float densityScale) {
         mDensityScale = densityScale;
     }
 
     public void setPagingTouchSlop(float pagingTouchSlop) {
         mPagingTouchSlop = pagingTouchSlop;
     }
 
     private float getPos(MotionEvent ev) {
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
 
     private float getTranslation(View v) {
         return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
     }
 
     private float getVelocity(VelocityTracker vt) {
         return mSwipeDirection == X ? vt.getXVelocity() :
                 vt.getYVelocity();
     }
 
     private ObjectAnimator createTranslationAnimation(View v, float newPos) {
         ObjectAnimator anim = ObjectAnimator.ofFloat(v,
                 mSwipeDirection == X ? "translationX" : "translationY", newPos);
         return anim;
     }
 
     private float getPerpendicularVelocity(VelocityTracker vt) {
         return mSwipeDirection == X ? vt.getYVelocity() :
                 vt.getXVelocity();
     }
 
     private void setTranslation(View v, float translate) {
         if (mSwipeDirection == X) {
             v.setTranslationX(translate);
         } else {
             v.setTranslationY(translate);
         }
     }
 
     private float getSize(View v) {
         return mSwipeDirection == X ? v.getMeasuredWidth() :
                 v.getMeasuredHeight();
     }
 
     private float getAlphaForOffset(View view) {
         float viewSize = getSize(view);
         final float fadeSize = ALPHA_FADE_END * viewSize;
         float result = 1.0f;
         float pos = getTranslation(view);
         if (pos >= viewSize * ALPHA_FADE_START) {
             result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
         } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
             result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
         }
         // Make .03 alpha the minimum so you always see the item a bit-- slightly below
         // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
         // a bit jarring
         return Math.max(0.03f, result);
     }
 
     // invalidate the view's own bounds all the way up the view hierarchy
     public static void invalidateGlobalRegion(View view) {
         invalidateGlobalRegion(
             view,
             new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
     }
 
     // invalidate a rectangle relative to the view's coordinate system all the way up the view
     // hierarchy
     public static void invalidateGlobalRegion(View view, RectF childBounds) {
         //childBounds.offset(view.getTranslationX(), view.getTranslationY());
         if (DEBUG_INVALIDATE)
             Log.v(TAG, "-------------");
         while (view.getParent() != null && view.getParent() instanceof View) {
             view = (View) view.getParent();
             view.getMatrix().mapRect(childBounds);
             view.invalidate((int) Math.floor(childBounds.left),
                             (int) Math.floor(childBounds.top),
                             (int) Math.ceil(childBounds.right),
                             (int) Math.ceil(childBounds.bottom));
             if (DEBUG_INVALIDATE) {
                 Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
                         + "," + (int) Math.floor(childBounds.top)
                         + "," + (int) Math.ceil(childBounds.right)
                         + "," + (int) Math.ceil(childBounds.bottom));
             }
         }
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         final int action = ev.getAction();
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
                 mCurrView = mCallback.getChildAtPosition(ev);
                 mVelocityTracker.clear();
                 if (mCurrView != null) {
                     mCurrAnimView = mCallback.getChildContentView(mCurrView);
                     mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                     mVelocityTracker.addMovement(ev);
                     mInitialTouchPos = getPos(ev);
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
                 if (mCurrView != null) {
                     mVelocityTracker.addMovement(ev);
                     float pos = getPos(ev);
                     float delta = pos - mInitialTouchPos;
                     if (Math.abs(delta) > mPagingTouchSlop) {
                         mCallback.onBeginDrag(mCurrView);
                         mDragging = true;
                         mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView);
                     }
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 mDragging = false;
                 mCurrView = null;
                 mCurrAnimView = null;
                 break;
         }
         return mDragging;
     }
 
     /**
      * @param view The view to be dismissed
      * @param velocity The desired pixels/second speed at which the view should move
      */
     public void dismissChild(final View view, float velocity) {
         final View animView = mCallback.getChildContentView(view);
         final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
         float newPos;
 
         if (velocity < 0
                 || (velocity == 0 && getTranslation(animView) < 0)
                 // if we use the Menu to dismiss an item in landscape, animate up
                 || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y)) {
             newPos = -getSize(animView);
         } else {
             newPos = getSize(animView);
         }
         int duration = MAX_ESCAPE_ANIMATION_DURATION;
         if (velocity != 0) {
             duration = Math.min(duration,
                                 (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math
                                         .abs(velocity)));
         } else {
             duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
         }
 
         animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
         anim.setInterpolator(sLinearInterpolator);
         anim.setDuration(duration);
         anim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animation) {
                 mCallback.onChildDismissed(view);
                 animView.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
                 if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                     animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
         });
         anim.start();
     }
 
     public void snapChild(final View view, float velocity) {
         final View animView = mCallback.getChildContentView(view);
         final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView);
         ObjectAnimator anim = createTranslationAnimation(animView, 0);
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
                 if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                     animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
         });
         anim.start();
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
         if (!mDragging) {
             return false;
         }
 
         mVelocityTracker.addMovement(ev);
         final int action = ev.getAction();
         switch (action) {
             case MotionEvent.ACTION_OUTSIDE:
             case MotionEvent.ACTION_MOVE:
                 if (mCurrView != null) {
                     float delta = getPos(ev) - mInitialTouchPos;
                     // don't let items that can't be dismissed be dragged more than
                     // maxScrollDistance
                     if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
                         float size = getSize(mCurrAnimView);
                         float maxScrollDistance = 0.15f * size;
                         if (Math.abs(delta) >= size) {
                             delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
                         } else {
                             delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
                         }
                     }
                     setTranslation(mCurrAnimView, delta);
                     if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
                         mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
                     }
                     invalidateGlobalRegion(mCurrView);
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 if (mCurrView != null) {
                     float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
                     mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
                     float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
                     float velocity = getVelocity(mVelocityTracker);
                     float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
 
                     // Decide whether to dismiss the current view
                     boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
                             Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView);
                     boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
                             (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
                             (velocity > 0) == (getTranslation(mCurrAnimView) > 0);
 
                     boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) &&
                             (childSwipedFastEnough || childSwipedFarEnough);
 
                     if (dismissChild) {
                         // flingadingy
                         dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
                     } else {
                         // snappity
                         mCallback.onDragCancelled(mCurrView);
                         snapChild(mCurrView, velocity);
                     }
                 }
                 break;
         }
         return true;
     }
 
     public interface Callback {
         View getChildAtPosition(MotionEvent ev);
 
         View getChildContentView(View v);
 
         boolean canChildBeDismissed(View v);
 
         void onBeginDrag(View v);
 
         void onChildDismissed(View v);
 
         void onDragCancelled(View v);
     }
 }
 
 

TestListView.java

package com.android.listviewtest;
 
 import android.animation.LayoutTransition;
 import android.content.Context;
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.ScrollView;
 import android.widget.SimpleAdapter;
 
 public class TestListView extends ScrollView implements SwipeHelper.Callback {
 	private SimpleAdapter mAdapter;
 	private SwipeHelper mSwipeHelper;
 	private LinearLayout mLinearLayout;
 	private Context mContext;
 
 	public TestListView(Context mContext, AttributeSet attrs) {
 		super(mContext, attrs, 0);
 		this.mContext = mContext;
 		float densityScale = getResources().getDisplayMetrics().density;
 		float pagingTouchSlop = ViewConfiguration.get(mContext)
 				.getScaledPagingTouchSlop();
 		mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale,
 				pagingTouchSlop);
 	}
 
 	public void setAdapter(SimpleAdapter Adapter) {
 		mAdapter = Adapter;
 		mAdapter.registerDataSetObserver(new DataSetObserver() {
             public void onChanged() {
                 update();
             }
 
             public void onInvalidated() {
                 update();
             }
         });
 		update();
 	}
 	
 	@Override
 	public void removeViewInLayout(final View view) {
 		dismissChild(view);
 	}
 
 	@Override
 	public boolean onInterceptTouchEvent(MotionEvent ev) {
 		return mSwipeHelper.onInterceptTouchEvent(ev)
 				|| super.onInterceptTouchEvent(ev);
 	}
 
 	@Override
 	public boolean onTouchEvent(MotionEvent ev) {
 		return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
 	}
 
 	public void dismissChild(View v) {
 		mSwipeHelper.dismissChild(v, 0);
 	}
 
 	@Override
 	public View getChildAtPosition(MotionEvent ev) {
 		final float x = ev.getX() + getScrollX();
 		final float y = ev.getY() + getScrollY();
 		for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
 			View item = mLinearLayout.getChildAt(i);
 			if (item.getVisibility() == View.VISIBLE && x >= item.getLeft()
 					&& x < item.getRight() && y >= item.getTop()
 					&& y < item.getBottom()) {
 				return item;
 			}
 		}
 		return null;
 	}
 
 	@Override
 	public View getChildContentView(View v) {
 		return v.findViewById(R.id.text1);
 	}
 
 	@Override
 	public boolean canChildBeDismissed(View v) {
 		return true;
 	}
 
 	@Override
 	public void onBeginDrag(View v) {
 		requestDisallowInterceptTouchEvent(true);
 		v.setActivated(true);
 	}
 
 	@Override
 	public void onChildDismissed(View v) {
 		mLinearLayout.removeView(v);
 	}
 
 	@Override
 	public void onDragCancelled(View v) {
 		v.setActivated(false);
 	}
 
 	
 	
 	@Override
 	protected void onFinishInflate() {
 		super.onFinishInflate();
 		setScrollbarFadingEnabled(true);
 		mLinearLayout = (LinearLayout) findViewById(R.id.linear_layout);
 	}
 	
 	
 	private void update()
 	{
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
             View old = null;
             if (i < mLinearLayout.getChildCount()) {
                 old = mLinearLayout.getChildAt(i);
                 old.setVisibility(View.VISIBLE);
             }
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (old == null) {
                 OnTouchListener noOpListener = new OnTouchListener() {
                     @Override
                     public boolean onTouch(View v, MotionEvent event) {
                         return true;
                     }
                 };
 
                 view.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
                     }
                 });
                 // We don't want a click sound when we dimiss recents
                 view.setSoundEffectsEnabled(false);
 
                 OnClickListener launchAppListener = new OnClickListener() {
                     public void onClick(View v) {
                     }
                 };
 
                 OnLongClickListener longClickListener = new OnLongClickListener() {
                     public boolean onLongClick(View v) {
                         return true;
                     }
                 };
 
                 mLinearLayout.addView(view);
             }
         }
         for (int i = mAdapter.getCount(); i < mLinearLayout.getChildCount(); i++) {
             mLinearLayout.getChildAt(i).setVisibility(View.GONE);
         }
 	}
 }
 

MainActivity.java
package com.android.listviewtest;  import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;  import android.os.Bundle; import android.app.Activity; import android.widget.ListView; import android.widget.ScrollView; import android.widget.SimpleAdapter;  public class MainActivity extends Activity { 	private TestListView mScrollView; 	private SimpleAdapter mAdapter; 	private List<Map<String, Object>> list = null;  	@Override 	public void onCreate(Bundle savedInstanceState) { 		super.onCreate(savedInstanceState); 		setContentView(R.layout.activity_main);  		mScrollView = (TestListView) findViewById(R.id.lv);  		list = new ArrayList<Map<String, Object>>();  		for (int n = 0; n < 10; n++) { 			Map<String, Object> hashmap = new HashMap<String, Object>(); 			hashmap.put("item", "" + n); 			list.add(hashmap); 		}  		mAdapter = new SimpleAdapter(MainActivity.this, list, 				R.layout.list_item, new String[] { "item" }, 				new int[] { R.id.text1 });  		mScrollView.setAdapter(mAdapter); 	}  } 



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值