Skip to content

Commit d8ca7e6

Browse files
committed
Merge pull request etsy#98 from DougSimonton/master
Support for long click and pressed state
2 parents fa002aa + 733bfb5 commit d8ca7e6

File tree

2 files changed

+160
-12
lines changed

2 files changed

+160
-12
lines changed

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

Lines changed: 151 additions & 11 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
@@ -812,6 +840,39 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
812840
super.requestDisallowInterceptTouchEvent(disallowIntercept);
813841
}
814842

843+
final class CheckForTap implements Runnable {
844+
public void run() {
845+
if (mTouchMode == TOUCH_MODE_DOWN) {
846+
mTouchMode = TOUCH_MODE_TAP;
847+
final View child = getChildAt(mMotionPosition);
848+
if (child != null && !child.hasFocusable()) {
849+
mLayoutMode = LAYOUT_NORMAL;
850+
851+
if (!mDataChanged) {
852+
layoutChildren();
853+
child.setPressed(true);
854+
setPressed(true);
855+
856+
final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
857+
final boolean longClickable = isLongClickable();
858+
859+
if (longClickable) {
860+
if (mPendingCheckForLongPress == null) {
861+
mPendingCheckForLongPress = new CheckForLongPress();
862+
}
863+
mPendingCheckForLongPress.rememberWindowAttachCount();
864+
postDelayed(mPendingCheckForLongPress, longPressTimeout);
865+
} else {
866+
mTouchMode = TOUCH_MODE_DONE_WAITING;
867+
}
868+
} else {
869+
mTouchMode = TOUCH_MODE_DONE_WAITING;
870+
}
871+
}
872+
}
873+
}
874+
}
875+
815876
private boolean onTouchDown(final MotionEvent event) {
816877
final int x = (int) event.getX();
817878
final int y = (int) event.getY();
@@ -831,7 +892,10 @@ private boolean onTouchDown(final MotionEvent event) {
831892
// is it a tap or a scroll .. we don't know yet!
832893
mTouchMode = TOUCH_MODE_DOWN;
833894

834-
// TODO : add handling for a click removed from here
895+
if (mPendingCheckForTap == null) {
896+
mPendingCheckForTap = new CheckForTap();
897+
}
898+
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
835899

836900
if (event.getEdgeFlags() != 0 && motionPosition < 0) {
837901
// If we couldn't find a view to click on, but the down event was touching
@@ -891,6 +955,12 @@ private boolean onTouchCancel(final MotionEvent event) {
891955
mTouchMode = TOUCH_MODE_IDLE;
892956
setPressed(false);
893957
invalidate(); // redraw selector
958+
final Handler handler = getHandler();
959+
960+
if (handler != null) {
961+
handler.removeCallbacks(mPendingCheckForLongPress);
962+
}
963+
894964
recycleVelocityTracker();
895965
mActivePointerId = INVALID_POINTER;
896966
return true;
@@ -909,7 +979,14 @@ private boolean onTouchUp(final MotionEvent event) {
909979

910980
setPressed(false);
911981
invalidate(); // redraw selector
982+
983+
final Handler handler = getHandler();
984+
if (handler != null) {
985+
handler.removeCallbacks(mPendingCheckForLongPress);
986+
}
987+
912988
recycleVelocityTracker();
989+
913990
mActivePointerId = INVALID_POINTER;
914991
return true;
915992
}
@@ -945,17 +1022,58 @@ private boolean onTouchUpScrolling(final MotionEvent event) {
9451022
}
9461023

9471024
private boolean onTouchUpTap(final MotionEvent event) {
948-
if (mPerformClick == null) {
949-
invalidate();
950-
mPerformClick = new PerformClick();
951-
}
9521025
final int motionPosition = mMotionPosition;
953-
if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
954-
final PerformClick performClick = mPerformClick;
955-
performClick.mClickMotionPosition = motionPosition;
956-
performClick.rememberWindowAttachCount();
957-
performClick.run();
1026+
if (motionPosition >= 0) {
1027+
final View child = getChildAt(motionPosition);
1028+
if (child != null && !child.hasFocusable()) {
1029+
if (mTouchMode != TOUCH_MODE_DOWN) {
1030+
child.setPressed(false);
1031+
}
1032+
1033+
if (mPerformClick == null) {
1034+
invalidate();
1035+
mPerformClick = new PerformClick();
1036+
}
1037+
1038+
final PerformClick performClick = mPerformClick;
1039+
performClick.mClickMotionPosition = motionPosition;
1040+
performClick.rememberWindowAttachCount();
1041+
1042+
// mResurrectToPosition = motionPosition;
1043+
1044+
if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
1045+
final Handler handler = getHandler();
1046+
if (handler != null) {
1047+
handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
1048+
mPendingCheckForTap : mPendingCheckForLongPress);
1049+
}
1050+
mLayoutMode = LAYOUT_NORMAL;
1051+
if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
1052+
mTouchMode = TOUCH_MODE_TAP;
1053+
layoutChildren();
1054+
child.setPressed(true);
1055+
setPressed(true);
1056+
postDelayed(new Runnable() {
1057+
public void run() {
1058+
child.setPressed(false);
1059+
setPressed(false);
1060+
if (!mDataChanged) {
1061+
post(performClick);
1062+
}
1063+
mTouchMode = TOUCH_MODE_IDLE;
1064+
}
1065+
}, ViewConfiguration.getPressedStateDuration());
1066+
} else {
1067+
mTouchMode = TOUCH_MODE_IDLE;
1068+
}
1069+
return true;
1070+
} else if (!mDataChanged && motionPosition >= 0 && mAdapter.isEnabled(motionPosition)) {
1071+
post(performClick);
1072+
}
1073+
}
9581074
}
1075+
mTouchMode = TOUCH_MODE_IDLE;
1076+
9591077
return true;
9601078
}
9611079

@@ -1011,7 +1129,10 @@ private boolean startScrollIfNeeded(final int y) {
10111129
mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop;
10121130
}
10131131

1014-
// TODO : LONG PRESS
1132+
final Handler handler = getHandler();
1133+
if (handler != null) {
1134+
handler.removeCallbacks(mPendingCheckForLongPress);
1135+
}
10151136
setPressed(false);
10161137
View motionView = getChildAt(mMotionPosition - mFirstPosition);
10171138
if (motionView != null) {
@@ -2754,6 +2875,25 @@ public void run() {
27542875
}
27552876
}
27562877
}
2878+
2879+
private boolean performLongPress(final View child,
2880+
final int longPressPosition, final long longPressId) {
2881+
boolean handled = false;
2882+
2883+
OnItemLongClickListener onItemLongClickListener = getOnItemLongClickListener();
2884+
if (onItemLongClickListener != null) {
2885+
handled = onItemLongClickListener.onItemLongClick(ExtendableListView.this, child,
2886+
longPressPosition, longPressId);
2887+
}
2888+
// if (!handled) {
2889+
// mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
2890+
// handled = super.showContextMenuForChild(AbsListView.this);
2891+
// }
2892+
if (handled) {
2893+
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
2894+
}
2895+
return handled;
2896+
}
27572897

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

