am 9d32d24d: Remove overscrolling

Merge commit '9d32d24dbd8a015c9d5c44ed4901d5a666eb8e7f' into froyo-plus-aosp

* commit '9d32d24dbd8a015c9d5c44ed4901d5a666eb8e7f':
  Remove overscrolling
This commit is contained in:
Adam Powell
2010-03-29 21:02:47 -07:00
committed by Android Git Automerger
17 changed files with 362 additions and 2486 deletions

View File

@@ -5960,39 +5960,6 @@
visibility="public"
>
</field>
<field name="overscrollFooter"
type="int"
transient="false"
volatile="false"
value="16843456"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="overscrollHeader"
type="int"
transient="false"
volatile="false"
value="16843455"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="overscrollMode"
type="int"
transient="false"
volatile="false"
value="16843450"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="padding"
type="int"
transient="false"
@@ -6800,7 +6767,7 @@
type="int"
transient="false"
volatile="false"
value="16843451"
value="16843450"
static="true"
final="true"
deprecated="not deprecated"
@@ -7911,7 +7878,7 @@
type="int"
transient="false"
volatile="false"
value="16843454"
value="16843453"
static="true"
final="true"
deprecated="not deprecated"
@@ -7922,7 +7889,7 @@
type="int"
transient="false"
volatile="false"
value="16843452"
value="16843451"
static="true"
final="true"
deprecated="not deprecated"
@@ -7933,7 +7900,7 @@
type="int"
transient="false"
volatile="false"
value="16843453"
value="16843452"
static="true"
final="true"
deprecated="not deprecated"
@@ -175488,17 +175455,6 @@
visibility="public"
>
</method>
<method name="getOverscrollMode"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getPaddingBottom"
return="int"
abstract="false"
@@ -176639,25 +176595,6 @@
<parameter name="heightMeasureSpec" type="int">
</parameter>
</method>
<method name="onOverscrolled"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="protected"
>
<parameter name="scrollX" type="int">
</parameter>
<parameter name="scrollY" type="int">
</parameter>
<parameter name="clampedX" type="boolean">
</parameter>
<parameter name="clampedY" type="boolean">
</parameter>
</method>
<method name="onRestoreInstanceState"
return="void"
abstract="false"
@@ -176811,35 +176748,6 @@
<parameter name="visibility" type="int">
</parameter>
</method>
<method name="overscrollBy"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="protected"
>
<parameter name="deltaX" type="int">
</parameter>
<parameter name="deltaY" type="int">
</parameter>
<parameter name="scrollX" type="int">
</parameter>
<parameter name="scrollY" type="int">
</parameter>
<parameter name="scrollRangeX" type="int">
</parameter>
<parameter name="scrollRangeY" type="int">
</parameter>
<parameter name="maxOverscrollX" type="int">
</parameter>
<parameter name="maxOverscrollY" type="int">
</parameter>
<parameter name="isTouchEvent" type="boolean">
</parameter>
</method>
<method name="performClick"
return="boolean"
abstract="false"
@@ -177666,19 +177574,6 @@
<parameter name="l" type="android.view.View.OnTouchListener">
</parameter>
</method>
<method name="setOverscrollMode"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="overscrollMode" type="int">
</parameter>
</method>
<method name="setPadding"
return="void"
abstract="false"
@@ -178301,39 +178196,6 @@
visibility="public"
>
</field>
<field name="OVERSCROLL_ALWAYS"
type="int"
transient="false"
volatile="false"
value="0"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="OVERSCROLL_IF_CONTENT_SCROLLS"
type="int"
transient="false"
volatile="false"
value="1"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="OVERSCROLL_NEVER"
type="int"
transient="false"
volatile="false"
value="2"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
<field name="PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET"
type="int[]"
transient="false"
@@ -193178,17 +193040,6 @@
visibility="public"
>
</method>
<method name="getUseWebViewBackgroundForOverscrollBackground"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getUseWideViewPort"
return="boolean"
abstract="false"
@@ -193781,19 +193632,6 @@
<parameter name="use" type="boolean">
</parameter>
</method>
<method name="setUseWebViewBackgroundForOverscrollBackground"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="view" type="boolean">
</parameter>
</method>
<method name="setUseWideViewPort"
return="void"
abstract="false"
@@ -204404,28 +204242,6 @@
visibility="public"
>
</method>
<method name="getOverscrollFooter"
return="android.graphics.drawable.Drawable"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getOverscrollHeader"
return="android.graphics.drawable.Drawable"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isItemChecked"
return="boolean"
abstract="false"
@@ -204571,32 +204387,6 @@
<parameter name="itemsCanFocus" type="boolean">
</parameter>
</method>
<method name="setOverscrollFooter"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="footer" type="android.graphics.drawable.Drawable">
</parameter>
</method>
<method name="setOverscrollHeader"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="header" type="android.graphics.drawable.Drawable">
</parameter>
</method>
<method name="setSelection"
return="void"
abstract="false"
@@ -205168,331 +204958,6 @@
</parameter>
</method>
</interface>
<class name="OverScroller"
extends="java.lang.Object"
abstract="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<constructor name="OverScroller"
type="android.widget.OverScroller"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
<constructor name="OverScroller"
type="android.widget.OverScroller"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
<parameter name="interpolator" type="android.view.animation.Interpolator">
</parameter>
</constructor>
<constructor name="OverScroller"
type="android.widget.OverScroller"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
</parameter>
<parameter name="interpolator" type="android.view.animation.Interpolator">
</parameter>
<parameter name="bounceCoefficientX" type="float">
</parameter>
<parameter name="bounceCoefficientY" type="float">
</parameter>
</constructor>
<method name="abortAnimation"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="computeScrollOffset"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="fling"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="startY" type="int">
</parameter>
<parameter name="velocityX" type="int">
</parameter>
<parameter name="velocityY" type="int">
</parameter>
<parameter name="minX" type="int">
</parameter>
<parameter name="maxX" type="int">
</parameter>
<parameter name="minY" type="int">
</parameter>
<parameter name="maxY" type="int">
</parameter>
</method>
<method name="fling"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="startY" type="int">
</parameter>
<parameter name="velocityX" type="int">
</parameter>
<parameter name="velocityY" type="int">
</parameter>
<parameter name="minX" type="int">
</parameter>
<parameter name="maxX" type="int">
</parameter>
<parameter name="minY" type="int">
</parameter>
<parameter name="maxY" type="int">
</parameter>
<parameter name="overX" type="int">
</parameter>
<parameter name="overY" type="int">
</parameter>
</method>
<method name="forceFinished"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
<parameter name="finished" type="boolean">
</parameter>
</method>
<method name="getCurrX"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getCurrY"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getFinalX"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getFinalY"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getStartX"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getStartY"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isFinished"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isOverscrolled"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="notifyHorizontalEdgeReached"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="finalX" type="int">
</parameter>
<parameter name="overX" type="int">
</parameter>
</method>
<method name="notifyVerticalEdgeReached"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startY" type="int">
</parameter>
<parameter name="finalY" type="int">
</parameter>
<parameter name="overY" type="int">
</parameter>
</method>
<method name="springback"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="startY" type="int">
</parameter>
<parameter name="minX" type="int">
</parameter>
<parameter name="maxX" type="int">
</parameter>
<parameter name="minY" type="int">
</parameter>
<parameter name="maxY" type="int">
</parameter>
</method>
<method name="startScroll"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="startY" type="int">
</parameter>
<parameter name="dx" type="int">
</parameter>
<parameter name="dy" type="int">
</parameter>
</method>
<method name="startScroll"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="startX" type="int">
</parameter>
<parameter name="startY" type="int">
</parameter>
<parameter name="dx" type="int">
</parameter>
<parameter name="dy" type="int">
</parameter>
<parameter name="duration" type="int">
</parameter>
</method>
</class>
<class name="PopupWindow"
extends="java.lang.Object"
abstract="false"

View File

