/* * Copyright (C) 2013 47 Degrees, LLC * http://47deg.com * hello@47deg.com * Copyright 2012 Roman Nurik * 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 com.roboo.like.google.swipelistview; import android.graphics.Rect; import android.os.Build; import android.os.Handler; import android.support.v4.view.MotionEventCompat; import android.util.Log; import android.view.*; import android.widget.AbsListView; import android.widget.ListView; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.animation.ValueAnimator; import com.nineoldandroids.view.ViewHelper; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static com.nineoldandroids.view.ViewHelper.setAlpha; import static com.nineoldandroids.view.ViewHelper.setTranslationX; import static com.nineoldandroids.view.ViewPropertyAnimator.animate; /** * SwipeListView 的触摸监听事件 */ public class SwipeListViewTouchListener implements View.OnTouchListener { private static final int DISPLACE_CHOICE = 80; private int swipeMode = SwipeListView.SWIPE_MODE_BOTH; private boolean swipeOpenOnLongPress = true; private boolean swipeClosesAllItemsWhenListMoves = true; private int swipeFrontView = 0; private int swipeBackView = 0; private Rect rect = new Rect(); // Cached ViewConfiguration and system-wide constant values private int slop; private int minFlingVelocity; private int maxFlingVelocity; private long configShortAnimationTime; private long animationTime; private float leftOffset = 0; private float rightOffset = 0; private int swipeDrawableChecked = 0; private int swipeDrawableUnchecked = 0; // Fixed properties private SwipeListView swipeListView; private int viewWidth = 1; // 1 and not 0 to prevent dividing by zero private List<PendingDismissData> pendingDismisses = new ArrayList<PendingDismissData>(); private int dismissAnimationRefCount = 0; /** 手指触摸屏幕按下时的X值 */ private float mDownX; private boolean swiping; private boolean swipingRight; private VelocityTracker velocityTracker; /** 手指触摸屏幕按下时列表项的位置 */ private int downPosition; private View parentView; private View frontView; private View backView; private boolean paused; /** 当前滑动的方式 */ private int swipeCurrentAction = SwipeListView.SWIPE_ACTION_NONE; private int swipeActionLeft = SwipeListView.SWIPE_ACTION_REVEAL; private int swipeActionRight = SwipeListView.SWIPE_ACTION_REVEAL; /** 用于存储列表项是否打开的状态标志 */ private List<Boolean> opened = new ArrayList<Boolean>(); private List<Boolean> openedRight = new ArrayList<Boolean>(); private boolean listViewMoving; /** 用于存储列表项是否选中的状态标志 */ private List<Boolean> checked = new ArrayList<Boolean>(); private int oldSwipeActionRight; private int oldSwipeActionLeft; /** * Constructor * * @param swipeListView * SwipeListView * @param swipeFrontView * front view Identifier * @param swipeBackView * back view Identifier */ public SwipeListViewTouchListener(SwipeListView swipeListView, int swipeFrontView, int swipeBackView) { this.swipeFrontView = swipeFrontView; this.swipeBackView = swipeBackView; ViewConfiguration vc = ViewConfiguration.get(swipeListView.getContext()); slop = vc.getScaledTouchSlop(); minFlingVelocity = vc.getScaledMinimumFlingVelocity(); maxFlingVelocity = vc.getScaledMaximumFlingVelocity(); configShortAnimationTime = swipeListView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime); animationTime = configShortAnimationTime; this.swipeListView = swipeListView; } /** * Sets current item's parent view * * @param parentView * Parent view */ private void setParentView(View parentView) { this.parentView = parentView; } /** * Sets current item's front view * * @param frontView * Front view */ private void setFrontView(View frontView) { this.frontView = frontView; frontView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { swipeListView.onClickFrontView(downPosition); } }); if (swipeOpenOnLongPress) { frontView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { openAnimate(downPosition); return false; } }); } } /** * Set current item's back view * * @param backView */ private void setBackView(View backView) { this.backView = backView; backView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { swipeListView.onClickBackView(downPosition); } }); } /** * @return true if the list is in motion */ public boolean isListViewMoving() { return listViewMoving; } /** * Sets animation time when the user drops the cell * * @param animationTime * milliseconds */ public void setAnimationTime(long animationTime) { if (animationTime > 0) { this.animationTime = animationTime; } else { this.animationTime = configShortAnimationTime; } } /** * Sets the right offset * * @param rightOffset * Offset */ public void setRightOffset(float rightOffset) { this.rightOffset = rightOffset; } /** * Set the left offset * * @param leftOffset * Offset */ public void setLeftOffset(float leftOffset) { this.leftOffset = leftOffset; } /** * Set if all item opened will be close when the user move ListView * * @param swipeClosesAllItemsWhenListMoves */ public void setSwipeClosesAllItemsWhenListMoves(boolean swipeClosesAllItemsWhenListMoves) { this.swipeClosesAllItemsWhenListMoves = swipeClosesAllItemsWhenListMoves; } /** * 设置长按列表项时是否滑动 * * @param swipeOpenOnLongPress */ public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) { this.swipeOpenOnLongPress = swipeOpenOnLongPress; } /** * 设置滑动方式 * * @param swipeMode */ public void setSwipeMode(int swipeMode) { this.swipeMode = swipeMode; } /** * 检测滑动是否可用 * * @return */ protected boolean isSwipeEnabled() { return swipeMode != SwipeListView.SWIPE_MODE_NONE; } /** * 获取向左滑动的标识id * * @return Action */ public int getSwipeActionLeft() { return swipeActionLeft; } /** * 设置向左滑动 * * @param swipeActionLeft * Action */ public void setSwipeActionLeft(int swipeActionLeft) { this.swipeActionLeft = swipeActionLeft; } /** * 获取向右滑动的标识id * * @return Action */ public int getSwipeActionRight() { return swipeActionRight; } /** * 设置向右滑动 * * @param swipeActionRight * Action */ public void setSwipeActionRight(int swipeActionRight) { this.swipeActionRight = swipeActionRight; } /** * 在选择模式下, 设置选中列表项的背景 * * @param swipeDrawableChecked * drawable */ protected void setSwipeDrawableChecked(int swipeDrawableChecked) { this.swipeDrawableChecked = swipeDrawableChecked; } /** * 在选择模式下, 设置未选中列表项的背景 * * @param swipeDrawableUnchecked * drawable */ protected void setSwipeDrawableUnchecked(int swipeDrawableUnchecked) { this.swipeDrawableUnchecked = swipeDrawableUnchecked; } /** * 当适配器修改时,重置所有的列表项 */ public void resetItems() { if (swipeListView.getAdapter() != null) { int count = swipeListView.getAdapter().getCount(); for (int i = opened.size(); i <= count; i++) { opened.add(false); openedRight.add(false); checked.add(false); } } } /** * 打开列表项 * * @param position * Position of list */ protected void openAnimate(int position) { openAnimate(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position); } /** * 关闭列表项 * * @param position * Position of list */ protected void closeAnimate(int position) { closeAnimate(swipeListView.getChildAt(position - swipeListView.getFirstVisiblePosition()).findViewById(swipeFrontView), position); } /** * 修改选择模式 * * @param position * position of list */ private void swapChoiceState(int position) { int lastCount = getCountSelected(); boolean lastChecked = checked.get(position); checked.set(position, !lastChecked); int count = lastChecked ? lastCount - 1 : lastCount + 1; if (lastCount == 0 && count == 1) { swipeListView.onChoiceStarted(); closeOpenedItems(); setActionsTo(SwipeListView.SWIPE_ACTION_CHOICE); } if (lastCount == 1 && count == 0) { swipeListView.onChoiceEnded(); returnOldActions(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { swipeListView.setItemChecked(position, !lastChecked); } swipeListView.onChoiceChanged(position, !lastChecked); reloadChoiceStateInView(frontView, position); } /** * Unselected choice state in item */ protected void unselectedChoiceStates() { int start = swipeListView.getFirstVisiblePosition(); int end = swipeListView.getLastVisiblePosition(); for (int i = 0; i < checked.size(); i++) { if (checked.get(i) && i >= start && i <= end) { reloadChoiceStateInView(swipeListView.getChildAt(i - start).findViewById(swipeFrontView), i); } checked.set(i, false); } swipeListView.onChoiceEnded(); returnOldActions(); } /** * 没有选中的列表项的状态 */ protected int dismiss(int position) { int start = swipeListView.getFirstVisiblePosition(); int end = swipeListView.getLastVisiblePosition(); View view = swipeListView.getChildAt(position - start); ++dismissAnimationRefCount; if (position >= start && position <= end) { performDismiss(view, position, false); return view.getHeight(); } else { pendingDismisses.add(new PendingDismissData(position, null)); return 0; } } /** * 重新绘制列表项选中与未选中时的状态 * * @param frontView * view to draw * @param position * position in list */ protected void reloadChoiceStateInView(View frontView, int position) { if (isChecked(position)) { if (swipeDrawableChecked > 0) frontView.setBackgroundResource(swipeDrawableChecked); } else { if (swipeDrawableUnchecked > 0) frontView.setBackgroundResource(swipeDrawableUnchecked); } } /** * 判断列表项是否被选中 * * @param position * position in list * @return */ protected boolean isChecked(int position) { return position < checked.size() && checked.get(position); } /** * 获取选中列表项的总数 * * @return */ protected int getCountSelected() { int count = 0; for (int i = 0; i < checked.size(); i++) { if (checked.get(i)) { count++; } } Log.d("SwipeListView", "selected: " + count); return count; } /** * 获取选中列表项的位置集合 * * @return */ protected List<Integer> getPositionsSelected() { List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < checked.size(); i++) { if (checked.get(i)) { list.add(i); } } return list; } /** * 打开列表项 * * @param view * affected view * @param position * Position of list */ private void openAnimate(View view, int position) { if (!opened.get(position)) { generateRevealAnimate(view, true, false, position); } } /** * 关闭列表项 * * @param view * affected view * @param position * Position of list */ private void closeAnimate(View view, int position) { if (opened.get(position)) { generateRevealAnimate(view, true, false, position); } } /** * 创建动画 * * @param view * affected view * @param swap * If state should change. If "false" returns to the original * position * @param swapRight * If swap is true, this parameter tells if move is to the right * or left * @param position * Position of list */ private void generateAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { Log.d("SwipeListView", "swap: " + swap + " - swapRight: " + swapRight + " - position: " + position); if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_REVEAL) { generateRevealAnimate(view, swap, swapRight, position); } if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_DISMISS) { generateDismissAnimate(parentView, swap, swapRight, position); } if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { generateChoiceAnimate(view, position); } } /** * 创建选择模式动画 * * @param view * affected view * @param position * list position */ private void generateChoiceAnimate(final View view, final int position) { animate(view).translationX(0).setDuration(animationTime).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { swipeListView.resetScrolling(); resetCell(); } }); } /** * 创建显示动画 * * @param view * affected view * @param swap * If will change state. If is "false" returns to the original * position * @param swapRight * If swap is true, this parameter tells if move is to the right * or left * @param position * Position of list */ private void generateDismissAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { int moveTo = 0; if (opened.get(position)) { if (!swap) { moveTo = openedRight.get(position) ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset); } } else { if (swap) { moveTo = swapRight ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset); } } int alpha = 1; if (swap) { ++dismissAnimationRefCount; alpha = 0; } animate(view).translationX(moveTo).alpha(alpha).setDuration(animationTime).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (swap) { closeOpenedItems(); performDismiss(view, position, true); } resetCell(); } }); } /** * Create reveal animation * * @param view * affected view * @param swap * If will change state. If "false" returns to the original * position * @param swapRight * If swap is true, this parameter tells if movement is toward * right or left * @param position * list position */ private void generateRevealAnimate(final View view, final boolean swap, final boolean swapRight, final int position) { int moveTo = 0; if (opened.get(position)) { if (!swap) { moveTo = openedRight.get(position) ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset); } } else { if (swap) { moveTo = swapRight ? (int) (viewWidth - rightOffset) : (int) (-viewWidth + leftOffset); } } animate(view).translationX(moveTo).setDuration(animationTime).setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { swipeListView.resetScrolling(); if (swap) { boolean aux = !opened.get(position); opened.set(position, aux); if (aux) { swipeListView.onOpened(position, swapRight); openedRight.set(position, swapRight); // view.findViewById(swipeBackView).setVisibility(View.VISIBLE); } else { // view.findViewById(swipeBackView).setVisibility(View.GONE); swipeListView.onClosed(position, openedRight.get(position)); } } resetCell(); } }); } private void resetCell() { if (downPosition != ListView.INVALID_POSITION) { if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { backView.setVisibility(View.VISIBLE); } frontView.setClickable(opened.get(downPosition)); frontView.setLongClickable(opened.get(downPosition)); frontView = null; backView = null; downPosition = ListView.INVALID_POSITION; } } /** * Set enabled * * @param enabled */ public void setEnabled(boolean enabled) { paused = !enabled; } /** * Return ScrollListener for ListView * * @return OnScrollListener */ public AbsListView.OnScrollListener makeScrollListener() { return new AbsListView.OnScrollListener() { private boolean isFirstItem = false; private boolean isLastItem = false; public void onScrollStateChanged(AbsListView absListView, int scrollState) { setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); if (swipeClosesAllItemsWhenListMoves && scrollState == SCROLL_STATE_TOUCH_SCROLL) { closeOpenedItems(); } if (scrollState == SCROLL_STATE_TOUCH_SCROLL) { listViewMoving = true; setEnabled(false); } if (scrollState != AbsListView.OnScrollListener.SCROLL_STATE_FLING && scrollState != SCROLL_STATE_TOUCH_SCROLL) { listViewMoving = false; downPosition = ListView.INVALID_POSITION; swipeListView.resetScrolling(); new Handler().postDelayed(new Runnable() { public void run() { setEnabled(true); } }, 500); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (isFirstItem) { boolean onSecondItemList = firstVisibleItem == 1; if (onSecondItemList) { isFirstItem = false; } } else { boolean onFirstItemList = firstVisibleItem == 0; if (onFirstItemList) { isFirstItem = true; swipeListView.onFirstListItem(); } } if (isLastItem) { boolean onBeforeLastItemList = firstVisibleItem + visibleItemCount == totalItemCount - 1; if (onBeforeLastItemList) { isLastItem = false; } } else { boolean onLastItemList = firstVisibleItem + visibleItemCount >= totalItemCount; if (onLastItemList) { isLastItem = true; swipeListView.onLastListItem(); } } } }; } /** * Close all opened items */ void closeOpenedItems() { if (opened != null) { int start = swipeListView.getFirstVisiblePosition(); int end = swipeListView.getLastVisiblePosition(); for (int i = start; i <= end; i++) { if (opened.get(i)) { closeAnimate(swipeListView.getChildAt(i - start).findViewById(swipeFrontView), i); } } } } /** * @see View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent) */ @Override public boolean onTouch(View view, MotionEvent motionEvent) { if (!isSwipeEnabled()) { return false; } if (viewWidth < 2) { viewWidth = swipeListView.getWidth(); } switch (MotionEventCompat.getActionMasked(motionEvent)) { case MotionEvent.ACTION_DOWN: { if (paused && downPosition != ListView.INVALID_POSITION) { return false; } swipeCurrentAction = SwipeListView.SWIPE_ACTION_NONE; int childCount = swipeListView.getChildCount(); int[] listViewCoords = new int[2]; swipeListView.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 = swipeListView.getChildAt(i); child.getHitRect(rect); int childPosition = swipeListView.getPositionForView(child); // dont allow swiping if this is on the header or footer or // IGNORE_ITEM_VIEW_TYPE or enabled is false on the adapter boolean allowSwipe = swipeListView.getAdapter().isEnabled(childPosition) && swipeListView.getAdapter().getItemViewType(childPosition) >= 0; if (allowSwipe && rect.contains(x, y)) { setParentView(child); setFrontView(child.findViewById(swipeFrontView)); mDownX = motionEvent.getRawX(); downPosition = childPosition; frontView.setClickable(!opened.get(downPosition)); frontView.setLongClickable(!opened.get(downPosition)); velocityTracker = VelocityTracker.obtain(); velocityTracker.addMovement(motionEvent); if (swipeBackView > 0) { setBackView(child.findViewById(swipeBackView)); } break; } } view.onTouchEvent(motionEvent); return true; } case MotionEvent.ACTION_UP: { if (velocityTracker == null || !swiping || downPosition == ListView.INVALID_POSITION) { break; } float deltaX = motionEvent.getRawX() - mDownX; velocityTracker.addMovement(motionEvent); velocityTracker.computeCurrentVelocity(1000); float velocityX = Math.abs(velocityTracker.getXVelocity()); if (!opened.get(downPosition)) { if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && velocityTracker.getXVelocity() > 0) { velocityX = 0; } if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && velocityTracker.getXVelocity() < 0) { velocityX = 0; } } float velocityY = Math.abs(velocityTracker.getYVelocity()); boolean swap = false; boolean swapRight = false; if (minFlingVelocity <= velocityX && velocityX <= maxFlingVelocity && velocityY * 2 < velocityX) { swapRight = velocityTracker.getXVelocity() > 0; Log.d("SwipeListView", "swapRight: " + swapRight + " - swipingRight: " + swipingRight); if (swapRight != swipingRight && swipeActionLeft != swipeActionRight) { swap = false; } else if (opened.get(downPosition) && openedRight.get(downPosition) && swapRight) { swap = false; } else if (opened.get(downPosition) && !openedRight.get(downPosition) && !swapRight) { swap = false; } else { swap = true; } } else if (Math.abs(deltaX) > viewWidth / 2) { swap = true; swapRight = deltaX > 0; } generateAnimate(frontView, swap, swapRight, downPosition); if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { swapChoiceState(downPosition); } velocityTracker.recycle(); velocityTracker = null; mDownX = 0; // change clickable front view // if (swap) { // frontView.setClickable(opened.get(downPosition)); // frontView.setLongClickable(opened.get(downPosition)); // } swiping = false; break; } case MotionEvent.ACTION_MOVE: { if (velocityTracker == null || paused || downPosition == ListView.INVALID_POSITION) { break; } velocityTracker.addMovement(motionEvent); velocityTracker.computeCurrentVelocity(1000); float velocityX = Math.abs(velocityTracker.getXVelocity()); float velocityY = Math.abs(velocityTracker.getYVelocity()); float deltaX = motionEvent.getRawX() - mDownX; float deltaMode = Math.abs(deltaX); int swipeMode = this.swipeMode; int changeSwipeMode = swipeListView.changeSwipeMode(downPosition); if (changeSwipeMode >= 0) { swipeMode = changeSwipeMode; } if (swipeMode == SwipeListView.SWIPE_MODE_NONE) { deltaMode = 0; } else if (swipeMode != SwipeListView.SWIPE_MODE_BOTH) { if (opened.get(downPosition)) { if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && deltaX < 0) { deltaMode = 0; } else if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && deltaX > 0) { deltaMode = 0; } } else { if (swipeMode == SwipeListView.SWIPE_MODE_LEFT && deltaX > 0) { deltaMode = 0; } else if (swipeMode == SwipeListView.SWIPE_MODE_RIGHT && deltaX < 0) { deltaMode = 0; } } } if (deltaMode > slop && swipeCurrentAction == SwipeListView.SWIPE_ACTION_NONE && velocityY < velocityX) { swiping = true; swipingRight = (deltaX > 0); Log.d("SwipeListView", "deltaX: " + deltaX + " - swipingRight: " + swipingRight); if (opened.get(downPosition)) { swipeListView.onStartClose(downPosition, swipingRight); swipeCurrentAction = SwipeListView.SWIPE_ACTION_REVEAL; } else { if (swipingRight && swipeActionRight == SwipeListView.SWIPE_ACTION_DISMISS) { swipeCurrentAction = SwipeListView.SWIPE_ACTION_DISMISS; } else if (!swipingRight && swipeActionLeft == SwipeListView.SWIPE_ACTION_DISMISS) { swipeCurrentAction = SwipeListView.SWIPE_ACTION_DISMISS; } else if (swipingRight && swipeActionRight == SwipeListView.SWIPE_ACTION_CHOICE) { swipeCurrentAction = SwipeListView.SWIPE_ACTION_CHOICE; } else if (!swipingRight && swipeActionLeft == SwipeListView.SWIPE_ACTION_CHOICE) { swipeCurrentAction = SwipeListView.SWIPE_ACTION_CHOICE; } else { swipeCurrentAction = SwipeListView.SWIPE_ACTION_REVEAL; } swipeListView.onStartOpen(downPosition, swipeCurrentAction, swipingRight); } swipeListView.requestDisallowInterceptTouchEvent(true); MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (MotionEventCompat.getActionIndex(motionEvent) << MotionEventCompat.ACTION_POINTER_INDEX_SHIFT)); swipeListView.onTouchEvent(cancelEvent); if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { backView.setVisibility(View.GONE); } } if (swiping && downPosition != ListView.INVALID_POSITION) { if (opened.get(downPosition)) { deltaX += openedRight.get(downPosition) ? viewWidth - rightOffset : -viewWidth + leftOffset; } move(deltaX); return true; } break; } } return false; } private void setActionsTo(int action) { oldSwipeActionRight = swipeActionRight; oldSwipeActionLeft = swipeActionLeft; swipeActionRight = action; swipeActionLeft = action; } protected void returnOldActions() { swipeActionRight = oldSwipeActionRight; swipeActionLeft = oldSwipeActionLeft; } /** * Moves the view * * @param deltaX * delta */ public void move(float deltaX) { swipeListView.onMove(downPosition, deltaX); float posX = ViewHelper.getX(frontView); if (opened.get(downPosition)) { posX += openedRight.get(downPosition) ? -viewWidth + rightOffset : viewWidth - leftOffset; } if (posX > 0 && !swipingRight) { Log.d("SwipeListView", "change to right"); swipingRight = !swipingRight; swipeCurrentAction = swipeActionRight; if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { backView.setVisibility(View.GONE); } else { backView.setVisibility(View.VISIBLE); } } if (posX < 0 && swipingRight) { Log.d("SwipeListView", "change to left"); swipingRight = !swipingRight; swipeCurrentAction = swipeActionLeft; if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { backView.setVisibility(View.GONE); } else { backView.setVisibility(View.VISIBLE); } } if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_DISMISS) { setTranslationX(parentView, deltaX); setAlpha(parentView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / viewWidth))); } else if (swipeCurrentAction == SwipeListView.SWIPE_ACTION_CHOICE) { if ((swipingRight && deltaX > 0 && posX < DISPLACE_CHOICE) || (!swipingRight && deltaX < 0 && posX > -DISPLACE_CHOICE) || (swipingRight && deltaX < DISPLACE_CHOICE) || (!swipingRight && deltaX > -DISPLACE_CHOICE)) { setTranslationX(frontView, deltaX); } } else { setTranslationX(frontView, deltaX); } } /** * Class that saves pending dismiss data */ 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; } } /** * Perform dismiss action * * @param dismissView * View * @param dismissPosition * Position of list */ protected void performDismiss(final View dismissView, final int dismissPosition, boolean doPendingDismiss) { final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); final int originalHeight = dismissView.getHeight(); ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(animationTime); if (doPendingDismiss) { animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { --dismissAnimationRefCount; if (dismissAnimationRefCount == 0) { removePendingDismisses(originalHeight); } } }); } animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { lp.height = (Integer) valueAnimator.getAnimatedValue(); dismissView.setLayoutParams(lp); } }); pendingDismisses.add(new PendingDismissData(dismissPosition, dismissView)); animator.start(); } protected void resetPendingDismisses() { pendingDismisses.clear(); } protected void handlerPendingDismisses(final int originalHeight) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { removePendingDismisses(originalHeight); } }, animationTime + 100); } private void removePendingDismisses(int originalHeight) { // No active animations, process all pending dismisses. // Sort by descending position Collections.sort(pendingDismisses); int[] dismissPositions = new int[pendingDismisses.size()]; for (int i = pendingDismisses.size() - 1; i >= 0; i--) { dismissPositions[i] = pendingDismisses.get(i).position; } swipeListView.onDismiss(dismissPositions); ViewGroup.LayoutParams lp; for (PendingDismissData pendingDismiss : pendingDismisses) { // Reset view presentation if (pendingDismiss.view != null) { setAlpha(pendingDismiss.view, 1f); setTranslationX(pendingDismiss.view, 0); lp = pendingDismiss.view.getLayoutParams(); lp.height = originalHeight; pendingDismiss.view.setLayoutParams(lp); } } resetPendingDismisses(); } }