Skip to content

Commit af5280c

Browse files
committed
Fix bug 5173029 - make fast scroller aware of scrolling containers
When a ListView with a FastScroller is located in a scrolling container, defer the start of the drag operation for a short time or until a touch slop is crossed. This allows these lists to be placed in containers like ViewPagers without immediately stealing touch events. Change-Id: I9b10b6993b24113c5e95c485bf57206747c73a84
1 parent acf7d98 commit af5280c

File tree

1 file changed

+117
-22
lines changed

1 file changed

+117
-22
lines changed

core/java/android/widget/FastScroller.java

Lines changed: 117 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
import android.os.SystemClock;
3030
import android.view.MotionEvent;
3131
import android.view.View;
32+
import android.view.ViewConfiguration;
3233
import android.widget.AbsListView.OnScrollListener;
3334

3435
/**
3536
* Helper class for AbsListView to draw and control the Fast Scroll thumb
3637
*/
3738
class FastScroller {
39+
private static final String TAG = "FastScroller";
3840

3941
// Minimum number of pages to justify showing a fast scroll thumb
4042
private static int MIN_PAGES = 4;
@@ -81,15 +83,15 @@ class FastScroller {
8183
private Drawable mOverlayDrawableLeft;
8284
private Drawable mOverlayDrawableRight;
8385

84-
private int mThumbH;
85-
private int mThumbW;
86-
private int mThumbY;
86+
int mThumbH;
87+
int mThumbW;
88+
int mThumbY;
8789

8890
private RectF mOverlayPos;
8991
private int mOverlaySize;
9092

91-
private AbsListView mList;
92-
private boolean mScrollCompleted;
93+
AbsListView mList;
94+
boolean mScrollCompleted;
9395
private int mVisibleItem;
9496
private Paint mPaint;
9597
private int mListOffset;
@@ -105,7 +107,7 @@ class FastScroller {
105107

106108
private Handler mHandler = new Handler();
107109

108-
private BaseAdapter mListAdapter;
110+
BaseAdapter mListAdapter;
109111
private SectionIndexer mSectionIndexer;
110112

111113
private boolean mChangedBounds;
@@ -118,10 +120,36 @@ class FastScroller {
118120

119121
private boolean mMatchDragPosition;
120122

123+
float mInitialTouchY;
124+
boolean mPendingDrag;
125+
private int mScaledTouchSlop;
126+
121127
private static final int FADE_TIMEOUT = 1500;
128+
private static final int PENDING_DRAG_DELAY = 180;
122129

123130
private final Rect mTmpRect = new Rect();
124131

132+
private final Runnable mDeferStartDrag = new Runnable() {
133+
public void run() {
134+
if (mList.mIsAttached) {
135+
beginDrag();
136+
137+
final int viewHeight = mList.getHeight();
138+
// Jitter
139+
int newThumbY = (int) mInitialTouchY - mThumbH + 10;
140+
if (newThumbY < 0) {
141+
newThumbY = 0;
142+
} else if (newThumbY + mThumbH > viewHeight) {
143+
newThumbY = viewHeight - mThumbH;
144+
}
145+
mThumbY = newThumbY;
146+
scrollTo((float) mThumbY / (viewHeight - mThumbH));
147+
}
148+
149+
mPendingDrag = false;
150+
}
151+
};
152+
125153
public FastScroller(Context context, AbsListView listView) {
126154
mList = listView;
127155
init(context);
@@ -264,6 +292,8 @@ private void init(Context context) {
264292

265293
ta.recycle();
266294

295+
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
296+
267297
mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
268298
android.os.Build.VERSION_CODES.HONEYCOMB;
269299

@@ -456,7 +486,7 @@ Object[] getSections() {
456486
return mSections;
457487
}
458488

459-
private void getSectionsFromIndexer() {
489+
void getSectionsFromIndexer() {
460490
Adapter adapter = mList.getAdapter();
461491
mSectionIndexer = null;
462492
if (adapter instanceof HeaderViewListAdapter) {
@@ -489,7 +519,7 @@ public void onSectionsChanged() {
489519
mListAdapter = null;
490520
}
491521

492-
private void scrollTo(float position) {
522+
void scrollTo(float position) {
493523
int count = mList.getCount();
494524
mScrollCompleted = false;
495525
float fThreshold = (1.0f / count) / 8;
@@ -647,12 +677,45 @@ private void cancelFling() {
647677
cancelFling.recycle();
648678
}
649679

680+
void cancelPendingDrag() {
681+
mList.removeCallbacks(mDeferStartDrag);
682+
mPendingDrag = false;
683+
}
684+
685+
void startPendingDrag() {
686+
mPendingDrag = true;
687+
mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY);
688+
}
689+
690+
void beginDrag() {
691+
setState(STATE_DRAGGING);
692+
if (mListAdapter == null && mList != null) {
693+
getSectionsFromIndexer();
694+
}
695+
if (mList != null) {
696+
mList.requestDisallowInterceptTouchEvent(true);
697+
mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
698+
}
699+
700+
cancelFling();
701+
}
702+
650703
boolean onInterceptTouchEvent(MotionEvent ev) {
651-
if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
652-
if (isPointInside(ev.getX(), ev.getY())) {
653-
setState(STATE_DRAGGING);
654-
return true;
655-
}
704+
switch (ev.getActionMasked()) {
705+
case MotionEvent.ACTION_DOWN:
706+
if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) {
707+
if (!mList.isInScrollingContainer()) {
708+
beginDrag();
709+
return true;
710+
}
711+
mInitialTouchY = ev.getY();
712+
startPendingDrag();
713+
}
714+
break;
715+
case MotionEvent.ACTION_UP:
716+
case MotionEvent.ACTION_CANCEL:
717+
cancelPendingDrag();
718+
break;
656719
}
657720
return false;
658721
}
@@ -666,19 +729,32 @@ boolean onTouchEvent(MotionEvent me) {
666729

667730
if (action == MotionEvent.ACTION_DOWN) {
668731
if (isPointInside(me.getX(), me.getY())) {
669-
setState(STATE_DRAGGING);
670-
if (mListAdapter == null && mList != null) {
671-
getSectionsFromIndexer();
732+
if (!mList.isInScrollingContainer()) {
733+
beginDrag();
734+
return true;
672735
}
673-
if (mList != null) {
674-
mList.requestDisallowInterceptTouchEvent(true);
675-
mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
736+
mInitialTouchY = me.getY();
737+
startPendingDrag();
738+
}
739+
} else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
740+
if (mPendingDrag) {
741+
// Allow a tap to scroll.
742+
beginDrag();
743+
744+
final int viewHeight = mList.getHeight();
745+
// Jitter
746+
int newThumbY = (int) me.getY() - mThumbH + 10;
747+
if (newThumbY < 0) {
748+
newThumbY = 0;
749+
} else if (newThumbY + mThumbH > viewHeight) {
750+
newThumbY = viewHeight - mThumbH;
676751
}
752+
mThumbY = newThumbY;
753+
scrollTo((float) mThumbY / (viewHeight - mThumbH));
677754

678-
cancelFling();
679-
return true;
755+
cancelPendingDrag();
756+
// Will hit the STATE_DRAGGING check below
680757
}
681-
} else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
682758
if (mState == STATE_DRAGGING) {
683759
if (mList != null) {
684760
// ViewGroup does the right thing already, but there might
@@ -698,6 +774,23 @@ boolean onTouchEvent(MotionEvent me) {
698774
return true;
699775
}
700776
} else if (action == MotionEvent.ACTION_MOVE) {
777+
if (mPendingDrag) {
778+
final float y = me.getY();
779+
if (Math.abs(y - mInitialTouchY) > mScaledTouchSlop) {
780+
setState(STATE_DRAGGING);
781+
if (mListAdapter == null && mList != null) {
782+
getSectionsFromIndexer();
783+
}
784+
if (mList != null) {
785+
mList.requestDisallowInterceptTouchEvent(true);
786+
mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
787+
}
788+
789+
cancelFling();
790+
cancelPendingDrag();
791+
// Will hit the STATE_DRAGGING check below
792+
}
793+
}
701794
if (mState == STATE_DRAGGING) {
702795
final int viewHeight = mList.getHeight();
703796
// Jitter
@@ -717,6 +810,8 @@ boolean onTouchEvent(MotionEvent me) {
717810
}
718811
return true;
719812
}
813+
} else if (action == MotionEvent.ACTION_CANCEL) {
814+
cancelPendingDrag();
720815
}
721816
return false;
722817
}

0 commit comments

Comments
 (0)