@@ -1512,40 +1512,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @hide
*/
static final int CANCEL_NEXT_UP_EVENT = 0x04000000;
/**
* Always allow a user to overscroll this view, provided it is a
* view that can scroll.
*
* @see #getOverscrollMode()
* @see #setOverscrollMode(int)
*/
public static final int OVERSCROLL_ALWAYS = 0;
/**
* Allow a user to overscroll this view only if the content is large
* enough to meaningfully scroll, provided it is a view that can scroll.
*
* @see #getOverscrollMode()
* @see #setOverscrollMode(int)
*/
public static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
/**
* Never allow a user to overscroll this view.
*
* @see #getOverscrollMode()
* @see #setOverscrollMode(int)
*/
public static final int OVERSCROLL_NEVER = 2;
/**
* Controls the overscroll mode for this view.
* See {@link #overscrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVERSCROLL_ALWAYS}, {@link #OVERSCROLL_IF_CONTENT_SCROLLS},
* and {@link #OVERSCROLL_NEVER}.
*/
private int mOverscrollMode = OVERSCROLL_ALWAYS;
/**
* The parent this view is attached to.
@@ -2103,9 +2069,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
});
}
break;
case R.styleable.View_overscrollMode:
mOverscrollMode = a.getInt(attr, OVERSCROLL_ALWAYS);
break;
}
}
@@ -8667,161 +8630,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
LayoutInflater factory = LayoutInflater.from(context);
return factory.inflate(resource, root);
}
/**
* Scroll the view with standard behavior for scrolling beyond the normal
* content boundaries. Views that call this method should override
* {@link #onOverscrolled(int, int, boolean, boolean)} to respond to the
* results of an overscroll operation.
*
* Views can use this method to handle any touch or fling-based scrolling.
*
* @param deltaX Change in X in pixels
* @param deltaY Change in Y in pixels
* @param scrollX Current X scroll value in pixels before applying deltaX
* @param scrollY Current Y scroll value in pixels before applying deltaY
* @param scrollRangeX Maximum content scroll range along the X axis
* @param scrollRangeY Maximum content scroll range along the Y axis
* @param maxOverscrollX Number of pixels to overscroll by in either direction
* along the X axis.
* @param maxOverscrollY Number of pixels to overscroll by in either direction
* along the Y axis.
* @param isTouchEvent true if this scroll operation is the result of a touch event.
* @return true if scrolling was clamped to an overscroll boundary along either
* axis, false otherwise.
*/
protected boolean overscrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverscrollX, int maxOverscrollY,
boolean isTouchEvent) {
final int overscrollMode = mOverscrollMode;
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
final boolean canScrollVertical =
computeVerticalScrollRange() > computeVerticalScrollExtent();
final boolean overscrollHorizontal = overscrollMode == OVERSCROLL_ALWAYS ||
(overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
final boolean overscrollVertical = overscrollMode == OVERSCROLL_ALWAYS ||
(overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
int newScrollX = scrollX + deltaX;
if (overscrollHorizontal) {
// Scale the scroll amount if we're in the dropoff zone
final int dropoffX = maxOverscrollX / 2;
final int dropoffLeft = -dropoffX;
final int dropoffRight = dropoffX + scrollRangeX;
if ((scrollX < dropoffLeft && deltaX < 0) ||
(scrollX > dropoffRight && deltaX > 0)) {
newScrollX = scrollX + deltaX / 2;
} else {
if (newScrollX > dropoffRight && deltaX > 0) {
int extra = newScrollX - dropoffRight;
newScrollX = dropoffRight + extra / 2;
} else if (newScrollX < dropoffLeft && deltaX < 0) {
int extra = newScrollX - dropoffLeft;
newScrollX = dropoffLeft + extra / 2;
}
}
} else {
maxOverscrollX = 0;
}
int newScrollY = scrollY + deltaY;
if (overscrollVertical) {
final int dropoffY = maxOverscrollY / 2;
final int dropoffTop = -dropoffY;
final int dropoffBottom = dropoffY + scrollRangeY;
if ((scrollY < dropoffTop && deltaY < 0) ||
(scrollY > dropoffBottom && deltaY > 0)) {
newScrollY = scrollY + deltaY / 2;
} else {
if (newScrollY > dropoffBottom && deltaY > 0) {
int extra = newScrollY - dropoffBottom;
newScrollY = dropoffBottom + extra / 2;
} else if (newScrollY < dropoffTop && deltaY < 0) {
int extra = newScrollY - dropoffTop;
newScrollY = dropoffTop + extra / 2;
}
}
} else {
maxOverscrollY = 0;
}
// Clamp values if at the limits and record
final int left = -maxOverscrollX;
final int right = maxOverscrollX + scrollRangeX;
final int top = -maxOverscrollY;
final int bottom = maxOverscrollY + scrollRangeY;
boolean clampedX = false;
if (newScrollX > right) {
newScrollX = right;
clampedX = true;
} else if (newScrollX < left) {
newScrollX = left;
clampedX = true;
}
boolean clampedY = false;
if (newScrollY > bottom) {
newScrollY = bottom;
clampedY = true;
} else if (newScrollY < top) {
newScrollY = top;
clampedY = true;
}
onOverscrolled(newScrollX, newScrollY, clampedX, clampedY);
return clampedX || clampedY;
}
/**
* Called by {@link #overscrollBy(int, int, int, int, int, int, int, int, boolean)} to
* respond to the results of an overscroll operation.
*
* @param scrollX New X scroll value in pixels
* @param scrollY New Y scroll value in pixels
* @param clampedX True if scrollX was clamped to an overscroll boundary
* @param clampedY True if scrollY was clamped to an overscroll boundary
*/
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Intentionally empty.
}
/**
* Returns the overscroll mode for this view. The result will be
* one of {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
* (allow overscrolling only if the view content is larger than the container),
* or {@link #OVERSCROLL_NEVER}.
*
* @return This view's overscroll mode.
*/
public int getOverscrollMode() {
return mOverscrollMode;
}
/**
* Set the overscroll mode for this view. Valid overscroll modes are
* {@link #OVERSCROLL_ALWAYS} (default), {@link #OVERSCROLL_IF_CONTENT_SCROLLS}
* (allow overscrolling only if the view content is larger than the container),
* or {@link #OVERSCROLL_NEVER}.
*
* Setting the overscroll mode of a view will have an effect only if the
* view is capable of scrolling.
*
* @param overscrollMode The new overscroll mode for this view.
*/
public void setOverscrollMode(int overscrollMode) {
if (overscrollMode != OVERSCROLL_ALWAYS &&
overscrollMode != OVERSCROLL_IF_CONTENT_SCROLLS &&
overscrollMode != OVERSCROLL_NEVER) {
throw new IllegalArgumentException("Invalid overscroll mode " + overscrollMode);
}
mOverscrollMode = overscrollMode;
}
/**
* A MeasureSpec encapsulates the layout requirements passed from parent to child.

View File

@@ -207,7 +207,6 @@ public class WebSettings {
private boolean mBuiltInZoomControls = false;
private boolean mAllowFileAccess = true;
private boolean mLoadWithOverviewMode = false;
private boolean mUseWebViewBackgroundOverscrollBackground = true;
// private WebSettings, not accessible by the host activity
static private int mDoubleTapToastCount = 3;
@@ -485,23 +484,6 @@ public class WebSettings {
return mLoadWithOverviewMode;
}
/**
* Set whether the WebView uses its background for over scroll background.
* If true, it will use the WebView's background. If false, it will use an
* internal pattern. Default is true.
*/
public void setUseWebViewBackgroundForOverscrollBackground(boolean view) {
mUseWebViewBackgroundOverscrollBackground = view;
}
/**
* Returns true if this WebView uses WebView's background instead of
* internal pattern for over scroll background.
*/
public boolean getUseWebViewBackgroundForOverscrollBackground() {
return mUseWebViewBackgroundOverscrollBackground;
}
/**
* Store whether the WebView is saving form data.
*/

View File

@@ -37,8 +37,8 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -77,7 +77,7 @@ import android.widget.CheckedTextView;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.OverScroller;
import android.widget.Scroller;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
import android.widget.ZoomControls;
@@ -90,8 +90,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -469,10 +469,7 @@ public class WebView extends AbsoluteLayout
// time for the longest scroll animation
private static final int MAX_DURATION = 750; // milliseconds
private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
private OverScroller mScroller;
private boolean mInOverScrollMode = false;
private static Paint mOverScrollBackground;
private static Paint mOverScrollBorder;
private Scroller mScroller;
private boolean mWrapContent;
private static final int MOTIONLESS_FALSE = 0;
@@ -848,7 +845,7 @@ public class WebView extends AbsoluteLayout
mViewManager = new ViewManager(this);
mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
mDatabase = WebViewDatabase.getInstance(context);
mScroller = new OverScroller(context);
mScroller = new Scroller(context);
updateMultiTouchSupport(context);
}
@@ -1051,8 +1048,7 @@ public class WebView extends AbsoluteLayout
* Return the amount of the titlebarview (if any) that is visible
*/
private int getVisibleTitleHeight() {
// need to restrict mScrollY due to over scroll
return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
return Math.max(getTitleHeight() - mScrollY, 0);
}
/*
@@ -1662,7 +1658,7 @@ public class WebView extends AbsoluteLayout
}
nativeClearCursor(); // start next trackball movement from page edge
if (bottom) {
return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
}
// Page down.
int h = getHeight();
@@ -1893,15 +1889,13 @@ public class WebView extends AbsoluteLayout
// Expects x in view coordinates
private int pinLocX(int x) {
if (mInOverScrollMode) return x;
return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
}
// Expects y in view coordinates
private int pinLocY(int y) {
if (mInOverScrollMode) return y;
return pinLoc(y, getViewHeightWithTitle(),
computeRealVerticalScrollRange() + getTitleHeight());
computeVerticalScrollRange() + getTitleHeight());
}
/**
@@ -2189,7 +2183,7 @@ public class WebView extends AbsoluteLayout
// Sets r to be our visible rectangle in content coordinates
private void calcOurContentVisibleRect(Rect r) {
calcOurVisibleRect(r);
// since we might overscroll, pin the rect to the bounds of the content
// pin the rect to the bounds of the content
r.left = Math.max(viewToContentX(r.left), 0);
// viewToContentY will remove the total height of the title bar. Add
// the visible height back in to account for the fact that if the title
@@ -2252,7 +2246,8 @@ public class WebView extends AbsoluteLayout
return false;
}
private int computeRealHorizontalScrollRange() {
@Override
protected int computeHorizontalScrollRange() {
if (mDrawHistory) {
return mHistoryWidth;
} else {
@@ -2262,27 +2257,7 @@ public class WebView extends AbsoluteLayout
}
@Override
protected int computeHorizontalScrollRange() {
int range = computeRealHorizontalScrollRange();
// Adjust reported range if overscrolled to compress the scroll bars
final int scrollX = mScrollX;
final int overscrollRight = computeMaxScrollX();
if (scrollX < 0) {
range -= scrollX;
} else if (scrollX > overscrollRight) {
range += scrollX - overscrollRight;
}
return range;
}
@Override
protected int computeHorizontalScrollOffset() {
return Math.max(mScrollX, 0);
}
private int computeRealVerticalScrollRange() {
protected int computeVerticalScrollRange() {
if (mDrawHistory) {
return mHistoryHeight;
} else {
@@ -2291,22 +2266,6 @@ public class WebView extends AbsoluteLayout
}
}
@Override
protected int computeVerticalScrollRange() {
int range = computeRealVerticalScrollRange();
// Adjust reported range if overscrolled to compress the scroll bars
final int scrollY = mScrollY;
final int overscrollBottom = computeMaxScrollY();
if (scrollY < 0) {
range -= scrollY;
} else if (scrollY > overscrollBottom) {
range += scrollY - overscrollBottom;
}
return range;
}
@Override
protected int computeVerticalScrollOffset() {
return Math.max(mScrollY - getTitleHeight(), 0);
@@ -2326,23 +2285,6 @@ public class WebView extends AbsoluteLayout
scrollBar.draw(canvas);
}
@Override
protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
boolean clampedY) {
mInOverScrollMode = false;
int maxX = computeMaxScrollX();
if (maxX == 0) {
// do not over scroll x if the page just fits the screen
scrollX = pinLocX(scrollX);
} else if (scrollX < 0 || scrollX > maxX) {
mInOverScrollMode = true;
}
if (scrollY < 0 || scrollY > computeMaxScrollY()) {
mInOverScrollMode = true;
}
super.scrollTo(scrollX, scrollY);
}
/**
* Get the url for the current page. This is not always the same as the url
* passed to WebViewClient.onPageStarted because although the load for
@@ -2688,14 +2630,13 @@ public class WebView extends AbsoluteLayout
if (mScroller.computeScrollOffset()) {
int oldX = mScrollX;
int oldY = mScrollY;
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
mScrollX = mScroller.getCurrX();
mScrollY = mScroller.getCurrY();
postInvalidate(); // So we draw again
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY,
computeMaxScrollX(), computeMaxScrollY(),
getViewWidth() / 3, getViewHeight() / 3, false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (oldX != mScrollX || oldY != mScrollY) {
// As onScrollChanged() is not called, sendOurVisibleRect()
// needs to be called explicitly.
sendOurVisibleRect();
}
} else {
super.computeScroll();
@@ -3106,13 +3047,8 @@ public class WebView extends AbsoluteLayout
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child == mTitleBar) {
// When drawing the title bar, move it horizontally to always show
// at the top of the WebView. While overscroll, stick the title bar
// on the top otherwise we may have two during loading, one is drawn
// here, another is drawn by the Browser.
// at the top of the WebView.
mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
if (mScrollY <= 0) {
mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
}
}
return super.drawChild(canvas, child, drawingTime);
}
@@ -3148,36 +3084,6 @@ public class WebView extends AbsoluteLayout
}
int saveCount = canvas.save();
if (mInOverScrollMode && !getSettings()
.getUseWebViewBackgroundForOverscrollBackground()) {
if (mOverScrollBackground == null) {
mOverScrollBackground = new Paint();
Bitmap bm = BitmapFactory.decodeResource(
mContext.getResources(),
com.android.internal.R.drawable.status_bar_background);
mOverScrollBackground.setShader(new BitmapShader(bm,
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
mOverScrollBorder = new Paint();
mOverScrollBorder.setStyle(Paint.Style.STROKE);
mOverScrollBorder.setStrokeWidth(0);
mOverScrollBorder.setColor(0xffbbbbbb);
}
int top = getTitleHeight();
int right = computeRealHorizontalScrollRange();
int bottom = top + computeRealVerticalScrollRange();
// first draw the background and anchor to the top of the view
canvas.save();
canvas.translate(mScrollX, mScrollY);
canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
- mScrollY, Region.Op.DIFFERENCE);
canvas.drawPaint(mOverScrollBackground);
canvas.restore();
// then draw the border
canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
// next clip the region for the content
canvas.clipRect(0, top, right, bottom);
}
if (mTitleBar != null) {
canvas.translate(0, (int) mTitleBar.getHeight());
}
@@ -3195,12 +3101,12 @@ public class WebView extends AbsoluteLayout
canvas.restoreToCount(saveCount);
// Now draw the shadow.
int titleH = getVisibleTitleHeight();
if (mTitleBar != null && titleH == 0) {
if (mTitleBar != null) {
int y = mScrollY + getVisibleTitleHeight();
int height = (int) (5f * getContext().getResources()
.getDisplayMetrics().density);
mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
mScrollY + height);
mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
y + height);
mTitleShadow.draw(canvas);
}
if (AUTO_REDRAW_HACK && mAutoRedraw) {
@@ -4232,14 +4138,12 @@ public class WebView extends AbsoluteLayout
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (!mInOverScrollMode) {
sendOurVisibleRect();
// update WebKit if visible title bar height changed. The logic is same
// as getVisibleTitleHeight.
int titleHeight = getTitleHeight();
if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
sendViewSizeZoom();
}
sendOurVisibleRect();
// update WebKit if visible title bar height changed. The logic is same
// as getVisibleTitleHeight.
int titleHeight = getTitleHeight();
if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
sendViewSizeZoom();
}
}
@@ -4309,7 +4213,7 @@ public class WebView extends AbsoluteLayout
public DragTrackerHandler(float x, float y, DragTracker proxy) {
mProxy = proxy;
int docBottom = computeRealVerticalScrollRange() + getTitleHeight();
int docBottom = computeVerticalScrollRange() + getTitleHeight();
int viewTop = getScrollY();
int viewBottom = viewTop + getHeight();
@@ -4322,7 +4226,7 @@ public class WebView extends AbsoluteLayout
" up/down= " + mMinDY + " " + mMaxDY);
}
int docRight = computeRealHorizontalScrollRange();
int docRight = computeHorizontalScrollRange();
int viewLeft = getScrollX();
int viewRight = viewLeft + getWidth();
mStartX = x;
@@ -4826,6 +4730,18 @@ public class WebView extends AbsoluteLayout
}
// do pan
int newScrollX = pinLocX(mScrollX + deltaX);
int newDeltaX = newScrollX - mScrollX;
if (deltaX != newDeltaX) {
deltaX = newDeltaX;
fDeltaX = (float) newDeltaX;
}
int newScrollY = pinLocY(mScrollY + deltaY);
int newDeltaY = newScrollY - mScrollY;
if (deltaY != newDeltaY) {
deltaY = newDeltaY;
fDeltaY = (float) newDeltaY;
}
boolean done = false;
boolean keepScrollBarsVisible = false;
if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
@@ -4985,12 +4901,6 @@ public class WebView extends AbsoluteLayout
mVelocityTracker.addMovement(ev);
doFling();
break;
} else {
if (mScroller.springback(mScrollX, mScrollY, 0,
computeMaxScrollX(), 0,
computeMaxScrollY())) {
invalidate();
}
}
mLastVelocity = 0;
WebViewCore.resumePriority();
@@ -5001,8 +4911,6 @@ public class WebView extends AbsoluteLayout
}
case MotionEvent.ACTION_CANCEL: {
if (mTouchMode == TOUCH_DRAG_MODE) {
mScroller.springback(mScrollX, mScrollY, 0,
computeMaxScrollX(), 0, computeMaxScrollY());
invalidate();
}
cancelWebCoreTouchEvent(contentX, contentY, false);
@@ -5062,9 +4970,7 @@ public class WebView extends AbsoluteLayout
private void doDrag(int deltaX, int deltaY) {
if ((deltaX | deltaY) != 0) {
overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
computeMaxScrollX(), computeMaxScrollY(),
getViewWidth() / 3, getViewHeight() / 3, true);
scrollBy(deltaX, deltaY);
}
if (!getSettings().getBuiltInZoomControls()) {
boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
@@ -5401,17 +5307,17 @@ public class WebView extends AbsoluteLayout
}
private int computeMaxScrollX() {
return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
}
private int computeMaxScrollY() {
return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
return Math.max(computeVerticalScrollRange() + getTitleHeight()
- getViewHeightWithTitle(), 0);
}
public void flingScroll(int vx, int vy) {
mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
computeMaxScrollY());
invalidate();
}
@@ -5440,10 +5346,6 @@ public class WebView extends AbsoluteLayout
}
if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
WebViewCore.resumePriority();
if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
0, computeMaxScrollY())) {
invalidate();
}
return;
}
float currentVelocity = mScroller.getCurrVelocity();
@@ -5471,9 +5373,7 @@ public class WebView extends AbsoluteLayout
mLastVelY = vy;
mLastVelocity = (float) Math.hypot(vx, vy);
// no horizontal overscroll if the content just fits
mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3);
mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
// TODO: duration is calculated based on velocity, if the range is
// small, the animation will stop before duration is up. We may
// want to calculate how long the animation is going to run to precisely
@@ -6523,10 +6423,6 @@ public class WebView extends AbsoluteLayout
case MotionEvent.ACTION_CANCEL:
if (mDeferTouchMode == TOUCH_DRAG_MODE) {
// no fling in defer process
mScroller.springback(mScrollX, mScrollY, 0,
computeMaxScrollX(), 0,
computeMaxScrollY());
invalidate();
WebViewCore.resumePriority();
}
mDeferTouchMode = TOUCH_DONE_MODE;

View File

@@ -127,17 +127,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
*/
static final int TOUCH_MODE_FLING = 4;
/**
* Indicates the touch gesture is an overscroll - a scroll beyond the beginning or end.
*/
static final int TOUCH_MODE_OVERSCROLL = 5;
/**
* Indicates the view is being flung outside of normal content bounds
* and will spring back.
*/
static final int TOUCH_MODE_OVERFLING = 6;
/**
* Regular layout - usually an unsolicited layout from the view system
*/
@@ -379,16 +368,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private ContextMenuInfo mContextMenuInfo = null;
/**
* Maximum distance to overscroll by
*/
int mOverscrollMax;
/**
* Content height divided by this is the overscroll limit.
*/
static final int OVERSCROLL_LIMIT_DIVISOR = 3;
/**
* Used to request a layout when we changed touch mode
*/
@@ -1095,10 +1074,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
int result;
if (mSmoothScrollbarEnabled) {
result = Math.max(mItemCount * 100, 0);
if (mScrollY != 0) {
// Compensate for overscroll
result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100));
}
} else {
result = mItemCount;
}
@@ -1171,8 +1146,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
layoutChildren();
mInLayout = false;
mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
}
/**
@@ -1947,10 +1920,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Check if we have moved far enough that it looks more like a
// scroll than a tap
final int distance = Math.abs(deltaY);
final boolean overscroll = mScrollY != 0;
if (overscroll || distance > mTouchSlop) {
if (distance > mTouchSlop) {
createScrollingCache();
mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
mTouchMode = TOUCH_MODE_SCROLL;
mMotionCorrection = deltaY;
final Handler handler = getHandler();
// Handler should not be null unless the AbsListView is not attached to a
@@ -1986,18 +1958,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// touch mode). Force an initial layout to get rid of the selection.
layoutChildren();
}
} else {
int touchMode = mTouchMode;
if (touchMode == TOUCH_MODE_OVERSCROLL || touchMode == TOUCH_MODE_OVERFLING) {
if (mFlingRunnable != null) {
mFlingRunnable.endFling();
}
if (mScrollY != 0) {
mScrollY = 0;
invalidate();
}
}
}
}
@@ -2028,63 +1988,49 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
switch (mTouchMode) {
case TOUCH_MODE_OVERFLING: {
mFlingRunnable.endFling();
mTouchMode = TOUCH_MODE_OVERSCROLL;
mLastY = (int) ev.getY();
mMotionCorrection = 0;
mActivePointerId = ev.getPointerId(0);
break;
}
default: {
mActivePointerId = ev.getPointerId(0);
final int x = (int) ev.getX();
final int y = (int) ev.getY();
int motionPosition = pointToPosition(x, y);
if (!mDataChanged) {
if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
&& (getAdapter().isEnabled(motionPosition))) {
// User clicked on an actual view (and was not stopping a fling). It might be a
// click or a scroll. Assume it is a click until proven otherwise
mTouchMode = TOUCH_MODE_DOWN;
// FIXME Debounce
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
// If we couldn't find a view to click on, but the down event was touching
// the edge, we will bail out and try again. This allows the edge correcting
// code in ViewRoot to try to find a nearby view to select
return false;
}
if (mTouchMode == TOUCH_MODE_FLING) {
// Stopped a fling. It is a scroll.
createScrollingCache();
mTouchMode = TOUCH_MODE_SCROLL;
mMotionCorrection = 0;
motionPosition = findMotionRow(y);
reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
mActivePointerId = ev.getPointerId(0);
final int x = (int) ev.getX();
final int y = (int) ev.getY();
int motionPosition = pointToPosition(x, y);
if (!mDataChanged) {
if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
&& (getAdapter().isEnabled(motionPosition))) {
// User clicked on an actual view (and was not stopping a fling). It might be a
// click or a scroll. Assume it is a click until proven otherwise
mTouchMode = TOUCH_MODE_DOWN;
// FIXME Debounce
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
// If we couldn't find a view to click on, but the down event was touching
// the edge, we will bail out and try again. This allows the edge correcting
// code in ViewRoot to try to find a nearby view to select
return false;
}
if (mTouchMode == TOUCH_MODE_FLING) {
// Stopped a fling. It is a scroll.
createScrollingCache();
mTouchMode = TOUCH_MODE_SCROLL;
mMotionCorrection = 0;
motionPosition = findMotionRow(y);
reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
}
}
if (motionPosition >= 0) {
// Remember where the motion event started
v = getChildAt(motionPosition - mFirstPosition);
mMotionViewOriginalTop = v.getTop();
}
mMotionX = x;
mMotionY = y;
mMotionPosition = motionPosition;
mLastY = Integer.MIN_VALUE;
break;
}
if (motionPosition >= 0) {
// Remember where the motion event started
v = getChildAt(motionPosition - mFirstPosition);
mMotionViewOriginalTop = v.getTop();
}
mMotionX = x;
mMotionY = y;
mMotionPosition = motionPosition;
mLastY = Integer.MIN_VALUE;
break;
}
@@ -2131,62 +2077,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// supposed to be
final int motionViewRealTop = motionView.getTop();
if (atEdge) {
// Apply overscroll
int overscroll = -incrementalDeltaY -
(motionViewRealTop - motionViewPrevTop);
overscrollBy(0, overscroll, 0, mScrollY, 0, 0,
0, getOverscrollMax(), true);
mTouchMode = TOUCH_MODE_OVERSCROLL;
invalidate();
}
}
mLastY = y;
}
break;
case TOUCH_MODE_OVERSCROLL:
if (y != mLastY) {
deltaY -= mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
final int oldScroll = mScrollY;
final int newScroll = oldScroll - incrementalDeltaY;
if ((oldScroll >= 0 && newScroll <= 0) ||
(oldScroll <= 0 && newScroll >= 0)) {
// Coming back to 'real' list scrolling
incrementalDeltaY = -newScroll;
mScrollY = 0;
// No need to do all this work if we're not going to move anyway
if (incrementalDeltaY != 0) {
trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
}
// Check to see if we are back in
View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
mTouchMode = TOUCH_MODE_SCROLL;
// We did not scroll the full amount. Treat this essentially like the
// start of a new touch scroll
final int motionPosition = findClosestMotionRow(y);
mMotionCorrection = 0;
motionView = getChildAt(motionPosition - mFirstPosition);
mMotionViewOriginalTop = motionView.getTop();
mMotionY = y;
mMotionPosition = motionPosition;
}
} else {
overscrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
0, getOverscrollMax(), true);
invalidate();
}
mLastY = y;
}
break;
}
break;
@@ -2286,23 +2182,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
}
break;
case TOUCH_MODE_OVERSCROLL:
if (mFlingRunnable == null) {
mFlingRunnable = new FlingRunnable();
}
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
if (Math.abs(initialVelocity) > mMinimumVelocity) {
mFlingRunnable.startOverfling(-initialVelocity);
} else {
mFlingRunnable.startSpringback();
}
break;
}
setPressed(false);
@@ -2332,36 +2211,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
case MotionEvent.ACTION_CANCEL: {
switch (mTouchMode) {
case TOUCH_MODE_OVERSCROLL:
if (mFlingRunnable == null) {
mFlingRunnable = new FlingRunnable();
}
mFlingRunnable.startSpringback();
break;
case TOUCH_MODE_OVERFLING:
// Do nothing - let it play out.
break;
default:
mTouchMode = TOUCH_MODE_REST;
setPressed(false);
View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
motionView.setPressed(false);
}
clearScrollingCache();
mTouchMode = TOUCH_MODE_REST;
setPressed(false);
View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
motionView.setPressed(false);
}
clearScrollingCache();
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
final Handler handler = getHandler();
if (handler != null) {
handler.removeCallbacks(mPendingCheckForLongPress);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mActivePointerId = INVALID_POINTER;
@@ -2386,45 +2251,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return true;
}
@Override
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
mScrollY = scrollY;
if (clampedY) {
// Velocity is broken by hitting the limit; don't start a fling off of this.
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
awakenScrollBars();
}
int getOverscrollMax() {
final int childCount = getChildCount();
if (childCount > 0) {
return Math.min(mOverscrollMax,
getChildAt(childCount - 1).getBottom() / OVERSCROLL_LIMIT_DIVISOR);
} else {
return mOverscrollMax;
}
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mFastScroller != null) {
final int scrollY = mScrollY;
if (scrollY != 0) {
// Pin the fast scroll thumb to the top/bottom during overscroll.
int restoreCount = canvas.save();
canvas.translate(0, (float) scrollY);
mFastScroller.draw(canvas);
canvas.restoreToCount(restoreCount);
} else {
mFastScroller.draw(canvas);
}
mFastScroller.draw(canvas);
}
}
@@ -2443,9 +2275,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
int touchMode = mTouchMode;
if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) {
return true;
}
final int x = (int) ev.getX();
final int y = (int) ev.getY();
@@ -2565,7 +2394,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
/**
* Tracks the decay of a fling scroll
*/
private final OverScroller mScroller;
private final Scroller mScroller;
/**
* Y value reported by mScroller on the previous fling
@@ -2573,7 +2402,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private int mLastFlingY;
FlingRunnable() {
mScroller = new OverScroller(getContext());
mScroller = new Scroller(getContext());
}
void start(int initialVelocity) {
@@ -2592,32 +2421,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
void startSpringback() {
if (mScroller.springback(0, mScrollY, 0, 0, 0, 0)) {
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
} else {
mTouchMode = TOUCH_MODE_REST;
}
}
void startOverfling(int initialVelocity) {
final int min = mScrollY > 0 ? Integer.MIN_VALUE : 0;
final int max = mScrollY > 0 ? 0 : Integer.MAX_VALUE;
mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0, min, max, 0, getHeight());
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
}
void edgeReached() {
mScroller.notifyVerticalEdgeReached(mScrollY, 0, getOverscrollMax());
mTouchMode = TOUCH_MODE_OVERFLING;
invalidate();
post(this);
}
void startScroll(int distance, int duration) {
int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
mLastFlingY = initialY;
@@ -2650,7 +2453,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return;
}
final OverScroller scroller = mScroller;
final Scroller scroller = mScroller;
boolean more = scroller.computeScrollOffset();
final int y = scroller.getCurrY();
@@ -2685,16 +2488,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (motionView != null) {
oldTop = motionView.getTop();
}
if (trackMotionScroll(delta, delta)) {
if (motionView != null) {
// Tweak the scroll for how far we overshot
int overshoot = -(delta - (motionView.getTop() - oldTop));
overscrollBy(0, overshoot, 0, mScrollY, 0, 0,
0, getOverscrollMax(), false);
}
edgeReached();
break;
}
trackMotionScroll(delta, delta);
if (more) {
invalidate();
@@ -2712,24 +2507,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
break;
}
case TOUCH_MODE_OVERFLING: {
final OverScroller scroller = mScroller;
if (scroller.computeScrollOffset()) {
final int scrollY = mScrollY;
final int deltaY = scroller.getCurrY() - scrollY;
if (overscrollBy(0, deltaY, 0, scrollY, 0, 0,
0, getOverscrollMax(), false)) {
startSpringback();
} else {
invalidate();
post(this);
}
} else {
endFling();
}
break;
}
}
}

View File

@@ -1873,12 +1873,7 @@ public class GridView extends AbsListView {
// TODO: Account for vertical spacing too
final int numColumns = mNumColumns;
final int rowCount = (mItemCount + numColumns - 1) / numColumns;
int result = Math.max(rowCount * 100, 0);
if (mScrollY != 0) {
// Compensate for overscroll
result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100));
}
return result;
return Math.max(rowCount * 100, 0);
}
}

View File

@@ -16,19 +16,19 @@
package android.widget;
import android.util.AttributeSet;
import android.graphics.Rect;
import android.view.View;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.KeyEvent;
import android.view.FocusFinder;
import android.view.MotionEvent;
import android.view.ViewParent;
import android.view.animation.AnimationUtils;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AnimationUtils;
import java.util.List;
@@ -63,7 +63,7 @@ public class HorizontalScrollView extends FrameLayout {
private long mLastScroll;
private final Rect mTempRect = new Rect();
private OverScroller mScroller;
private Scroller mScroller;
/**
* Flag to indicate that we are moving focus ourselves. This is so the
@@ -189,7 +189,7 @@ public class HorizontalScrollView extends FrameLayout {
private void initScrollView() {
mScroller = new OverScroller(getContext());
mScroller = new Scroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
@@ -456,9 +456,6 @@ public class HorizontalScrollView extends FrameLayout {
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
invalidate();
}
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -516,11 +513,7 @@ public class HorizontalScrollView extends FrameLayout {
final int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
final int oldX = mScrollX;
final int oldY = mScrollY;
overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0,
getOverscrollMax(), 0, true);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
scrollBy(deltaX, 0);
}
break;
case MotionEvent.ACTION_UP:
@@ -529,15 +522,8 @@ public class HorizontalScrollView extends FrameLayout {
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
if (getChildCount() > 0) {
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
fling(-initialVelocity);
} else {
final int right = getScrollRange();
if (mScroller.springback(mScrollX, mScrollY, 0, right, 0, 0)) {
invalidate();
}
}
if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
fling(-initialVelocity);
}
mActivePointerId = INVALID_POINTER;
@@ -551,9 +537,6 @@ public class HorizontalScrollView extends FrameLayout {
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
if (mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0)) {
invalidate();
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker != null) {
@@ -586,32 +569,6 @@ public class HorizontalScrollView extends FrameLayout {
}
}
@Override
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
if (!mScroller.isFinished()) {
mScrollX = scrollX;
mScrollY = scrollY;
if (clampedX) {
mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
}
} else {
super.scrollTo(scrollX, scrollY);
}
awakenScrollBars();
}
private int getOverscrollMax() {
int childCount = getChildCount();
int containerOverscroll = (getWidth() - mPaddingLeft - mPaddingRight) / 3;
if (childCount > 0) {
return Math.min(containerOverscroll, getChildAt(0).getWidth() / 3);
} else {
return containerOverscroll;
}
}
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
@@ -994,16 +951,7 @@ public class HorizontalScrollView extends FrameLayout {
return contentWidth;
}
int scrollRange = getChildAt(0).getRight();
final int scrollX = mScrollX;
final int overscrollRight = Math.max(0, scrollRange - contentWidth);
if (scrollX < 0) {
scrollRange -= scrollX;
} else if (scrollX > overscrollRight) {
scrollRange += scrollX - overscrollRight;
}
return scrollRange;
return getChildAt(0).getRight();
}
@Override
@@ -1064,10 +1012,15 @@ public class HorizontalScrollView extends FrameLayout {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
getOverscrollMax(), 0, false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (getChildCount() > 0) {
View child = getChildAt(0);
x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
if (x != oldX || y != oldY) {
mScrollX = x;
mScrollY = y;
onScrollChanged(x, y, oldX, oldY);
}
}
// Keep on drawing until the animation has finished.
@@ -1302,7 +1255,7 @@ public class HorizontalScrollView extends FrameLayout {
int right = getChildAt(0).getWidth();
mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0,
Math.max(0, right - width), 0, 0, width/2, 0);
Math.max(0, right - width), 0, 0);
final boolean movingRight = velocityX > 0;

View File

@@ -118,9 +118,6 @@ public class ListView extends AbsListView {
Drawable mDivider;
int mDividerHeight;
Drawable mOverscrollHeader;
Drawable mOverscrollFooter;
private boolean mIsCacheColorOpaque;
private boolean mDividerIsOpaque;
private boolean mClipDivider;
@@ -175,16 +172,6 @@ public class ListView extends AbsListView {
setDivider(d);
}
final Drawable osHeader = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollHeader);
if (osHeader != null) {
setOverscrollHeader(osHeader);
}
final Drawable osFooter = a.getDrawable(com.android.internal.R.styleable.ListView_overscrollFooter);
if (osFooter != null) {
setOverscrollFooter(osFooter);
}
// Use the height specified, zero being the default
final int dividerHeight = a.getDimensionPixelSize(
com.android.internal.R.styleable.ListView_dividerHeight, 0);
@@ -2955,52 +2942,14 @@ public class ListView extends AbsListView {
}
super.setCacheColorHint(color);
}
void drawOverscrollHeader(Canvas canvas, Drawable drawable, Rect bounds) {
final int height = drawable.getMinimumHeight();
canvas.save();
canvas.clipRect(bounds);
final int span = bounds.bottom - bounds.top;
if (span < height) {
bounds.top = bounds.bottom - height;
}
drawable.setBounds(bounds);
drawable.draw(canvas);
canvas.restore();
}
void drawOverscrollFooter(Canvas canvas, Drawable drawable, Rect bounds) {
final int height = drawable.getMinimumHeight();
canvas.save();
canvas.clipRect(bounds);
final int span = bounds.bottom - bounds.top;
if (span < height) {
bounds.bottom = bounds.top + height;
}
drawable.setBounds(bounds);
drawable.draw(canvas);
canvas.restore();
}
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the dividers
final int dividerHeight = mDividerHeight;
final Drawable overscrollHeader = mOverscrollHeader;
final Drawable overscrollFooter = mOverscrollFooter;
final boolean drawOverscrollHeader = overscrollHeader != null;
final boolean drawOverscrollFooter = overscrollFooter != null;
final boolean drawDividers = dividerHeight > 0 && mDivider != null;
if (drawDividers || drawOverscrollHeader || drawOverscrollFooter) {
if (drawDividers) {
// Only modify the top and bottom in the loop, we set the left and right here
final Rect bounds = mTempRect;
bounds.left = mPaddingLeft;
@@ -3031,28 +2980,14 @@ public class ListView extends AbsListView {
if (!mStackFromBottom) {
int bottom = 0;
// Draw top divider or header for overscroll
final int scrollY = mScrollY;
if (count > 0 && scrollY < 0) {
if (drawOverscrollHeader) {
bounds.bottom = 0;
bounds.top = scrollY;
drawOverscrollHeader(canvas, overscrollHeader, bounds);
} else if (drawDividers) {
bounds.bottom = 0;
bounds.top = -dividerHeight;
drawDivider(canvas, bounds, -1);
}
}
for (int i = 0; i < count; i++) {
if ((headerDividers || first + i >= headerCount) &&
(footerDividers || first + i < footerLimit)) {
View child = getChildAt(i);
bottom = child.getBottom();
// Don't draw dividers next to items that are not enabled
if (drawDividers &&
(bottom < listBottom && !(drawOverscrollFooter && i == count - 1))) {
if (drawDividers) {
if ((areAllItemsSelectable ||
(adapter.isEnabled(first + i) && (i == count - 1 ||
adapter.isEnabled(first + i + 1))))) {
@@ -3067,28 +3002,13 @@ public class ListView extends AbsListView {
}
}
}
final int overFooterBottom = mBottom + mScrollY;
if (drawOverscrollFooter && first + count == itemCount &&
overFooterBottom > bottom) {
bounds.top = bottom;
bounds.bottom = overFooterBottom;
drawOverscrollFooter(canvas, overscrollFooter, bounds);
}
} else {
int top;
int listTop = mListPadding.top;
final int scrollY = mScrollY;
if (count > 0 && drawOverscrollHeader) {
bounds.top = scrollY;
bounds.bottom = getChildAt(0).getTop();
drawOverscrollHeader(canvas, overscrollHeader, bounds);
}
final int start = drawOverscrollHeader ? 1 : 0;
for (int i = start; i < count; i++) {
for (int i = 0; i < count; i++) {
if ((headerDividers || first + i >= headerCount) &&
(footerDividers || first + i < footerLimit)) {
View child = getChildAt(i);
@@ -3114,17 +3034,10 @@ public class ListView extends AbsListView {
}
}
if (count > 0 && scrollY > 0) {
if (drawOverscrollFooter) {
final int absListBottom = mBottom;
bounds.top = absListBottom;
bounds.bottom = absListBottom + scrollY;
drawOverscrollFooter(canvas, overscrollFooter, bounds);
} else if (drawDividers) {
bounds.top = listBottom;
bounds.bottom = listBottom + dividerHeight;
drawDivider(canvas, bounds, -1);
}
if (count > 0 && scrollY > 0 && drawDividers) {
bounds.top = listBottom;
bounds.bottom = listBottom + dividerHeight;
drawDivider(canvas, bounds, -1);
}
}
}
@@ -3233,45 +3146,6 @@ public class ListView extends AbsListView {
invalidate();
}
/**
* Sets the drawable that will be drawn above all other list content.
* This area can become visible when the user overscrolls the list.
*
* @param header The drawable to use
*/
public void setOverscrollHeader(Drawable header) {
mOverscrollHeader = header;
if (mScrollY < 0) {
invalidate();
}
}
/**
* @return The drawable that will be drawn above all other list content
*/
public Drawable getOverscrollHeader() {
return mOverscrollHeader;
}
/**
* Sets the drawable that will be drawn below all other list content.
* This area can become visible when the user overscrolls the list,
* or when the list's content does not fully fill the container area.
*
* @param footer The drawable to use
*/
public void setOverscrollFooter(Drawable footer) {
mOverscrollFooter = footer;
invalidate();
}
/**
* @return The drawable that will be drawn below all other list content
*/
public Drawable getOverscrollFooter() {
return mOverscrollFooter;
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
@@ -3691,20 +3565,6 @@ public class ListView extends AbsListView {
mCheckedIdStates.clear();
}
}
@Override
int getOverscrollMax() {
if (mStackFromBottom) {
final int childCount = getChildCount();
if (childCount > 0) {
return Math.min(mOverscrollMax,
(getHeight() - getChildAt(0).getTop()) / OVERSCROLL_LIMIT_DIVISOR);
} else {
return mOverscrollMax;
}
}
return super.getOverscrollMax();
}
static class SavedState extends BaseSavedState {
SparseBooleanArray checkState;

View File

@@ -1,682 +0,0 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import android.content.Context;
import android.util.FloatMath;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Scroller.MagneticScroller;
/**
* This class encapsulates scrolling with the ability to overshoot the bounds
* of a scrolling operation. This class is a drop-in replacement for
* {@link android.widget.Scroller} in most cases.
*/
public class OverScroller {
private int mMode;
private MagneticOverScroller mScrollerX;
private MagneticOverScroller mScrollerY;
private final Interpolator mInterpolator;
private static final int DEFAULT_DURATION = 250;
private static final int SCROLL_MODE = 0;
private static final int FLING_MODE = 1;
/**
* Creates an OverScroller with a viscous fluid scroll interpolator.
* @param context
*/
public OverScroller(Context context) {
this(context, null);
}
/**
* Creates an OverScroller with default edge bounce coefficients.
* @param context The context of this application.
* @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
* be used.
*/
public OverScroller(Context context, Interpolator interpolator) {
this(context, interpolator, MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT,
MagneticOverScroller.DEFAULT_BOUNCE_COEFFICIENT);
}
/**
* Creates an OverScroller.
* @param context The context of this application.
* @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
* be used.
* @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
* velocity which is preserved in the bounce when the horizontal edge is reached. A null value
* means no bounce.
* @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction.
*/
public OverScroller(Context context, Interpolator interpolator,
float bounceCoefficientX, float bounceCoefficientY) {
mInterpolator = interpolator;
mScrollerX = new MagneticOverScroller();
mScrollerY = new MagneticOverScroller();
MagneticScroller.initializeFromContext(context);
mScrollerX.setBounceCoefficient(bounceCoefficientX);
mScrollerY.setBounceCoefficient(bounceCoefficientY);
}
/**
*
* Returns whether the scroller has finished scrolling.
*
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mScrollerX.mFinished && mScrollerY.mFinished;
}
/**
* Force the finished field to a particular value. Contrary to
* {@link #abortAnimation()}, forcing the animation to finished
* does NOT cause the scroller to move to the final x and y
* position.
*
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
mScrollerX.mFinished = mScrollerY.mFinished = finished;
}
/**
* Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
return mScrollerX.mCurrentPosition;
}
/**
* Returns the current Y offset in the scroll.
*
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
return mScrollerY.mCurrentPosition;
}
/**
* @hide
* Returns the current velocity.
*
* @return The original velocity less the deceleration, norm of the X and Y velocity vector.
*/
public float getCurrVelocity() {
float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
return FloatMath.sqrt(squaredNorm);
}
/**
* Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final int getStartX() {
return mScrollerX.mStart;
}
/**
* Returns the start Y offset in the scroll.
*
* @return The start Y offset as an absolute distance from the origin.
*/
public final int getStartY() {
return mScrollerY.mStart;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final X offset as an absolute distance from the origin.
*/
public final int getFinalX() {
return mScrollerX.mFinal;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final Y offset as an absolute distance from the origin.
*/
public final int getFinalY() {
return mScrollerY.mFinal;
}
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*
* @hide Pending removal once nothing depends on it
* @deprecated OverScrollers don't necessarily have a fixed duration.
* This function will lie to the best of its ability.
*/
public final int getDuration() {
return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
}
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
*
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*
* @hide Pending removal once nothing depends on it
* @deprecated OverScrollers don't necessarily have a fixed duration.
* Instead of setting a new final position and extending
* the duration of an existing scroll, use startScroll
* to begin a new animation.
*/
public void extendDuration(int extend) {
mScrollerX.extendDuration(extend);
mScrollerY.extendDuration(extend);
}
/**
* Sets the final position (X) for this scroller.
*
* @param newX The new X offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalY(int)
*
* @hide Pending removal once nothing depends on it
* @deprecated OverScroller's final position may change during an animation.
* Instead of setting a new final position and extending
* the duration of an existing scroll, use startScroll
* to begin a new animation.
*/
public void setFinalX(int newX) {
mScrollerX.setFinalPosition(newX);
}
/**
* Sets the final position (Y) for this scroller.
*
* @param newY The new Y offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalX(int)
*
* @hide Pending removal once nothing depends on it
* @deprecated OverScroller's final position may change during an animation.
* Instead of setting a new final position and extending
* the duration of an existing scroll, use startScroll
* to begin a new animation.
*/
public void setFinalY(int newY) {
mScrollerY.setFinalPosition(newY);
}
/**
* Call this when you want to know the new location. If it returns true, the
* animation is not yet finished.
*/
public boolean computeScrollOffset() {
if (isFinished()) {
return false;
}
switch (mMode) {
case SCROLL_MODE:
long time = AnimationUtils.currentAnimationTimeMillis();
// Any scroller can be used for time, since they were started
// together in scroll mode. We use X here.
final long elapsedTime = time - mScrollerX.mStartTime;
final int duration = mScrollerX.mDuration;
if (elapsedTime < duration) {
float q = (float) (elapsedTime) / duration;
if (mInterpolator == null)
q = Scroller.viscousFluid(q);
else
q = mInterpolator.getInterpolation(q);
mScrollerX.updateScroll(q);
mScrollerY.updateScroll(q);
} else {
abortAnimation();
}
break;
case FLING_MODE:
if (!mScrollerX.mFinished) {
if (!mScrollerX.update()) {
if (!mScrollerX.continueWhenFinished()) {
mScrollerX.finish();
}
}
}
if (!mScrollerY.mFinished) {
if (!mScrollerY.update()) {
if (!mScrollerY.continueWhenFinished()) {
mScrollerY.finish();
}
}
}
break;
}
return true;
}
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
* duration.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
*/
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
/**
* Start scrolling by providing a starting point and the distance to travel.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
* @param duration Duration of the scroll in milliseconds.
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mScrollerX.startScroll(startX, dx, duration);
mScrollerY.startScroll(startY, dy, duration);
}
/**
* Call this when you want to 'spring back' into a valid coordinate range.
*
* @param startX Starting X coordinate
* @param startY Starting Y coordinate
* @param minX Minimum valid X value
* @param maxX Maximum valid X value
* @param minY Minimum valid Y value
* @param maxY Minimum valid Y value
* @return true if a springback was initiated, false if startX and startY were
* already within the valid range.
*/
public boolean springback(int startX, int startY, int minX, int maxX, int minY, int maxY) {
mMode = FLING_MODE;
// Make sure both methods are called.
final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
return spingbackX || spingbackY;
}
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
/**
* Start scrolling based on a fling gesture. The distance traveled will
* depend on the initial velocity of the fling.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
* second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
* second
* @param minX Minimum X value. The scroller will not scroll past this point
* unless overX > 0. If overfling is allowed, it will use minX as
* a springback boundary.
* @param maxX Maximum X value. The scroller will not scroll past this point
* unless overX > 0. If overfling is allowed, it will use maxX as
* a springback boundary.
* @param minY Minimum Y value. The scroller will not scroll past this point
* unless overY > 0. If overfling is allowed, it will use minY as
* a springback boundary.
* @param maxY Maximum Y value. The scroller will not scroll past this point
* unless overY > 0. If overfling is allowed, it will use maxY as
* a springback boundary.
* @param overX Overfling range. If > 0, horizontal overfling in either
* direction will be possible.
* @param overY Overfling range. If > 0, vertical overfling in either
* direction will be possible.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY, int overX, int overY) {
mMode = FLING_MODE;
mScrollerX.fling(startX, velocityX, minX, maxX, overX);
mScrollerY.fling(startY, velocityY, minY, maxY, overY);
}
/**
* Notify the scroller that we've reached a horizontal boundary.
* Normally the information to handle this will already be known
* when the animation is started, such as in a call to one of the
* fling functions. However there are cases where this cannot be known
* in advance. This function will transition the current motion and
* animate from startX to finalX as appropriate.
*
* @param startX Starting/current X position
* @param finalX Desired final X position
* @param overX Magnitude of overscroll allowed. This should be the maximum
* desired distance from finalX. Absolute value - must be positive.
*/
public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
mScrollerX.notifyEdgeReached(startX, finalX, overX);
}
/**
* Notify the scroller that we've reached a vertical boundary.
* Normally the information to handle this will already be known
* when the animation is started, such as in a call to one of the
* fling functions. However there are cases where this cannot be known
* in advance. This function will animate a parabolic motion from
* startY to finalY.
*
* @param startY Starting/current Y position
* @param finalY Desired final Y position
* @param overY Magnitude of overscroll allowed. This should be the maximum
* desired distance from finalY.
*/
public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
mScrollerY.notifyEdgeReached(startY, finalY, overY);
}
/**
* Returns whether the current Scroller is currently returning to a valid position.
* Valid bounds were provided by the
* {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
*
* One should check this value before calling
* {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
* to restore a valid position will then be stopped. The caller has to take into account
* the fact that the started scroll will start from an overscrolled position.
*
* @return true when the current position is overscrolled and in the process of
* interpolating back to a valid value.
*/
public boolean isOverscrolled() {
return ((!mScrollerX.mFinished &&
mScrollerX.mState != MagneticOverScroller.TO_EDGE) ||
(!mScrollerY.mFinished &&
mScrollerY.mState != MagneticOverScroller.TO_EDGE));
}
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating causes the scroller to move to the final x and y
* positions.
*
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
mScrollerX.finish();
mScrollerY.finish();
}
/**
* Returns the time elapsed since the beginning of the scrolling.
*
* @return The elapsed time in milliseconds.
*
* @hide
*/
public int timePassed() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
return (int) (time - startTime);
}
static class MagneticOverScroller extends Scroller.MagneticScroller {
private static final int TO_EDGE = 0;
private static final int TO_BOUNDARY = 1;
private static final int TO_BOUNCE = 2;
private int mState = TO_EDGE;
// The allowed overshot distance before boundary is reached.
private int mOver;
// Duration in milliseconds to go back from edge to edge. Springback is half of it.
private static final int OVERSCROLL_SPRINGBACK_DURATION = 200;
// Oscillation period
private static final float TIME_COEF =
1000.0f * (float) Math.PI / OVERSCROLL_SPRINGBACK_DURATION;
// If the velocity is smaller than this value, no bounce is triggered
// when the edge limits are reached (would result in a zero pixels
// displacement anyway).
private static final float MINIMUM_VELOCITY_FOR_BOUNCE = 140.0f;
// Proportion of the velocity that is preserved when the edge is reached.
private static final float DEFAULT_BOUNCE_COEFFICIENT = 0.16f;
private float mBounceCoefficient = DEFAULT_BOUNCE_COEFFICIENT;
void setBounceCoefficient(float coefficient) {
mBounceCoefficient = coefficient;
}
boolean springback(int start, int min, int max) {
mFinished = true;
mStart = start;
mVelocity = 0;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = 0;
if (start < min) {
startSpringback(start, min, false);
} else if (start > max) {
startSpringback(start, max, true);
}
return !mFinished;
}
private void startSpringback(int start, int end, boolean positive) {
mFinished = false;
mState = TO_BOUNCE;
mStart = mFinal = end;
mDuration = OVERSCROLL_SPRINGBACK_DURATION;
mStartTime -= OVERSCROLL_SPRINGBACK_DURATION / 2;
mVelocity = (int) (Math.abs(end - start) * TIME_COEF * (positive ? 1.0 : -1.0f));
}
void fling(int start, int velocity, int min, int max, int over) {
mState = TO_EDGE;
mOver = over;
super.fling(start, velocity, min, max);
if (start > max) {
if (start >= max + over) {
springback(max + over, min, max);
} else {
if (velocity <= 0) {
springback(start, min, max);
} else {
long time = AnimationUtils.currentAnimationTimeMillis();
final double durationSinceEdge =
Math.atan((start-max) * TIME_COEF / velocity) / TIME_COEF;
mStartTime = (int) (time - 1000.0f * durationSinceEdge);
// Simulate a bounce that started from edge
mStart = max;
mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
onEdgeReached();
}
}
} else {
if (start < min) {
if (start <= min - over) {
springback(min - over, min, max);
} else {
if (velocity >= 0) {
springback(start, min, max);
} else {
long time = AnimationUtils.currentAnimationTimeMillis();
final double durationSinceEdge =
Math.atan((start-min) * TIME_COEF / velocity) / TIME_COEF;
mStartTime = (int) (time - 1000.0f * durationSinceEdge);
// Simulate a bounce that started from edge
mStart = min;
mVelocity = (int) (velocity / Math.cos(durationSinceEdge * TIME_COEF));
onEdgeReached();
}
}
}
}
}
void notifyEdgeReached(int start, int end, int over) {
mDeceleration = getDeceleration(mVelocity);
// Local time, used to compute edge crossing time.
float timeCurrent = mCurrVelocity / mDeceleration;
final int distance = end - start;
float timeEdge = -(float) Math.sqrt((2.0f * distance / mDeceleration)
+ (timeCurrent * timeCurrent));
mVelocity = (int) (mDeceleration * timeEdge);
// Simulate a symmetric bounce that started from edge
mStart = end;
mOver = over;
long time = AnimationUtils.currentAnimationTimeMillis();
mStartTime = (int) (time - 1000.0f * (timeCurrent - timeEdge));
onEdgeReached();
}
private void onEdgeReached() {
// mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
final float distance = mVelocity / TIME_COEF;
if (Math.abs(distance) < mOver) {
// Spring force will bring us back to final position
mState = TO_BOUNCE;
mFinal = mStart;
mDuration = OVERSCROLL_SPRINGBACK_DURATION;
} else {
// Velocity is too high, we will hit the boundary limit
mState = TO_BOUNDARY;
int over = mVelocity > 0 ? mOver : -mOver;
mFinal = mStart + over;
mDuration = (int) (1000.0f * Math.asin(over / distance) / TIME_COEF);
}
}
@Override
boolean continueWhenFinished() {
switch (mState) {
case TO_EDGE:
// Duration from start to null velocity
int duration = (int) (-1000.0f * mVelocity / mDeceleration);
if (mDuration < duration) {
// If the animation was clamped, we reached the edge
mStart = mFinal;
// Speed when edge was reached
mVelocity = (int) (mVelocity + mDeceleration * mDuration / 1000.0f);
mStartTime += mDuration;
onEdgeReached();
} else {
// Normal stop, no need to continue
return false;
}
break;
case TO_BOUNDARY:
mStartTime += mDuration;
startSpringback(mFinal, mFinal - (mVelocity > 0 ? mOver:-mOver), mVelocity > 0);
break;
case TO_BOUNCE:
//mVelocity = (int) (mVelocity * BOUNCE_COEFFICIENT);
mVelocity = (int) (mVelocity * mBounceCoefficient);
if (Math.abs(mVelocity) < MINIMUM_VELOCITY_FOR_BOUNCE) {
return false;
}
mStartTime += mDuration;
break;
}
update();
return true;
}
/*
* Update the current position and velocity for current time. Returns
* true if update has been done and false if animation duration has been
* reached.
*/
@Override
boolean update() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final long duration = time - mStartTime;
if (duration > mDuration) {
return false;
}
double distance;
final float t = duration / 1000.0f;
if (mState == TO_EDGE) {
mCurrVelocity = mVelocity + mDeceleration * t;
distance = mVelocity * t + mDeceleration * t * t / 2.0f;
} else {
final float d = t * TIME_COEF;
mCurrVelocity = mVelocity * (float)Math.cos(d);
distance = mVelocity / TIME_COEF * Math.sin(d);
}
mCurrentPosition = mStart + (int) distance;
return true;
}
}
}

