1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.support.v4.widget; 17 18import android.content.Context; 19import android.graphics.Canvas; 20import android.os.Build; 21 22/** 23 * Helper for accessing {@link android.widget.EdgeEffect} introduced after 24 * API level 4 in a backwards compatible fashion. 25 * 26 * This class is used to access {@link android.widget.EdgeEffect} on platform versions 27 * that support it. When running on older platforms it will result in no-ops. It should 28 * be used by views that wish to use the standard Android visual effects at the edges 29 * of scrolling containers. 30 */ 31public final class EdgeEffectCompat { 32 private Object mEdgeEffect; 33 34 private static final EdgeEffectImpl IMPL; 35 36 static { 37 if (Build.VERSION.SDK_INT >= 21) { 38 IMPL = new EdgeEffectLollipopImpl(); // Lollipop 39 } else if (Build.VERSION.SDK_INT >= 14) { // ICS 40 IMPL = new EdgeEffectIcsImpl(); 41 } else { 42 IMPL = new BaseEdgeEffectImpl(); 43 } 44 } 45 46 interface EdgeEffectImpl { 47 public Object newEdgeEffect(Context context); 48 public void setSize(Object edgeEffect, int width, int height); 49 public boolean isFinished(Object edgeEffect); 50 public void finish(Object edgeEffect); 51 public boolean onPull(Object edgeEffect, float deltaDistance); 52 public boolean onRelease(Object edgeEffect); 53 public boolean onAbsorb(Object edgeEffect, int velocity); 54 public boolean draw(Object edgeEffect, Canvas canvas); 55 public boolean onPull(Object edgeEffect, float deltaDistance, float displacement); 56 } 57 58 /** 59 * Null implementation to use pre-ICS 60 */ 61 static class BaseEdgeEffectImpl implements EdgeEffectImpl { 62 @Override 63 public Object newEdgeEffect(Context context) { 64 return null; 65 } 66 67 @Override 68 public void setSize(Object edgeEffect, int width, int height) { 69 } 70 71 @Override 72 public boolean isFinished(Object edgeEffect) { 73 return true; 74 } 75 76 @Override 77 public void finish(Object edgeEffect) { 78 } 79 80 @Override 81 public boolean onPull(Object edgeEffect, float deltaDistance) { 82 return false; 83 } 84 85 @Override 86 public boolean onRelease(Object edgeEffect) { 87 return false; 88 } 89 90 @Override 91 public boolean onAbsorb(Object edgeEffect, int velocity) { 92 return false; 93 } 94 95 @Override 96 public boolean draw(Object edgeEffect, Canvas canvas) { 97 return false; 98 } 99 100 @Override 101 public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) { 102 return false; 103 } 104 } 105 106 static class EdgeEffectIcsImpl implements EdgeEffectImpl { 107 @Override 108 public Object newEdgeEffect(Context context) { 109 return EdgeEffectCompatIcs.newEdgeEffect(context); 110 } 111 112 @Override 113 public void setSize(Object edgeEffect, int width, int height) { 114 EdgeEffectCompatIcs.setSize(edgeEffect, width, height); 115 } 116 117 @Override 118 public boolean isFinished(Object edgeEffect) { 119 return EdgeEffectCompatIcs.isFinished(edgeEffect); 120 } 121 122 @Override 123 public void finish(Object edgeEffect) { 124 EdgeEffectCompatIcs.finish(edgeEffect); 125 } 126 127 @Override 128 public boolean onPull(Object edgeEffect, float deltaDistance) { 129 return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance); 130 } 131 132 @Override 133 public boolean onRelease(Object edgeEffect) { 134 return EdgeEffectCompatIcs.onRelease(edgeEffect); 135 } 136 137 @Override 138 public boolean onAbsorb(Object edgeEffect, int velocity) { 139 return EdgeEffectCompatIcs.onAbsorb(edgeEffect, velocity); 140 } 141 142 @Override 143 public boolean draw(Object edgeEffect, Canvas canvas) { 144 return EdgeEffectCompatIcs.draw(edgeEffect, canvas); 145 } 146 147 @Override 148 public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) { 149 return EdgeEffectCompatIcs.onPull(edgeEffect, deltaDistance); 150 } 151 } 152 153 static class EdgeEffectLollipopImpl extends EdgeEffectIcsImpl { 154 @Override 155 public boolean onPull(Object edgeEffect, float deltaDistance, float displacement) { 156 return EdgeEffectCompatLollipop.onPull(edgeEffect, deltaDistance, displacement); 157 } 158 } 159 160 /** 161 * Construct a new EdgeEffect themed using the given context. 162 * 163 * <p>Note: On platform versions that do not support EdgeEffect, all operations 164 * on the newly constructed object will be mocked/no-ops.</p> 165 * 166 * @param context Context to use for theming the effect 167 */ 168 public EdgeEffectCompat(Context context) { 169 mEdgeEffect = IMPL.newEdgeEffect(context); 170 } 171 172 /** 173 * Set the size of this edge effect in pixels. 174 * 175 * @param width Effect width in pixels 176 * @param height Effect height in pixels 177 */ 178 public void setSize(int width, int height) { 179 IMPL.setSize(mEdgeEffect, width, height); 180 } 181 182 /** 183 * Reports if this EdgeEffectCompat's animation is finished. If this method returns false 184 * after a call to {@link #draw(Canvas)} the host widget should schedule another 185 * drawing pass to continue the animation. 186 * 187 * @return true if animation is finished, false if drawing should continue on the next frame. 188 */ 189 public boolean isFinished() { 190 return IMPL.isFinished(mEdgeEffect); 191 } 192 193 /** 194 * Immediately finish the current animation. 195 * After this call {@link #isFinished()} will return true. 196 */ 197 public void finish() { 198 IMPL.finish(mEdgeEffect); 199 } 200 201 /** 202 * A view should call this when content is pulled away from an edge by the user. 203 * This will update the state of the current visual effect and its associated animation. 204 * The host view should always {@link android.view.View#invalidate()} if this method 205 * returns true and draw the results accordingly. 206 * 207 * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to 208 * 1.f (full length of the view) or negative values to express change 209 * back toward the edge reached to initiate the effect. 210 * @return true if the host view should call invalidate, false if it should not. 211 * @deprecated use {@link #onPull(float, float)} 212 */ 213 @Deprecated 214 public boolean onPull(float deltaDistance) { 215 return IMPL.onPull(mEdgeEffect, deltaDistance); 216 } 217 218 /** 219 * A view should call this when content is pulled away from an edge by the user. 220 * This will update the state of the current visual effect and its associated animation. 221 * The host view should always {@link android.view.View#invalidate()} if this method 222 * returns true and draw the results accordingly. 223 * 224 * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to 225 * 1.f (full length of the view) or negative values to express change 226 * back toward the edge reached to initiate the effect. 227 * @param displacement The displacement from the starting side of the effect of the point 228 * initiating the pull. In the case of touch this is the finger position. 229 * Values may be from 0-1. 230 * @return true if the host view should call invalidate, false if it should not. 231 */ 232 public boolean onPull(float deltaDistance, float displacement) { 233 return IMPL.onPull(mEdgeEffect, deltaDistance, displacement); 234 } 235 236 /** 237 * Call when the object is released after being pulled. 238 * This will begin the "decay" phase of the effect. After calling this method 239 * the host view should {@link android.view.View#invalidate()} if this method 240 * returns true and thereby draw the results accordingly. 241 * 242 * @return true if the host view should invalidate, false if it should not. 243 */ 244 public boolean onRelease() { 245 return IMPL.onRelease(mEdgeEffect); 246 } 247 248 /** 249 * Call when the effect absorbs an impact at the given velocity. 250 * Used when a fling reaches the scroll boundary. 251 * 252 * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller}, 253 * the method <code>getCurrVelocity</code> will provide a reasonable approximation 254 * to use here.</p> 255 * 256 * @param velocity Velocity at impact in pixels per second. 257 * @return true if the host view should invalidate, false if it should not. 258 */ 259 public boolean onAbsorb(int velocity) { 260 return IMPL.onAbsorb(mEdgeEffect, velocity); 261 } 262 263 /** 264 * Draw into the provided canvas. Assumes that the canvas has been rotated 265 * accordingly and the size has been set. The effect will be drawn the full 266 * width of X=0 to X=width, beginning from Y=0 and extending to some factor < 267 * 1.f of height. 268 * 269 * @param canvas Canvas to draw into 270 * @return true if drawing should continue beyond this frame to continue the 271 * animation 272 */ 273 public boolean draw(Canvas canvas) { 274 return IMPL.draw(mEdgeEffect, canvas); 275 } 276} 277