1 /* 2 * Copyright (C) 2022 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 */ 16 17 package com.android.internal.dynamicanimation.animation; 18 19 import android.animation.AnimationHandler; 20 import android.animation.ValueAnimator; 21 import android.annotation.FloatRange; 22 import android.annotation.MainThread; 23 import android.annotation.NonNull; 24 import android.annotation.SuppressLint; 25 import android.os.Looper; 26 import android.util.AndroidRuntimeException; 27 import android.util.FloatProperty; 28 import android.view.View; 29 30 import java.util.ArrayList; 31 32 /** 33 * This class is the base class of physics-based animations. It manages the animation's 34 * lifecycle such as {@link #start()} and {@link #cancel()}. This base class also handles the common 35 * setup for all the subclass animations. For example, DynamicAnimation supports adding 36 * {@link OnAnimationEndListener} and {@link OnAnimationUpdateListener} so that the important 37 * animation events can be observed through the callbacks. The start conditions for any subclass of 38 * DynamicAnimation can be set using {@link #setStartValue(float)} and 39 * {@link #setStartVelocity(float)}. 40 * 41 * @param <T> subclass of DynamicAnimation 42 */ 43 public abstract class DynamicAnimation<T extends DynamicAnimation<T>> 44 implements AnimationHandler.AnimationFrameCallback { 45 46 /** 47 * ViewProperty holds the access of a property of a {@link View}. When an animation is 48 * created with a {@link ViewProperty} instance, the corresponding property value of the view 49 * will be updated through this ViewProperty instance. 50 */ 51 public abstract static class ViewProperty extends FloatProperty<View> { ViewProperty(String name)52 private ViewProperty(String name) { 53 super(name); 54 } 55 } 56 57 /** 58 * View's translationX property. 59 */ 60 public static final ViewProperty TRANSLATION_X = new ViewProperty("translationX") { 61 @Override 62 public void setValue(View view, float value) { 63 view.setTranslationX(value); 64 } 65 66 @Override 67 public Float get(View view) { 68 return view.getTranslationX(); 69 } 70 }; 71 72 /** 73 * View's translationY property. 74 */ 75 public static final ViewProperty TRANSLATION_Y = new ViewProperty("translationY") { 76 @Override 77 public void setValue(View view, float value) { 78 view.setTranslationY(value); 79 } 80 81 @Override 82 public Float get(View view) { 83 return view.getTranslationY(); 84 } 85 }; 86 87 /** 88 * View's translationZ property. 89 */ 90 public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") { 91 @Override 92 public void setValue(View view, float value) { 93 view.setTranslationZ(value); 94 } 95 96 @Override 97 public Float get(View view) { 98 return view.getTranslationZ(); 99 } 100 }; 101 102 /** 103 * View's scaleX property. 104 */ 105 public static final ViewProperty SCALE_X = new ViewProperty("scaleX") { 106 @Override 107 public void setValue(View view, float value) { 108 view.setScaleX(value); 109 } 110 111 @Override 112 public Float get(View view) { 113 return view.getScaleX(); 114 } 115 }; 116 117 /** 118 * View's scaleY property. 119 */ 120 public static final ViewProperty SCALE_Y = new ViewProperty("scaleY") { 121 @Override 122 public void setValue(View view, float value) { 123 view.setScaleY(value); 124 } 125 126 @Override 127 public Float get(View view) { 128 return view.getScaleY(); 129 } 130 }; 131 132 /** 133 * View's rotation property. 134 */ 135 public static final ViewProperty ROTATION = new ViewProperty("rotation") { 136 @Override 137 public void setValue(View view, float value) { 138 view.setRotation(value); 139 } 140 141 @Override 142 public Float get(View view) { 143 return view.getRotation(); 144 } 145 }; 146 147 /** 148 * View's rotationX property. 149 */ 150 public static final ViewProperty ROTATION_X = new ViewProperty("rotationX") { 151 @Override 152 public void setValue(View view, float value) { 153 view.setRotationX(value); 154 } 155 156 @Override 157 public Float get(View view) { 158 return view.getRotationX(); 159 } 160 }; 161 162 /** 163 * View's rotationY property. 164 */ 165 public static final ViewProperty ROTATION_Y = new ViewProperty("rotationY") { 166 @Override 167 public void setValue(View view, float value) { 168 view.setRotationY(value); 169 } 170 171 @Override 172 public Float get(View view) { 173 return view.getRotationY(); 174 } 175 }; 176 177 /** 178 * View's x property. 179 */ 180 public static final ViewProperty X = new ViewProperty("x") { 181 @Override 182 public void setValue(View view, float value) { 183 view.setX(value); 184 } 185 186 @Override 187 public Float get(View view) { 188 return view.getX(); 189 } 190 }; 191 192 /** 193 * View's y property. 194 */ 195 public static final ViewProperty Y = new ViewProperty("y") { 196 @Override 197 public void setValue(View view, float value) { 198 view.setY(value); 199 } 200 201 @Override 202 public Float get(View view) { 203 return view.getY(); 204 } 205 }; 206 207 /** 208 * View's z property. 209 */ 210 public static final ViewProperty Z = new ViewProperty("z") { 211 @Override 212 public void setValue(View view, float value) { 213 view.setZ(value); 214 } 215 216 @Override 217 public Float get(View view) { 218 return view.getZ(); 219 } 220 }; 221 222 /** 223 * View's alpha property. 224 */ 225 public static final ViewProperty ALPHA = new ViewProperty("alpha") { 226 @Override 227 public void setValue(View view, float value) { 228 view.setAlpha(value); 229 } 230 231 @Override 232 public Float get(View view) { 233 return view.getAlpha(); 234 } 235 }; 236 237 // Properties below are not RenderThread compatible 238 /** 239 * View's scrollX property. 240 */ 241 public static final ViewProperty SCROLL_X = new ViewProperty("scrollX") { 242 @Override 243 public void setValue(View view, float value) { 244 view.setScrollX((int) value); 245 } 246 247 @Override 248 public Float get(View view) { 249 return (float) view.getScrollX(); 250 } 251 }; 252 253 /** 254 * View's scrollY property. 255 */ 256 public static final ViewProperty SCROLL_Y = new ViewProperty("scrollY") { 257 @Override 258 public void setValue(View view, float value) { 259 view.setScrollY((int) value); 260 } 261 262 @Override 263 public Float get(View view) { 264 return (float) view.getScrollY(); 265 } 266 }; 267 268 /** 269 * The minimum visible change in pixels that can be visible to users. 270 */ 271 @SuppressLint("MinMaxConstant") 272 public static final float MIN_VISIBLE_CHANGE_PIXELS = 1f; 273 /** 274 * The minimum visible change in degrees that can be visible to users. 275 */ 276 @SuppressLint("MinMaxConstant") 277 public static final float MIN_VISIBLE_CHANGE_ROTATION_DEGREES = 1f / 10f; 278 /** 279 * The minimum visible change in alpha that can be visible to users. 280 */ 281 @SuppressLint("MinMaxConstant") 282 public static final float MIN_VISIBLE_CHANGE_ALPHA = 1f / 256f; 283 /** 284 * The minimum visible change in scale that can be visible to users. 285 */ 286 @SuppressLint("MinMaxConstant") 287 public static final float MIN_VISIBLE_CHANGE_SCALE = 1f / 500f; 288 289 // Use the max value of float to indicate an unset state. 290 private static final float UNSET = Float.MAX_VALUE; 291 292 // Multiplier to the min visible change value for value threshold 293 private static final float THRESHOLD_MULTIPLIER = 0.75f; 294 295 // Internal tracking for velocity. 296 float mVelocity = 0; 297 298 // Internal tracking for value. 299 float mValue = UNSET; 300 301 // Tracks whether start value is set. If not, the animation will obtain the value at the time 302 // of starting through the getter and use that as the starting value of the animation. 303 boolean mStartValueIsSet = false; 304 305 // Target to be animated. 306 final Object mTarget; 307 308 // View property id. 309 final FloatProperty mProperty; 310 311 // Package private tracking of animation lifecycle state. Visible to subclass animations. 312 boolean mRunning = false; 313 314 // Min and max values that defines the range of the animation values. 315 float mMaxValue = Float.MAX_VALUE; 316 float mMinValue = -mMaxValue; 317 318 // Last frame time. Always gets reset to -1 at the end of the animation. 319 private long mLastFrameTime = 0; 320 321 private float mMinVisibleChange; 322 323 // List of end listeners 324 private final ArrayList<OnAnimationEndListener> mEndListeners = new ArrayList<>(); 325 326 // List of update listeners 327 private final ArrayList<OnAnimationUpdateListener> mUpdateListeners = new ArrayList<>(); 328 329 // Animation handler used to schedule updates for this animation. 330 private AnimationHandler mAnimationHandler; 331 332 // Internal state for value/velocity pair. 333 static class MassState { 334 float mValue; 335 float mVelocity; 336 } 337 338 /** 339 * Creates a dynamic animation with the given FloatValueHolder instance. 340 * 341 * @param floatValueHolder the FloatValueHolder instance to be animated. 342 */ DynamicAnimation(final FloatValueHolder floatValueHolder)343 DynamicAnimation(final FloatValueHolder floatValueHolder) { 344 mTarget = null; 345 mProperty = new FloatProperty("FloatValueHolder") { 346 @Override 347 public Float get(Object object) { 348 return floatValueHolder.getValue(); 349 } 350 351 @Override 352 public void setValue(Object object, float value) { 353 floatValueHolder.setValue(value); 354 } 355 }; 356 mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS; 357 } 358 359 /** 360 * Creates a dynamic animation to animate the given property for the given {@link View} 361 * 362 * @param object the Object whose property is to be animated 363 * @param property the property to be animated 364 */ 365 DynamicAnimation(K object, FloatProperty<K> property)366 <K> DynamicAnimation(K object, FloatProperty<K> property) { 367 mTarget = object; 368 mProperty = property; 369 if (mProperty == ROTATION || mProperty == ROTATION_X 370 || mProperty == ROTATION_Y) { 371 mMinVisibleChange = MIN_VISIBLE_CHANGE_ROTATION_DEGREES; 372 } else if (mProperty == ALPHA) { 373 mMinVisibleChange = MIN_VISIBLE_CHANGE_ALPHA; 374 } else if (mProperty == SCALE_X || mProperty == SCALE_Y) { 375 mMinVisibleChange = MIN_VISIBLE_CHANGE_SCALE; 376 } else { 377 mMinVisibleChange = MIN_VISIBLE_CHANGE_PIXELS; 378 } 379 } 380 381 /** 382 * Sets the start value of the animation. If start value is not set, the animation will get 383 * the current value for the view's property, and use that as the start value. 384 * 385 * @param startValue start value for the animation 386 * @return the Animation whose start value is being set 387 */ 388 @SuppressWarnings("unchecked") setStartValue(float startValue)389 public T setStartValue(float startValue) { 390 mValue = startValue; 391 mStartValueIsSet = true; 392 return (T) this; 393 } 394 395 /** 396 * Start velocity of the animation. Default velocity is 0. Unit: change in property per 397 * second (e.g. pixels per second, scale/alpha value change per second). 398 * 399 * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity 400 * through touch events), it is recommended to define such a value in dp/second and convert it 401 * to pixel/second based on the density of the screen to achieve a consistent look across 402 * different screens. 403 * 404 * <p>To convert from dp/second to pixel/second: 405 * <pre class="prettyprint"> 406 * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond, 407 * getResources().getDisplayMetrics()); 408 * </pre> 409 * 410 * @param startVelocity start velocity of the animation 411 * @return the Animation whose start velocity is being set 412 */ 413 @SuppressWarnings("unchecked") setStartVelocity(float startVelocity)414 public T setStartVelocity(float startVelocity) { 415 mVelocity = startVelocity; 416 return (T) this; 417 } 418 419 /** 420 * Sets the max value of the animation. Animations will not animate beyond their max value. 421 * Whether or not animation will come to an end when max value is reached is dependent on the 422 * child animation's implementation. 423 * 424 * @param max maximum value of the property to be animated 425 * @return the Animation whose max value is being set 426 */ 427 @SuppressWarnings("unchecked") setMaxValue(float max)428 public T setMaxValue(float max) { 429 // This max value should be checked and handled in the subclass animations, instead of 430 // assuming the end of the animations when the max/min value is hit in the base class. 431 // The reason is that hitting max/min value may just be a transient state, such as during 432 // the spring oscillation. 433 mMaxValue = max; 434 return (T) this; 435 } 436 437 /** 438 * Sets the min value of the animation. Animations will not animate beyond their min value. 439 * Whether or not animation will come to an end when min value is reached is dependent on the 440 * child animation's implementation. 441 * 442 * @param min minimum value of the property to be animated 443 * @return the Animation whose min value is being set 444 */ 445 @SuppressWarnings("unchecked") setMinValue(float min)446 public T setMinValue(float min) { 447 mMinValue = min; 448 return (T) this; 449 } 450 451 /** 452 * Adds an end listener to the animation for receiving onAnimationEnd callbacks. If the listener 453 * is {@code null} or has already been added to the list of listeners for the animation, no op. 454 * 455 * @param listener the listener to be added 456 * @return the animation to which the listener is added 457 */ 458 @SuppressWarnings("unchecked") addEndListener(OnAnimationEndListener listener)459 public T addEndListener(OnAnimationEndListener listener) { 460 if (!mEndListeners.contains(listener)) { 461 mEndListeners.add(listener); 462 } 463 return (T) this; 464 } 465 466 /** 467 * Removes the end listener from the animation, so as to stop receiving animation end callbacks. 468 * 469 * @param listener the listener to be removed 470 */ removeEndListener(OnAnimationEndListener listener)471 public void removeEndListener(OnAnimationEndListener listener) { 472 removeEntry(mEndListeners, listener); 473 } 474 475 /** 476 * Adds an update listener to the animation for receiving per-frame animation update callbacks. 477 * If the listener is {@code null} or has already been added to the list of listeners for the 478 * animation, no op. 479 * 480 * <p>Note that update listener should only be added before the start of the animation. 481 * 482 * @param listener the listener to be added 483 * @return the animation to which the listener is added 484 * @throws UnsupportedOperationException if the update listener is added after the animation has 485 * started 486 */ 487 @SuppressWarnings("unchecked") addUpdateListener(OnAnimationUpdateListener listener)488 public T addUpdateListener(OnAnimationUpdateListener listener) { 489 if (isRunning()) { 490 // Require update listener to be added before the animation, such as when we start 491 // the animation, we know whether the animation is RenderThread compatible. 492 throw new UnsupportedOperationException("Error: Update listeners must be added before" 493 + "the animation."); 494 } 495 if (!mUpdateListeners.contains(listener)) { 496 mUpdateListeners.add(listener); 497 } 498 return (T) this; 499 } 500 501 /** 502 * Removes the update listener from the animation, so as to stop receiving animation update 503 * callbacks. 504 * 505 * @param listener the listener to be removed 506 */ removeUpdateListener(OnAnimationUpdateListener listener)507 public void removeUpdateListener(OnAnimationUpdateListener listener) { 508 removeEntry(mUpdateListeners, listener); 509 } 510 511 512 /** 513 * This method sets the minimal change of animation value that is visible to users, which helps 514 * determine a reasonable threshold for the animation's termination condition. It is critical 515 * to set the minimal visible change for custom properties (i.e. non-<code>ViewProperty</code>s) 516 * unless the custom property is in pixels. 517 * 518 * <p>For custom properties, this minimum visible change defaults to change in pixel 519 * (i.e. {@link #MIN_VISIBLE_CHANGE_PIXELS}. It is recommended to adjust this value that is 520 * reasonable for the property to be animated. A general rule of thumb to calculate such a value 521 * is: minimum visible change = range of custom property value / corresponding pixel range. For 522 * example, if the property to be animated is a progress (from 0 to 100) that corresponds to a 523 * 200-pixel change. Then the min visible change should be 100 / 200. (i.e. 0.5). 524 * 525 * <p>It's not necessary to call this method when animating {@link ViewProperty}s, as the 526 * minimum visible change will be derived from the property. For example, if the property to be 527 * animated is in pixels (i.e. {@link #TRANSLATION_X}, {@link #TRANSLATION_Y}, 528 * {@link #TRANSLATION_Z}, @{@link #SCROLL_X} or {@link #SCROLL_Y}), the default minimum visible 529 * change is 1 (pixel). For {@link #ROTATION}, {@link #ROTATION_X} or {@link #ROTATION_Y}, the 530 * animation will use {@link #MIN_VISIBLE_CHANGE_ROTATION_DEGREES} as the min visible change, 531 * which is 1/10. Similarly, the minimum visible change for alpha ( 532 * i.e. {@link #MIN_VISIBLE_CHANGE_ALPHA} is defined as 1 / 256. 533 * 534 * @param minimumVisibleChange minimum change in property value that is visible to users 535 * @return the animation whose min visible change is being set 536 * @throws IllegalArgumentException if the given threshold is not positive 537 */ 538 @SuppressWarnings("unchecked") setMinimumVisibleChange(@loatRangefrom = 0.0, fromInclusive = false) float minimumVisibleChange)539 public T setMinimumVisibleChange(@FloatRange(from = 0.0, fromInclusive = false) 540 float minimumVisibleChange) { 541 if (minimumVisibleChange <= 0) { 542 throw new IllegalArgumentException("Minimum visible change must be positive."); 543 } 544 mMinVisibleChange = minimumVisibleChange; 545 setValueThreshold(minimumVisibleChange * THRESHOLD_MULTIPLIER); 546 return (T) this; 547 } 548 549 /** 550 * Returns the minimum change in the animation property that could be visibly different to 551 * users. 552 * 553 * @return minimum change in property value that is visible to users 554 */ getMinimumVisibleChange()555 public float getMinimumVisibleChange() { 556 return mMinVisibleChange; 557 } 558 559 /** 560 * Remove {@code null} entries from the list. 561 */ removeNullEntries(ArrayList<T> list)562 private static <T> void removeNullEntries(ArrayList<T> list) { 563 // Clean up null entries 564 for (int i = list.size() - 1; i >= 0; i--) { 565 if (list.get(i) == null) { 566 list.remove(i); 567 } 568 } 569 } 570 571 /** 572 * Remove an entry from the list by marking it {@code null} and clean up later. 573 */ removeEntry(ArrayList<T> list, T entry)574 private static <T> void removeEntry(ArrayList<T> list, T entry) { 575 int id = list.indexOf(entry); 576 if (id >= 0) { 577 list.set(id, null); 578 } 579 } 580 581 /****************Animation Lifecycle Management***************/ 582 583 /** 584 * Starts an animation. If the animation has already been started, no op. Note that calling 585 * {@link #start()} will not immediately set the property value to start value of the animation. 586 * The property values will be changed at each animation pulse, which happens before the draw 587 * pass. As a result, the changes will be reflected in the next frame, the same as if the values 588 * were set immediately. This method should only be called on main thread. 589 * 590 * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler 591 * is created on the same thread as the first call to start/cancel an animation. All the 592 * subsequent animation lifecycle manipulations need to be on that same thread, until the 593 * AnimationHandler is reset (using [setAnimationHandler]). 594 * 595 * @throws AndroidRuntimeException if this method is not called on the same thread as the 596 * animation handler 597 */ 598 @MainThread start()599 public void start() { 600 if (!isCurrentThread()) { 601 throw new AndroidRuntimeException("Animations may only be started on the same thread " 602 + "as the animation handler"); 603 } 604 if (!mRunning) { 605 startAnimationInternal(); 606 } 607 } 608 isCurrentThread()609 boolean isCurrentThread() { 610 return Thread.currentThread() == Looper.myLooper().getThread(); 611 } 612 613 /** 614 * Cancels the on-going animation. If the animation hasn't started, no op. 615 * 616 * Unless a AnimationHandler is provided via setAnimationHandler, a default AnimationHandler 617 * is created on the same thread as the first call to start/cancel an animation. All the 618 * subsequent animation lifecycle manipulations need to be on that same thread, until the 619 * AnimationHandler is reset (using [setAnimationHandler]). 620 * 621 * @throws AndroidRuntimeException if this method is not called on the same thread as the 622 * animation handler 623 */ 624 @MainThread cancel()625 public void cancel() { 626 if (!isCurrentThread()) { 627 throw new AndroidRuntimeException("Animations may only be canceled from the same " 628 + "thread as the animation handler"); 629 } 630 if (mRunning) { 631 endAnimationInternal(true); 632 } 633 } 634 635 /** 636 * Returns whether the animation is currently running. 637 * 638 * @return {@code true} if the animation is currently running, {@code false} otherwise 639 */ isRunning()640 public boolean isRunning() { 641 return mRunning; 642 } 643 644 /************************** Private APIs below ********************************/ 645 646 // This gets called when the animation is started, to finish the setup of the animation 647 // before the animation pulsing starts. startAnimationInternal()648 private void startAnimationInternal() { 649 if (!mRunning) { 650 mRunning = true; 651 if (!mStartValueIsSet) { 652 mValue = getPropertyValue(); 653 } 654 // Sanity check: 655 if (mValue > mMaxValue || mValue < mMinValue) { 656 throw new IllegalArgumentException("Starting value need to be in between min" 657 + " value and max value"); 658 } 659 getAnimationHandler().addAnimationFrameCallback(this, 0); 660 } 661 } 662 663 /** 664 * This gets call on each frame of the animation. Animation value and velocity are updated 665 * in this method based on the new frame time. The property value of the view being animated 666 * is then updated. The animation's ending conditions are also checked in this method. Once 667 * the animation reaches equilibrium, the animation will come to its end, and end listeners 668 * will be notified, if any. 669 */ 670 @Override doAnimationFrame(long frameTime)671 public boolean doAnimationFrame(long frameTime) { 672 if (mLastFrameTime == 0) { 673 // First frame. 674 mLastFrameTime = frameTime; 675 setPropertyValue(mValue); 676 return false; 677 } 678 long deltaT = frameTime - mLastFrameTime; 679 mLastFrameTime = frameTime; 680 float durationScale = ValueAnimator.getDurationScale(); 681 deltaT = durationScale == 0.0f ? Integer.MAX_VALUE : (long) (deltaT / durationScale); 682 boolean finished = updateValueAndVelocity(deltaT); 683 // Clamp value & velocity. 684 mValue = Math.min(mValue, mMaxValue); 685 mValue = Math.max(mValue, mMinValue); 686 687 setPropertyValue(mValue); 688 689 if (finished) { 690 endAnimationInternal(false); 691 } 692 return finished; 693 } 694 695 @Override commitAnimationFrame(long frameTime)696 public void commitAnimationFrame(long frameTime) { 697 doAnimationFrame(frameTime); 698 } 699 700 /** 701 * Updates the animation state (i.e. value and velocity). This method is package private, so 702 * subclasses can override this method to calculate the new value and velocity in their custom 703 * way. 704 * 705 * @param deltaT time elapsed in millisecond since last frame 706 * @return whether the animation has finished 707 */ updateValueAndVelocity(long deltaT)708 abstract boolean updateValueAndVelocity(long deltaT); 709 710 /** 711 * Internal method to reset the animation states when animation is finished/canceled. 712 */ endAnimationInternal(boolean canceled)713 private void endAnimationInternal(boolean canceled) { 714 mRunning = false; 715 getAnimationHandler().removeCallback(this); 716 mLastFrameTime = 0; 717 mStartValueIsSet = false; 718 for (int i = 0; i < mEndListeners.size(); i++) { 719 if (mEndListeners.get(i) != null) { 720 mEndListeners.get(i).onAnimationEnd(this, canceled, mValue, mVelocity); 721 } 722 } 723 removeNullEntries(mEndListeners); 724 } 725 726 /** 727 * Updates the property value through the corresponding setter. 728 */ 729 @SuppressWarnings("unchecked") setPropertyValue(float value)730 void setPropertyValue(float value) { 731 mProperty.setValue(mTarget, value); 732 for (int i = 0; i < mUpdateListeners.size(); i++) { 733 if (mUpdateListeners.get(i) != null) { 734 mUpdateListeners.get(i).onAnimationUpdate(this, mValue, mVelocity); 735 } 736 } 737 removeNullEntries(mUpdateListeners); 738 } 739 740 /** 741 * Returns the default threshold. 742 */ getValueThreshold()743 float getValueThreshold() { 744 return mMinVisibleChange * THRESHOLD_MULTIPLIER; 745 } 746 747 /** 748 * Obtain the property value through the corresponding getter. 749 */ 750 @SuppressWarnings("unchecked") getPropertyValue()751 private float getPropertyValue() { 752 return (Float) mProperty.get(mTarget); 753 } 754 755 /** 756 * Returns the {@link AnimationHandler} used to schedule updates for this animator. 757 * 758 * @return the {@link AnimationHandler} for this animator. 759 */ 760 @NonNull getAnimationHandler()761 public AnimationHandler getAnimationHandler() { 762 return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance(); 763 } 764 765 /****************Sub class animations**************/ 766 /** 767 * Returns the acceleration at the given value with the given velocity. 768 **/ getAcceleration(float value, float velocity)769 abstract float getAcceleration(float value, float velocity); 770 771 /** 772 * Returns whether the animation has reached equilibrium. 773 */ isAtEquilibrium(float value, float velocity)774 abstract boolean isAtEquilibrium(float value, float velocity); 775 776 /** 777 * Updates the default value threshold for the animation based on the property to be animated. 778 */ setValueThreshold(float threshold)779 abstract void setValueThreshold(float threshold); 780 781 /** 782 * An animation listener that receives end notifications from an animation. 783 */ 784 public interface OnAnimationEndListener { 785 /** 786 * Notifies the end of an animation. Note that this callback will be invoked not only when 787 * an animation reach equilibrium, but also when the animation is canceled. 788 * 789 * @param animation animation that has ended or was canceled 790 * @param canceled whether the animation has been canceled 791 * @param value the final value when the animation stopped 792 * @param velocity the final velocity when the animation stopped 793 */ onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity)794 void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, 795 float velocity); 796 } 797 798 /** 799 * Implementors of this interface can add themselves as update listeners 800 * to an <code>DynamicAnimation</code> instance to receive callbacks on every animation 801 * frame, after the current frame's values have been calculated for that 802 * <code>DynamicAnimation</code>. 803 */ 804 public interface OnAnimationUpdateListener { 805 806 /** 807 * Notifies the occurrence of another frame of the animation. 808 * 809 * @param animation animation that the update listener is added to 810 * @param value the current value of the animation 811 * @param velocity the current velocity of the animation 812 */ onAnimationUpdate(DynamicAnimation animation, float value, float velocity)813 void onAnimationUpdate(DynamicAnimation animation, float value, float velocity); 814 } 815 } 816