1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.android_webview;
7 import android.content.Context;
8 import android.graphics.Canvas;
9 import android.view.View;
10 import android.widget.EdgeEffect;
13 * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
15 class OverScrollGlow {
16 private View mHostView;
18 private EdgeEffect mEdgeGlowTop;
19 private EdgeEffect mEdgeGlowBottom;
20 private EdgeEffect mEdgeGlowLeft;
21 private EdgeEffect mEdgeGlowRight;
23 private int mOverScrollDeltaX;
24 private int mOverScrollDeltaY;
26 private boolean mShouldPull;
28 public OverScrollGlow(Context context, View host) {
30 mEdgeGlowTop = new EdgeEffect(context);
31 mEdgeGlowBottom = new EdgeEffect(context);
32 mEdgeGlowLeft = new EdgeEffect(context);
33 mEdgeGlowRight = new EdgeEffect(context);
36 public void setShouldPull(boolean shouldPull) {
37 mShouldPull = shouldPull;
41 * Pull leftover touch scroll distance into one of the edge glows as appropriate.
43 * @param x Current X scroll offset
44 * @param y Current Y scroll offset
45 * @param oldX Old X scroll offset
46 * @param oldY Old Y scroll offset
47 * @param maxX Maximum range for horizontal scrolling
48 * @param maxY Maximum range for vertical scrolling
50 public void pullGlow(int x, int y, int oldX, int oldY, int maxX, int maxY) {
51 if (!mShouldPull) return;
52 // Only show overscroll bars if there was no movement in any direction
53 // as a result of scrolling.
54 if (oldX == mHostView.getScrollX() && oldY == mHostView.getScrollY()) {
55 // Don't show left/right glows if we fit the whole content.
56 // Also don't show if there was vertical movement.
58 final int pulledToX = oldX + mOverScrollDeltaX;
60 mEdgeGlowLeft.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
61 if (!mEdgeGlowRight.isFinished()) {
62 mEdgeGlowRight.onRelease();
64 } else if (pulledToX > maxX) {
65 mEdgeGlowRight.onPull((float) mOverScrollDeltaX / mHostView.getWidth());
66 if (!mEdgeGlowLeft.isFinished()) {
67 mEdgeGlowLeft.onRelease();
70 mOverScrollDeltaX = 0;
73 if (maxY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
74 final int pulledToY = oldY + mOverScrollDeltaY;
76 mEdgeGlowTop.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
77 if (!mEdgeGlowBottom.isFinished()) {
78 mEdgeGlowBottom.onRelease();
80 } else if (pulledToY > maxY) {
81 mEdgeGlowBottom.onPull((float) mOverScrollDeltaY / mHostView.getHeight());
82 if (!mEdgeGlowTop.isFinished()) {
83 mEdgeGlowTop.onRelease();
86 mOverScrollDeltaY = 0;
92 * Absorb leftover fling velocity into one of the edge glows as appropriate.
94 * @param x Current X scroll offset
95 * @param y Current Y scroll offset
96 * @param oldX Old X scroll offset
97 * @param oldY Old Y scroll offset
98 * @param rangeX Maximum range for horizontal scrolling
99 * @param rangeY Maximum range for vertical scrolling
100 * @param currentFlingVelocity Current fling velocity
102 public void absorbGlow(int x, int y, int oldX, int oldY, int rangeX, int rangeY,
103 float currentFlingVelocity) {
104 if (rangeY > 0 || mHostView.getOverScrollMode() == View.OVER_SCROLL_ALWAYS) {
105 if (y < 0 && oldY >= 0) {
106 mEdgeGlowTop.onAbsorb((int) currentFlingVelocity);
107 if (!mEdgeGlowBottom.isFinished()) {
108 mEdgeGlowBottom.onRelease();
110 } else if (y > rangeY && oldY <= rangeY) {
111 mEdgeGlowBottom.onAbsorb((int) currentFlingVelocity);
112 if (!mEdgeGlowTop.isFinished()) {
113 mEdgeGlowTop.onRelease();
119 if (x < 0 && oldX >= 0) {
120 mEdgeGlowLeft.onAbsorb((int) currentFlingVelocity);
121 if (!mEdgeGlowRight.isFinished()) {
122 mEdgeGlowRight.onRelease();
124 } else if (x > rangeX && oldX <= rangeX) {
125 mEdgeGlowRight.onAbsorb((int) currentFlingVelocity);
126 if (!mEdgeGlowLeft.isFinished()) {
127 mEdgeGlowLeft.onRelease();
134 * Set touch delta values indicating the current amount of overscroll.
139 public void setOverScrollDeltas(int deltaX, int deltaY) {
140 mOverScrollDeltaX += deltaX;
141 mOverScrollDeltaY += deltaY;
145 * Draw the glow effect along the sides of the widget.
147 * @param canvas Canvas to draw into, transformed into view coordinates.
148 * @param maxScrollX maximum horizontal scroll offset
149 * @param maxScrollY maximum vertical scroll offset
150 * @return true if glow effects are still animating and the view should invalidate again.
152 public boolean drawEdgeGlows(Canvas canvas, int maxScrollX, int maxScrollY) {
153 final int scrollX = mHostView.getScrollX();
154 final int scrollY = mHostView.getScrollY();
155 final int width = mHostView.getWidth();
156 int height = mHostView.getHeight();
158 boolean invalidateForGlow = false;
159 if (!mEdgeGlowTop.isFinished()) {
160 final int restoreCount = canvas.save();
162 canvas.translate(scrollX, Math.min(0, scrollY));
163 mEdgeGlowTop.setSize(width, height);
164 invalidateForGlow |= mEdgeGlowTop.draw(canvas);
165 canvas.restoreToCount(restoreCount);
167 if (!mEdgeGlowBottom.isFinished()) {
168 final int restoreCount = canvas.save();
170 canvas.translate(-width + scrollX, Math.max(maxScrollY, scrollY) + height);
171 canvas.rotate(180, width, 0);
172 mEdgeGlowBottom.setSize(width, height);
173 invalidateForGlow |= mEdgeGlowBottom.draw(canvas);
174 canvas.restoreToCount(restoreCount);
176 if (!mEdgeGlowLeft.isFinished()) {
177 final int restoreCount = canvas.save();
180 canvas.translate(-height - scrollY, Math.min(0, scrollX));
181 mEdgeGlowLeft.setSize(height, width);
182 invalidateForGlow |= mEdgeGlowLeft.draw(canvas);
183 canvas.restoreToCount(restoreCount);
185 if (!mEdgeGlowRight.isFinished()) {
186 final int restoreCount = canvas.save();
189 canvas.translate(scrollY, -(Math.max(scrollX, maxScrollX) + width));
190 mEdgeGlowRight.setSize(height, width);
191 invalidateForGlow |= mEdgeGlowRight.draw(canvas);
192 canvas.restoreToCount(restoreCount);
194 return invalidateForGlow;
198 * @return True if any glow is still animating
200 public boolean isAnimating() {
201 return (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished()
202 || !mEdgeGlowLeft.isFinished() || !mEdgeGlowRight.isFinished());
206 * Release all glows from any touch pulls in progress.
208 public void releaseAll() {
209 mEdgeGlowTop.onRelease();
210 mEdgeGlowBottom.onRelease();
211 mEdgeGlowLeft.onRelease();
212 mEdgeGlowRight.onRelease();