sample/src/main/java/com/etsy/android/sample/StaggeredGridActivity.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import com.etsy.android.grid.StaggeredGridView;
1818

19-
public class StaggeredGridActivity extends Activity implements AbsListView.OnScrollListener, AbsListView.OnItemClickListener {
19+
public class StaggeredGridActivity extends Activity implements AbsListView.OnScrollListener, AbsListView.OnItemClickListener, AdapterView.OnItemLongClickListener {
2020

2121
private static final String TAG = "StaggeredGridActivity";
2222
public static final String SAVED_DATA_KEY = "SAVED_DATA";
@@ -64,6 +64,7 @@ protected void onCreate(Bundle savedInstanceState) {
6464
mGridView.setAdapter(mAdapter);
6565
mGridView.setOnScrollListener(this);
6666
mGridView.setOnItemClickListener(this);
67+
mGridView.setOnItemLongClickListener(this);
6768
}
6869

6970
@Override
@@ -131,4 +132,11 @@ private void onLoadMoreItems() {
131132
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
132133
Toast.makeText(this, "Item Clicked: " + position, Toast.LENGTH_SHORT).show();
133134
}
135+
136+
@Override
137+
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
138+
{
139+
Toast.makeText(this, "Item Long Clicked: " + position, Toast.LENGTH_SHORT).show();
140+
return true;
141+
}
134142
}

0 commit comments

Comments
 (0)