Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.

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

1 file changed

Lines changed: 147 additions & 9 deletions

File tree

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)