/* * 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.roboo.like.google.swipelistview; import java.util.List; 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.View; import android.view.ViewConfiguration; import android.widget.ListAdapter; import android.widget.ListView; import com.roboo.like.google.R; /** * 提供滑动操纵功能的ListView */ public class SwipeListView extends ListView { /** * 默认的滑动操作 */ public final static int SWIPE_MODE_DEFAULT = -1; /** * 不允许滑动操作 */ public final static int SWIPE_MODE_NONE = 0; /** * 左右都可以进行滑动操作 */ public final static int SWIPE_MODE_BOTH = 1; /** * 向右滑动操作 */ public final static int SWIPE_MODE_RIGHT = 2; /** * 向左滑动操作 */ 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; /** * 滑动之后列表项消失 */ public final static int SWIPE_ACTION_DISMISS = 1; /** * 滑动之后列表项被选中,再次滑动释放 */ public final static int SWIPE_ACTION_CHOICE = 2; /** * 列表项不可以滑动 */ public final static int SWIPE_ACTION_NONE = 3; /** * 默认显示在前面的list item */ public final static String SWIPE_DEFAULT_FRONT_VIEW = "swipelist_frontview"; /** * list item 背后显示 */ public final static String SWIPE_DEFAULT_BACK_VIEW = "swipelist_backview"; /** * 没有移动的标志 */ private final static int TOUCH_STATE_REST = 0; /** * 在X方向上滚动 */ private final static int TOUCH_STATE_SCROLLING_X = 1; /** * 在Y方向上滚动 */ private final static int TOUCH_STATE_SCROLLING_Y = 2; private int touchState = TOUCH_STATE_REST; private float lastMotionX; private float lastMotionY; private int mTouchSlop; int swipeFrontView = 0; int swipeBackView = 0; /** * 滑动监听事件 */ private SwipeListViewListener swipeListViewListener; /** * 滑动触摸事件 */ private SwipeListViewTouchListener mTouchListener; /** * 当你通过代码创建{@see SwipeListView}时,你需要指定滑动后显示在前面和后面的id * * @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); } public SwipeListView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public SwipeListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } private void init(AttributeSet attrs) { int swipeMode = SWIPE_MODE_BOTH; /** 长按打开滑动 */ boolean swipeOpenOnLongPress = true; /** 当ListView滚动时,滑动重置 */ 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()); mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); mTouchListener = new SwipeListViewTouchListener(this, swipeFrontView, swipeBackView); if (swipeAnimationTime > 0) { mTouchListener.setAnimationTime(swipeAnimationTime); } mTouchListener.setRightOffset(swipeOffsetRight); mTouchListener.setLeftOffset(swipeOffsetLeft); mTouchListener.setSwipeActionLeft(swipeActionLeft); mTouchListener.setSwipeActionRight(swipeActionRight); mTouchListener.setSwipeMode(swipeMode); mTouchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList); mTouchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress); mTouchListener.setSwipeDrawableChecked(swipeDrawableChecked); mTouchListener.setSwipeDrawableUnchecked(swipeDrawableUnchecked); setOnTouchListener(mTouchListener); setOnScrollListener(mTouchListener.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) { mTouchListener.reloadChoiceStateInView(convertView.findViewById(swipeFrontView), position); } /** * 列表项是否被选中 * * @param 列表项的位置 * @return */ public boolean isChecked(int position) { return mTouchListener.isChecked(position); } /** * 获取选中列表项的位置集合 * * @return */ public List<Integer> getPositionsSelected() { return mTouchListener.getPositionsSelected(); } /** * 获取选中的总数 * * @return */ public int getCountSelected() { return mTouchListener.getCountSelected(); } /** * 未选中列表项的状态 */ public void unselectedChoiceStates() { mTouchListener.unselectedChoiceStates(); } /** * @see android.widget.ListView#setAdapter(android.widget.ListAdapter) */ @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); if(adapter == null) { return; } mTouchListener.resetItems(); adapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); onListChanged(); mTouchListener.resetItems(); } }); } /** * Dismiss item * * @param position * Position that you want open */ public void dismiss(int position) { int height = mTouchListener.dismiss(position); if (height > 0) { mTouchListener.handlerPendingDismisses(height); } else { int[] dismissPositions = new int[1]; dismissPositions[0] = position; onDismiss(dismissPositions); mTouchListener.resetPendingDismisses(); } } /** * Dismiss items selected */ public void dismissSelected() { List<Integer> list = mTouchListener.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 = mTouchListener.dismiss(position); if (auxHeight > 0) { height = auxHeight; } } if (height > 0) { mTouchListener.handlerPendingDismisses(height); } else { onDismiss(dismissPositions); mTouchListener.resetPendingDismisses(); } mTouchListener.returnOldActions(); } /** * Open ListView's item * * @param position * Position that you want open */ public void openAnimate(int position) { mTouchListener.openAnimate(position); } /** * Close ListView's item * * @param position * Position that you want open */ public void closeAnimate(int position) { mTouchListener.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) { mTouchListener.setRightOffset(offsetRight); } /** * Set offset on left * * @param offsetLeft * Offset */ public void setOffsetLeft(float offsetLeft) { mTouchListener.setLeftOffset(offsetLeft); } /** * Set if all items opened will be closed when the user moves the ListView * * @param swipeCloseAllItemsWhenMoveList */ public void setSwipeCloseAllItemsWhenMoveList(boolean swipeCloseAllItemsWhenMoveList) { mTouchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList); } /** * Sets if the user can open an item with long pressing on cell * * @param swipeOpenOnLongPress */ public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) { mTouchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress); } /** * Set swipe mode * * @param swipeMode */ public void setSwipeMode(int swipeMode) { mTouchListener.setSwipeMode(swipeMode); } /** * Return action on left * * @return Action */ public int getSwipeActionLeft() { return mTouchListener.getSwipeActionLeft(); } /** * Set action on left * * @param swipeActionLeft * Action */ public void setSwipeActionLeft(int swipeActionLeft) { mTouchListener.setSwipeActionLeft(swipeActionLeft); } /** * Return action on right * * @return Action */ public int getSwipeActionRight() { return mTouchListener.getSwipeActionRight(); } /** * Set action on right * * @param swipeActionRight * Action */ public void setSwipeActionRight(int swipeActionRight) { mTouchListener.setSwipeActionRight(swipeActionRight); } /** * Sets animation time when user drops cell * * @param animationTime * milliseconds */ public void setAnimationTime(long animationTime) { mTouchListener.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() && mTouchListener.isSwipeEnabled()) { if (touchState == TOUCH_STATE_SCROLLING_X) { return mTouchListener.onTouch(this, ev); } switch (action) { case MotionEvent.ACTION_MOVE: checkInMoving(x, y); return touchState == TOUCH_STATE_SCROLLING_Y; case MotionEvent.ACTION_DOWN: mTouchListener.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: mTouchListener.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.mTouchSlop; 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() { mTouchListener.closeOpenedItems(); } }