XML attribute for overscrolling behavior
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 57fdd8b..e5db120 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1504,6 +1504,31 @@
* @hide
*/
private static final int PREPRESSED = 0x02000000;
+
+ /**
+ * Always allow a user to overscroll this view, provided it is a
+ * view that can scroll.
+ */
+ private 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.
+ */
+ private static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1;
+
+ /**
+ * Never allow a user to overscroll this view.
+ */
+ private static final int OVERSCROLL_NEVER = 2;
+
+ /**
+ * Controls the overscroll mode for this view.
+ * See {@link #overscrollBy(int, int, int, int, int, int, int, int)},
+ * {@link #OVERSCROLL_ALWAYS}, {@link #OVERSCROLL_IF_CONTENT_SCROLLS},
+ * and {@link #OVERSCROLL_NEVER}.
+ */
+ private int mOverscrollMode = OVERSCROLL_ALWAYS;
/**
* The parent this view is attached to.
@@ -2053,6 +2078,9 @@
});
}
break;
+ case R.styleable.View_overscrollMode:
+ mOverscrollMode = a.getInt(attr, OVERSCROLL_ALWAYS);
+ break;
}
}
@@ -8573,43 +8601,59 @@
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverscrollX, int maxOverscrollY) {
- // 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;
- int newScrollX;
- if ((scrollX < dropoffLeft && deltaX < 0) ||
- (scrollX > dropoffRight && deltaX > 0)) {
- newScrollX = scrollX + deltaX / 2;
- } else {
- newScrollX = scrollX + deltaX;
- 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;
- }
- }
+ 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);
- final int dropoffY = maxOverscrollY / 2;
- final int dropoffTop = -dropoffY;
- final int dropoffBottom = dropoffY + scrollRangeY;
- int newScrollY;
- if ((scrollY < dropoffTop && deltaY < 0) ||
- (scrollY > dropoffBottom && deltaY > 0)) {
- newScrollY = scrollY + deltaY / 2;
- } else {
- newScrollY = scrollY + deltaY;
- 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;
+ 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;
@@ -8636,8 +8680,8 @@
// Bump the device with some haptic feedback if we're at the edge
// and didn't start there.
- if ((clampedX && scrollX != left && scrollX != right) ||
- (clampedY && scrollY != top && scrollY != bottom)) {
+ if ((overscrollHorizontal && clampedX && scrollX != left && scrollX != right) ||
+ (overscrollVertical && clampedY && scrollY != top && scrollY != bottom)) {
performHapticFeedback(HapticFeedbackConstants.SCROLL_BARRIER);
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4c77bdc..254efe7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2589,7 +2589,8 @@
if (trackMotionScroll(delta, delta)) {
if (motionView != null) {
// Tweak the scroll for how far we overshot
- mScrollY -= delta - (motionView.getTop() - oldTop);
+ int overshoot = -(delta - (motionView.getTop() - oldTop));
+ overscrollBy(0, overshoot, 0, mScrollY, 0, 0, 0, getOverscrollMax());
}
float vel = scroller.getCurrVelocity();
if (delta > 0) {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0f6a5c1..ff93984 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1307,6 +1307,19 @@
<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
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 82c02eb5..b334337 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1230,6 +1230,7 @@
<public type="attr" name="installLocation" id="0x010102b7" />
<public type="attr" name="safeMode" id="0x010102b8" />
<public type="attr" name="webTextViewStyle" id="0x010102b9" />
+ <public type="attr" name="overscrollMode" id="0x010102ba" />
<public type="anim" name="cycle_interpolator" id="0x010a000c" />