View File

@@ -59,7 +59,7 @@ public class ScrollView extends FrameLayout {
private long mLastScroll;
private final Rect mTempRect = new Rect();
private OverScroller mScroller;
private Scroller mScroller;
/**
* Flag to indicate that we are moving focus ourselves. This is so the
@@ -185,7 +185,7 @@ public class ScrollView extends FrameLayout {
private void initScrollView() {
mScroller = new OverScroller(getContext());
mScroller = new Scroller(getContext());
setFocusable(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setWillNotDraw(false);
@@ -453,9 +453,6 @@ public class ScrollView extends FrameLayout {
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
invalidate();
}
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
@@ -513,11 +510,7 @@ public class ScrollView extends FrameLayout {
final int deltaY = (int) (mLastMotionY - y);
mLastMotionY = y;
final int oldX = mScrollX;
final int oldY = mScrollY;
overscrollBy(0, deltaY, 0, mScrollY, 0, getScrollRange(),
0, getOverscrollMax(), true);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
scrollBy(0, deltaY);
}
break;
case MotionEvent.ACTION_UP:
@@ -526,15 +519,8 @@ public class ScrollView extends FrameLayout {
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (getChildCount() > 0) {
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
fling(-initialVelocity);
} else {
final int bottom = getScrollRange();
if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, bottom)) {
invalidate();
}
}
if (getChildCount() > 0 && Math.abs(initialVelocity) > mMinimumVelocity) {
fling(-initialVelocity);
}
mActivePointerId = INVALID_POINTER;
@@ -548,9 +534,6 @@ public class ScrollView extends FrameLayout {
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
if (mScroller.springback(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
invalidate();
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
if (mVelocityTracker != null) {
@@ -583,32 +566,6 @@ public class ScrollView extends FrameLayout {
}
}
@Override
protected void onOverscrolled(int scrollX, int scrollY,
boolean clampedX, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
if (!mScroller.isFinished()) {
mScrollX = scrollX;
mScrollY = scrollY;
if (clampedY) {
mScroller.springback(mScrollX, mScrollY, 0, 0, 0, getScrollRange());
}
} else {
super.scrollTo(scrollX, scrollY);
}
awakenScrollBars();
}
private int getOverscrollMax() {
int childCount = getChildCount();
int containerOverscroll = (getHeight() - mPaddingBottom - mPaddingTop) / 3;
if (childCount > 0) {
return Math.min(containerOverscroll, getChildAt(0).getHeight() / 3);
} else {
return containerOverscroll;
}
}
private int getScrollRange() {
int scrollRange = 0;
if (getChildCount() > 0) {
@@ -995,16 +952,7 @@ public class ScrollView extends FrameLayout {
return contentHeight;
}
int scrollRange = getChildAt(0).getBottom();
final int scrollY = mScrollY;
final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
if (scrollY < 0) {
scrollRange -= scrollY;
} else if (scrollY > overscrollBottom) {
scrollRange += scrollY - overscrollBottom;
}
return scrollRange;
return getChildAt(0).getBottom();
}
@Override
@@ -1065,12 +1013,17 @@ public class ScrollView extends FrameLayout {
int x = mScroller.getCurrX();
int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
overscrollBy(x - oldX, y - oldY, oldX, oldY, 0, getScrollRange(),
0, getOverscrollMax(), false);
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (getChildCount() > 0) {
View child = getChildAt(0);
x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
if (x != oldX || y != oldY) {
mScrollX = x;
mScrollY = y;
onScrollChanged(x, y, oldX, oldY);
}
}
// Keep on drawing until the animation has finished.
postInvalidate();
}
@@ -1304,7 +1257,7 @@ public class ScrollView extends FrameLayout {
int bottom = getChildAt(0).getHeight();
mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0,
Math.max(0, bottom - height), 0, height/2);
Math.max(0, bottom - height));
final boolean movingDown = velocityY > 0;

View File

@@ -16,10 +16,8 @@
package android.widget;
import android.content.Context;
import android.hardware.SensorManager;
import android.util.FloatMath;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -27,34 +25,48 @@ import android.view.animation.Interpolator;
/**
* This class encapsulates scrolling. The duration of the scroll
* is either specified along with the distance or depends on the initial fling velocity.
* Past this time, the scrolling is automatically moved to its final stage and
* computeScrollOffset() will always return false to indicate that scrolling is over.
* can be passed in the constructor and specifies the maximum time that
* the scrolling animation should take. Past this time, the scrolling is
* automatically moved to its final stage and computeScrollOffset()
* will always return false to indicate that scrolling is over.
*/
public class Scroller {
private int mMode;
private MagneticScroller mScrollerX;
private MagneticScroller mScrollerY;
private int mStartX;
private int mStartY;
private int mFinalX;
private int mFinalY;
private final Interpolator mInterpolator;
private int mMinX;
private int mMaxX;
private int mMinY;
private int mMaxY;
private int mCurrX;
private int mCurrY;
private long mStartTime;
private int mDuration;
private float mDurationReciprocal;
private float mDeltaX;
private float mDeltaY;
private float mViscousFluidScale;
private float mViscousFluidNormalize;
private boolean mFinished;
private Interpolator mInterpolator;
private float mCoeffX = 0.0f;
private float mCoeffY = 1.0f;
private float mVelocity;
private static final int DEFAULT_DURATION = 250;
private static final int SCROLL_MODE = 0;
private static final int FLING_MODE = 1;
// This controls the viscous fluid effect (how much of it)
private final static float VISCOUS_FLUID_SCALE = 8.0f;
private static float VISCOUS_FLUID_NORMALIZE;
static {
// Set a neutral value that will be used in the next call to viscousFluid().
VISCOUS_FLUID_NORMALIZE = 1.0f;
VISCOUS_FLUID_NORMALIZE = 1.0f / viscousFluid(1.0f);
}
private final float mDeceleration;
/**
* Create a Scroller with a viscous fluid scroll interpolator.
* Create a Scroller with the default duration and interpolator.
*/
public Scroller(Context context) {
this(context, null);
@@ -65,13 +77,15 @@ public class Scroller {
* null, the default (viscous) interpolator will be used.
*/
public Scroller(Context context, Interpolator interpolator) {
mScrollerX = new MagneticScroller();
mScrollerY = new MagneticScroller();
MagneticScroller.initializeFromContext(context);
mFinished = true;
mInterpolator = interpolator;
float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = SensorManager.GRAVITY_EARTH // g (m/s^2)
* 39.37f // inch/meter
* ppi // pixels per inch
* ViewConfiguration.getScrollFriction();
}
/**
*
* Returns whether the scroller has finished scrolling.
@@ -79,148 +93,150 @@ public class Scroller {
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mScrollerX.mFinished && mScrollerY.mFinished;
return mFinished;
}
/**
* Force the finished field to a particular value.
*
*
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
mScrollerX.mFinished = mScrollerY.mFinished = finished;
mFinished = finished;
}
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
return mDuration;
}
/**
* Returns the current X offset in the scroll.
* Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final int getCurrX() {
return mScrollerX.mCurrentPosition;
return mCurrX;
}
/**
* Returns the current Y offset in the scroll.
* Returns the current Y offset in the scroll.
*
* @return The new Y offset as an absolute distance from the origin.
*/
public final int getCurrY() {
return mScrollerY.mCurrentPosition;
return mCurrY;
}
/**
* @hide
* Returns the current velocity.
*
* @return The original velocity less the deceleration, norm of the X and Y velocity vector.
*
* @return The original velocity less the deceleration. Result may be
* negative.
*/
public float getCurrVelocity() {
float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
return FloatMath.sqrt(squaredNorm);
return mVelocity - mDeceleration * timePassed() / 2000.0f;
}
/**
* Returns the start X offset in the scroll.
* Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final int getStartX() {
return mScrollerX.mStart;
return mStartX;
}
/**
* Returns the start Y offset in the scroll.
* Returns the start Y offset in the scroll.
*
* @return The start Y offset as an absolute distance from the origin.
*/
public final int getStartY() {
return mScrollerY.mStart;
return mStartY;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final X offset as an absolute distance from the origin.
*/
public final int getFinalX() {
return mScrollerX.mFinal;
return mFinalX;
}
/**
* Returns where the scroll will end. Valid only for "fling" scrolls.
*
* @return The final Y offset as an absolute distance from the origin.
*/
public final int getFinalY() {
return mScrollerY.mFinal;
return mFinalY;
}
/**
* Call this when you want to know the new location. If it returns true, the
* animation is not yet finished.
*/
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location.
*/
public boolean computeScrollOffset() {
if (isFinished()) {
if (mFinished) {
return false;
}
switch (mMode) {
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
long time = AnimationUtils.currentAnimationTimeMillis();
// Any scroller can be used for time, since they were started
// together in scroll mode. We use X here.
final long elapsedTime = time - mScrollerX.mStartTime;
final int duration = mScrollerX.mDuration;
if (elapsedTime < duration) {
float q = (float) (elapsedTime) / duration;
if (mInterpolator == null)
q = viscousFluid(q);
else
q = mInterpolator.getInterpolation(q);
mScrollerX.updateScroll(q);
mScrollerY.updateScroll(q);
} else {
abortAnimation();
float x = (float)timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
if ((mCurrX == mFinalX) && (mCurrY == mFinalY)) {
mFinished = true;
}
break;
case FLING_MODE:
if (!mScrollerX.mFinished) {
if (!mScrollerX.update()) {
if (!mScrollerX.continueWhenFinished()) {
mScrollerX.finish();
}
}
}
float timePassedSeconds = timePassed / 1000.0f;
float distance = (mVelocity * timePassedSeconds)
- (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
mCurrX = mStartX + Math.round(distance * mCoeffX);
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distance * mCoeffY);
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (!mScrollerY.mFinished) {
if (!mScrollerY.update()) {
if (!mScrollerY.continueWhenFinished()) {
mScrollerY.finish();
}
}
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
@@ -254,39 +270,83 @@ public class Scroller {
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mScrollerX.startScroll(startX, dx, duration);
mScrollerY.startScroll(startY, dy, duration);
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
// This controls the viscous fluid effect (how much of it)
mViscousFluidScale = 8.0f;
// must be set to 1.0 (used in viscousFluid())
mViscousFluidNormalize = 1.0f;
mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
/**
* Start scrolling based on a fling gesture. The distance traveled will
* depend on the initial velocity of the fling. Velocity is slowed down by a
* constant deceleration until it reaches 0 or the limits are reached.
* Start scrolling based on a fling gesture. The distance travelled will
* depend on the initial velocity of the fling.
*
* @param startX Starting point of the scroll (X)
* @param startY Starting point of the scroll (Y)
* @param velocityX Initial velocity of the fling (X) measured in pixels per
* second.
* second.
* @param velocityY Initial velocity of the fling (Y) measured in pixels per
* second.
* second
* @param minX Minimum X value. The scroller will not scroll past this
* point.
* point.
* @param maxX Maximum X value. The scroller will not scroll past this
* point.
* point.
* @param minY Minimum Y value. The scroller will not scroll past this
* point.
* point.
* @param maxY Maximum Y value. The scroller will not scroll past this
* point.
* point.
*/
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
mMode = FLING_MODE;
mScrollerX.fling(startX, velocityX, minX, maxX);
mScrollerY.fling(startY, velocityY, minY, maxY);
}
mFinished = false;
static float viscousFluid(float x) {
x *= VISCOUS_FLUID_SCALE;
float velocity = (float)Math.hypot(velocityX, velocityY);
mVelocity = velocity;
mDuration = (int) (1000 * velocity / mDeceleration); // Duration is in
// milliseconds
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mCoeffX = velocity == 0 ? 1.0f : velocityX / velocity;
mCoeffY = velocity == 0 ? 1.0f : velocityY / velocity;
int totalDistance = (int) ((velocity * velocity) / (2 * mDeceleration));
mMinX = minX;
mMaxX = maxX;
mMinY = minY;
mMaxY = maxY;
mFinalX = startX + Math.round(totalDistance * mCoeffX);
// Pin to mMinX <= mFinalX <= mMaxX
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);
mFinalY = startY + Math.round(totalDistance * mCoeffY);
// Pin to mMinY <= mFinalY <= mMaxY
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
private float viscousFluid(float x)
{
x *= mViscousFluidScale;
if (x < 1.0f) {
x -= (1.0f - (float)Math.exp(-x));
} else {
@@ -294,237 +354,70 @@ public class Scroller {
x = 1.0f - (float)Math.exp(1.0f - x);
x = start + x * (1.0f - start);
}
x *= VISCOUS_FLUID_NORMALIZE;
x *= mViscousFluidNormalize;
return x;
}
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
*
*
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
mScrollerX.finish();
mScrollerY.finish();
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
*
*
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*/
public void extendDuration(int extend) {
mScrollerX.extendDuration(extend);
mScrollerY.extendDuration(extend);
int passed = timePassed();
mDuration = passed + extend;
mDurationReciprocal = 1.0f / (float)mDuration;
mFinished = false;
}
/**
* Returns the time elapsed since the beginning of the scrolling.
*
*
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
return (int) (time - startTime);
return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
/**
* Sets the final position (X) for this scroller.
*
*
* @param newX The new X offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalY(int)
*/
public void setFinalX(int newX) {
mScrollerX.setFinalPosition(newX);
mFinalX = newX;
mDeltaX = mFinalX - mStartX;
mFinished = false;
}
/**
* Sets the final position (Y) for this scroller.
*
*
* @param newY The new Y offset as an absolute distance from the origin.
* @see #extendDuration(int)
* @see #setFinalX(int)
*/
public void setFinalY(int newY) {
mScrollerY.setFinalPosition(newY);
}
static class MagneticScroller {
// Initial position
int mStart;
// Current position
int mCurrentPosition;
// Final position
int mFinal;
// Initial velocity
int mVelocity;
// Current velocity
float mCurrVelocity;
// Constant current deceleration
float mDeceleration;
// Animation starting time, in system milliseconds
long mStartTime;
// Animation duration, in milliseconds
int mDuration;
// Whether the animation is currently in progress
boolean mFinished;
// Constant gravity value, used to scale deceleration
static float GRAVITY;
static void initializeFromContext(Context context) {
final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
GRAVITY = SensorManager.GRAVITY_EARTH // g (m/s^2)
* 39.37f // inch/meter
* ppi // pixels per inch
* ViewConfiguration.getScrollFriction();
}
MagneticScroller() {
mFinished = true;
}
void updateScroll(float q) {
mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
}
/*
* Update the current position and velocity for current time. Returns
* true if update has been done and false if animation duration has been
* reached.
*/
boolean update() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final long duration = time - mStartTime;
if (duration > mDuration) {
return false;
}
final float t = duration / 1000.0f;
mCurrVelocity = mVelocity + mDeceleration * t;
final float distance = mVelocity * t + mDeceleration * t * t / 2.0f;
mCurrentPosition = mStart + (int) distance;
return true;
}
/*
* Get a signed deceleration that will reduce the velocity.
*/
static float getDeceleration(int velocity) {
return velocity > 0 ? -GRAVITY : GRAVITY;
}
/*
* Returns the time (in milliseconds) it will take to go from start to end.
*/
static int computeDuration(int start, int end, float initialVelocity, float deceleration) {
final int distance = start - end;
final float discriminant = initialVelocity * initialVelocity - 2.0f * deceleration
* distance;
if (discriminant >= 0.0f) {
float delta = (float) Math.sqrt(discriminant);
if (deceleration < 0.0f) {
delta = -delta;
}
return (int) (1000.0f * (-initialVelocity - delta) / deceleration);
}
// End position can not be reached
return 0;
}
void startScroll(int start, int distance, int duration) {
mFinished = false;
mStart = start;
mFinal = start + distance;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = duration;
// Unused
mDeceleration = 0.0f;
mVelocity = 0;
}
void fling(int start, int velocity, int min, int max) {
mFinished = false;
mStart = start;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mVelocity = velocity;
mDeceleration = getDeceleration(velocity);
// A start from an invalid position immediately brings back to a valid position
if (mStart < min) {
mDuration = 0;
mFinal = min;
return;
}
if (mStart > max) {
mDuration = 0;
mFinal = max;
return;
}
// Duration are expressed in milliseconds
mDuration = (int) (-1000.0f * velocity / mDeceleration);
mFinal = start - Math.round((velocity * velocity) / (2.0f * mDeceleration));
// Clamp to a valid final position
if (mFinal < min) {
mFinal = min;
mDuration = computeDuration(mStart, min, mVelocity, mDeceleration);
}
if (mFinal > max) {
mFinal = max;
mDuration = computeDuration(mStart, max, mVelocity, mDeceleration);
}
}
void finish() {
mCurrentPosition = mFinal;
// Not reset since WebView relies on this value for fast fling.
// mCurrVelocity = 0.0f;
mFinished = true;
}
boolean continueWhenFinished() {
return false;
}
void setFinalPosition(int position) {
mFinal = position;
mFinished = false;
}
void extendDuration(int extend) {
final long time = AnimationUtils.currentAnimationTimeMillis();
final int elapsedTime = (int) (time - mStartTime);
mDuration = elapsedTime + extend;
mFinished = false;
}
mFinalY = newY;
mDeltaY = mFinalY - mStartY;
mFinished = false;
}
}

View File

@@ -80,8 +80,7 @@
android:paddingTop="2dip"
android:paddingBottom="12dip"
android:paddingLeft="14dip"
android:paddingRight="10dip"
android:overscrollMode="ifContentScrolls">
android:paddingRight="10dip">
<TextView android:id="@+id/message"
style="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"

View File

@@ -17,8 +17,7 @@
<!-- Layout used as the dialog's content View for EditTextPreference. -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overscrollMode="ifContentScrolls">
android:layout_height="match_parent">
<LinearLayout
android:id="@+android:id/edittext_container"

View File

@@ -31,5 +31,4 @@
android:layout_marginTop="5px"
android:cacheColorHint="@null"
android:divider="@android:drawable/divider_horizontal_bright"
android:scrollbars="vertical"
android:overscrollMode="ifContentScrolls" />
android:scrollbars="vertical" />

View File

@@ -86,7 +86,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="none"
android:overscrollMode="ifContentScrolls"
>
<com.android.server.status.NotificationLinearLayout
android:id="@+id/notificationLinearLayout"

View File

@@ -1307,19 +1307,6 @@
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity). -->
<attr name="onClick" format="string" />
<!-- Defines overscrolling behavior. This property is used only if the
View is scrollable. Overscrolling is the ability for the user to
scroll a View beyond its content boundaries into empty space. -->
<attr name="overscrollMode">
<!-- Always allow the user to overscroll the content. -->
<enum name="always" value="0" />
<!-- Only allow the user to overscroll content if the content is large
enough to meaningfully scroll. -->
<enum name="ifContentScrolls" value="1" />
<!-- Never overscroll. -->
<enum name="never" value="2" />
</attr>
</declare-styleable>
<!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
@@ -1747,10 +1734,6 @@
<!-- When set to false, the ListView will not draw the divider before each footer view.
The default value is true. -->
<attr name="footerDividersEnabled" format="boolean" />
<!-- Drawable to draw above list content. -->
<attr name="overscrollHeader" format="reference|color" />
<!-- Drawable to draw below list content. -->
<attr name="overscrollFooter" format="reference|color" />
</declare-styleable>
<declare-styleable name="MenuView">
<!-- Default appearance of menu item text. -->

View File

@@ -1230,13 +1230,10 @@
<public type="attr" name="installLocation" id="0x010102b7" />
<public type="attr" name="vmSafeMode" id="0x010102b8" />
<public type="attr" name="webTextViewStyle" id="0x010102b9" />
<public type="attr" name="overscrollMode" id="0x010102ba" />
<public type="attr" name="restoreAnyVersion" id="0x010102bb" />
<public type="attr" name="tabStripLeft" id="0x010102bc" />
<public type="attr" name="tabStripRight" id="0x010102bd" />
<public type="attr" name="tabStripEnabled" id="0x010102be" />
<public type="attr" name="overscrollHeader" id="0x010102bf" />
<public type="attr" name="overscrollFooter" id="0x010102c0" />
<public type="attr" name="restoreAnyVersion" id="0x010102ba" />
<public type="attr" name="tabStripLeft" id="0x010102bb" />
<public type="attr" name="tabStripRight" id="0x010102bc" />
<public type="attr" name="tabStripEnabled" id="0x010102bd" />
<public type="id" name="custom" id="0x0102002b" />