Android UI--SwipeDismissListView_直接删除
SwipeDismissTouchListener.java
1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.wzq.swipe2dismiss; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.app.ListActivity; 23 import android.app.ListFragment; 24 import android.view.MotionEvent; 25 import android.view.VelocityTracker; 26 import android.view.View; 27 import android.view.ViewConfiguration; 28 import android.view.ViewGroup; 29 import android.widget.AdapterView; 30 import android.widget.ListView; 31 32 /** 33 * A {@link View.OnTouchListener} that makes any {@link View} dismissable when the 34 * user swipes (drags her finger) horizontally across the view. 35 * 36 * <p><em>For {@link ListView} list items that don't manage their own touch events 37 * (i.e. you're using 38 * {@link ListView#setOnItemClickListener(AdapterView.OnItemClickListener)} 39 * or an equivalent listener on {@link ListActivity} or 40 * {@link ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.</em></p> 41 * 42 * <p>Example usage:</p> 43 * 44 * <pre> 45 * view.setOnTouchListener(new SwipeDismissTouchListener( 46 * view, 47 * null, // Optional token/cookie object 48 * new SwipeDismissTouchListener.OnDismissCallback() { 49 * public void onDismiss(View view, Object token) { 50 * parent.removeView(view); 51 * } 52 * })); 53 * </pre> 54 * 55 * <p>This class Requires API level 12 or later due to use of {@link 56 * android.view.ViewPropertyAnimator}.</p> 57 * 58 * @see SwipeDismissListViewTouchListener 59 */ 60 public class SwipeDismissTouchListener implements View.OnTouchListener { 61 // Cached ViewConfiguration and system-wide constant values 62 private int mSlop; 63 private int mMinFlingVelocity; 64 private int mMaxFlingVelocity; 65 private long mAnimationTime; 66 67 // Fixed properties 68 private View mView; 69 private DismissCallbacks mCallbacks; 70 private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero 71 72 // Transient properties 73 private float mDownX; 74 private float mDownY; 75 private boolean mSwiping; 76 private int mSwipingSlop; 77 private Object mToken; 78 private VelocityTracker mVelocityTracker; 79 private float mTranslationX; 80 81 /** 82 * The callback interface used by {@link SwipeDismissTouchListener} to inform its client 83 * about a successful dismissal of the view for which it was created. 84 */ 85 public interface DismissCallbacks { 86 /** 87 * Called to determine whether the view can be dismissed. 88 */ 89 boolean canDismiss(Object token); 90 91 /** 92 * Called when the user has indicated they she would like to dismiss the view. 93 * 94 * @param view The originating {@link View} to be dismissed. 95 * @param token The optional token passed to this object's constructor. 96 */ 97 void onDismiss(View view, Object token); 98 } 99 100 /** 101 * Constructs a new swipe-to-dismiss touch listener for the given view. 102 * 103 * @param view The view to make dismissable. 104 * @param token An optional token/cookie object to be passed through to the callback. 105 * @param callbacks The callback to trigger when the user has indicated that she would like to 106 * dismiss this view. 107 */ 108 public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) { 109 ViewConfiguration vc = ViewConfiguration.get(view.getContext()); 110 mSlop = vc.getScaledTouchSlop(); 111 mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; 112 mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); 113 mAnimationTime = view.getContext().getResources().getInteger( 114 android.R.integer.config_shortAnimTime); 115 mView = view; 116 mToken = token; 117 mCallbacks = callbacks; 118 } 119 120 @Override 121 public boolean onTouch(View view, MotionEvent motionEvent) { 122 // offset because the view is translated during swipe 123 motionEvent.offsetLocation(mTranslationX, 0); 124 125 if (mViewWidth < 2) { 126 mViewWidth = mView.getWidth(); 127 } 128 129 switch (motionEvent.getActionMasked()) { 130 case MotionEvent.ACTION_DOWN: { 131 // TODO: ensure this is a finger, and set a flag 132 mDownX = motionEvent.getRawX(); 133 mDownY = motionEvent.getRawY(); 134 if (mCallbacks.canDismiss(mToken)) { 135 mVelocityTracker = VelocityTracker.obtain(); 136 mVelocityTracker.addMovement(motionEvent); 137 } 138 return false; 139 } 140 141 case MotionEvent.ACTION_UP: { 142 if (mVelocityTracker == null) { 143 break; 144 } 145 146 float deltaX = motionEvent.getRawX() - mDownX; 147 mVelocityTracker.addMovement(motionEvent); 148 mVelocityTracker.computeCurrentVelocity(1000); 149 float velocityX = mVelocityTracker.getXVelocity(); 150 float absVelocityX = Math.abs(velocityX); 151 float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); 152 boolean dismiss = false; 153 boolean dismissRight = false; 154 if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { 155 dismiss = true; 156 dismissRight = deltaX > 0; 157 } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity 158 && absVelocityY < absVelocityX 159 && absVelocityY < absVelocityX && mSwiping) { 160 // dismiss only if flinging in the same direction as dragging 161 dismiss = (velocityX < 0) == (deltaX < 0); 162 dismissRight = mVelocityTracker.getXVelocity() > 0; 163 } 164 if (dismiss) { 165 // dismiss 166 mView.animate() 167 .translationX(dismissRight ? mViewWidth : -mViewWidth) 168 .alpha(0) 169 .setDuration(mAnimationTime) 170 .setListener(new AnimatorListenerAdapter() { 171 @Override 172 public void onAnimationEnd(Animator animation) { 173 performDismiss(); 174 } 175 }); 176 } else if (mSwiping) { 177 // cancel 178 mView.animate() 179 .translationX(0) 180 .alpha(1) 181 .setDuration(mAnimationTime) 182 .setListener(null); 183 } 184 mVelocityTracker.recycle(); 185 mVelocityTracker = null; 186 mTranslationX = 0; 187 mDownX = 0; 188 mDownY = 0; 189 mSwiping = false; 190 break; 191 } 192 193 case MotionEvent.ACTION_CANCEL: { 194 if (mVelocityTracker == null) { 195 break; 196 } 197 198 mView.animate() 199 .translationX(0) 200 .alpha(1) 201 .setDuration(mAnimationTime) 202 .setListener(null); 203 mVelocityTracker.recycle(); 204 mVelocityTracker = null; 205 mTranslationX = 0; 206 mDownX = 0; 207 mDownY = 0; 208 mSwiping = false; 209 break; 210 } 211 212 case MotionEvent.ACTION_MOVE: { 213 if (mVelocityTracker == null) { 214 break; 215 } 216 217 mVelocityTracker.addMovement(motionEvent); 218 float deltaX = motionEvent.getRawX() - mDownX; 219 float deltaY = motionEvent.getRawY() - mDownY; 220 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { 221 mSwiping = true; 222 mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); 223 mView.getParent().requestDisallowInterceptTouchEvent(true); 224 225 226 MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); 227 cancelEvent.setAction(MotionEvent.ACTION_CANCEL | 228 (motionEvent.getActionIndex() << 229 MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 230 mView.onTouchEvent(cancelEvent); 231 cancelEvent.recycle(); 232 } 233 234 if (mSwiping) { 235 mTranslationX = deltaX; 236 mView.setTranslationX(deltaX - mSwipingSlop); 237 // TODO: use an ease-out interpolator or such 238 // mView.setAlpha(Math.max(0f, Math.min(1f,1f - 2f * Math.abs(deltaX) / mViewWidth))); 239 mView.setAlpha(Math.max(0f, Math.min(1f, 1f - Math.abs(deltaX) * 2f / mViewWidth))); 240 return true; 241 } 242 break; 243 } 244 } 245 return false; 246 } 247 248 private void performDismiss() { 249 // Animate the dismissed view to zero-height and then fire the dismiss callback. 250 // This triggers layout on each animation frame; in the future we may want to do something 251 // smarter and more performant. 252 253 final ViewGroup.LayoutParams lp = mView.getLayoutParams(); 254 final int originalHeight = mView.getHeight(); 255 256 ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime); 257 258 animator.addListener(new AnimatorListenerAdapter() { 259 @Override 260 public void onAnimationEnd(Animator animation) { 261 mCallbacks.onDismiss(mView, mToken); 262 // Reset view presentation 263 mView.setAlpha(1f); 264 mView.setTranslationX(0); 265 lp.height = originalHeight; 266 mView.setLayoutParams(lp); 267 } 268 }); 269 270 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 271 @Override 272 public void onAnimationUpdate(ValueAnimator valueAnimator) { 273 lp.height = (Integer) valueAnimator.getAnimatedValue(); 274 mView.setLayoutParams(lp); 275 } 276 }); 277 278 animator.start(); 279 } 280 }
SwipeDismissListViewTouchListener.java
1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.wzq.swipe2dismiss; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.graphics.Rect; 23 import android.os.SystemClock; 24 import android.view.MotionEvent; 25 import android.view.VelocityTracker; 26 import android.view.View; 27 import android.view.ViewConfiguration; 28 import android.view.ViewGroup; 29 import android.view.ViewPropertyAnimator; 30 import android.widget.AbsListView; 31 import android.widget.ListView; 32 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.List; 36 37 /** 38 * A {@link View.OnTouchListener} that makes the list items in a {@link ListView} 39 * dismissable. {@link ListView} is given special treatment because by default it handles touches 40 * for its list items... i.e. it's in charge of drawing the pressed state (the list selector), 41 * handling list item clicks, etc. 42 * 43 * <p>After creating the listener, the caller should also call 44 * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing 45 * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is 46 * already assigned, the caller should still pass scroll changes through to this listener. This will 47 * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view 48 * scrolling.</p> 49 * 50 * <p>Example usage:</p> 51 * 52 * <pre> 53 * SwipeDismissListViewTouchListener touchListener = 54 * new SwipeDismissListViewTouchListener( 55 * listView, 56 * new SwipeDismissListViewTouchListener.OnDismissCallback() { 57 * public void onDismiss(ListView listView, int[] reverseSortedPositions) { 58 * for (int position : reverseSortedPositions) { 59 * adapter.remove(adapter.getItem(position)); 60 * } 61 * adapter.notifyDataSetChanged(); 62 * } 63 * }); 64 * listView.setOnTouchListener(touchListener); 65 * listView.setOnScrollListener(touchListener.makeScrollListener()); 66 * </pre> 67 * 68 * <p>This class Requires API level 12 or later due to use of {@link 69 * ViewPropertyAnimator}.</p> 70 * 71 * <p>For a generalized {@link View.OnTouchListener} that makes any view dismissable, 72 * see {@link SwipeDismissTouchListener}.</p> 73 * 74 * @see SwipeDismissTouchListener 75 */ 76 public class SwipeDismissListViewTouchListener implements View.OnTouchListener { 77 // Cached ViewConfiguration and system-wide constant values 78 private int mSlop; 79 private int mMinFlingVelocity; 80 private int mMaxFlingVelocity; 81 private long mAnimationTime; 82 83 // Fixed properties 84 private ListView mListView; 85 private DismissCallbacks mCallbacks; 86 private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero 87 88 // Transient properties 89 private List<PendingDismissData> mPendingDismisses = new ArrayList<PendingDismissData>(); 90 private int mDismissAnimationRefCount = 0; 91 private float mDownX; 92 private float mDownY; 93 private boolean mSwiping; 94 private int mSwipingSlop; 95 private VelocityTracker mVelocityTracker; 96 private int mDownPosition; 97 private View mDownView; 98 private boolean mPaused; 99 100 /** 101 * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client 102 * about a successful dismissal of one or more list item positions. 103 */ 104 public interface DismissCallbacks { 105 /** 106 * Called to determine whether the given position can be dismissed. 107 */ 108 boolean canDismiss(int position); 109 110 /** 111 * Called when the user has indicated they she would like to dismiss one or more list item 112 * positions. 113 * 114 * @param listView The originating {@link ListView}. 115 * @param reverseSortedPositions An array of positions to dismiss, sorted in descending 116 * order for convenience. 117 */ 118 void onDismiss(ListView listView, int[] reverseSortedPositions); 119 } 120 121 /** 122 * Constructs a new swipe-to-dismiss touch listener for the given list view. 123 * 124 * @param listView The list view whose items should be dismissable. 125 * @param callbacks The callback to trigger when the user has indicated that she would like to 126 * dismiss one or more list items. 127 */ 128 public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) { 129 ViewConfiguration vc = ViewConfiguration.get(listView.getContext()); 130 mSlop = vc.getScaledTouchSlop(); 131 mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16; 132 mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); 133 mAnimationTime = listView.getContext().getResources().getInteger( 134 android.R.integer.config_shortAnimTime); 135 mListView = listView; 136 mCallbacks = callbacks; 137 } 138 139 /** 140 * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures. 141 * 142 * @param enabled Whether or not to watch for gestures. 143 */ 144 public void setEnabled(boolean enabled) { 145 mPaused = !enabled; 146 } 147 148 /** 149 * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link 150 * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}. 151 * If a scroll listener is already assigned, the caller should still pass scroll changes through 152 * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is 153 * paused during list view scrolling.</p> 154 * 155 * @see SwipeDismissListViewTouchListener 156 */ 157 public AbsListView.OnScrollListener makeScrollListener() { 158 return new AbsListView.OnScrollListener() { 159 @Override 160 public void onScrollStateChanged(AbsListView absListView, int scrollState) { 161 setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 162 } 163 164 @Override 165 public void onScroll(AbsListView absListView, int i, int i1, int i2) { 166 } 167 }; 168 } 169 170 @Override 171 public boolean onTouch(View view, MotionEvent motionEvent) { 172 if (mViewWidth < 2) { 173 mViewWidth = mListView.getWidth(); 174 } 175 176 switch (motionEvent.getActionMasked()) { 177 case MotionEvent.ACTION_DOWN: { 178 if (mPaused) { 179 return false; 180 } 181 182 // TODO: ensure this is a finger, and set a flag 183 184 // Find the child view that was touched (perform a hit test) 185 Rect rect = new Rect(); 186 int childCount = mListView.getChildCount(); 187 int[] listViewCoords = new int[2]; 188 mListView.getLocationOnScreen(listViewCoords); 189 int x = (int) motionEvent.getRawX() - listViewCoords[0]; 190 int y = (int) motionEvent.getRawY() - listViewCoords[1]; 191 View child; 192 for (int i = 0; i < childCount; i++) { 193 child = mListView.getChildAt(i); 194 child.getHitRect(rect); 195 if (rect.contains(x, y)) { 196 mDownView = child; 197 break; 198 } 199 } 200 //另外一种获取mDownView 的方法,不知是否有效~~ 201 // int downViewIndex = mListView.pointToPosition((int)motionEvent.getX(),(int)motionEvent.getY()); 202 // mDownView = mListView.getChildAt(downViewIndex - mListView.getFirstVisiblePosition()); 203 204 if (mDownView != null) { 205 mDownX = motionEvent.getRawX(); 206 mDownY = motionEvent.getRawY(); 207 mDownPosition = mListView.getPositionForView(mDownView); 208 if (mCallbacks.canDismiss(mDownPosition)) { 209 mVelocityTracker = VelocityTracker.obtain(); 210 mVelocityTracker.addMovement(motionEvent); 211 } else { 212 mDownView = null; 213 } 214 } 215 return false; 216 } 217 218 case MotionEvent.ACTION_CANCEL: { 219 if (mVelocityTracker == null) { 220 break; 221 } 222 223 if (mDownView != null && mSwiping) { 224 // cancel 225 mDownView.animate() 226 .translationX(0) 227 .alpha(1) 228 .setDuration(mAnimationTime) 229 .setListener(null); 230 } 231 mVelocityTracker.recycle(); 232 mVelocityTracker = null; 233 mDownX = 0; 234 mDownY = 0; 235 mDownView = null; 236 mDownPosition = ListView.INVALID_POSITION; 237 mSwiping = false; 238 break; 239 } 240 241 case MotionEvent.ACTION_UP: { 242 if (mVelocityTracker == null) { 243 break; 244 } 245 246 float deltaX = motionEvent.getRawX() - mDownX; 247 mVelocityTracker.addMovement(motionEvent); 248 mVelocityTracker.computeCurrentVelocity(1000); 249 float velocityX = mVelocityTracker.getXVelocity(); 250 float absVelocityX = Math.abs(velocityX); 251 float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); 252 boolean dismiss = false; 253 boolean dismissRight = false; 254 if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) { 255 dismiss = true; 256 dismissRight = deltaX > 0; 257 } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity 258 && absVelocityY < absVelocityX && mSwiping) { 259 // dismiss only if flinging in the same direction as dragging 260 dismiss = (velocityX < 0) == (deltaX < 0); 261 dismissRight = mVelocityTracker.getXVelocity() > 0; 262 } 263 if (dismiss && mDownPosition != ListView.INVALID_POSITION) { 264 // dismiss 265 final View downView = mDownView; // mDownView gets null'd before animation ends 266 final int downPosition = mDownPosition; 267 ++mDismissAnimationRefCount; 268 mDownView.animate() 269 .translationX(dismissRight ? mViewWidth : -mViewWidth) 270 .alpha(0) 271 .setDuration(mAnimationTime) 272 .setListener(new AnimatorListenerAdapter() { 273 @Override 274 public void onAnimationEnd(Animator animation) { 275 performDismiss(downView, downPosition); 276 } 277 }); 278 } else { 279 // cancel 280 mDownView.animate() 281 .translationX(0) 282 .alpha(1) 283 .setDuration(mAnimationTime) 284 .setListener(null); 285 } 286 mVelocityTracker.recycle(); 287 mVelocityTracker = null; 288 mDownX = 0; 289 mDownY = 0; 290 mDownView = null; 291 mDownPosition = ListView.INVALID_POSITION; 292 mSwiping = false; 293 break; 294 } 295 296 case MotionEvent.ACTION_MOVE: { 297 if (mVelocityTracker == null || mPaused) { 298 break; 299 } 300 301 mVelocityTracker.addMovement(motionEvent); 302 float deltaX = motionEvent.getRawX() - mDownX; 303 float deltaY = motionEvent.getRawY() - mDownY; 304 if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) { 305 mSwiping = true; 306 mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop); 307 mListView.requestDisallowInterceptTouchEvent(true); 308 309 // Cancel ListView's touch (un-highlighting the item) 310 MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); 311 cancelEvent.setAction(MotionEvent.ACTION_CANCEL | 312 (motionEvent.getActionIndex() 313 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); 314 mListView.onTouchEvent(cancelEvent); 315 cancelEvent.recycle(); 316 } 317 318 if (mSwiping) { 319 mDownView.setTranslationX(deltaX - mSwipingSlop); 320 mDownView.setAlpha(Math.max(0f, Math.min(1f, 321 1f - 2f * Math.abs(deltaX) / mViewWidth))); 322 return true; 323 } 324 break; 325 } 326 } 327 return false; 328 } 329 330 class PendingDismissData implements Comparable<PendingDismissData> { 331 public int position; 332 public View view; 333 334 public PendingDismissData(int position, View view) { 335 this.position = position; 336 this.view = view; 337 } 338 339 @Override 340 public int compareTo(PendingDismissData other) { 341 // Sort by descending position 342 return other.position - position; 343 } 344 } 345 346 private void performDismiss(final View dismissView, final int dismissPosition) { 347 // Animate the dismissed list item to zero-height and fire the dismiss callback when 348 // all dismissed list item animations have completed. This triggers layout on each animation 349 // frame; in the future we may want to do something smarter and more performant. 350 351 final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); 352 final int originalHeight = dismissView.getHeight(); 353 354 ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime); 355 356 animator.addListener(new AnimatorListenerAdapter() { 357 @Override 358 public void onAnimationEnd(Animator animation) { 359 --mDismissAnimationRefCount; 360 if (mDismissAnimationRefCount == 0) { 361 // No active animations, process all pending dismisses. 362 // Sort by descending position 363 Collections.sort(mPendingDismisses); 364 365 int[] dismissPositions = new int[mPendingDismisses.size()]; 366 for (int i = mPendingDismisses.size() - 1; i >= 0; i--) { 367 dismissPositions[i] = mPendingDismisses.get(i).position; 368 } 369 mCallbacks.onDismiss(mListView, dismissPositions); 370 371 // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss 372 // animation with a stale position 373 mDownPosition = ListView.INVALID_POSITION; 374 375 ViewGroup.LayoutParams lp; 376 for (PendingDismissData pendingDismiss : mPendingDismisses) { 377 // Reset view presentation // TODO 378 pendingDismiss.view.setAlpha(1f); 379 pendingDismiss.view.setTranslationX(0); 380 lp = pendingDismiss.view.getLayoutParams(); 381 lp.height = originalHeight; 382 pendingDismiss.view.setLayoutParams(lp); 383 } 384 385 // Send a cancel event 386 long time = SystemClock.uptimeMillis(); 387 MotionEvent cancelEvent = MotionEvent.obtain(time, time, 388 MotionEvent.ACTION_CANCEL, 0, 0, 0); 389 mListView.dispatchTouchEvent(cancelEvent); 390 391 mPendingDismisses.clear(); 392 } 393 } 394 }); 395 396 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 397 @Override 398 public void onAnimationUpdate(ValueAnimator valueAnimator) { 399 lp.height = (Integer) valueAnimator.getAnimatedValue();// TODO 400 dismissView.setLayoutParams(lp); 401 } 402 }); 403 404 mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView)); 405 animator.start(); 406 } 407 }
SwipeDismissActivity.java
1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.wzq.swipe2dismiss; 18 19 import android.app.ListActivity; 20 import android.os.Bundle; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.widget.ArrayAdapter; 24 import android.widget.Button; 25 import android.widget.ListView; 26 import android.widget.Toast; 27 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 31 import com.example.wzq1412.R; 32 33 public class Swipe2DismissActivity extends ListActivity { 34 ArrayAdapter<String> mAdapter; 35 36 public void onCreate(Bundle savedInstanceState) { 37 super.onCreate(savedInstanceState); 38 setContentView(R.layout.a_swipe2dismiss); 39 40 // Set up ListView example 41 String[] items = new String[20]; 42 for (int i = 0; i < items.length; i++) { 43 items[i] = "Item " + (i + 1); 44 } 45 46 mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, 47 android.R.id.text1, new ArrayList<String>(Arrays.asList(items))); 48 setListAdapter(mAdapter); 49 50 ListView listView = getListView(); 51 // Create a ListView-specific touch listener. ListViews are given 52 // special treatment because 53 // by default they handle touches for their list items... i.e. they're 54 // in charge of drawing 55 // the pressed state (the list selector), handling list item clicks, 56 // etc. 57 SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener( 58 listView, new SwipeDismissListViewTouchListener.DismissCallbacks() { 59 @Override 60 public boolean canDismiss(int position) { 61 return true; 62 } 63 64 @Override 65 public void onDismiss(ListView listView, int[] reverseSortedPositions) { 66 // TODO at 67 // java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251) 68 if (mAdapter.getCount() == 0) { 69 return; 70 } 71 for (int position : reverseSortedPositions) { 72 if (position >= mAdapter.getCount()) { 73 return; 74 } 75 mAdapter.remove(mAdapter.getItem(position)); 76 } 77 mAdapter.notifyDataSetChanged(); 78 } 79 }); 80 listView.setOnTouchListener(touchListener); 81 // Setting this scroll listener is required to ensure that during 82 // ListView scrolling, 83 // we don't look for swipes. 84 listView.setOnScrollListener(touchListener.makeScrollListener()); 85 86 // Set up normal ViewGroup example 87 final ViewGroup dismissableContainer = (ViewGroup) findViewById(R.id.dismissable_container); 88 for (int i = 0; i < items.length; i++) { 89 final Button dismissableButton = new Button(this); 90 dismissableButton.setLayoutParams(new ViewGroup.LayoutParams( 91 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 92 dismissableButton.setText("Button " + (i + 1)); 93 dismissableButton.setOnClickListener(new View.OnClickListener() { 94 @Override 95 public void onClick(View view) { 96 Toast.makeText(Swipe2DismissActivity.this, 97 "Clicked " + ((Button) view).getText(), Toast.LENGTH_SHORT).show(); 98 } 99 }); 100 // Create a generic swipe-to-dismiss touch listener. 101 dismissableButton.setOnTouchListener(new SwipeDismissTouchListener(dismissableButton, 102 null, new SwipeDismissTouchListener.DismissCallbacks() { 103 @Override 104 public boolean canDismiss(Object token) { 105 return true; 106 } 107 108 @Override 109 public void onDismiss(View view, Object token) { 110 dismissableContainer.removeView(dismissableButton); 111 } 112 })); 113 dismissableContainer.addView(dismissableButton); 114 } 115 } 116 117 @Override 118 protected void onListItemClick(ListView listView, View view, int position, long id) { 119 Toast.makeText(this, "Clicked " + getListAdapter().getItem(position).toString(), 120 Toast.LENGTH_SHORT).show(); 121 } 122 }
a_swipe2dismiss.xml
1 <!-- 2 Copyright 2013 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 --> 16 17 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 18 android:layout_width="match_parent" 19 android:layout_height="match_parent" 20 android:orientation="horizontal" 21 android:baselineAligned="false" 22 android:padding="16dp"> 23 24 <LinearLayout android:layout_width="0dp" 25 android:layout_weight="1" 26 android:layout_height="match_parent" 27 android:layout_marginRight="8dp" 28 android:orientation="vertical"> 29 30 <TextView android:layout_width="match_parent" 31 android:layout_height="wrap_content" 32 style="?android:listSeparatorTextViewStyle" 33 android:text="Simple Views" /> 34 35 <ScrollView android:fillViewport="true" 36 android:layout_width="match_parent" 37 android:layout_weight="1" 38 android:layout_height="0dp"> 39 <LinearLayout android:id="@+id/dismissable_container" 40 android:layout_width="match_parent" 41 android:layout_height="wrap_content" 42 android:orientation="vertical" /> 43 </ScrollView> 44 45 </LinearLayout> 46 47 <LinearLayout android:layout_width="0dp" 48 android:layout_weight="1" 49 android:layout_height="match_parent" 50 android:layout_marginLeft="8dp" 51 android:orientation="vertical"> 52 53 <TextView android:layout_width="match_parent" 54 android:layout_height="wrap_content" 55 style="?android:listSeparatorTextViewStyle" 56 android:text="ListView" /> 57 58 <ListView 59 android:id="@android:id/list" 60 android:layout_weight="1" 61 android:layout_width="match_parent" 62 android:layout_height="0dp" /> 63 </LinearLayout> 64 </LinearLayout>