OSDN Git Service

Fix bug 5173029 - make fast scroller aware of scrolling containers
authorAdam Powell <adamp@google.com>
Wed, 12 Oct 2011 01:36:34 +0000 (18:36 -0700)
committerAdam Powell <adamp@google.com>
Wed, 12 Oct 2011 02:06:13 +0000 (19:06 -0700)
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

core/java/android/widget/FastScroller.java

index 51506e8..083a952 100644 (file)
@@ -29,12 +29,14 @@ import android.os.Handler;
 import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.widget.AbsListView.OnScrollListener;
 
 /**
  * Helper class for AbsListView to draw and control the Fast Scroll thumb
  */
 class FastScroller {
+    private static final String TAG = "FastScroller";
    
     // Minimum number of pages to justify showing a fast scroll thumb
     private static int MIN_PAGES = 4;
@@ -81,15 +83,15 @@ class FastScroller {
     private Drawable mOverlayDrawableLeft;
     private Drawable mOverlayDrawableRight;
 
-    private int mThumbH;
-    private int mThumbW;
-    private int mThumbY;
+    int mThumbH;
+    int mThumbW;
+    int mThumbY;
 
     private RectF mOverlayPos;
     private int mOverlaySize;
 
-    private AbsListView mList;
-    private boolean mScrollCompleted;
+    AbsListView mList;
+    boolean mScrollCompleted;
     private int mVisibleItem;
     private Paint mPaint;
     private int mListOffset;
@@ -105,7 +107,7 @@ class FastScroller {
     
     private Handler mHandler = new Handler();
     
-    private BaseAdapter mListAdapter;
+    BaseAdapter mListAdapter;
     private SectionIndexer mSectionIndexer;
 
     private boolean mChangedBounds;
@@ -118,10 +120,36 @@ class FastScroller {
 
     private boolean mMatchDragPosition;
 
+    float mInitialTouchY;
+    boolean mPendingDrag;
+    private int mScaledTouchSlop;
+
     private static final int FADE_TIMEOUT = 1500;
+    private static final int PENDING_DRAG_DELAY = 180;
 
     private final Rect mTmpRect = new Rect();
 
+    private final Runnable mDeferStartDrag = new Runnable() {
+        public void run() {
+            if (mList.mIsAttached) {
+                beginDrag();
+
+                final int viewHeight = mList.getHeight();
+                // Jitter
+                int newThumbY = (int) mInitialTouchY - mThumbH + 10;
+                if (newThumbY < 0) {
+                    newThumbY = 0;
+                } else if (newThumbY + mThumbH > viewHeight) {
+                    newThumbY = viewHeight - mThumbH;
+                }
+                mThumbY = newThumbY;
+                scrollTo((float) mThumbY / (viewHeight - mThumbH));
+            }
+
+            mPendingDrag = false;
+        }
+    };
+
     public FastScroller(Context context, AbsListView listView) {
         mList = listView;
         init(context);
@@ -264,6 +292,8 @@ class FastScroller {
 
         ta.recycle();
 
+        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
         mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
                 android.os.Build.VERSION_CODES.HONEYCOMB;
 
@@ -456,7 +486,7 @@ class FastScroller {
         return mSections;
     }
 
-    private void getSectionsFromIndexer() {
+    void getSectionsFromIndexer() {
         Adapter adapter = mList.getAdapter();
         mSectionIndexer = null;
         if (adapter instanceof HeaderViewListAdapter) {
@@ -489,7 +519,7 @@ class FastScroller {
         mListAdapter = null;
     }
 
-    private void scrollTo(float position) {
+    void scrollTo(float position) {
         int count = mList.getCount();
         mScrollCompleted = false;
         float fThreshold = (1.0f / count) / 8;
@@ -647,12 +677,45 @@ class FastScroller {
         cancelFling.recycle();
     }
     
+    void cancelPendingDrag() {
+        mList.removeCallbacks(mDeferStartDrag);
+        mPendingDrag = false;
+    }
+
+    void startPendingDrag() {
+        mPendingDrag = true;
+        mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY);
+    }
+
+    void beginDrag() {
+        setState(STATE_DRAGGING);
+        if (mListAdapter == null && mList != null) {
+            getSectionsFromIndexer();
+        }
+        if (mList != null) {
+            mList.requestDisallowInterceptTouchEvent(true);
+            mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+        }
+
+        cancelFling();
+    }
+
     boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
-            if (isPointInside(ev.getX(), ev.getY())) {
-                setState(STATE_DRAGGING);
-                return true;
-            }
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) {
+                    if (!mList.isInScrollingContainer()) {
+                        beginDrag();
+                        return true;
+                    }
+                    mInitialTouchY = ev.getY();
+                    startPendingDrag();
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                cancelPendingDrag();
+                break;
         }
         return false;
     }
@@ -666,19 +729,32 @@ class FastScroller {
 
         if (action == MotionEvent.ACTION_DOWN) {
             if (isPointInside(me.getX(), me.getY())) {
-                setState(STATE_DRAGGING);
-                if (mListAdapter == null && mList != null) {
-                    getSectionsFromIndexer();
+                if (!mList.isInScrollingContainer()) {
+                    beginDrag();
+                    return true;
                 }
-                if (mList != null) {
-                    mList.requestDisallowInterceptTouchEvent(true);
-                    mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+                mInitialTouchY = me.getY();
+                startPendingDrag();
+            }
+        } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
+            if (mPendingDrag) {
+                // Allow a tap to scroll.
+                beginDrag();
+
+                final int viewHeight = mList.getHeight();
+                // Jitter
+                int newThumbY = (int) me.getY() - mThumbH + 10;
+                if (newThumbY < 0) {
+                    newThumbY = 0;
+                } else if (newThumbY + mThumbH > viewHeight) {
+                    newThumbY = viewHeight - mThumbH;
                 }
+                mThumbY = newThumbY;
+                scrollTo((float) mThumbY / (viewHeight - mThumbH));
 
-                cancelFling();
-                return true;
+                cancelPendingDrag();
+                // Will hit the STATE_DRAGGING check below
             }
-        } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
             if (mState == STATE_DRAGGING) {
                 if (mList != null) {
                     // ViewGroup does the right thing already, but there might
@@ -698,6 +774,23 @@ class FastScroller {
                 return true;
             }
         } else if (action == MotionEvent.ACTION_MOVE) {
+            if (mPendingDrag) {
+                final float y = me.getY();
+                if (Math.abs(y - mInitialTouchY) > mScaledTouchSlop) {
+                    setState(STATE_DRAGGING);
+                    if (mListAdapter == null && mList != null) {
+                        getSectionsFromIndexer();
+                    }
+                    if (mList != null) {
+                        mList.requestDisallowInterceptTouchEvent(true);
+                        mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+                    }
+
+                    cancelFling();
+                    cancelPendingDrag();
+                    // Will hit the STATE_DRAGGING check below
+                }
+            }
             if (mState == STATE_DRAGGING) {
                 final int viewHeight = mList.getHeight();
                 // Jitter
@@ -717,6 +810,8 @@ class FastScroller {
                 }
                 return true;
             }
+        } else if (action == MotionEvent.ACTION_CANCEL) {
+            cancelPendingDrag();
         }
         return false;
     }