SwipeListView(开源项目)
/*
* Copyright (C) 2013 47 Degrees, LLC
* http://47deg.com
* hello@47deg.com
*
* 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.fortysevendeg.android.swipelistview;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import java.util.List;
import com.example.customerswipterlistview.R;
/**
* ListView subclass that provides the swipe functionality
*/
public class SwipeListView extends ListView {
/**
* Used when user want change swipe list mode on some rows
*/
public final static int SWIPE_MODE_DEFAULT = -1;
/**
* Disables all swipes
*/
public final static int SWIPE_MODE_NONE = 0;
/**
* Enables both left and right swipe
*/
public final static int SWIPE_MODE_BOTH = 1;
/**
* Enables right swipe
*/
public final static int SWIPE_MODE_RIGHT = 2;
/**
* Enables left swipe
*/
public final static int SWIPE_MODE_LEFT = 3;
/**
* Binds the swipe gesture to reveal a view behind the row (Drawer style)
*/
public final static int SWIPE_ACTION_REVEAL = 0;
/**
* Dismisses the cell when swiped over
*/
public final static int SWIPE_ACTION_DISMISS = 1;
/**
* Marks the cell as checked when swiped and release
*/
public final static int SWIPE_ACTION_CHOICE = 2;
/**
* No action when swiped
*/
public final static int SWIPE_ACTION_NONE = 3;
/**
* Default ids for front view
*/
public final static String SWIPE_DEFAULT_FRONT_VIEW = "swipelist_frontview";
/**
* Default id for back view
*/
public final static String SWIPE_DEFAULT_BACK_VIEW = "swipelist_backview";
/**
* Indicates no movement
*/
private final static int TOUCH_STATE_REST = 0;
/**
* State scrolling x position
*/
private final static int TOUCH_STATE_SCROLLING_X = 1;
/**
* State scrolling y position
*/
private final static int TOUCH_STATE_SCROLLING_Y = 2;
private int touchState = TOUCH_STATE_REST;
private float lastMotionX;
private float lastMotionY;
private int touchSlop;
int swipeFrontView = 0;
int swipeBackView = 0;
/**
* Internal listener for common swipe events
*/
private SwipeListViewListener swipeListViewListener;
/**
* Internal touch listener
*/
private SwipeListViewTouchListener touchListener;
/**
* If you create a View programmatically you need send back and front
* identifier
*
* @param context
* Context
* @param swipeBackView
* Back Identifier
* @param swipeFrontView
* Front Identifier
*/
public SwipeListView(Context context, int swipeBackView, int swipeFrontView) {
super(context);
this.swipeFrontView = swipeFrontView;
this.swipeBackView = swipeBackView;
init(null);
}
/**
* @see android.widget.ListView#ListView(android.content.Context,
* android.util.AttributeSet)
*/
public SwipeListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
/**
* @see android.widget.ListView#ListView(android.content.Context,
* android.util.AttributeSet, int)
*/
public SwipeListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Init ListView
*
* @param attrs
* AttributeSet
*/
private void init(AttributeSet attrs) {
int swipeMode = SWIPE_MODE_BOTH;
boolean swipeOpenOnLongPress = true;
boolean swipeCloseAllItemsWhenMoveList = true;
long swipeAnimationTime = 0;
float swipeOffsetLeft = 0;
float swipeOffsetRight = 0;
int swipeDrawableChecked = 0;
int swipeDrawableUnchecked = 0;
int swipeActionLeft = SWIPE_ACTION_REVEAL;
int swipeActionRight = SWIPE_ACTION_REVEAL;
if (attrs != null) {
TypedArray styled = getContext().obtainStyledAttributes(attrs,
R.styleable.SwipeListView);
swipeMode = styled.getInt(R.styleable.SwipeListView_swipeMode,
SWIPE_MODE_BOTH);
swipeActionLeft = styled.getInt(
R.styleable.SwipeListView_swipeActionLeft,
SWIPE_ACTION_REVEAL);
swipeActionRight = styled.getInt(
R.styleable.SwipeListView_swipeActionRight,
SWIPE_ACTION_REVEAL);
swipeOffsetLeft = styled.getDimension(
R.styleable.SwipeListView_swipeOffsetLeft, 0);
swipeOffsetRight = styled.getDimension(
R.styleable.SwipeListView_swipeOffsetRight, 0);
swipeOpenOnLongPress = styled.getBoolean(
R.styleable.SwipeListView_swipeOpenOnLongPress, true);
swipeAnimationTime = styled.getInteger(
R.styleable.SwipeListView_swipeAnimationTime, 0);
swipeCloseAllItemsWhenMoveList = styled.getBoolean(
R.styleable.SwipeListView_swipeCloseAllItemsWhenMoveList,
true);
swipeDrawableChecked = styled.getResourceId(
R.styleable.SwipeListView_swipeDrawableChecked, 0);
swipeDrawableUnchecked = styled.getResourceId(
R.styleable.SwipeListView_swipeDrawableUnchecked, 0);
swipeFrontView = styled.getResourceId(
R.styleable.SwipeListView_swipeFrontView, 0);
swipeBackView = styled.getResourceId(
R.styleable.SwipeListView_swipeBackView, 0);
}
if (swipeFrontView == 0 || swipeBackView == 0) {
swipeFrontView = getContext().getResources().getIdentifier(
SWIPE_DEFAULT_FRONT_VIEW, "id",
getContext().getPackageName());
swipeBackView = getContext().getResources().getIdentifier(
SWIPE_DEFAULT_BACK_VIEW, "id",
getContext().getPackageName());
if (swipeFrontView == 0 || swipeBackView == 0) {
throw new RuntimeException(
String.format(
"You forgot the attributes swipeFrontView or swipeBackView. You can add this attributes or use '%s' and '%s' identifiers",
SWIPE_DEFAULT_FRONT_VIEW,
SWIPE_DEFAULT_BACK_VIEW));
}
}
final ViewConfiguration configuration = ViewConfiguration
.get(getContext());
touchSlop = ViewConfigurationCompat
.getScaledPagingTouchSlop(configuration);
touchListener = new SwipeListViewTouchListener(this, swipeFrontView,
swipeBackView);
if (swipeAnimationTime > 0) {
touchListener.setAnimationTime(swipeAnimationTime);
}
touchListener.setRightOffset(swipeOffsetRight);
touchListener.setLeftOffset(swipeOffsetLeft);
touchListener.setSwipeActionLeft(swipeActionLeft);
touchListener.setSwipeActionRight(swipeActionRight);
touchListener.setSwipeMode(swipeMode);
touchListener
.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
touchListener.setSwipeDrawableChecked(swipeDrawableChecked);
touchListener.setSwipeDrawableUnchecked(swipeDrawableUnchecked);
setOnTouchListener(touchListener);
setOnScrollListener(touchListener.makeScrollListener());
}
/**
* Recycle cell. This method should be called from getView in Adapter when
* use SWIPE_ACTION_CHOICE
*
* @param convertView
* parent view
* @param position
* position in list
*/
public void recycle(View convertView, int position) {
touchListener.reloadChoiceStateInView(
convertView.findViewById(swipeFrontView), position);
}
public void ReSetLeftOffset(final View convertView) {
final View bankView = convertView.findViewById(swipeBackView);
if (bankView != null) {
bankView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
public void onGlobalLayout() {
Log.e("tag",
"convertView.width="
+ convertView.getWidth() + "width="
+ bankView.getWidth());
touchListener.setLeftOffset((convertView.getWidth() - bankView
.getWidth()));
bankView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
}
}
/*
* 获取控件宽
*/
public static int getWidth(View view) {
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
return (view.getMeasuredWidth());
}
/**
* Get if item is selected
*
* @param position
* position in list
* @return
*/
public boolean isChecked(int position) {
return touchListener.isChecked(position);
}
/**
* Get positions selected
*
* @return
*/
public List<Integer> getPositionsSelected() {
return touchListener.getPositionsSelected();
}
/**
* Count selected
*
* @return
*/
public int getCountSelected() {
return touchListener.getCountSelected();
}
/**
* Unselected choice state in item
*/
public void unselectedChoiceStates() {
touchListener.unselectedChoiceStates();
}
/**
* @see android.widget.ListView#setAdapter(android.widget.ListAdapter)
*/
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
touchListener.resetItems();
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
onListChanged();
touchListener.resetItems();
}
});
}
/**
* Dismiss item
*
* @param position
* Position that you want open
*/
public void dismiss(int position) {
int height = touchListener.dismiss(position);
if (height > 0) {
touchListener.handlerPendingDismisses(height);
} else {
int[] dismissPositions = new int[1];
dismissPositions[0] = position;
onDismiss(dismissPositions);
touchListener.resetPendingDismisses();
}
}
/**
* Dismiss items selected
*/
public void dismissSelected() {
List<Integer> list = touchListener.getPositionsSelected();
int[] dismissPositions = new int[list.size()];
int height = 0;
for (int i = 0; i < list.size(); i++) {
int position = list.get(i);
dismissPositions[i] = position;
int auxHeight = touchListener.dismiss(position);
if (auxHeight > 0) {
height = auxHeight;
}
}
if (height > 0) {
touchListener.handlerPendingDismisses(height);
} else {
onDismiss(dismissPositions);
touchListener.resetPendingDismisses();
}
touchListener.returnOldActions();
}
/**
* Open ListView's item
*
* @param position
* Position that you want open
*/
public void openAnimate(int position) {
touchListener.openAnimate(position);
}
/**
* Close ListView's item
*
* @param position
* Position that you want open
*/
public void closeAnimate(int position) {
touchListener.closeAnimate(position);
}
/**
* Notifies onDismiss
*
* @param reverseSortedPositions
* All dismissed positions
*/
protected void onDismiss(int[] reverseSortedPositions) {
if (swipeListViewListener != null) {
swipeListViewListener.onDismiss(reverseSortedPositions);
}
}
/**
* Start open item
*
* @param position
* list item
* @param action
* current action
* @param right
* to right
*/
protected void onStartOpen(int position, int action, boolean right) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onStartOpen(position, action, right);
}
}
/**
* Start close item
*
* @param position
* list item
* @param right
*/
protected void onStartClose(int position, boolean right) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onStartClose(position, right);
}
}
/**
* Notifies onClickFrontView
*
* @param position
* item clicked
*/
protected void onClickFrontView(int position) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onClickFrontView(position);
}
}
/**
* Notifies onClickBackView
*
* @param position
* back item clicked
*/
protected void onClickBackView(int position) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onClickBackView(position);
}
}
/**
* Notifies onOpened
*
* @param position
* Item opened
* @param toRight
* If should be opened toward the right
*/
protected void onOpened(int position, boolean toRight) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onOpened(position, toRight);
}
}
/**
* Notifies onClosed
*
* @param position
* Item closed
* @param fromRight
* If open from right
*/
protected void onClosed(int position, boolean fromRight) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onClosed(position, fromRight);
}
}
/**
* Notifies onChoiceChanged
*
* @param position
* position that choice
* @param selected
* if item is selected or not
*/
protected void onChoiceChanged(int position, boolean selected) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onChoiceChanged(position, selected);
}
}
/**
* User start choice items
*/
protected void onChoiceStarted() {
if (swipeListViewListener != null) {
swipeListViewListener.onChoiceStarted();
}
}
/**
* User end choice items
*/
protected void onChoiceEnded() {
if (swipeListViewListener != null) {
swipeListViewListener.onChoiceEnded();
}
}
/**
* User is in first item of list
*/
protected void onFirstListItem() {
if (swipeListViewListener != null) {
swipeListViewListener.onFirstListItem();
}
}
/**
* User is in last item of list
*/
protected void onLastListItem() {
if (swipeListViewListener != null) {
swipeListViewListener.onLastListItem();
}
}
/**
* Notifies onListChanged
*/
protected void onListChanged() {
if (swipeListViewListener != null) {
swipeListViewListener.onListChanged();
}
}
/**
* Notifies onMove
*
* @param position
* Item moving
* @param x
* Current position
*/
protected void onMove(int position, float x) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
swipeListViewListener.onMove(position, x);
}
}
protected int changeSwipeMode(int position) {
if (swipeListViewListener != null
&& position != ListView.INVALID_POSITION) {
return swipeListViewListener.onChangeSwipeMode(position);
}
return SWIPE_MODE_DEFAULT;
}
/**
* Sets the Listener
*
* @param swipeListViewListener
* Listener
*/
public void setSwipeListViewListener(
SwipeListViewListener swipeListViewListener) {
this.swipeListViewListener = swipeListViewListener;
}
/**
* Resets scrolling
*/
public void resetScrolling() {
touchState = TOUCH_STATE_REST;
}
/**
* Set offset on right
*
* @param offsetRight
* Offset
*/
public void setOffsetRight(float offsetRight) {
touchListener.setRightOffset(offsetRight);
}
/**
* Set offset on left
*
* @param offsetLeft
* Offset
*/
public void setOffsetLeft(float offsetLeft) {
touchListener.setLeftOffset(offsetLeft);
}
/**
* Set if all items opened will be closed when the user moves the ListView
*
* @param swipeCloseAllItemsWhenMoveList
*/
public void setSwipeCloseAllItemsWhenMoveList(
boolean swipeCloseAllItemsWhenMoveList) {
touchListener
.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
}
/**
* Sets if the user can open an item with long pressing on cell
*
* @param swipeOpenOnLongPress
*/
public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) {
touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
}
/**
* Set swipe mode
*
* @param swipeMode
*/
public void setSwipeMode(int swipeMode) {
touchListener.setSwipeMode(swipeMode);
}
/**
* Return action on left
*
* @return Action
*/
public int getSwipeActionLeft() {
return touchListener.getSwipeActionLeft();
}
/**
* Set action on left
*
* @param swipeActionLeft
* Action
*/
public void setSwipeActionLeft(int swipeActionLeft) {
touchListener.setSwipeActionLeft(swipeActionLeft);
}
/**
* Return action on right
*
* @return Action
*/
public int getSwipeActionRight() {
return touchListener.getSwipeActionRight();
}
/**
* Set action on right
*
* @param swipeActionRight
* Action
*/
public void setSwipeActionRight(int swipeActionRight) {
touchListener.setSwipeActionRight(swipeActionRight);
}
/**
* Sets animation time when user drops cell
*
* @param animationTime
* milliseconds
*/
public void setAnimationTime(long animationTime) {
touchListener.setAnimationTime(animationTime);
}
/**
* @see android.widget.ListView#onInterceptTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = MotionEventCompat.getActionMasked(ev);
final float x = ev.getX();
final float y = ev.getY();
if (isEnabled() && touchListener.isSwipeEnabled()) {
if (touchState == TOUCH_STATE_SCROLLING_X) {
return touchListener.onTouch(this, ev);
}
switch (action) {
case MotionEvent.ACTION_MOVE:
checkInMoving(x, y);
return touchState == TOUCH_STATE_SCROLLING_Y;
case MotionEvent.ACTION_DOWN:
touchListener.onTouch(this, ev);
touchState = TOUCH_STATE_REST;
lastMotionX = x;
lastMotionY = y;
return false;
case MotionEvent.ACTION_CANCEL:
touchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_UP:
touchListener.onTouch(this, ev);
return touchState == TOUCH_STATE_SCROLLING_Y;
default:
break;
}
}
return super.onInterceptTouchEvent(ev);
}
/**
* Check if the user is moving the cell
*
* @param x
* Position X
* @param y
* Position Y
*/
private void checkInMoving(float x, float y) {
final int xDiff = (int) Math.abs(x - lastMotionX);
final int yDiff = (int) Math.abs(y - lastMotionY);
final int touchSlop = this.touchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
if (xMoved) {
touchState = TOUCH_STATE_SCROLLING_X;
lastMotionX = x;
lastMotionY = y;
}
if (yMoved) {
touchState = TOUCH_STATE_SCROLLING_Y;
lastMotionX = x;
lastMotionY = y;
}
}
/**
* Close all opened items
*/
public void closeOpenedItems() {
touchListener.closeOpenedItems();
}
}
BaseSwipeListViewListener
package com.fortysevendeg.android.swipelistview;
public class BaseSwipeListViewListener implements SwipeListViewListener {
@Override
public void onOpened(int position, boolean toRight) {
}
@Override
public void onClosed(int position, boolean fromRight) {
}
@Override
public void onListChanged() {
}
@Override
public void onMove(int position, float x) {
}
@Override
public void onStartOpen(int position, int action, boolean right) {
}
@Override
public void onStartClose(int position, boolean right) {
}
@Override
public void onClickFrontView(int position) {
}
@Override
public void onClickBackView(int position) {
}
@Override
public void onDismiss(int[] reverseSortedPositions) {
}
@Override
public int onChangeSwipeMode(int position) {
return SwipeListView.SWIPE_MODE_DEFAULT;
}
@Override
public void onChoiceChanged(int position, boolean selected) {
}
@Override
public void onChoiceStarted() {
}
@Override
public void onChoiceEnded() {
}
@Override
public void onFirstListItem() {
}
@Override
public void onLastListItem() {
}
}
SwipeListViewListener
/*
* Copyright (C) 2013 47 Degrees, LLC
* http://47deg.com
* hello@47deg.com
*
* 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.fortysevendeg.android.swipelistview;
/**
* Listener to get callback notifications for the SwipeListView
*/
public interface SwipeListViewListener {
/**
* Called when open animation finishes
* @param position list item
* @param toRight Open to right
*/
void onOpened(int position, boolean toRight);
/**
* Called when close animation finishes
* @param position list item
* @param fromRight Close from right
*/
void onClosed(int position, boolean fromRight);
/**
* Called when the list changed
*/
void onListChanged();
/**
* Called when user is moving an item
* @param position list item
* @param x Current position X
*/
void onMove(int position, float x);
/**
* Start open item
* @param position list item
* @param action current action
* @param right to right
*/
void onStartOpen(int position, int action, boolean right);
/**
* Start close item
* @param position list item
* @param right
*/
void onStartClose(int position, boolean right);
/**
* Called when user clicks on the front view
* @param position list item
*/
void onClickFrontView(int position);
/**
* Called when user clicks on the back view
* @param position list item
*/
void onClickBackView(int position);
/**
* Called when user dismisses items
* @param reverseSortedPositions Items dismissed
*/
void onDismiss(int[] reverseSortedPositions);
/**
* Used when user want to change swipe list mode on some rows. Return SWIPE_MODE_DEFAULT
* if you don't want to change swipe list mode
* @param position position that you want to change
* @return type
*/
int onChangeSwipeMode(int position);
/**
* Called when user choice item
* @param position position that choice
* @param selected if item is selected or not
*/
void onChoiceChanged(int position, boolean selected);
/**
* User start choice items
*/
void onChoiceStarted();
/**
* User end choice items
*/
void onChoiceEnded();
/**
* User is in first item of list
*/
void onFirstListItem();
/**
* User is in last item of list
*/
void onLastListItem();
}
/*
* 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.fortysevendeg.android.swipelistview;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.*;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
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;
/**
* Touch listener impl for the 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;
private float downX;
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(final 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;
}
/**
* Set if the user can open an item with long press on cell
*
* @param swipeOpenOnLongPress
*/
public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) {
this.swipeOpenOnLongPress = swipeOpenOnLongPress;
}
/**
* Sets the swipe mode
*
* @param swipeMode
*/
public void setSwipeMode(int swipeMode) {
this.swipeMode = swipeMode;
}
/**
* Check is swiping is enabled
*
* @return
*/
protected boolean isSwipeEnabled() {
return swipeMode != SwipeListView.SWIPE_MODE_NONE;
}
/**
* Return action on left
*
* @return Action
*/
public int getSwipeActionLeft() {
return swipeActionLeft;
}
/**
* Set action on left
*
* @param swipeActionLeft
* Action
*/
public void setSwipeActionLeft(int swipeActionLeft) {
this.swipeActionLeft = swipeActionLeft;
}
/**
* Return action on right
*
* @return Action
*/
public int getSwipeActionRight() {
return swipeActionRight;
}
/**
* Set action on right
*
* @param swipeActionRight
* Action
*/
public void setSwipeActionRight(int swipeActionRight) {
this.swipeActionRight = swipeActionRight;
}
/**
* Set drawable checked (only SWIPE_ACTION_CHOICE)
*
* @param swipeDrawableChecked
* drawable
*/
protected void setSwipeDrawableChecked(int swipeDrawableChecked) {
this.swipeDrawableChecked = swipeDrawableChecked;
}
/**
* Set drawable unchecked (only SWIPE_ACTION_CHOICE)
*
* @param swipeDrawableUnchecked
* drawable
*/
protected void setSwipeDrawableUnchecked(int swipeDrawableUnchecked) {
this.swipeDrawableUnchecked = swipeDrawableUnchecked;
}
/**
* Adds new items when adapter is modified
*/
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);
}
}
}
/**
* Open item
*
* @param position
* Position of list
*/
protected void openAnimate(int position) {
openAnimate(
swipeListView.getChildAt(
position - swipeListView.getFirstVisiblePosition())
.findViewById(swipeFrontView), position);
}
/**
* Close item
*
* @param position
* Position of list
*/
protected void closeAnimate(int position) {
closeAnimate(
swipeListView.getChildAt(
position - swipeListView.getFirstVisiblePosition())
.findViewById(swipeFrontView), position);
}
/**
* Swap choice state in item
*
* @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();
}
/**
* Unselected choice state in item
*/
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;
}
}
/**
* Draw cell for display if item is selected or not
*
* @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);
}
}
/**
* Get if item is selected
*
* @param position
* position in list
* @return
*/
protected boolean isChecked(int position) {
return position < checked.size() && checked.get(position);
}
/**
* Count selected
*
* @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;
}
/**
* Get positions selected
*
* @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;
}
/**
* Open item
*
* @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);
}
}
/**
* Close item
*
* @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);
}
}
/**
* Create animation
*
* @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);
}
}
/**
* Create choice animation
*
* @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();
}
});
}
/**
* Create dismiss animation
*
* @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);
} else {
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;
@Override
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();
Log.e("tag","viewwidth="+viewWidth);
}
switch (motionEvent.getActionMasked()) {
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 = null;
for (int i = 0; i < childCount; i++) {
child = swipeListView.getChildAt(i);
child.getHitRect(rect);
Log.e("tag","child.width="+child.getWidth());
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));
downX = 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() - downX;
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;
downX = 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() - downX;
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
| (motionEvent.getActionIndex() << MotionEvent.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();
}
}
package com.fortysevendeg.android.swipelistview;
import com.example.customerswipterlistview.R;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
public class XSwipeListView extends SwipeListView implements OnScrollListener {
private float mLastY = -1; // save event y
private Scroller mScroller; // used for scroll back
private OnScrollListener mScrollListener; // user's scroll listener
// the interface to trigger refresh and load more.
private IXListViewListener mListViewListener;
// -- header view
private XListViewHeader mHeaderView;
// header view content, use it to calculate the Header's height. And hide it
// when disable pull refresh.
private RelativeLayout mHeaderViewContent;
private TextView mHeaderTimeView;
private int mHeaderViewHeight; // header view's height
private boolean mEnablePullRefresh = true;
private boolean mPullRefreshing = false; // is refreashing.
// -- footer view
private XListViewFooter mFooterView;
private boolean mEnablePullLoad;
private boolean mPullLoading;
private boolean mIsFooterReady = false;
// total list items, used to detect is at the bottom of listview.
private int mTotalItemCount;
// for mScroller, scroll back from header or footer.
private int mScrollBack;
public boolean isCanRefresh = true;
private final static int SCROLLBACK_HEADER = 0;
private final static int SCROLLBACK_FOOTER = 1;
private final static int SCROLL_DURATION = 400; // scroll back duration
private final static int PULL_LOAD_MORE_DELTA = 75; // when pull up >= 50px
// at bottom, trigger
// load more.
private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
// feature.
public XSwipeListView(Context context, AttributeSet attrs) {
super(context, attrs);
initWithContext(context);
}
public XSwipeListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initWithContext(context);
}
private void initWithContext(Context context) {
mScroller = new Scroller(context, new DecelerateInterpolator());
// XListView need the scroll event, and it will dispatch the event to
// user's listener (as a proxy).
super.setOnScrollListener(this);
// init header view
mHeaderView = new XListViewHeader(context);
mHeaderViewContent = (RelativeLayout) mHeaderView
.findViewById(R.id.xlistview_header_content);
mHeaderTimeView = (TextView) mHeaderView
.findViewById(R.id.xlistview_header_time);
addHeaderView(mHeaderView);
// init footer view
mFooterView = new XListViewFooter(context);
// init header height
mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
public void onGlobalLayout() {
mHeaderViewHeight = mHeaderViewContent.getHeight();
getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
}
@Override
public void setAdapter(ListAdapter adapter) {
// make sure XListViewFooter is the last footer view, and only add once.
if (mIsFooterReady == false) {
mIsFooterReady = true;
addFooterView(mFooterView);
}
super.setAdapter(adapter);
}
/**
* enable or disable pull down refresh feature.
*
* @param enable
*/
public void setPullRefreshEnable(boolean enable) {
mEnablePullRefresh = enable;
if (!mEnablePullRefresh) { // disable, hide the content
mHeaderViewContent.setVisibility(View.INVISIBLE);
} else {
mHeaderViewContent.setVisibility(View.VISIBLE);
}
}
/**
* enable or disable pull up load more feature.
*
* @param enable
*/
public void setPullLoadEnable(boolean enable) {
mEnablePullLoad = enable;
if (!mEnablePullLoad) {
mFooterView.hide();
mFooterView.setOnClickListener(null);
} else {
mPullLoading = false;
mFooterView.show();
mFooterView.setState(XListViewFooter.STATE_NORMAL);
mFooterView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startLoadMore();
}
});
}
}
/**
* stop refresh, reset header view.
*/
public void stopRefresh() {
if (mPullRefreshing == true) {
mPullRefreshing = false;
resetHeaderHeight();
}
}
/**
* stop load more, reset footer view.
*/
public void stopLoadMore() {
if (mPullLoading == true) {
mPullLoading = false;
mFooterView.setState(XListViewFooter.STATE_NORMAL);
}
}
/**
* set last refresh time
*
* @param time
*/
public void setRefreshTime(String time) {
mHeaderTimeView.setText(time);
}
private void invokeOnScrolling() {
if (mScrollListener instanceof OnXScrollListener) {
OnXScrollListener l = (OnXScrollListener) mScrollListener;
l.onXScrolling(this);
}
}
private void updateHeaderHeight(float delta) {
mHeaderView.setVisiableHeight((int) delta
+ mHeaderView.getVisiableHeight());
if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头
if (mHeaderView.getVisiableHeight() > mHeaderViewHeight + 30) {
mHeaderView.setState(XListViewHeader.STATE_READY);
} else {
mHeaderView.setState(XListViewHeader.STATE_NORMAL);
}
}
setSelection(0); // scroll to top each time
}
/**
* reset header view's height.
*/
private void resetHeaderHeight() {
int height = mHeaderView.getVisiableHeight();
if (height == 0) // not visible.
return;
// refreshing and header isn't shown fully. do nothing.
if (mPullRefreshing && height <= mHeaderViewHeight) {
return;
}
int finalHeight = 0; // default: scroll back to dismiss header.
// is refreshing, just scroll back to show all the header.
if (mPullRefreshing && height > mHeaderViewHeight) {
finalHeight = mHeaderViewHeight;
}
mScrollBack = SCROLLBACK_HEADER;
mScroller.startScroll(0, height, 0, finalHeight - height,
SCROLL_DURATION);
// trigger computeScroll
invalidate();
}
private void updateFooterHeight(float delta) {
int height = mFooterView.getBottomMargin() + (int) delta;
if (mEnablePullLoad && !mPullLoading) {
if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load
// more.
mFooterView.setState(XListViewFooter.STATE_READY);
} else {
mFooterView.setState(XListViewFooter.STATE_NORMAL);
}
}
mFooterView.setBottomMargin(height);
// setSelection(mTotalItemCount - 1); // scroll to bottom
}
private void resetFooterHeight() {
int bottomMargin = mFooterView.getBottomMargin();
if (bottomMargin > 0) {
mScrollBack = SCROLLBACK_FOOTER;
mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
SCROLL_DURATION);
invalidate();
}
}
private void startLoadMore() {
mPullLoading = true;
mFooterView.setState(XListViewFooter.STATE_LOADING);
if (mListViewListener != null) {
mListViewListener.onLoadMore();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mLastY == -1) {
mLastY = ev.getRawY();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = ev.getRawY() - mLastY;
mLastY = ev.getRawY();
if (getFirstVisiblePosition() == 0
&& (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
// the first item is showing, header has shown or pull down.
updateHeaderHeight(deltaY / OFFSET_RADIO);
invokeOnScrolling();
} else if (getLastVisiblePosition() == mTotalItemCount - 1
&& (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
// last item, already pulled up or want to pull up.
updateFooterHeight(-deltaY / OFFSET_RADIO);
}
break;
default:
mLastY = -1; // reset
if (getFirstVisiblePosition() == 0) {
// invoke refresh
if (mEnablePullRefresh
&& mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
mPullRefreshing = true;
mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
if (mListViewListener != null) {
setRefreshTime(String.valueOf(System.currentTimeMillis()));
mListViewListener.onRefresh();
}
}
resetHeaderHeight();
} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
// invoke load more.
if (mEnablePullLoad
&& mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA
&& mIsFooterReady) {
startLoadMore();
}
resetFooterHeight();
}
break;
}
return super.onTouchEvent(ev);
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mScrollBack == SCROLLBACK_HEADER) {
mHeaderView.setVisiableHeight(mScroller.getCurrY());
} else {
mFooterView.setBottomMargin(mScroller.getCurrY());
}
postInvalidate();
invokeOnScrolling();
}
super.computeScroll();
}
@Override
public void setOnScrollListener(OnScrollListener l) {
mScrollListener = l;
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mScrollListener != null) {
mScrollListener.onScrollStateChanged(view, scrollState);
}
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// send to user's listener
mTotalItemCount = totalItemCount;
if (mScrollListener != null) {
mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
totalItemCount);
}
}
public void setXListViewListener(IXListViewListener l) {
mListViewListener = l;
}
/**
* you can listen ListView.OnScrollListener or this one. it will invoke
* onXScrolling when header/footer scroll back.
*/
public interface OnXScrollListener extends OnScrollListener {
public void onXScrolling(View view);
}
/**
* implements this interface to get refresh/load more event.
*/
public interface IXListViewListener {
public void onRefresh();
public void onLoadMore();
}
/**
*
*/
public void SetVisableFootView() {
if (this.getFooterViewsCount() > 0) {
this.removeFooterView(mFooterView);
mIsFooterReady = false;
}
}
}
xlistivew:
http://blog.youkuaiyun.com/fuweiping/article/details/8033178