/*
* 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 com.actionbarsherlock.R;
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.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.ListAdapter;
import android.widget.ListView;
/**
* 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_CHECK = 2;
/**
* No action when swiped
*/
public final static int SWIPE_ACTION_NONE = 3;
/**
* 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 BaseSwipeListViewListener 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 ListView#ListView(android.content.Context, android.util.AttributeSet)
*/
public SwipeListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
/**
* @see 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 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);
swipeFrontView = styled.getResourceId(R.styleable.SwipeListView_swipeFrontView, 0);
swipeBackView = styled.getResourceId(R.styleable.SwipeListView_swipeBackView, 0);
}
if (swipeFrontView == 0 || swipeBackView == 0) {
throw new RuntimeException("Missed attribute swipeFrontView or swipeBackView");
}
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);
setOnTouchListener(touchListener);
setOnScrollListener(touchListener.makeScrollListener());
}
/**
* @see 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();
}
});
}
/**
* 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) {
swipeListViewListener.onStartOpen(position, action, right);
}
}
/**
* Start close item
* @param position list item
* @param right
*/
protected void onStartClose(int position, boolean right) {
if (swipeListViewListener != null) {
swipeListViewListener.onStartClose(position, right);
}
}
/**
* Notifies onClickFrontView
*
* @param position item clicked
*/
protected void onClickFrontView(int position) {
if (swipeListViewListener != null) {
swipeListViewListener.onClickFrontView(position);
}
}
/**
* Notifies onClickBackView
*
* @param position back item clicked
*/
protected void onClickBackView(int position) {
if (swipeListViewListener != null) {
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) {
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) {
swipeListViewListener.onClosed(position, fromRight);
}
}
/**
* 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) {
swipeListViewListener.onMove(position, x);
}
}
protected int changeSwipeMode(int position) {
if (swipeListViewListener != null) {
return swipeListViewListener.onChangeSwipeMode(position);
}
return SWIPE_MODE_DEFAULT;
}
/**
* Sets the Listener
*
* @param swipeListViewListener Listener
*/
public void setSwipeListViewListener(BaseSwipeListViewListener 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 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 (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;
}
}
}