diff --git a/demo/src/main/java/com/daimajia/swipedemo/ListViewExample.java b/demo/src/main/java/com/daimajia/swipedemo/ListViewExample.java index ab6edf4c..1a945e3b 100644 --- a/demo/src/main/java/com/daimajia/swipedemo/ListViewExample.java +++ b/demo/src/main/java/com/daimajia/swipedemo/ListViewExample.java @@ -68,7 +68,7 @@ public boolean onTouch(View v, MotionEvent event) { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { Toast.makeText(mContext, "OnItemLongClickListener", Toast.LENGTH_SHORT).show(); - return false; + return true; } }); mListView.setOnScrollListener(new AbsListView.OnScrollListener() { diff --git a/demo/src/main/java/com/daimajia/swipedemo/MyActivity.java b/demo/src/main/java/com/daimajia/swipedemo/MyActivity.java index 0db17605..f7ba4476 100644 --- a/demo/src/main/java/com/daimajia/swipedemo/MyActivity.java +++ b/demo/src/main/java/com/daimajia/swipedemo/MyActivity.java @@ -4,6 +4,7 @@ import android.content.Intent; import android.graphics.Color; import android.os.Bundle; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -28,9 +29,9 @@ protected void onCreate(Bundle savedInstanceState) { sample1 = (SwipeLayout) findViewById(R.id.sample1); sample1.setShowMode(SwipeLayout.ShowMode.PullOut); - sample1.setDragEdges(SwipeLayout.DragEdge.Left, SwipeLayout.DragEdge.Right, SwipeLayout.DragEdge.Top); +// sample1.setDragEdges(SwipeLayout.DragEdge.Left, SwipeLayout.DragEdge.Right, SwipeLayout.DragEdge.Top); // When using multiple drag edges it's a good idea to pass the ids of the views that you're using for the left, right, top bottom views (-1 if you're not using a particular view) - sample1.setBottomViewIds(R.id.bottom_wrapper, R.id.bottom_wrapper_2, R.id.starbott, SwipeLayout.EMPTY_LAYOUT); + sample1.setBottomViewIds(R.id.bottom_wrapper, R.id.bottom_wrapper_2, R.id.starbott, R.id.starbott); sample1.addRevealListener(R.id.delete, new SwipeLayout.OnRevealListener() { @Override public void onReveal(View child, SwipeLayout.DragEdge edge, float fraction, int distance) { @@ -38,6 +39,21 @@ public void onReveal(View child, SwipeLayout.DragEdge edge, float fraction, int } }); + sample1.getSurfaceView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(MyActivity.this, "Click on surface", Toast.LENGTH_SHORT).show(); + Log.d(MyActivity.class.getName(), "click on surface"); + } + }); + sample1.getSurfaceView().setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + Toast.makeText(MyActivity.this, "longClick on surface", Toast.LENGTH_SHORT).show(); + Log.d(MyActivity.class.getName(), "longClick on surface"); + return true; + } + }); sample1.findViewById(R.id.star2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -103,6 +119,12 @@ public void onClick(View v) { Toast.makeText(MyActivity.this, "Yo", Toast.LENGTH_SHORT).show(); } }); + sample2.getSurfaceView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(MyActivity.this, "Click on surface", Toast.LENGTH_SHORT).show(); + } + }); //sample3 @@ -120,12 +142,18 @@ public void onReveal(View child, SwipeLayout.DragEdge edge, float fraction, int child.setBackgroundColor(c); } }); - sample3.findViewById(R.id.star).setOnClickListener(new View.OnClickListener() { + sample3.findViewById(R.id.bottom_wrapper_child1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MyActivity.this, "Yo!", Toast.LENGTH_SHORT).show(); } }); + sample3.getSurfaceView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(MyActivity.this, "Click on surface", Toast.LENGTH_SHORT).show(); + } + }); } diff --git a/demo/src/main/java/com/daimajia/swipedemo/adapter/ListViewAdapter.java b/demo/src/main/java/com/daimajia/swipedemo/adapter/ListViewAdapter.java index 074e2de2..e34f31de 100644 --- a/demo/src/main/java/com/daimajia/swipedemo/adapter/ListViewAdapter.java +++ b/demo/src/main/java/com/daimajia/swipedemo/adapter/ListViewAdapter.java @@ -43,6 +43,12 @@ public void onDoubleClick(SwipeLayout layout, boolean surface) { Toast.makeText(mContext, "DoubleClick", Toast.LENGTH_SHORT).show(); } }); + v.findViewById(R.id.delete).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Toast.makeText(mContext, "click delete", Toast.LENGTH_SHORT).show(); + } + }); return v; } diff --git a/demo/src/main/res/drawable/dark_gray.xml b/demo/src/main/res/drawable/dark_gray.xml new file mode 100644 index 00000000..46878503 --- /dev/null +++ b/demo/src/main/res/drawable/dark_gray.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/red.xml b/demo/src/main/res/drawable/red.xml new file mode 100644 index 00000000..707047e9 --- /dev/null +++ b/demo/src/main/res/drawable/red.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/white.xml b/demo/src/main/res/drawable/white.xml new file mode 100644 index 00000000..8dfff14a --- /dev/null +++ b/demo/src/main/res/drawable/white.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/layout/listview_item.xml b/demo/src/main/res/layout/listview_item.xml index 89620277..4b2c5074 100644 --- a/demo/src/main/res/layout/listview_item.xml +++ b/demo/src/main/res/layout/listview_item.xml @@ -36,7 +36,7 @@ android:layout_width="0dp" android:layout_height="40dp" android:layout_weight="4" - android:background="#ffffff" + android:background="@drawable/white" android:text="Yes,Delete" android:textColor="#FF5534" /> diff --git a/demo/src/main/res/layout/main.xml b/demo/src/main/res/layout/main.xml index 759c1c98..cf05988e 100644 --- a/demo/src/main/res/layout/main.xml +++ b/demo/src/main/res/layout/main.xml @@ -1,12 +1,17 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent"> + diff --git a/demo/src/main/res/layout/sample1.xml b/demo/src/main/res/layout/sample1.xml index 1dab32d8..350bdb0f 100644 --- a/demo/src/main/res/layout/sample1.xml +++ b/demo/src/main/res/layout/sample1.xml @@ -1,9 +1,11 @@ - + android:layout_height="80dp" + app:clickToClose="true"> @@ -61,7 +64,7 @@ android:id="@+id/trash2" android:src="@drawable/trash" android:layout_width="70dp" - android:background="#FF3B30" + android:background="@drawable/red" android:paddingLeft="25dp" android:paddingRight="25dp" android:layout_height="match_parent" /> @@ -74,7 +77,8 @@ android:layout_height="match_parent"> - - - - + android:layout_height="match_parent" /> diff --git a/demo/src/main/res/layout/sample2.xml b/demo/src/main/res/layout/sample2.xml index 539e5fa9..08da313e 100644 --- a/demo/src/main/res/layout/sample2.xml +++ b/demo/src/main/res/layout/sample2.xml @@ -26,7 +26,7 @@ android:id="@+id/trash" android:src="@drawable/trash" android:layout_width="70dp" - android:background="#FF3B30" + android:background="@drawable/red" android:paddingLeft="25dp" android:paddingRight="25dp" android:layout_height="match_parent" /> @@ -35,7 +35,7 @@ - - - + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml index 3768db71..35111398 100644 --- a/demo/src/main/res/values/colors.xml +++ b/demo/src/main/res/values/colors.xml @@ -2,4 +2,8 @@ #E8E8E0 #DBDBD3 + #FF3B00 + #990000 + #4C535B + #ff7e8a97 \ No newline at end of file diff --git a/library/src/main/java/com/daimajia/swipe/SwipeLayout.java b/library/src/main/java/com/daimajia/swipe/SwipeLayout.java index 8568aad9..a451bf98 100644 --- a/library/src/main/java/com/daimajia/swipe/SwipeLayout.java +++ b/library/src/main/java/com/daimajia/swipe/SwipeLayout.java @@ -3,56 +3,50 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.view.GestureDetector; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; -import android.widget.Adapter; +import android.widget.AbsListView; import android.widget.AdapterView; -import android.widget.BaseAdapter; import android.widget.FrameLayout; -import android.widget.ListAdapter; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class SwipeLayout extends FrameLayout { - + @Deprecated public static final int EMPTY_LAYOUT = -1; - private static final int DRAG_LEFT = 1; private static final int DRAG_RIGHT = 2; private static final int DRAG_TOP = 4; private static final int DRAG_BOTTOM = 8; + private static final DragEdge DefaultDragEdge = DragEdge.Right; private int mTouchSlop; - private int mLeftIndex; - private int mRightIndex; - private int mTopIndex; - private int mBottomIndex; - - private int mCurrentDirectionIndex = 0; + private DragEdge mCurrentDragEdge = DefaultDragEdge; private ViewDragHelper mDragHelper; private int mDragDistance = 0; - private List mDragEdges; + private LinkedHashMap mDragEdges = new LinkedHashMap(); private ShowMode mShowMode; - private float mLeftEdgeSwipeOffset; - private float mRightEdgeSwipeOffset; - private float mTopEdgeSwipeOffset; - private float mBottomEdgeSwipeOffset; - - private Map mBottomViewIdMap = new HashMap(); - private boolean mBottomViewIdsSet = false; + private float[] mEdgeSwipesOffset = new float[4]; private List mSwipeListeners = new ArrayList(); private List mSwipeDeniers = new ArrayList(); @@ -62,20 +56,16 @@ public class SwipeLayout extends FrameLayout { private DoubleClickListener mDoubleClickListener; private boolean mSwipeEnabled = true; - private boolean mLeftSwipeEnabled = true; - private boolean mRightSwipeEnabled = true; - private boolean mTopSwipeEnabled = true; - private boolean mBottomSwipeEnabled = true; + private boolean[] mSwipesEnabled = new boolean[]{true, true, true, true}; + private boolean mClickToClose = false; public static enum DragEdge { Left, - Right, Top, - Bottom; + Right, + Bottom } - ; - public static enum ShowMode { LayDown, PullOut @@ -96,28 +86,28 @@ public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeLayout); int dragEdgeChoices = a.getInt(R.styleable.SwipeLayout_drag_edge, DRAG_RIGHT); - mLeftEdgeSwipeOffset = a.getDimension(R.styleable.SwipeLayout_leftEdgeSwipeOffset, 0); - mRightEdgeSwipeOffset = a.getDimension(R.styleable.SwipeLayout_rightEdgeSwipeOffset, 0); - mTopEdgeSwipeOffset = a.getDimension(R.styleable.SwipeLayout_topEdgeSwipeOffset, 0); - mBottomEdgeSwipeOffset = a.getDimension(R.styleable.SwipeLayout_bottomEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Left.ordinal()] = a.getDimension(R.styleable.SwipeLayout_leftEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Right.ordinal()] = a.getDimension(R.styleable.SwipeLayout_rightEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Top.ordinal()] = a.getDimension(R.styleable.SwipeLayout_topEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Bottom.ordinal()] = a.getDimension(R.styleable.SwipeLayout_bottomEdgeSwipeOffset, 0); + setClickToClose(a.getBoolean(R.styleable.SwipeLayout_clickToClose, mClickToClose)); - mDragEdges = new ArrayList(); if ((dragEdgeChoices & DRAG_LEFT) == DRAG_LEFT) { - mDragEdges.add(DragEdge.Left); - } - if ((dragEdgeChoices & DRAG_RIGHT) == DRAG_RIGHT) { - mDragEdges.add(DragEdge.Right); + mDragEdges.put(DragEdge.Left, null); } if ((dragEdgeChoices & DRAG_TOP) == DRAG_TOP) { - mDragEdges.add(DragEdge.Top); + mDragEdges.put(DragEdge.Top, null); + } + if ((dragEdgeChoices & DRAG_RIGHT) == DRAG_RIGHT) { + mDragEdges.put(DragEdge.Right, null); } if ((dragEdgeChoices & DRAG_BOTTOM) == DRAG_BOTTOM) { - mDragEdges.add(DragEdge.Bottom); + mDragEdges.put(DragEdge.Bottom, null); } - populateIndexes(); int ordinal = a.getInt(R.styleable.SwipeLayout_show_mode, ShowMode.PullOut.ordinal()); mShowMode = ShowMode.values()[ordinal]; a.recycle(); + } public interface SwipeListener { @@ -226,7 +216,7 @@ public void removeAllRevealListeners(int childId) { @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (child == getSurfaceView()) { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + switch (mCurrentDragEdge) { case Top: case Bottom: return getPaddingLeft(); @@ -241,9 +231,9 @@ public int clampViewPositionHorizontal(View child, int left, int dx) { return getPaddingLeft() - mDragDistance; break; } - } else if (getBottomViews().get(mCurrentDirectionIndex) == child) { + } else if (getCurrentBottomView() == child) { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + switch (mCurrentDragEdge) { case Top: case Bottom: return getPaddingLeft(); @@ -267,7 +257,7 @@ public int clampViewPositionHorizontal(View child, int left, int dx) { @Override public int clampViewPositionVertical(View child, int top, int dy) { if (child == getSurfaceView()) { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + switch (mCurrentDragEdge) { case Left: case Right: return getPaddingTop(); @@ -285,7 +275,9 @@ public int clampViewPositionVertical(View child, int top, int dy) { } } } else { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + View surfaceView = getSurfaceView(); + int surfaceViewTop = surfaceView==null?0:surfaceView.getTop(); + switch (mCurrentDragEdge) { case Left: case Right: return getPaddingTop(); @@ -293,9 +285,9 @@ public int clampViewPositionVertical(View child, int top, int dy) { if (mShowMode == ShowMode.PullOut) { if (top > getPaddingTop()) return getPaddingTop(); } else { - if (getSurfaceView().getTop() + dy < getPaddingTop()) + if (surfaceViewTop + dy < getPaddingTop()) return getPaddingTop(); - if (getSurfaceView().getTop() + dy > getPaddingTop() + mDragDistance) + if (surfaceViewTop + dy > getPaddingTop() + mDragDistance) return getPaddingTop() + mDragDistance; } break; @@ -304,9 +296,9 @@ public int clampViewPositionVertical(View child, int top, int dy) { if (top < getMeasuredHeight() - mDragDistance) return getMeasuredHeight() - mDragDistance; } else { - if (getSurfaceView().getTop() + dy >= getPaddingTop()) + if (surfaceViewTop + dy >= getPaddingTop()) return getPaddingTop(); - if (getSurfaceView().getTop() + dy <= getPaddingTop() - mDragDistance) + if (surfaceViewTop + dy <= getPaddingTop() - mDragDistance) return getPaddingTop() - mDragDistance; } } @@ -316,7 +308,11 @@ public int clampViewPositionVertical(View child, int top, int dy) { @Override public boolean tryCaptureView(View child, int pointerId) { - return child == getSurfaceView() || getBottomViews().contains(child); + boolean result = child == getSurfaceView() || getBottomViews().contains(child); + if(result){ + isCloseBeforeDrag = getOpenStatus() == Status.Close; + } + return result; } @Override @@ -329,59 +325,60 @@ public int getViewVerticalDragRange(View child) { return mDragDistance; } + boolean isCloseBeforeDrag = true; @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); - for (SwipeListener l : mSwipeListeners) + for (SwipeListener l : mSwipeListeners){ l.onHandRelease(SwipeLayout.this, xvel, yvel); - if (releasedChild == getSurfaceView()) { - processSurfaceRelease(xvel, yvel); - } else if (getBottomViews().contains(releasedChild)) { - if (getShowMode() == ShowMode.PullOut) { - processBottomPullOutRelease(xvel, yvel); - } else if (getShowMode() == ShowMode.LayDown) { - processBottomLayDownMode(xvel, yvel); - } } + processHandRelease(xvel, yvel, isCloseBeforeDrag); invalidate(); } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - int evLeft = getSurfaceView().getLeft(), evRight = getSurfaceView().getRight(), evTop = getSurfaceView() - .getTop(), evBottom = getSurfaceView().getBottom(); - if (changedView == getSurfaceView()) { - - if (mShowMode == ShowMode.PullOut) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left - || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) - getBottomViews().get(mCurrentDirectionIndex).offsetLeftAndRight(dx); - else getBottomViews().get(mCurrentDirectionIndex).offsetTopAndBottom(dy); + View surfaceView = getSurfaceView(); + if(surfaceView==null) return; + View currentBottomView = getCurrentBottomView(); + int evLeft = surfaceView.getLeft(), + evRight = surfaceView.getRight(), + evTop = surfaceView.getTop(), + evBottom = surfaceView.getBottom(); + if (changedView == surfaceView) { + + if (mShowMode == ShowMode.PullOut && currentBottomView!=null) { + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right){ + currentBottomView.offsetLeftAndRight(dx); + } else { + currentBottomView.offsetTopAndBottom(dy); + } } } else if (getBottomViews().contains(changedView)) { if (mShowMode == ShowMode.PullOut) { - getSurfaceView().offsetLeftAndRight(dx); - getSurfaceView().offsetTopAndBottom(dy); + surfaceView.offsetLeftAndRight(dx); + surfaceView.offsetTopAndBottom(dy); } else { - Rect rect = computeBottomLayDown(mDragEdges.get(mCurrentDirectionIndex)); - getBottomViews().get(mCurrentDirectionIndex).layout(rect.left, rect.top, rect.right, rect.bottom); + Rect rect = computeBottomLayDown(mCurrentDragEdge); + if(currentBottomView!=null){ + currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom); + } - int newLeft = getSurfaceView().getLeft() + dx, newTop = getSurfaceView().getTop() + dy; + int newLeft = surfaceView.getLeft() + dx, newTop = surfaceView.getTop() + dy; - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left && newLeft < getPaddingLeft()) + if (mCurrentDragEdge == DragEdge.Left && newLeft < getPaddingLeft()) newLeft = getPaddingLeft(); - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right && newLeft > getPaddingLeft()) + else if (mCurrentDragEdge == DragEdge.Right && newLeft > getPaddingLeft()) newLeft = getPaddingLeft(); - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top && newTop < getPaddingTop()) + else if (mCurrentDragEdge == DragEdge.Top && newTop < getPaddingTop()) newTop = getPaddingTop(); - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Bottom && newTop > getPaddingTop()) + else if (mCurrentDragEdge == DragEdge.Bottom && newTop > getPaddingTop()) newTop = getPaddingTop(); - getSurfaceView() - .layout(newLeft, newTop, newLeft + getMeasuredWidth(), newTop + getMeasuredHeight()); + surfaceView.layout(newLeft, newTop, newLeft + getMeasuredWidth(), newTop + getMeasuredHeight()); } } @@ -397,15 +394,6 @@ else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Bottom && newTop > g * the dispatchRevealEvent method may not always get accurate position, it * makes the view may not always get the event when the view is totally * show( fraction = 1), so , we need to calculate every time. - * - * @param child - * @param relativePosition - * @param edge - * @param surfaceLeft - * @param surfaceTop - * @param surfaceRight - * @param surfaceBottom - * @return */ protected boolean isViewTotallyFirstShowed(View child, Rect relativePosition, DragEdge edge, int surfaceLeft, int surfaceTop, int surfaceRight, int surfaceBottom) { @@ -534,7 +522,10 @@ protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, boolean open) } if (status == Status.Open) { - getBottomViews().get(mCurrentDirectionIndex).setEnabled(true); + View currentBottomView = getCurrentBottomView(); + if(currentBottomView!=null){ + currentBottomView.setEnabled(true); + } for (SwipeListener l : mSwipeListeners) { l.onOpen(SwipeLayout.this); } @@ -548,15 +539,19 @@ protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, boolean open) */ private void safeBottomView() { Status status = getOpenStatus(); - List bottoms = getBottomViews(); + List bottoms = getBottomViews(); if (status == Status.Close) { - for (ViewGroup bottom : bottoms) { - if (bottom.getVisibility() != INVISIBLE) bottom.setVisibility(INVISIBLE); + for (View bottom : bottoms) { + if (bottom!=null && bottom.getVisibility() != INVISIBLE){ + bottom.setVisibility(INVISIBLE); + } } } else { - if (bottoms.get(mCurrentDirectionIndex).getVisibility() != VISIBLE) - bottoms.get(mCurrentDirectionIndex).setVisibility(VISIBLE); + View currentBottomView = getCurrentBottomView(); + if (currentBottomView!=null && currentBottomView.getVisibility() != VISIBLE){ + currentBottomView.setVisibility(VISIBLE); + } } } @@ -566,13 +561,13 @@ protected void dispatchRevealEvent(final int surfaceLeft, final int surfaceTop, for (Map.Entry> entry : mRevealListeners.entrySet()) { View child = entry.getKey(); Rect rect = getRelativePosition(child); - if (isViewShowing(child, rect, mDragEdges.get(mCurrentDirectionIndex), surfaceLeft, surfaceTop, + if (isViewShowing(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop, surfaceRight, surfaceBottom)) { mShowEntirely.put(child, false); int distance = 0; float fraction = 0f; if (getShowMode() == ShowMode.LayDown) { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + switch (mCurrentDragEdge) { case Left: distance = rect.left - surfaceLeft; fraction = distance / (float) child.getWidth(); @@ -591,7 +586,7 @@ protected void dispatchRevealEvent(final int surfaceLeft, final int surfaceTop, break; } } else if (getShowMode() == ShowMode.PullOut) { - switch (mDragEdges.get(mCurrentDirectionIndex)) { + switch (mCurrentDragEdge) { case Left: distance = rect.right - getPaddingLeft(); fraction = distance / (float) child.getWidth(); @@ -612,22 +607,22 @@ protected void dispatchRevealEvent(final int surfaceLeft, final int surfaceTop, } for (OnRevealListener l : entry.getValue()) { - l.onReveal(child, mDragEdges.get(mCurrentDirectionIndex), Math.abs(fraction), distance); + l.onReveal(child, mCurrentDragEdge, Math.abs(fraction), distance); if (Math.abs(fraction) == 1) { mShowEntirely.put(child, true); } } } - if (isViewTotallyFirstShowed(child, rect, mDragEdges.get(mCurrentDirectionIndex), surfaceLeft, surfaceTop, + if (isViewTotallyFirstShowed(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop, surfaceRight, surfaceBottom)) { mShowEntirely.put(child, true); for (OnRevealListener l : entry.getValue()) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left - || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) - l.onReveal(child, mDragEdges.get(mCurrentDirectionIndex), 1, child.getWidth()); + if (mCurrentDragEdge == DragEdge.Left + || mCurrentDragEdge == DragEdge.Right) + l.onReveal(child, mCurrentDragEdge, 1, child.getWidth()); else - l.onReveal(child, mDragEdges.get(mCurrentDirectionIndex), 1, child.getHeight()); + l.onReveal(child, mCurrentDragEdge, 1, child.getHeight()); } } @@ -660,300 +655,272 @@ public void addOnLayoutListener(OnLayout l) { public void removeOnLayoutListener(OnLayout l) { if (mOnLayoutListeners != null) mOnLayoutListeners.remove(l); } - + public void addDrag(DragEdge dragEdge, View child){ + addDrag(dragEdge, child, null); + } + public void addDrag(DragEdge dragEdge, View child, ViewGroup.LayoutParams params){ + if(params==null){ + params = generateDefaultLayoutParams(); + } + if(!checkLayoutParams(params)){ + params = generateLayoutParams(params); + } + int gravity = -1; + switch (dragEdge){ + case Left:gravity = Gravity.LEFT;break; + case Right:gravity = Gravity.RIGHT;break; + case Top:gravity = Gravity.TOP;break; + case Bottom:gravity = Gravity.BOTTOM;break; + } + if(params instanceof FrameLayout.LayoutParams){ + ((LayoutParams) params).gravity = gravity; + } + addView(child, 0, params); + } @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int childCount = getChildCount(); - if (childCount != 1 + mDragEdges.size()) { - throw new IllegalStateException("You need to have one surface view plus one view for each of your drag edges"); + public void addView(View child, int index, ViewGroup.LayoutParams params) { + int gravity = Gravity.NO_GRAVITY; + try { + gravity = (Integer) params.getClass().getField("gravity").get(params); + } catch (Exception e) { + e.printStackTrace(); } - for (int i = 0; i < childCount; i++) { - if (!(getChildAt(i) instanceof ViewGroup)) { - throw new IllegalArgumentException("All the children in SwipeLayout must be an instance of ViewGroup"); + + if(gravity>0){ + gravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)); + + if((gravity & Gravity.LEFT) == Gravity.LEFT){ + mDragEdges.put(DragEdge.Left, child); + } + if((gravity & Gravity.RIGHT) == Gravity.RIGHT){ + mDragEdges.put(DragEdge.Right, child); + } + if((gravity & Gravity.TOP) == Gravity.TOP){ + mDragEdges.put(DragEdge.Top, child); + } + if((gravity & Gravity.BOTTOM) == Gravity.BOTTOM){ + mDragEdges.put(DragEdge.Bottom, child); + } + }else{ + for(Map.Entry entry : mDragEdges.entrySet()){ + if(entry.getValue() == null){ + //means used the drag_edge attr, the no gravity child should be use set + mDragEdges.put(entry.getKey(), child); + break; + } } } + if(child==null || child.getParent() == this){ + return; + } + super.addView(child, index, params); + } - if (mShowMode == ShowMode.PullOut) - layoutPullOut(); - else if (mShowMode == ShowMode.LayDown) layoutLayDown(); - - safeBottomView(); + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + updateBottomViews(); if (mOnLayoutListeners != null) for (int i = 0; i < mOnLayoutListeners.size(); i++) { mOnLayoutListeners.get(i).onLayout(this); } - } void layoutPullOut() { Rect rect = computeSurfaceLayoutArea(false); - getSurfaceView().layout(rect.left, rect.top, rect.right, rect.bottom); + View surfaceView = getSurfaceView(); + if(surfaceView!=null){ + surfaceView.layout(rect.left, rect.top, rect.right, rect.bottom); + bringChildToFront(surfaceView); + } rect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect); - getBottomViews().get(mCurrentDirectionIndex).layout(rect.left, rect.top, rect.right, rect.bottom); - bringChildToFront(getSurfaceView()); + View currentBottomView = getCurrentBottomView(); + if(currentBottomView!=null){ + currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom); + } } void layoutLayDown() { Rect rect = computeSurfaceLayoutArea(false); - getSurfaceView().layout(rect.left, rect.top, rect.right, rect.bottom); + View surfaceView = getSurfaceView(); + if(surfaceView!=null){ + surfaceView.layout(rect.left, rect.top, rect.right, rect.bottom); + bringChildToFront(surfaceView); + } rect = computeBottomLayoutAreaViaSurface(ShowMode.LayDown, rect); - getBottomViews().get(mCurrentDirectionIndex).layout(rect.left, rect.top, rect.right, rect.bottom); - bringChildToFront(getSurfaceView()); + View currentBottomView = getCurrentBottomView(); + if(currentBottomView!=null){ + currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom); + } } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); + private boolean mIsBeingDragged; + private void checkCanDrag(MotionEvent ev){ + if(mIsBeingDragged) return; + if(getOpenStatus()==Status.Middle){ + mIsBeingDragged = true; + return; + } + Status status = getOpenStatus(); + float distanceX = ev.getRawX() - sX; + float distanceY = ev.getRawY() - sY; + float angle = Math.abs(distanceY / distanceX); + angle = (float) Math.toDegrees(Math.atan(angle)); + if (getOpenStatus() == Status.Close) { + DragEdge dragEdge; + if (angle < 45) { + if (distanceX > 0 && isLeftSwipeEnabled()) { + dragEdge = DragEdge.Left; + } else if (distanceX < 0 && isRightSwipeEnabled()) { + dragEdge = DragEdge.Right; + }else return; - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left - || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) - mDragDistance = getBottomViews().get(mCurrentDirectionIndex).getMeasuredWidth() - - dp2px(getCurrentOffset()); - else mDragDistance = getBottomViews().get(mCurrentDirectionIndex).getMeasuredHeight() - - dp2px(getCurrentOffset()); - } + } else { + if (distanceY > 0 && isTopSwipeEnabled()) { + dragEdge = DragEdge.Top; + } else if (distanceY < 0 && isBottomSwipeEnabled()) { + dragEdge = DragEdge.Bottom; + }else return; + } + setCurrentDragEdge(dragEdge); + } - private boolean mTouchConsumedByChild = false; + boolean doNothing = false; + if (mCurrentDragEdge == DragEdge.Right) { + boolean suitable = (status == Status.Open && distanceX > mTouchSlop) + || (status == Status.Close && distanceX < -mTouchSlop); + suitable = suitable || (status == Status.Middle); - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { + if (angle > 30 || !suitable) { + doNothing = true; + } + } - if (!isEnabled() || !isEnabledInAdapterView()) { - return true; + if (mCurrentDragEdge == DragEdge.Left) { + boolean suitable = (status == Status.Open && distanceX < -mTouchSlop) + || (status == Status.Close && distanceX > mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle > 30 || !suitable) { + doNothing = true; + } } + if (mCurrentDragEdge == DragEdge.Top) { + boolean suitable = (status == Status.Open && distanceY < -mTouchSlop) + || (status == Status.Close && distanceY > mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle < 60 || !suitable) { + doNothing = true; + } + } + + if (mCurrentDragEdge == DragEdge.Bottom) { + boolean suitable = (status == Status.Open && distanceY > mTouchSlop) + || (status == Status.Close && distanceY < -mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle < 60 || !suitable) { + doNothing = true; + } + } + mIsBeingDragged = !doNothing; + } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { if (!isSwipeEnabled()) { return false; } - + if(mClickToClose && getOpenStatus() == Status.Open && isTouchOnSurface(ev)){ + return true; + } for (SwipeDenier denier : mSwipeDeniers) { if (denier != null && denier.shouldDenySwipe(ev)) { return false; } } - // - // if a child wants to handle the touch event, - // then let it do it. - // - int action = ev.getActionMasked(); - switch (action) { + + switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: - Status status = getOpenStatus(); - if (status == Status.Close) { - mTouchConsumedByChild = childNeedHandleTouchEvent(getSurfaceView(), ev) != null; - } else if (status == Status.Open) { - mTouchConsumedByChild = childNeedHandleTouchEvent(getBottomViews().get(mCurrentDirectionIndex), ev) != null; + mDragHelper.processTouchEvent(ev); + mIsBeingDragged = false; + sX = ev.getRawX(); + sY = ev.getRawY(); + //if the swipe is in middle state(scrolling), should intercept the touch + if(getOpenStatus() == Status.Middle){ + mIsBeingDragged = true; + } + break; + case MotionEvent.ACTION_MOVE: + boolean beforeCheck = mIsBeingDragged; + checkCanDrag(ev); + if (mIsBeingDragged) { + ViewParent parent = getParent(); + if(parent!=null){ + parent.requestDisallowInterceptTouchEvent(true); + } + } + if(!beforeCheck && mIsBeingDragged){ + //let children has one chance to catch the touch, and request the swipe not intercept + //useful when swipeLayout wrap a swipeLayout or other gestural layout + return false; } break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mTouchConsumedByChild = false; - } - - if (mTouchConsumedByChild) return false; - return mDragHelper.shouldInterceptTouchEvent(ev); - } - - /** - * if the ViewGroup children want to handle this event. - * - * @param v - * @param event - * @return - */ - private View childNeedHandleTouchEvent(ViewGroup v, MotionEvent event) { - if (v == null) return null; - if (v.onTouchEvent(event)) return v; - - int childCount = v.getChildCount(); - for (int i = childCount - 1; i >= 0; i--) { - View child = v.getChildAt(i); - if (child instanceof ViewGroup) { - View grandChild = childNeedHandleTouchEvent((ViewGroup) child, event); - if (grandChild != null) return grandChild; - } else { - if (childNeedHandleTouchEvent(v.getChildAt(i), event)) return v.getChildAt(i); - } - } - return null; - } - - /** - * if the view (v) wants to handle this event. - * - * @param v - * @param event - * @return - */ - private boolean childNeedHandleTouchEvent(View v, MotionEvent event) { - if (v == null) return false; - - int[] loc = new int[2]; - v.getLocationOnScreen(loc); - int left = loc[0], top = loc[1]; - if (event.getRawX() > left && event.getRawX() < left + v.getWidth() && event.getRawY() > top - && event.getRawY() < top + v.getHeight()) { - return v.onTouchEvent(event); + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(ev); + break; + default://handle other action, such as ACTION_POINTER_DOWN/UP + mDragHelper.processTouchEvent(ev); } - - return false; + return mIsBeingDragged; } private float sX = -1, sY = -1; - private boolean shouldAllowSwipe() { - if (mCurrentDirectionIndex == mLeftIndex && !mLeftSwipeEnabled) return false; - if (mCurrentDirectionIndex == mRightIndex && !mRightSwipeEnabled) return false; - if (mCurrentDirectionIndex == mTopIndex && !mTopSwipeEnabled) return false; - if (mCurrentDirectionIndex == mBottomIndex && !mBottomSwipeEnabled) return false; - return true; - } - @Override public boolean onTouchEvent(MotionEvent event) { - if (!isEnabledInAdapterView() || !isEnabled()) return true; - if (!isSwipeEnabled()) return super.onTouchEvent(event); int action = event.getActionMasked(); - ViewParent parent = getParent(); - gestureDetector.onTouchEvent(event); - Status status = getOpenStatus(); - ViewGroup touching = null; - if (status == Status.Close) { - touching = getSurfaceView(); - } else if (status == Status.Open) { - touching = getBottomViews().get(mCurrentDirectionIndex); - } switch (action) { case MotionEvent.ACTION_DOWN: mDragHelper.processTouchEvent(event); - parent.requestDisallowInterceptTouchEvent(true); - sX = event.getRawX(); sY = event.getRawY(); - if (touching != null) touching.setPressed(true); - return true; - case MotionEvent.ACTION_MOVE: { - float distanceX = event.getRawX() - sX; - float distanceY = event.getRawY() - sY; - float angle = Math.abs(distanceY / distanceX); - angle = (float) Math.toDegrees(Math.atan(angle)); - if (getOpenStatus() == Status.Close) { - int lastCurrentDirectionIndex = mCurrentDirectionIndex; - if (angle < 45) { - if (mLeftIndex != -1 && distanceX > 0 && isLeftSwipeEnabled()) { - mCurrentDirectionIndex = mLeftIndex; - } else if (mRightIndex != -1 && distanceX < 0 && isRightSwipeEnabled()) { - mCurrentDirectionIndex = mRightIndex; - } - } else { - if (mTopIndex != -1 && distanceY > 0 && isTopSwipeEnabled()) { - mCurrentDirectionIndex = mTopIndex; - } else if (mBottomIndex != -1 && distanceY < 0 && isBottomSwipeEnabled()) { - mCurrentDirectionIndex = mBottomIndex; - } - } - if (lastCurrentDirectionIndex != mCurrentDirectionIndex) { - updateBottomViews(); - } - } - if (!shouldAllowSwipe()) return super.onTouchEvent(event); - - boolean doNothing = false; - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) { - boolean suitable = (status == Status.Open && distanceX > mTouchSlop) - || (status == Status.Close && distanceX < -mTouchSlop); - suitable = suitable || (status == Status.Middle); - if (angle > 30 || !suitable) { - doNothing = true; - } - } - - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) { - boolean suitable = (status == Status.Open && distanceX < -mTouchSlop) - || (status == Status.Close && distanceX > mTouchSlop); - suitable = suitable || status == Status.Middle; - - if (angle > 30 || !suitable) { - doNothing = true; - } - } - - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) { - boolean suitable = (status == Status.Open && distanceY < -mTouchSlop) - || (status == Status.Close && distanceY > mTouchSlop); - suitable = suitable || status == Status.Middle; - - if (angle < 60 || !suitable) { - doNothing = true; - } - } - - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Bottom) { - boolean suitable = (status == Status.Open && distanceY > mTouchSlop) - || (status == Status.Close && distanceY < -mTouchSlop); - suitable = suitable || status == Status.Middle; - - if (angle < 60 || !suitable) { - doNothing = true; - } - } - - if (doNothing) { - parent.requestDisallowInterceptTouchEvent(false); - return false; - } else { - if (touching != null) { - touching.setPressed(false); - } - parent.requestDisallowInterceptTouchEvent(true); + case MotionEvent.ACTION_MOVE: { + //the drag state and the direction are already judged at onInterceptTouchEvent + checkCanDrag(event); + if(mIsBeingDragged){ + getParent().requestDisallowInterceptTouchEvent(true); mDragHelper.processTouchEvent(event); } break; } case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - sX = -1; - sY = -1; - if (touching != null) { - touching.setPressed(false); - } - } - default: - parent.requestDisallowInterceptTouchEvent(true); + case MotionEvent.ACTION_CANCEL: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(event); + break; + + default://handle other action, such as ACTION_POINTER_DOWN/UP mDragHelper.processTouchEvent(event); } - return true; + return super.onTouchEvent(event) || mIsBeingDragged || action == MotionEvent.ACTION_DOWN; + } + public boolean isClickToClose() { + return mClickToClose; } - /** - * if working in {@link android.widget.AdapterView}, we should response - * {@link android.widget.Adapter} isEnable(int position). - * - * @return true when item is enabled, else disabled. - */ - private boolean isEnabledInAdapterView() { - AdapterView adapterView = getAdapterView(); - boolean enable = true; - if (adapterView != null) { - Adapter adapter = adapterView.getAdapter(); - if (adapter != null) { - int p = adapterView.getPositionForView(SwipeLayout.this); - if (adapter instanceof BaseAdapter) { - enable = ((BaseAdapter) adapter).isEnabled(p); - } else if (adapter instanceof ListAdapter) { - enable = ((ListAdapter) adapter).isEnabled(p); - } - } - } - return enable; + public void setClickToClose(boolean mClickToClose) { + this.mClickToClose = mClickToClose; } public void setSwipeEnabled(boolean enabled) { @@ -965,115 +932,162 @@ public boolean isSwipeEnabled() { } public boolean isLeftSwipeEnabled() { - return mLeftSwipeEnabled; + View bottomView = mDragEdges.get(DragEdge.Left); + return bottomView != null && bottomView.getParent() == this + && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Left.ordinal()]; } public void setLeftSwipeEnabled(boolean leftSwipeEnabled) { - this.mLeftSwipeEnabled = leftSwipeEnabled; + this.mSwipesEnabled[DragEdge.Left.ordinal()] = leftSwipeEnabled; } public boolean isRightSwipeEnabled() { - return mRightSwipeEnabled; + View bottomView = mDragEdges.get(DragEdge.Right); + return bottomView!=null && bottomView.getParent()==this + && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Right.ordinal()]; } public void setRightSwipeEnabled(boolean rightSwipeEnabled) { - this.mRightSwipeEnabled = rightSwipeEnabled; + this.mSwipesEnabled[DragEdge.Right.ordinal()] = rightSwipeEnabled; } public boolean isTopSwipeEnabled() { - return mTopSwipeEnabled; + View bottomView = mDragEdges.get(DragEdge.Top); + return bottomView!=null && bottomView.getParent()==this + && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Top.ordinal()]; } public void setTopSwipeEnabled(boolean topSwipeEnabled) { - this.mTopSwipeEnabled = topSwipeEnabled; + this.mSwipesEnabled[DragEdge.Top.ordinal()] = topSwipeEnabled; } public boolean isBottomSwipeEnabled() { - return mBottomSwipeEnabled; + View bottomView = mDragEdges.get(DragEdge.Bottom); + return bottomView!=null && bottomView.getParent()==this + && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Bottom.ordinal()]; } public void setBottomSwipeEnabled(boolean bottomSwipeEnabled) { - this.mBottomSwipeEnabled = bottomSwipeEnabled; + this.mSwipesEnabled[DragEdge.Bottom.ordinal()] = bottomSwipeEnabled; } - private boolean insideAdapterView() { return getAdapterView() != null; } private AdapterView getAdapterView() { ViewParent t = getParent(); - while (t != null) { - if (t instanceof AdapterView) { - return (AdapterView) t; - } - t = t.getParent(); + if (t instanceof AdapterView) { + return (AdapterView) t; } return null; } - private void performAdapterViewItemClick(MotionEvent e) { + private void performAdapterViewItemClick() { + if(getOpenStatus()!= Status.Close) return; ViewParent t = getParent(); - while (t != null) { - if (t instanceof AdapterView) { - AdapterView view = (AdapterView) t; - int p = view.getPositionForView(SwipeLayout.this); - if (p != AdapterView.INVALID_POSITION - && view.performItemClick(view.getChildAt(p - view.getFirstVisiblePosition()), p, view - .getAdapter().getItemId(p))) return; - } else { - if (t instanceof View && ((View) t).performClick()) return; + if (t instanceof AdapterView) { + AdapterView view = (AdapterView) t; + int p = view.getPositionForView(SwipeLayout.this); + if (p != AdapterView.INVALID_POSITION){ + view.performItemClick(view.getChildAt(p - view.getFirstVisiblePosition()), p, view + .getAdapter().getItemId(p)); } - t = t.getParent(); } } - - private GestureDetector gestureDetector = new GestureDetector(getContext(), new SwipeDetector()); - - class SwipeDetector extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onDown(MotionEvent e) { - return true; + private boolean performAdapterViewItemLongClick() { + if(getOpenStatus()!= Status.Close) return false; + ViewParent t = getParent(); + if (t instanceof AdapterView) { + AdapterView view = (AdapterView) t; + int p = view.getPositionForView(SwipeLayout.this); + if (p == AdapterView.INVALID_POSITION) return false; + long vId = view.getItemIdAtPosition(p); + boolean handled = false; + try { + Method m = AbsListView.class.getDeclaredMethod("performLongPress", View.class, int.class, long.class); + m.setAccessible(true); + handled = (boolean) m.invoke(view, SwipeLayout.this, p, vId); + + } catch (Exception e) { + e.printStackTrace(); + + if (view.getOnItemLongClickListener() != null) { + handled = view.getOnItemLongClickListener().onItemLongClick(view, SwipeLayout.this, p, vId); + } + if (handled) { + view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + } + return handled; } - - /** - * Simulate the touch event lifecycle. If you use SwipeLayout in - * {@link android.widget.AdapterView} ({@link android.widget.ListView}, - * {@link android.widget.GridView} etc.) It will manually call - * {@link android.widget.AdapterView}.performItemClick, - * performItemLongClick. - * - * @param e - * @return - */ - @Override - public boolean onSingleTapUp(MotionEvent e) { - if (mDoubleClickListener == null) { - performAdapterViewItemClick(e); + return false; + } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if(insideAdapterView()){ + if(clickListener==null){ + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + performAdapterViewItemClick(); + } + }); + } + if(longClickListener==null){ + setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + performAdapterViewItemLongClick(); + return true; + } + }); } - return true; } + } + OnClickListener clickListener; + @Override + public void setOnClickListener(OnClickListener l) { + super.setOnClickListener(l); + clickListener = l; + } + OnLongClickListener longClickListener; + @Override + public void setOnLongClickListener(OnLongClickListener l) { + super.setOnLongClickListener(l); + longClickListener = l; + } - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - if (mDoubleClickListener != null) { - performAdapterViewItemClick(e); - } - return true; + private Rect hitSurfaceRect; + private boolean isTouchOnSurface(MotionEvent ev){ + View surfaceView = getSurfaceView(); + if(surfaceView==null){ + return false; } + if(hitSurfaceRect == null){ + hitSurfaceRect = new Rect(); + } + surfaceView.getHitRect(hitSurfaceRect); + return hitSurfaceRect.contains((int) ev.getX(), (int) ev.getY()); + } + private GestureDetector gestureDetector = new GestureDetector(getContext(), new SwipeDetector()); + class SwipeDetector extends GestureDetector.SimpleOnGestureListener { @Override - public void onLongPress(MotionEvent e) { - performLongClick(); + public boolean onSingleTapUp(MotionEvent e) { + if(mClickToClose && isTouchOnSurface(e)){ + close(); + } + return super.onSingleTapUp(e); } - @Override public boolean onDoubleTap(MotionEvent e) { if (mDoubleClickListener != null) { View target; - ViewGroup bottom = getBottomViews().get(mCurrentDirectionIndex); - ViewGroup surface = getSurfaceView(); - if (e.getX() > bottom.getLeft() && e.getX() < bottom.getRight() && e.getY() > bottom.getTop() - && e.getY() < bottom.getBottom()) { + View bottom = getCurrentBottomView(); + View surface = getSurfaceView(); + if (bottom!=null && e.getX() > bottom.getLeft() && e.getX() < bottom.getRight() + && e.getY() > bottom.getTop() && e.getY() < bottom.getBottom()) { target = bottom; } else { target = surface; @@ -1084,23 +1098,14 @@ public boolean onDoubleTap(MotionEvent e) { } } - public void setDragEdge(DragEdge dragEdge) { - mDragEdges = new ArrayList(); - mDragEdges.add(dragEdge); - mCurrentDirectionIndex = 0; - populateIndexes(); - requestLayout(); - updateBottomViews(); - } - /** * set the drag distance, it will force set the bottom view's width or * height via this value. * - * @param max + * @param max max distance in dp unit */ public void setDragDistance(int max) { - if (max < 0) throw new IllegalArgumentException("Drag distance can not be < 0"); + if (max < 0) max = 0; mDragDistance = dp2px(max); requestLayout(); } @@ -1118,7 +1123,7 @@ public void setShowMode(ShowMode mode) { } public DragEdge getDragEdge() { - return mDragEdges.get(mCurrentDirectionIndex); + return mCurrentDragEdge; } public int getDragDistance() { @@ -1129,71 +1134,30 @@ public ShowMode getShowMode() { return mShowMode; } - public ViewGroup getSurfaceView() { - return (ViewGroup) getChildAt(getChildCount() - 1); + /**return null if there is no surface view(no children) */ + public View getSurfaceView() { + if(getChildCount()==0) return null; + return getChildAt(getChildCount() - 1); } - public List getBottomViews() { - List lvg = new ArrayList(); - // If the user has provided a map for views to - if (mBottomViewIdsSet) { - if (mDragEdges.contains(DragEdge.Left)) { - lvg.add(mLeftIndex, ((ViewGroup) findViewById(mBottomViewIdMap.get(DragEdge.Left)))); - } - if (mDragEdges.contains(DragEdge.Right)) { - lvg.add(mRightIndex, ((ViewGroup) findViewById(mBottomViewIdMap.get(DragEdge.Right)))); - } - if (mDragEdges.contains(DragEdge.Top)) { - lvg.add(mTopIndex, ((ViewGroup) findViewById(mBottomViewIdMap.get(DragEdge.Top)))); - } - if (mDragEdges.contains(DragEdge.Bottom)) { - lvg.add(mBottomIndex, ((ViewGroup) findViewById(mBottomViewIdMap.get(DragEdge.Bottom)))); - } - } - // Default behaviour is to simply use the first n-1 children in the order they're listed in the layout - // and return them in - else { - for (int i = 0; i < (getChildCount() - 1); i++) { - lvg.add((ViewGroup) getChildAt(i)); - } + /**return null if there is no bottom view */ + @Nullable + public View getCurrentBottomView(){ + List bottoms = getBottomViews(); + if(mCurrentDragEdge.ordinal() < bottoms.size()){ + return bottoms.get(mCurrentDragEdge.ordinal()); } - return lvg; + return null; } - - // Pass the id of the view if set, otherwise pass -1 - public void setBottomViewIds(int left, int right, int top, int bottom) { - if (mDragEdges.contains(DragEdge.Left)) { - if (left == EMPTY_LAYOUT) { - mBottomViewIdsSet = false; - } else { - mBottomViewIdMap.put(DragEdge.Left, left); - mBottomViewIdsSet = true; - } - } - if (mDragEdges.contains(DragEdge.Right)) { - if (right == EMPTY_LAYOUT) { - mBottomViewIdsSet = false; - } else { - mBottomViewIdMap.put(DragEdge.Right, right); - mBottomViewIdsSet = true; - } - } - if (mDragEdges.contains(DragEdge.Top)) { - if (top == EMPTY_LAYOUT) { - mBottomViewIdsSet = false; - } else { - mBottomViewIdMap.put(DragEdge.Top, top); - mBottomViewIdsSet = true; - } - } - if (mDragEdges.contains(DragEdge.Bottom)) { - if (bottom == EMPTY_LAYOUT) { - mBottomViewIdsSet = false; - } else { - mBottomViewIdMap.put(DragEdge.Bottom, bottom); - mBottomViewIdsSet = true; - } + /** + * @return all bottomViews: left, top, right, bottom (may null if the edge is not set) + */ + public List getBottomViews() { + ArrayList bottoms = new ArrayList(); + for(DragEdge dragEdge : DragEdge.values()){ + bottoms.add(mDragEdges.get(dragEdge)); } + return bottoms; } public enum Status { @@ -1209,8 +1173,12 @@ public enum Status { * Middle. */ public Status getOpenStatus() { - int surfaceLeft = getSurfaceView().getLeft(); - int surfaceTop = getSurfaceView().getTop(); + View surfaceView = getSurfaceView(); + if(surfaceView==null){ + return Status.Close; + } + int surfaceLeft = surfaceView.getLeft(); + int surfaceTop = surfaceView.getTop(); if (surfaceLeft == getPaddingLeft() && surfaceTop == getPaddingTop()) return Status.Close; if (surfaceLeft == (getPaddingLeft() - mDragDistance) || surfaceLeft == (getPaddingLeft() + mDragDistance) @@ -1220,102 +1188,57 @@ public Status getOpenStatus() { return Status.Middle; } + /** * Process the surface release event. * - * @param xvel - * @param yvel + * @param xvel xVelocity + * @param yvel yVelocity + * @param isCloseBeforeDragged the open state before drag */ - private void processSurfaceRelease(float xvel, float yvel) { - if (xvel == 0 && getOpenStatus() == Status.Middle) close(); - - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left - || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) { - if (xvel > 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) - open(); + protected void processHandRelease(float xvel, float yvel, boolean isCloseBeforeDragged) { + float minVelocity = mDragHelper.getMinVelocity(); + View surfaceView = getSurfaceView(); + DragEdge currentDragEdge = mCurrentDragEdge; + if(currentDragEdge == null || surfaceView == null){ + return; + } + float willOpenPercent = (isCloseBeforeDragged ? .25f : .75f); + if(currentDragEdge == DragEdge.Left){ + if(xvel > minVelocity) open(); + else if(xvel < -minVelocity) close(); + else{ + float openPercent = 1f * getSurfaceView().getLeft() / mDragDistance; + if(openPercent > willOpenPercent ) open(); else close(); } - if (xvel < 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) - close(); - else open(); - } - } else { - if (yvel > 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) - open(); + }else if(currentDragEdge == DragEdge.Right){ + if(xvel > minVelocity) close(); + else if(xvel < -minVelocity) open(); + else{ + float openPercent = 1f * (-getSurfaceView().getLeft()) / mDragDistance; + if(openPercent > willOpenPercent ) open(); else close(); } - if (yvel < 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) - close(); - else open(); - } - } - } - - /** - * process bottom (PullOut mode) hand release event. - * - * @param xvel - * @param yvel - */ - private void processBottomPullOutRelease(float xvel, float yvel) { - - if (xvel == 0 && getOpenStatus() == Status.Middle) close(); - - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left - || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) { - if (xvel > 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) - open(); + }else if(currentDragEdge == DragEdge.Top){ + if(yvel > minVelocity) open(); + else if(yvel < -minVelocity) close(); + else{ + float openPercent = 1f * getSurfaceView().getTop() / mDragDistance; + if(openPercent > willOpenPercent ) open(); else close(); } - if (xvel < 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) - close(); - else open(); - } - } else { - if (yvel > 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) - open(); + }else if(currentDragEdge == DragEdge.Bottom){ + if(yvel > minVelocity) close(); + else if(yvel < -minVelocity) open(); + else{ + float openPercent = 1f * (-getSurfaceView().getTop()) / mDragDistance; + if(openPercent > willOpenPercent ) open(); else close(); } - - if (yvel < 0) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) - close(); - else open(); - } } } - /** - * process bottom (LayDown mode) hand release event. - * - * @param xvel - * @param yvel - */ - private void processBottomLayDownMode(float xvel, float yvel) { - - if (xvel == 0 && getOpenStatus() == Status.Middle) close(); - - int l = getPaddingLeft(), t = getPaddingTop(); - - if (xvel < 0 && mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) - l -= mDragDistance; - if (xvel > 0 && mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) l += mDragDistance; - - if (yvel > 0 && mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) t += mDragDistance; - if (yvel < 0 && mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Bottom) - t -= mDragDistance; - - mDragHelper.smoothSlideViewTo(getSurfaceView(), l, t); - invalidate(); - } - /** * smoothly open surface. */ @@ -1328,18 +1251,23 @@ public void open(boolean smooth) { } public void open(boolean smooth, boolean notify) { - ViewGroup surface = getSurfaceView(), bottom = getBottomViews().get(mCurrentDirectionIndex); + View surface = getSurfaceView(), bottom = getCurrentBottomView(); + if(surface == null){ + return; + } int dx, dy; Rect rect = computeSurfaceLayoutArea(true); if (smooth) { - mDragHelper.smoothSlideViewTo(getSurfaceView(), rect.left, rect.top); + mDragHelper.smoothSlideViewTo(surface, rect.left, rect.top); } else { dx = rect.left - surface.getLeft(); dy = rect.top - surface.getTop(); surface.layout(rect.left, rect.top, rect.right, rect.bottom); if (getShowMode() == ShowMode.PullOut) { Rect bRect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect); - bottom.layout(bRect.left, bRect.top, bRect.right, bRect.bottom); + if(bottom!=null){ + bottom.layout(bRect.left, bRect.top, bRect.right, bRect.bottom); + } } if (notify) { dispatchRevealEvent(rect.left, rect.top, rect.right, rect.bottom); @@ -1352,44 +1280,17 @@ public void open(boolean smooth, boolean notify) { } public void open(DragEdge edge) { - switch (edge) { - case Left: - mCurrentDirectionIndex = mLeftIndex; - case Right: - mCurrentDirectionIndex = mRightIndex; - case Top: - mCurrentDirectionIndex = mTopIndex; - case Bottom: - mCurrentDirectionIndex = mBottomIndex; - } + setCurrentDragEdge(edge); open(true, true); } public void open(boolean smooth, DragEdge edge) { - switch (edge) { - case Left: - mCurrentDirectionIndex = mLeftIndex; - case Right: - mCurrentDirectionIndex = mRightIndex; - case Top: - mCurrentDirectionIndex = mTopIndex; - case Bottom: - mCurrentDirectionIndex = mBottomIndex; - } + setCurrentDragEdge(edge); open(smooth, true); } public void open(boolean smooth, boolean notify, DragEdge edge) { - switch (edge) { - case Left: - mCurrentDirectionIndex = mLeftIndex; - case Right: - mCurrentDirectionIndex = mRightIndex; - case Top: - mCurrentDirectionIndex = mTopIndex; - case Bottom: - mCurrentDirectionIndex = mBottomIndex; - } + setCurrentDragEdge(edge); open(smooth, notify); } @@ -1411,7 +1312,10 @@ public void close(boolean smooth) { * @param notify if notify all the listeners. */ public void close(boolean smooth, boolean notify) { - ViewGroup surface = getSurfaceView(); + View surface = getSurfaceView(); + if(surface==null){ + return; + } int dx, dy; if (smooth) mDragHelper.smoothSlideViewTo(getSurfaceView(), getPaddingLeft(), getPaddingTop()); @@ -1440,20 +1344,19 @@ public void toggle(boolean smooth) { else if (getOpenStatus() == Status.Close) open(smooth); } + /** * a helper function to compute the Rect area that surface will hold in. - * * @param open open status or close status. - * @return */ private Rect computeSurfaceLayoutArea(boolean open) { int l = getPaddingLeft(), t = getPaddingTop(); if (open) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) + if (mCurrentDragEdge == DragEdge.Left) l = getPaddingLeft() + mDragDistance; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) + else if (mCurrentDragEdge == DragEdge.Right) l = getPaddingLeft() - mDragDistance; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) + else if (mCurrentDragEdge == DragEdge.Top) t = getPaddingTop() + mDragDistance; else t = getPaddingTop() - mDragDistance; } @@ -1462,30 +1365,31 @@ else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) private Rect computeBottomLayoutAreaViaSurface(ShowMode mode, Rect surfaceArea) { Rect rect = surfaceArea; + View bottomView = getCurrentBottomView(); int bl = rect.left, bt = rect.top, br = rect.right, bb = rect.bottom; if (mode == ShowMode.PullOut) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) + if (mCurrentDragEdge == DragEdge.Left) bl = rect.left - mDragDistance; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) + else if (mCurrentDragEdge == DragEdge.Right) bl = rect.right; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) + else if (mCurrentDragEdge == DragEdge.Top) bt = rect.top - mDragDistance; else bt = rect.bottom; - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left || mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) { + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { bb = rect.bottom; - br = bl + getBottomViews().get(mCurrentDirectionIndex).getMeasuredWidth(); + br = bl + (bottomView == null? 0 :bottomView.getMeasuredWidth()); } else { - bb = bt + getBottomViews().get(mCurrentDirectionIndex).getMeasuredHeight(); + bb = bt + (bottomView == null? 0 :bottomView.getMeasuredHeight()); br = rect.right; } } else if (mode == ShowMode.LayDown) { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) + if (mCurrentDragEdge == DragEdge.Left) br = bl + mDragDistance; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) + else if (mCurrentDragEdge == DragEdge.Right) bl = br - mDragDistance; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) + else if (mCurrentDragEdge == DragEdge.Top) bb = bt + mDragDistance; else bt = bb - mDragDistance; @@ -1524,56 +1428,90 @@ private int dp2px(float dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); } - public List getDragEdges() { + + /**Deprecated, use {@link #addDrag(DragEdge, View)} */ + @Deprecated + public void setDragEdge(DragEdge dragEdge) { + if(getChildCount() >= 2){ + mDragEdges.put(dragEdge, getChildAt(getChildCount()-2)); + } + setCurrentDragEdge(dragEdge); + } + + protected void onViewRemoved(View child) { + for(Map.Entry entry : new HashMap(mDragEdges).entrySet()){ + if(entry.getValue() == child){ + mDragEdges.remove(entry.getKey()); + } + } + } + public Map getDragEdgeMap(){ return mDragEdges; } - public void setDragEdges(List mDragEdges) { - this.mDragEdges = mDragEdges; - mCurrentDirectionIndex = 0; - populateIndexes(); - updateBottomViews(); + /**Deprecated, use {@link #getDragEdgeMap()} */ + @Deprecated + public List getDragEdges() { + return new ArrayList(mDragEdges.keySet()); } - public void setDragEdges(DragEdge... mDragEdges) { - this.mDragEdges = new ArrayList(); - for (DragEdge e : mDragEdges) { - this.mDragEdges.add(e); + /**Deprecated, use {@link #addDrag(DragEdge, View)} */ + @Deprecated + public void setDragEdges(List dragEdges) { + for (int i = 0, size = Math.min(dragEdges.size(), getChildCount() - 1); i < size; i++) { + DragEdge dragEdge = dragEdges.get(i); + mDragEdges.put(dragEdge, getChildAt(i)); + } + if(dragEdges.size()==0 || dragEdges.contains(DefaultDragEdge)){ + setCurrentDragEdge(DefaultDragEdge); + }else{ + setCurrentDragEdge(dragEdges.get(0)); } - mCurrentDirectionIndex = 0; - populateIndexes(); - updateBottomViews(); } - private void populateIndexes() { - mLeftIndex = this.mDragEdges.indexOf(DragEdge.Left); - mRightIndex = this.mDragEdges.indexOf(DragEdge.Right); - mTopIndex = this.mDragEdges.indexOf(DragEdge.Top); - mBottomIndex = this.mDragEdges.indexOf(DragEdge.Bottom); + /**Deprecated, use {@link #addDrag(DragEdge, View)} */ + @Deprecated + public void setDragEdges(DragEdge... mDragEdges) { + setDragEdges(Arrays.asList(mDragEdges)); + } + /** + * Deprecated, use {@link #addDrag(DragEdge, View)} + * When using multiple drag edges it's a good idea to pass the ids of the views that + * you're using for the left, right, top bottom views (-1 if you're not using a particular view) + */ + @Deprecated + public void setBottomViewIds(int leftId, int rightId, int topId, int bottomId) { + addDrag(DragEdge.Left, findViewById(leftId)); + addDrag(DragEdge.Right, findViewById(rightId)); + addDrag(DragEdge.Top, findViewById(topId)); + addDrag(DragEdge.Bottom, findViewById(bottomId)); } private float getCurrentOffset() { - if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Left) return mLeftEdgeSwipeOffset; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Right) - return mRightEdgeSwipeOffset; - else if (mDragEdges.get(mCurrentDirectionIndex) == DragEdge.Top) return mTopEdgeSwipeOffset; - else return mBottomEdgeSwipeOffset; + if(mCurrentDragEdge==null) return 0; + return mEdgeSwipesOffset[mCurrentDragEdge.ordinal()]; + } + + private void setCurrentDragEdge(DragEdge dragEdge){ + if(mCurrentDragEdge != dragEdge){ + mCurrentDragEdge = dragEdge; + updateBottomViews(); + } } private void updateBottomViews() { -// removeAllViews(); -// addView(getBottomViews().get(mCurrentDirectionIndex)); -// addView(getSurfaceView()); -// getBottomViews().get(mCurrentDirectionIndex).bringToFront(); -// getSurfaceView().bringToFront(); + View currentBottomView = getCurrentBottomView(); + if(currentBottomView!=null){ + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { + mDragDistance = currentBottomView.getMeasuredWidth() - dp2px(getCurrentOffset()); + } + else mDragDistance = currentBottomView.getMeasuredHeight() - dp2px(getCurrentOffset()); + } + if (mShowMode == ShowMode.PullOut) layoutPullOut(); else if (mShowMode == ShowMode.LayDown) layoutLayDown(); safeBottomView(); - - if (mOnLayoutListeners != null) for (int i = 0; i < mOnLayoutListeners.size(); i++) { - mOnLayoutListeners.get(i).onLayout(this); - } } } diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 249b944e..a671fc5f 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -15,5 +15,6 @@ + \ No newline at end of file