Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit af5280cc authored by Adam Powell's avatar Adam Powell
Browse files

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
parent acf7d982
Loading
Loading
Loading
Loading
+117 −22
Original line number Original line Diff line number Diff line
@@ -29,12 +29,14 @@ import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AbsListView.OnScrollListener;


/**
/**
 * Helper class for AbsListView to draw and control the Fast Scroll thumb
 * Helper class for AbsListView to draw and control the Fast Scroll thumb
 */
 */
class FastScroller {
class FastScroller {
    private static final String TAG = "FastScroller";
   
   
    // Minimum number of pages to justify showing a fast scroll thumb
    // Minimum number of pages to justify showing a fast scroll thumb
    private static int MIN_PAGES = 4;
    private static int MIN_PAGES = 4;
@@ -81,15 +83,15 @@ class FastScroller {
    private Drawable mOverlayDrawableLeft;
    private Drawable mOverlayDrawableLeft;
    private Drawable mOverlayDrawableRight;
    private Drawable mOverlayDrawableRight;


    private int mThumbH;
    int mThumbH;
    private int mThumbW;
    int mThumbW;
    private int mThumbY;
    int mThumbY;


    private RectF mOverlayPos;
    private RectF mOverlayPos;
    private int mOverlaySize;
    private int mOverlaySize;


    private AbsListView mList;
    AbsListView mList;
    private boolean mScrollCompleted;
    boolean mScrollCompleted;
    private int mVisibleItem;
    private int mVisibleItem;
    private Paint mPaint;
    private Paint mPaint;
    private int mListOffset;
    private int mListOffset;
@@ -105,7 +107,7 @@ class FastScroller {
    
    
    private Handler mHandler = new Handler();
    private Handler mHandler = new Handler();
    
    
    private BaseAdapter mListAdapter;
    BaseAdapter mListAdapter;
    private SectionIndexer mSectionIndexer;
    private SectionIndexer mSectionIndexer;


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


    private boolean mMatchDragPosition;
    private boolean mMatchDragPosition;


    float mInitialTouchY;
    boolean mPendingDrag;
    private int mScaledTouchSlop;

    private static final int FADE_TIMEOUT = 1500;
    private static final int FADE_TIMEOUT = 1500;
    private static final int PENDING_DRAG_DELAY = 180;


    private final Rect mTmpRect = new Rect();
    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) {
    public FastScroller(Context context, AbsListView listView) {
        mList = listView;
        mList = listView;
        init(context);
        init(context);
@@ -264,6 +292,8 @@ class FastScroller {


        ta.recycle();
        ta.recycle();


        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
        mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >=
                android.os.Build.VERSION_CODES.HONEYCOMB;
                android.os.Build.VERSION_CODES.HONEYCOMB;


@@ -456,7 +486,7 @@ class FastScroller {
        return mSections;
        return mSections;
    }
    }


    private void getSectionsFromIndexer() {
    void getSectionsFromIndexer() {
        Adapter adapter = mList.getAdapter();
        Adapter adapter = mList.getAdapter();
        mSectionIndexer = null;
        mSectionIndexer = null;
        if (adapter instanceof HeaderViewListAdapter) {
        if (adapter instanceof HeaderViewListAdapter) {
@@ -489,7 +519,7 @@ class FastScroller {
        mListAdapter = null;
        mListAdapter = null;
    }
    }


    private void scrollTo(float position) {
    void scrollTo(float position) {
        int count = mList.getCount();
        int count = mList.getCount();
        mScrollCompleted = false;
        mScrollCompleted = false;
        float fThreshold = (1.0f / count) / 8;
        float fThreshold = (1.0f / count) / 8;
@@ -647,12 +677,45 @@ class FastScroller {
        cancelFling.recycle();
        cancelFling.recycle();
    }
    }
    
    
    boolean onInterceptTouchEvent(MotionEvent ev) {
    void cancelPendingDrag() {
        if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) {
        mList.removeCallbacks(mDeferStartDrag);
            if (isPointInside(ev.getX(), ev.getY())) {
        mPendingDrag = false;
    }

    void startPendingDrag() {
        mPendingDrag = true;
        mList.postDelayed(mDeferStartDrag, PENDING_DRAG_DELAY);
    }

    void beginDrag() {
        setState(STATE_DRAGGING);
        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) {
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                if (mState > STATE_NONE && isPointInside(ev.getX(), ev.getY())) {
                    if (!mList.isInScrollingContainer()) {
                        beginDrag();
                        return true;
                        return true;
                    }
                    }
                    mInitialTouchY = ev.getY();
                    startPendingDrag();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                cancelPendingDrag();
                break;
        }
        }
        return false;
        return false;
    }
    }
@@ -666,19 +729,32 @@ class FastScroller {


        if (action == MotionEvent.ACTION_DOWN) {
        if (action == MotionEvent.ACTION_DOWN) {
            if (isPointInside(me.getX(), me.getY())) {
            if (isPointInside(me.getX(), me.getY())) {
                setState(STATE_DRAGGING);
                if (!mList.isInScrollingContainer()) {
                if (mListAdapter == null && mList != null) {
                    beginDrag();
                    getSectionsFromIndexer();
                    return true;
                }
                }
                if (mList != null) {
                mInitialTouchY = me.getY();
                    mList.requestDisallowInterceptTouchEvent(true);
                startPendingDrag();
                    mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
            }
            }
        } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here
            if (mPendingDrag) {
                // Allow a tap to scroll.
                beginDrag();


                cancelFling();
                final int viewHeight = mList.getHeight();
                return true;
                // 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));

                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 (mState == STATE_DRAGGING) {
                if (mList != null) {
                if (mList != null) {
                    // ViewGroup does the right thing already, but there might
                    // ViewGroup does the right thing already, but there might
@@ -698,6 +774,23 @@ class FastScroller {
                return true;
                return true;
            }
            }
        } else if (action == MotionEvent.ACTION_MOVE) {
        } 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) {
            if (mState == STATE_DRAGGING) {
                final int viewHeight = mList.getHeight();
                final int viewHeight = mList.getHeight();
                // Jitter
                // Jitter
@@ -717,6 +810,8 @@ class FastScroller {
                }
                }
                return true;
                return true;
            }
            }
        } else if (action == MotionEvent.ACTION_CANCEL) {
            cancelPendingDrag();
        }
        }
        return false;
        return false;
    }
    }