diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-24 17:26:38 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-24 17:26:38 +0000 |
commit | 5719f8f6aff4369c125df3e0f728fd1cd3efa254 (patch) | |
tree | 6f8e30c52852cbdeeab7c2d4ad62655b4c773534 /android_webview/java | |
parent | 71a86c28cc057c8d8afab97d0f2d6dad74127329 (diff) | |
download | chromium_src-5719f8f6aff4369c125df3e0f728fd1cd3efa254.zip chromium_src-5719f8f6aff4369c125df3e0f728fd1cd3efa254.tar.gz chromium_src-5719f8f6aff4369c125df3e0f728fd1cd3efa254.tar.bz2 |
[android_webview] Implement setOverScrollMode.
This implements AwContents.setOverScrollMode and adds the OverScrollGlow
class that takes care of drawing the overscroll glow.
BUG=b/6946454
TEST=manual: launch test shell, overscroll a page
Java-only change. Ran through trybots.
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/19619002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@213456 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview/java')
-rw-r--r-- | android_webview/java/src/org/chromium/android_webview/AwContents.java | 79 | ||||
-rw-r--r-- | android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java | 210 |
2 files changed, 270 insertions, 19 deletions
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index c94c1ab..00dc0ab 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -143,8 +143,9 @@ public class AwContents { private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; private final InternalAccessDelegate mInternalAccessAdapter; private final AwLayoutSizer mLayoutSizer; - private final AwScrollOffsetManager mScrollOffsetManager; private final AwZoomControls mZoomControls; + private final AwScrollOffsetManager mScrollOffsetManager; + private OverScrollGlow mOverScrollGlow; // This can be accessed on any thread after construction. See AwContentsIoThreadClient. private final AwSettings mSettings; @@ -481,6 +482,8 @@ public class AwContents { mContentsClient.setDIPScale(mDIPScale); mScrollOffsetManager = new AwScrollOffsetManager(new AwScrollOffsetManagerDelegate()); + setOverScrollMode(mContainerView.getOverScrollMode()); + setNewAwContents(nativeInit(browserContext)); } @@ -659,6 +662,12 @@ public class AwContents { Log.w(TAG, "nativeOnDraw failed; clearing to background color."); canvas.drawColor(getEffectiveBackgroundColor()); } + + if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas, + mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), + mScrollOffsetManager.computeMaximumVerticalScrollOffset())) { + mContainerView.invalidate(); + } } public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -673,23 +682,6 @@ public class AwContents { return (int) Math.ceil(mContentViewCore.getContentWidthCss()); } - /** - * Called by the embedder when the scroll offset of the containing view has changed. - * @see View#onScrollChanged(int,int) - */ - public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { - mScrollOffsetManager.onContainerViewScrollChanged(l, t); - } - - /** - * Called by the embedder when the containing view is to be scrolled or overscrolled. - * @see View#onOverScrolled(int,int,int,int) - */ - public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY) { - mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); - } - public Picture capturePicture() { return nativeCapturePicture(mNativeAwContents); } @@ -854,6 +846,44 @@ public class AwContents { } /** + * @see View#setOverScrollMode(int) + */ + public void setOverScrollMode(int mode) { + if (mode != View.OVER_SCROLL_NEVER) { + mOverScrollGlow = new OverScrollGlow(mContainerView); + } else { + mOverScrollGlow = null; + } + } + + /** + * Called by the embedder when the scroll offset of the containing view has changed. + * @see View#onScrollChanged(int,int) + */ + public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { + mScrollOffsetManager.onContainerViewScrollChanged(l, t); + } + + /** + * Called by the embedder when the containing view is to be scrolled or overscrolled. + * @see View#onOverScrolled(int,int,int,int) + */ + public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, + boolean clampedY) { + int oldX = mContainerView.getScrollX(); + int oldY = mContainerView.getScrollY(); + + mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); + + if (mOverScrollGlow != null) { + mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(), + oldX, oldY, + mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), + mScrollOffsetManager.computeMaximumVerticalScrollOffset()); + } + } + + /** * @see View#computeHorizontalScrollRange() */ public int computeHorizontalScrollRange() { @@ -1250,6 +1280,10 @@ public class AwContents { (int)Math.round(event.getY(actionIndex) / mDIPScale)); } + if (mOverScrollGlow != null && event.getActionMasked() == MotionEvent.ACTION_UP) { + mOverScrollGlow.releaseAll(); + } + return rv; } @@ -1382,7 +1416,6 @@ public class AwContents { nativeSetVisibility(mNativeAwContents, mIsVisible); } - /** * Key for opaque state in bundle. Note this is only public for tests. */ @@ -1632,7 +1665,15 @@ public class AwContents { @CalledByNative private void didOverscroll(int deltaX, int deltaY) { + if (mOverScrollGlow != null) { + mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); + } + mScrollOffsetManager.overscrollBy(deltaX, deltaY); + + if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { + mContainerView.invalidate(); + } } // ------------------------------------------------------------------------------------------- diff --git a/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java b/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java new file mode 100644 index 0000000..5023c39 --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/OverScrollGlow.java @@ -0,0 +1,210 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.android_webview; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.EdgeEffect; + +/** + * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges. + */ +class OverScrollGlow { + private View mHostView; + + private EdgeEffect mEdgeGlowTop; + private EdgeEffect mEdgeGlowBottom; + private EdgeEffect mEdgeGlowLeft; + private EdgeEffect mEdgeGlowRight; + + private int mOverScrollDeltaX; + private int mOverScrollDeltaY; + + public OverScrollGlow(View host) { + mHostView = host; + Context context = host.getContext(); + mEdgeGlowTop = new EdgeEffect(context); + mEdgeGlowBottom = new EdgeEffect(context); + mEdgeGlowLeft = new EdgeEffect(context); + mEdgeGlowRight = new EdgeEffect(context); + } + + /** + * Pull leftover touch scroll distance into one of the edge glows as appropriate. + * + * @param x Current X scroll offset + * @param y Current Y scroll offset + * @param oldX Old X scroll offset + * @param oldY Old Y scroll offset + * @param maxX Maximum range for horizontal scrolling + * @param maxY Maximum range for vertical scrolling + */ + public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) { + // Only show overscroll bars if there was no movement in any direction + // as a result of scrolling. + if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) { + // Don't show left/right glows if we fit the whole content. + // Also don't show if there was vertical movement. + if (maxX > 0) { + final int pulledToX = oldX + mOverScrollDeltaX; + if (pulledToX < 0) { + mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); + if (!mEdgeGlowRight.isFinished()) { + mEdgeGlowRight.onRelease(); + } + } else if (pulledToX > maxX) { + mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth()); + if (!mEdgeGlowLeft.isFinished()) { + mEdgeGlowLeft.onRelease(); + } + } + mOverScrollDeltaX = 0; + } + + if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { + final int pulledToY = oldY + mOverScrollDeltaY; + if (pulledToY < 0) { + mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); + if (!mEdgeGlowBottom.isFinished()) { + mEdgeGlowBottom.onRelease(); + } + } else if (pulledToY > maxY) { + mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight()); + if (!mEdgeGlowTop.isFinished()) { + mEdgeGlowTop.onRelease(); + } + } + mOverScrollDeltaY = 0; + } + } + } + + /** + * Absorb leftover fling velocity into one of the edge glows as appropriate. + * + * @param x Current X scroll offset + * @param y Current Y scroll offset + * @param oldX Old X scroll offset + * @param oldY Old Y scroll offset + * @param rangeX Maximum range for horizontal scrolling + * @param rangeY Maximum range for vertical scrolling + * @param currentFlingVelocity Current fling velocity + */ + public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY, + float currentFlingVelocity) { + if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) { + if (y < 0 && oldY >= 0) { + mEdgeGlowTop.onAbsorb((int) currentFlingVelocity); + if (!mEdgeGlowBottom.isFinished()) { + mEdgeGlowBottom.onRelease(); + } + } else if (y > rangeY && oldY <= rangeY) { + mEdgeGlowBottom.onAbsorb((int) currentFlingVelocity); + if (!mEdgeGlowTop.isFinished()) { + mEdgeGlowTop.onRelease(); + } + } + } + + if (rangeX > 0) { + if (x < 0 && oldX >= 0) { + mEdgeGlowLeft.onAbsorb((int) currentFlingVelocity); + if (!mEdgeGlowRight.isFinished()) { + mEdgeGlowRight.onRelease(); + } + } else if (x > rangeX && oldX <= rangeX) { + mEdgeGlowRight.onAbsorb((int) currentFlingVelocity); + if (!mEdgeGlowLeft.isFinished()) { + mEdgeGlowLeft.onRelease(); + } + } + } + } + + /** + * Set touch delta values indicating the current amount of overscroll. + * + * @param deltaX + * @param deltaY + */ + public void setOverScrollDeltas(int deltaX, int deltaY) { + mOverScrollDeltaX += deltaX; + mOverScrollDeltaY += deltaY; + } + + /** + * Draw the glow effect along the sides of the widget. + * + * @param canvas Canvas to draw into, transformed into view coordinates. + * @param maxScrollX maximum horizontal scroll offset + * @param maxScrollY maximum vertical scroll offset + * @return true if glow effects are still animating and the view should invalidate again. + */ + public boolean drawEdgeGlows(Canvas canvas, int maxScrollX, int maxScrollY) { + final int scrollX = mHostView.getScrollX(); + final int scrollY = mHostView.getScrollY(); + final int width = mHostView.getWidth(); + int height = mHostView.getHeight(); + + boolean invalidateForGlow = false; + if (!mEdgeGlowTop.isFinished()) { + final int restoreCount = canvas.save(); + + canvas.translate(scrollX, Math.min(0, scrollY)); + mEdgeGlowTop.setSize(width, height); + invalidateForGlow |= mEdgeGlowTop.draw(canvas); + canvas.restoreToCount(restoreCount); + } + if (!mEdgeGlowBottom.isFinished()) { + final int restoreCount = canvas.save(); + + canvas.translate(-width + scrollX, Math.max(maxScrollY, scrollY) + height); + canvas.rotate(180, width, 0); + mEdgeGlowBottom.setSize(width, height); + invalidateForGlow |= mEdgeGlowBottom.draw(canvas); + canvas.restoreToCount(restoreCount); + } + if (!mEdgeGlowLeft.isFinished()) { + final int restoreCount = canvas.save(); + + canvas.rotate(270); + canvas.translate(-height - scrollY, Math.min(0, scrollX)); + mEdgeGlowLeft.setSize(height, width); + invalidateForGlow |= mEdgeGlowLeft.draw(canvas); + canvas.restoreToCount(restoreCount); + } + if (!mEdgeGlowRight.isFinished()) { + final int restoreCount = canvas.save(); + + canvas.rotate(90); + canvas.translate(scrollY, -(Math.max(scrollX, maxScrollX) + width)); + mEdgeGlowRight.setSize(height, width); + invalidateForGlow |= mEdgeGlowRight.draw(canvas); + canvas.restoreToCount(restoreCount); + } + return invalidateForGlow; + } + + /** + * @return True if any glow is still animating + */ + public boolean isAnimating() { + return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished() || + !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished()); + } + + /** + * Release all glows from any touch pulls in progress. + */ + public void releaseAll() { + mEdgeGlowTop.onRelease(); + mEdgeGlowBottom.onRelease(); + mEdgeGlowLeft.onRelease(); + mEdgeGlowRight.onRelease(); + } +} |