Skip to content

Commit 04ee653

Browse files
committed
Support for long click and pressed state
Based on code from AbsListView, added support for long press so that an onItemLongClickListener will be called for a long press on cards, and added the pressed state for cards when they are clicked or long-clicked.
1 parent 84a06f8 commit 04ee653

File tree

1 file changed

+147
-9
lines changed

1 file changed

+147
-9
lines changed

library/src/main/java/com/etsy/android/grid/ExtendableListView.java

Lines changed: 147 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import android.content.Context;
2121
import android.database.DataSetObserver;
2222
import android.graphics.Rect;
23+
import android.os.Handler;
2324
import android.os.Parcel;
2425
import android.os.Parcelable;
2526
import android.support.v4.util.SparseArrayCompat;
@@ -138,6 +139,33 @@ public abstract class ExtendableListView extends AbsListView {
138139

139140
protected boolean mClipToPadding;
140141
private PerformClick mPerformClick;
142+
143+
private Runnable mPendingCheckForTap;
144+
private CheckForLongPress mPendingCheckForLongPress;
145+
146+
private class CheckForLongPress extends WindowRunnnable implements Runnable {
147+
public void run() {
148+
final int motionPosition = mMotionPosition;
149+
final View child = getChildAt(motionPosition);
150+
if (child != null) {
151+
final int longPressPosition = mMotionPosition;
152+
final long longPressId = mAdapter.getItemId(mMotionPosition + mFirstPosition);
153+
154+
boolean handled = false;
155+
if (sameWindow() && !mDataChanged) {
156+
handled = performLongPress(child, longPressPosition + mFirstPosition, longPressId);
157+
}
158+
if (handled) {
159+
mTouchMode = TOUCH_MODE_IDLE;
160+
setPressed(false);
161+
child.setPressed(false);
162+
} else {
163+
mTouchMode = TOUCH_MODE_DONE_WAITING;
164+
}
165+
166+
}
167+
}
168+
}
141169

142170
/**
143171
* A class that represents a fixed view in a list, for example a header at the top
@@ -806,6 +834,39 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
806834
super.requestDisallowInterceptTouchEvent(disallowIntercept);
807835
}
808836

837+
final class CheckForTap implements Runnable {
838+
public void run() {
839+
if (mTouchMode == TOUCH_MODE_DOWN) {
840+
mTouchMode = TOUCH_MODE_TAP;
841+
final View child = getChildAt(mMotionPosition);
842+
if (child != null && !child.hasFocusable()) {
843+
mLayoutMode = LAYOUT_NORMAL;
844+
845+
if (!mDataChanged) {
846+
layoutChildren();
847+
child.setPressed(true);
848+
setPressed(true);
849+
850+
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
851+
final boolean longClickable = isLongClickable();
852+
853+
if (longClickable) {
854+
if (mPendingCheckForLongPress == null) {
855+
mPendingCheckForLongPress = new CheckForLongPress();
856+
}
857+
mPendingCheckForLongPress.rememberWindowAttachCount();
858+
postDelayed(mPendingCheckForLongPress, longPressTimeout);
859+
} else {
860+
mTouchMode = TOUCH_MODE_DONE_WAITING;
861+
}
862+
} else {
863+
mTouchMode = TOUCH_MODE_DONE_WAITING;
864+
}
865+
}
866+
}
867+
}
868+
}
869+
809870
private boolean onTouchDown(final MotionEvent event) {
810871
final int x = (int) event.getX();
811872
final int y = (int) event.getY();
@@ -825,7 +886,10 @@ private boolean onTouchDown(final MotionEvent event) {
825886
// is it a tap or a scroll .. we don't know yet!
826887
mTouchMode = TOUCH_MODE_DOWN;
827888

828-
// TODO : add handling for a click removed from here
889+
if (mPendingCheckForTap == null) {
890+
mPendingCheckForTap = new CheckForTap();
891+
}
892+
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
829893

830894
if (event.getEdgeFlags() != 0 && motionPosition < 0) {
831895
// If we couldn't find a view to click on, but the down event was touching
@@ -885,6 +949,12 @@ private boolean onTouchCancel(final MotionEvent event) {
885949
mTouchMode = TOUCH_MODE_IDLE;
886950
setPressed(false);
887951
invalidate(); // redraw selector
952+
final Handler handler = getHandler();
953+
954+
if (handler != null) {
955+
handler.removeCallbacks(mPendingCheckForLongPress);
956+
}
957+
888958
recycleVelocityTracker();
889959
mActivePointerId = INVALID_POINTER;
890960
return true;
@@ -903,7 +973,14 @@ private boolean onTouchUp(final MotionEvent event) {
903973

904974
setPressed(false);
905975
invalidate(); // redraw selector
976+
977+
final Handler handler = getHandler();
978+
if (handler != null) {
979+
handler.removeCallbacks(mPendingCheckForLongPress);
980+
}
981+
906982
recycleVelocityTracker();
983+
907984
mActivePointerId = INVALID_POINTER;
908985
return true;
909986
}
@@ -939,17 +1016,56 @@ private boolean onTouchUpScrolling(final MotionEvent event) {
9391016
}
9401017

9411018
private boolean onTouchUpTap(final MotionEvent event) {
942-
if (mPerformClick == null) {
943-
invalidate();
944-
mPerformClick = new PerformClick();
945-
}
9461019
final int motionPosition = mMotionPosition;
947-
if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
1020+
final View child = getChildAt(motionPosition);
1021+
if (child != null && !child.hasFocusable()) {
1022+
if (mTouchMode != TOUCH_MODE_DOWN) {
1023+
child.setPressed(false);
1024+
}
1025+
1026+
if (mPerformClick == null) {
1027+
invalidate();
1028+
mPerformClick = new PerformClick();
1029+
}
1030+
9481031
final PerformClick performClick = mPerformClick;
9491032
performClick.mClickMotionPosition = motionPosition;
9501033
performClick.rememberWindowAttachCount();
951-
performClick.run();
1034+
1035+
// mResurrectToPosition = motionPosition;
1036+
1037+
if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
1038+
final Handler handler = getHandler();
1039+
if (handler != null) {
1040+
handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
1041+
mPendingCheckForTap : mPendingCheckForLongPress);
1042+
}
1043+
mLayoutMode = LAYOUT_NORMAL;
1044+
if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
1045+
mTouchMode = TOUCH_MODE_TAP;
1046+
layoutChildren();
1047+
child.setPressed(true);
1048+
setPressed(true);
1049+
postDelayed(new Runnable() {
1050+
public void run() {
1051+
child.setPressed(false);
1052+
setPressed(false);
1053+
if (!mDataChanged) {
1054+
post(performClick);
1055+
}
1056+
mTouchMode = TOUCH_MODE_IDLE;
1057+
}
1058+
}, ViewConfiguration.getPressedStateDuration());
1059+
} else {
1060+
mTouchMode = TOUCH_MODE_IDLE;
1061+
}
1062+
return true;
1063+
} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
1064+
post(performClick);
1065+
}
9521066
}
1067+
mTouchMode = TOUCH_MODE_IDLE;
1068+
9531069
return true;
9541070
}
9551071

@@ -1005,7 +1121,10 @@ private boolean startScrollIfNeeded(final int y) {
10051121
mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
10061122
}
10071123

1008-
// TODO : LONG PRESS
1124+
final Handler handler = getHandler();
1125+
if (handler != null) {
1126+
handler.removeCallbacks(mPendingCheckForLongPress);
1127+
}
10091128
setPressed(false);
10101129
View motionView = getChildAt(mMotionPosition - mFirstPosition);
10111130
if (motionView != null) {
@@ -2741,11 +2860,30 @@ public void run() {
27412860
final View view = getChildAt(motionPosition); // a fix by @pboos
27422861

27432862
if (view != null) {
2744-
performItemClick(view, motionPosition + mFirstPosition, adapter.getItemId(motionPosition));
2863+
performItemClick(view, motionPosition + mFirstPosition, adapter.getItemId(motionPosition + mFirstPosition));
27452864
}
27462865
}
27472866
}
27482867
}
2868+
2869+
private boolean performLongPress(final View child,
2870+
final int longPressPosition, final long longPressId) {
2871+
boolean handled = false;
2872+
2873+
OnItemLongClickListener onItemLongClickListener = getOnItemLongClickListener();
2874+
if (onItemLongClickListener != null) {
2875+
handled = onItemLongClickListener.onItemLongClick(ExtendableListView.this, child,
2876+
longPressPosition, longPressId);
2877+
}
2878+
// if (!handled) {
2879+
// mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
2880+
// handled = super.showContextMenuForChild(AbsListView.this);
2881+
// }
2882+
if (handled) {
2883+
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2884+
}
2885+
return handled;
2886+
}
27492887

27502888
/**
27512889
* A base class for Runnables that will check that their view is still attached to

0 commit comments

Comments
 (0)