Commit a5d12418 authored by Ryan Landay's avatar Ryan Landay Committed by Commit Bot

Update scrolling logic in Android horizontal tab switcher

The Android horizontal tab switcher currently feels very "slippery." This is
largely because we currently have a bug in the logic that gives flings a "boost"
to the next tab where, if you start dragging a tab over so that it's already the
centered tab, we apply the boost anyway, so you actually end up scrolling by two
tabs. The minimum scroll distance to move over by one tab is also currently
fairly large, which makes it easier to run into the bugging fling boost
behavior.

This CL fixes these two issues. I'm also introducing some non-linearity into the
scroll distance function. After this CL, the velocity range to scroll by one tab
is fairly large, then the velocity range to scroll by two tabs is somewhat
smaller, and then the ranges to scroll by 3 through 11 tabs are fairly small,
and then the range to scroll by 12 tabs is fairly large again.

Bug: 849417
Change-Id: I9f3d442a191091a126410526c9097c445eb9fe40
Reviewed-on: https://chromium-review.googlesource.com/1109212
Commit-Queue: Ryan Landay <rlanday@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570102}
parent b1897d2d
...@@ -91,7 +91,21 @@ public class NonOverlappingStack extends Stack { ...@@ -91,7 +91,21 @@ public class NonOverlappingStack extends Stack {
* Multiplier for adjusting the scrolling friction from the amount provided by * Multiplier for adjusting the scrolling friction from the amount provided by
* ViewConfiguration. * ViewConfiguration.
*/ */
private static final float FRICTION_MULTIPLIER = 0.2f; private static final float FRICTION_MULTIPLIER = 0.6f;
/**
* For short scrolls of duration less than this (in milliseconds), we assume the user wants to
* scroll over to the next tab. If the scroll is longer in duration, we assume they're
* reconsidering their scroll, so we leave them on the current tab (unless they drag over far
* enough to center a new tab).
*/
private static final int SCROLL_BOOST_TIMEOUT_MS = 250;
/**
* The minimum fraction of a tab the user has to scroll over by before we apply the boost to
* scroll them to the next tab.
*/
private static final float SCROLL_BOOST_THRESHOLD = 0.05f;
/** /**
* Used to prevent mScrollOffset from being changed as a result of clamping during the switch * Used to prevent mScrollOffset from being changed as a result of clamping during the switch
...@@ -106,6 +120,11 @@ public class NonOverlappingStack extends Stack { ...@@ -106,6 +120,11 @@ public class NonOverlappingStack extends Stack {
*/ */
private boolean mSwitchedAway; private boolean mSwitchedAway;
/** Time at which the last touch down event occurred. */
private long mLastTouchDownTime;
/** Index of the tab that was centered when the last touch down event occurred. */
private int mCenteredTabAtTouchDown;
/** /**
* @param layout The parent layout. * @param layout The parent layout.
*/ */
...@@ -179,6 +198,14 @@ public class NonOverlappingStack extends Stack { ...@@ -179,6 +198,14 @@ public class NonOverlappingStack extends Stack {
updateScrollSnap(); updateScrollSnap();
} }
@Override
public void onDown(long time) {
super.onDown(time);
mLastTouchDownTime = time;
mCenteredTabAtTouchDown = getCenteredTabIndex();
mScroller.setCenteredYSnapIndexAtTouchDown(mCenteredTabAtTouchDown);
}
@Override @Override
public void onLongPress(long time, float x, float y) { public void onLongPress(long time, float x, float y) {
// Ignore long presses // Ignore long presses
...@@ -191,13 +218,26 @@ public class NonOverlappingStack extends Stack { ...@@ -191,13 +218,26 @@ public class NonOverlappingStack extends Stack {
@Override @Override
protected void springBack(long time) { protected void springBack(long time) {
if (mScroller.isFinished()) { if (!mScroller.isFinished()) return;
int newTarget = Math.round(mScrollTarget / mSpacing) * mSpacing;
mScroller.springBack(0, (int) mScrollTarget, 0, 0, newTarget, newTarget, time); int offsetAtTouchDown = -mCenteredTabAtTouchDown * mSpacing;
float scrollFractionToNextTab = (offsetAtTouchDown - mScrollOffset) / mSpacing;
int newCenteredTab;
// Make quick, short scrolls go over to the next tab (if a scroll is short but not quick, we
// assume the user might have decided to stay on the current tab).
if (time < mLastTouchDownTime + SCROLL_BOOST_TIMEOUT_MS
&& Math.abs(scrollFractionToNextTab) > SCROLL_BOOST_THRESHOLD) {
newCenteredTab = mCenteredTabAtTouchDown + (int) Math.signum(scrollFractionToNextTab);
} else {
newCenteredTab = getCenteredTabIndex();
}
int newTarget = -newCenteredTab * mSpacing;
mScroller.flingYTo((int) mScrollTarget, newTarget, time);
setScrollTarget(newTarget, false); setScrollTarget(newTarget, false);
mLayout.requestUpdate(); mLayout.requestUpdate();
} }
}
@Override @Override
protected float getSpacingScreen() { protected float getSpacingScreen() {
...@@ -273,6 +313,11 @@ public class NonOverlappingStack extends Stack { ...@@ -273,6 +313,11 @@ public class NonOverlappingStack extends Stack {
return 0; return 0;
} }
@Override
protected boolean allowOverscroll() {
return false;
}
@Override @Override
protected int computeSpacing(int layoutTabCount) { protected int computeSpacing(int layoutTabCount) {
return (int) Math.round( return (int) Math.round(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment