1 /* 2 * Copyright 2018 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 package androidx.core.view; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorListenerAdapter; 20 import android.animation.ValueAnimator; 21 import android.os.Build; 22 import android.view.View; 23 import android.view.animation.Interpolator; 24 25 import java.lang.ref.WeakReference; 26 27 public final class ViewPropertyAnimatorCompat { 28 private static final String TAG = "ViewAnimatorCompat"; 29 private WeakReference<View> mView; 30 Runnable mStartAction = null; 31 Runnable mEndAction = null; 32 int mOldLayerType = -1; 33 // HACK ALERT! Choosing this id knowing that the framework does not use it anywhere 34 // internally and apps should use ids higher than it 35 static final int LISTENER_TAG_ID = 0x7e000000; 36 ViewPropertyAnimatorCompat(View view)37 ViewPropertyAnimatorCompat(View view) { 38 mView = new WeakReference<View>(view); 39 } 40 41 static class ViewPropertyAnimatorListenerApi14 implements ViewPropertyAnimatorListener { 42 ViewPropertyAnimatorCompat mVpa; 43 boolean mAnimEndCalled; 44 ViewPropertyAnimatorListenerApi14(ViewPropertyAnimatorCompat vpa)45 ViewPropertyAnimatorListenerApi14(ViewPropertyAnimatorCompat vpa) { 46 mVpa = vpa; 47 } 48 49 @Override onAnimationStart(View view)50 public void onAnimationStart(View view) { 51 // Reset our end called flag, since this is a new animation... 52 mAnimEndCalled = false; 53 54 if (mVpa.mOldLayerType > -1) { 55 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 56 } 57 if (mVpa.mStartAction != null) { 58 Runnable startAction = mVpa.mStartAction; 59 mVpa.mStartAction = null; 60 startAction.run(); 61 } 62 Object listenerTag = view.getTag(LISTENER_TAG_ID); 63 ViewPropertyAnimatorListener listener = null; 64 if (listenerTag instanceof ViewPropertyAnimatorListener) { 65 listener = (ViewPropertyAnimatorListener) listenerTag; 66 } 67 if (listener != null) { 68 listener.onAnimationStart(view); 69 } 70 } 71 72 @Override onAnimationEnd(View view)73 public void onAnimationEnd(View view) { 74 if (mVpa.mOldLayerType > -1) { 75 view.setLayerType(mVpa.mOldLayerType, null); 76 mVpa.mOldLayerType = -1; 77 } 78 if (Build.VERSION.SDK_INT >= 16 || !mAnimEndCalled) { 79 // Pre-v16 seems to have a bug where onAnimationEnd is called 80 // twice, therefore we only dispatch on the first call 81 if (mVpa.mEndAction != null) { 82 Runnable endAction = mVpa.mEndAction; 83 mVpa.mEndAction = null; 84 endAction.run(); 85 } 86 Object listenerTag = view.getTag(LISTENER_TAG_ID); 87 ViewPropertyAnimatorListener listener = null; 88 if (listenerTag instanceof ViewPropertyAnimatorListener) { 89 listener = (ViewPropertyAnimatorListener) listenerTag; 90 } 91 if (listener != null) { 92 listener.onAnimationEnd(view); 93 } 94 mAnimEndCalled = true; 95 } 96 } 97 98 @Override onAnimationCancel(View view)99 public void onAnimationCancel(View view) { 100 Object listenerTag = view.getTag(LISTENER_TAG_ID); 101 ViewPropertyAnimatorListener listener = null; 102 if (listenerTag instanceof ViewPropertyAnimatorListener) { 103 listener = (ViewPropertyAnimatorListener) listenerTag; 104 } 105 if (listener != null) { 106 listener.onAnimationCancel(view); 107 } 108 } 109 } 110 111 /** 112 * Sets the duration for the underlying animator that animates the requested properties. 113 * By default, the animator uses the default value for ValueAnimator. Calling this method 114 * will cause the declared value to be used instead. 115 * 116 * @param value The length of ensuing property animations, in milliseconds. The value 117 * cannot be negative. 118 * @return This object, allowing calls to methods in this class to be chained. 119 */ setDuration(long value)120 public ViewPropertyAnimatorCompat setDuration(long value) { 121 View view; 122 if ((view = mView.get()) != null) { 123 view.animate().setDuration(value); 124 } 125 return this; 126 } 127 128 /** 129 * This method will cause the View's <code>alpha</code> property to be animated to the 130 * specified value. Animations already running on the property will be canceled. 131 * 132 * @param value The value to be animated to. 133 * @return This object, allowing calls to methods in this class to be chained. 134 */ alpha(float value)135 public ViewPropertyAnimatorCompat alpha(float value) { 136 View view; 137 if ((view = mView.get()) != null) { 138 view.animate().alpha(value); 139 } 140 return this; 141 } 142 143 /** 144 * This method will cause the View's <code>alpha</code> property to be animated by the 145 * specified value. Animations already running on the property will be canceled. 146 * 147 * @param value The amount to be animated by, as an offset from the current value. 148 * @return This object, allowing calls to methods in this class to be chained. 149 */ alphaBy(float value)150 public ViewPropertyAnimatorCompat alphaBy(float value) { 151 View view; 152 if ((view = mView.get()) != null) { 153 view.animate().alphaBy(value); 154 } 155 return this; 156 } 157 158 /** 159 * This method will cause the View's <code>translationX</code> property to be animated to the 160 * specified value. Animations already running on the property will be canceled. 161 * 162 * @param value The value to be animated to. 163 * @return This object, allowing calls to methods in this class to be chained. 164 */ translationX(float value)165 public ViewPropertyAnimatorCompat translationX(float value) { 166 View view; 167 if ((view = mView.get()) != null) { 168 view.animate().translationX(value); 169 } 170 return this; 171 } 172 173 /** 174 * This method will cause the View's <code>translationY</code> property to be animated to the 175 * specified value. Animations already running on the property will be canceled. 176 * 177 * @param value The value to be animated to. 178 * @return This object, allowing calls to methods in this class to be chained. 179 */ translationY(float value)180 public ViewPropertyAnimatorCompat translationY(float value) { 181 View view; 182 if ((view = mView.get()) != null) { 183 view.animate().translationY(value); 184 } 185 return this; 186 } 187 188 /** 189 * Specifies an action to take place when the next animation ends. The action is only 190 * run if the animation ends normally; if the ViewPropertyAnimator is canceled during 191 * that animation, the runnable will not run. 192 * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate 193 * choreographing ViewPropertyAnimator animations with other animations or actions 194 * in the application. 195 * 196 * <p>For example, the following code animates a view to x=200 and then back to 0:</p> 197 * <pre> 198 * Runnable endAction = new Runnable() { 199 * public void run() { 200 * view.animate().x(0); 201 * } 202 * }; 203 * view.animate().x(200).withEndAction(endAction); 204 * </pre> 205 * 206 * <p>For API 14 and 15, this method will run by setting 207 * a listener on the ViewPropertyAnimatorCompat object and running the action 208 * in that listener's {@link ViewPropertyAnimatorListener#onAnimationEnd(View)} method.</p> 209 * 210 * @param runnable The action to run when the next animation ends. 211 * @return This object, allowing calls to methods in this class to be chained. 212 */ withEndAction(Runnable runnable)213 public ViewPropertyAnimatorCompat withEndAction(Runnable runnable) { 214 View view; 215 if ((view = mView.get()) != null) { 216 if (Build.VERSION.SDK_INT >= 16) { 217 view.animate().withEndAction(runnable); 218 } else { 219 setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this)); 220 mEndAction = runnable; 221 } 222 } 223 return this; 224 } 225 226 /** 227 * Returns the current duration of property animations. If the duration was set on this 228 * object, that value is returned. Otherwise, the default value of the underlying Animator 229 * is returned. 230 * 231 * @see #setDuration(long) 232 * @return The duration of animations, in milliseconds. 233 */ getDuration()234 public long getDuration() { 235 View view; 236 if ((view = mView.get()) != null) { 237 return view.animate().getDuration(); 238 } else { 239 return 0; 240 } 241 } 242 243 /** 244 * Sets the interpolator for the underlying animator that animates the requested properties. 245 * By default, the animator uses the default interpolator for ValueAnimator. Calling this method 246 * will cause the declared object to be used instead. 247 * 248 * @param value The TimeInterpolator to be used for ensuing property animations. 249 * @return This object, allowing calls to methods in this class to be chained. 250 */ setInterpolator(Interpolator value)251 public ViewPropertyAnimatorCompat setInterpolator(Interpolator value) { 252 View view; 253 if ((view = mView.get()) != null) { 254 view.animate().setInterpolator(value); 255 } 256 return this; 257 } 258 259 /** 260 * Returns the timing interpolator that this animation uses. 261 * 262 * @return The timing interpolator for this animation. 263 */ getInterpolator()264 public Interpolator getInterpolator() { 265 View view; 266 if ((view = mView.get()) != null) { 267 if (Build.VERSION.SDK_INT >= 18) { 268 return (Interpolator) view.animate().getInterpolator(); 269 } 270 } 271 return null; 272 } 273 274 /** 275 * Sets the startDelay for the underlying animator that animates the requested properties. 276 * By default, the animator uses the default value for ValueAnimator. Calling this method 277 * will cause the declared value to be used instead. 278 * 279 * @param value The delay of ensuing property animations, in milliseconds. The value 280 * cannot be negative. 281 * @return This object, allowing calls to methods in this class to be chained. 282 */ setStartDelay(long value)283 public ViewPropertyAnimatorCompat setStartDelay(long value) { 284 View view; 285 if ((view = mView.get()) != null) { 286 view.animate().setStartDelay(value); 287 } 288 return this; 289 } 290 291 /** 292 * Returns the current startDelay of property animations. If the startDelay was set on this 293 * object, that value is returned. Otherwise, the default value of the underlying Animator 294 * is returned. 295 * 296 * @see #setStartDelay(long) 297 * @return The startDelay of animations, in milliseconds. 298 */ getStartDelay()299 public long getStartDelay() { 300 View view; 301 if ((view = mView.get()) != null) { 302 return view.animate().getStartDelay(); 303 } else { 304 return 0; 305 } 306 } 307 308 /** 309 * This method will cause the View's <code>rotation</code> property to be animated to the 310 * specified value. Animations already running on the property will be canceled. 311 * 312 * @param value The value to be animated to. 313 * @return This object, allowing calls to methods in this class to be chained. 314 */ rotation(float value)315 public ViewPropertyAnimatorCompat rotation(float value) { 316 View view; 317 if ((view = mView.get()) != null) { 318 view.animate().rotation(value); 319 } 320 return this; 321 } 322 323 /** 324 * This method will cause the View's <code>rotation</code> property to be animated by the 325 * specified value. Animations already running on the property will be canceled. 326 * 327 * @param value The amount to be animated by, as an offset from the current value. 328 * @return This object, allowing calls to methods in this class to be chained. 329 */ rotationBy(float value)330 public ViewPropertyAnimatorCompat rotationBy(float value) { 331 View view; 332 if ((view = mView.get()) != null) { 333 view.animate().rotationBy(value); 334 } 335 return this; 336 } 337 338 /** 339 * This method will cause the View's <code>rotationX</code> property to be animated to the 340 * specified value. Animations already running on the property will be canceled. 341 * 342 * @param value The value to be animated to. 343 * @return This object, allowing calls to methods in this class to be chained. 344 */ rotationX(float value)345 public ViewPropertyAnimatorCompat rotationX(float value) { 346 View view; 347 if ((view = mView.get()) != null) { 348 view.animate().rotationX(value); 349 } 350 return this; 351 } 352 353 /** 354 * This method will cause the View's <code>rotationX</code> property to be animated by the 355 * specified value. Animations already running on the property will be canceled. 356 * 357 * @param value The amount to be animated by, as an offset from the current value. 358 * @return This object, allowing calls to methods in this class to be chained. 359 */ rotationXBy(float value)360 public ViewPropertyAnimatorCompat rotationXBy(float value) { 361 View view; 362 if ((view = mView.get()) != null) { 363 view.animate().rotationXBy(value); 364 } 365 return this; 366 } 367 368 /** 369 * This method will cause the View's <code>rotationY</code> property to be animated to the 370 * specified value. Animations already running on the property will be canceled. 371 * 372 * @param value The value to be animated to. 373 * @return This object, allowing calls to methods in this class to be chained. 374 */ rotationY(float value)375 public ViewPropertyAnimatorCompat rotationY(float value) { 376 View view; 377 if ((view = mView.get()) != null) { 378 view.animate().rotationY(value); 379 } 380 return this; 381 } 382 383 /** 384 * This method will cause the View's <code>rotationY</code> property to be animated by the 385 * specified value. Animations already running on the property will be canceled. 386 * 387 * @param value The amount to be animated by, as an offset from the current value. 388 * @return This object, allowing calls to methods in this class to be chained. 389 */ rotationYBy(float value)390 public ViewPropertyAnimatorCompat rotationYBy(float value) { 391 View view; 392 if ((view = mView.get()) != null) { 393 view.animate().rotationYBy(value); 394 } 395 return this; 396 } 397 398 /** 399 * This method will cause the View's <code>scaleX</code> property to be animated to the 400 * specified value. Animations already running on the property will be canceled. 401 * 402 * @param value The value to be animated to. 403 * @return This object, allowing calls to methods in this class to be chained. 404 */ scaleX(float value)405 public ViewPropertyAnimatorCompat scaleX(float value) { 406 View view; 407 if ((view = mView.get()) != null) { 408 view.animate().scaleX(value); 409 } 410 return this; 411 } 412 413 /** 414 * This method will cause the View's <code>scaleX</code> property to be animated by the 415 * specified value. Animations already running on the property will be canceled. 416 * 417 * @param value The amount to be animated by, as an offset from the current value. 418 * @return This object, allowing calls to methods in this class to be chained. 419 */ scaleXBy(float value)420 public ViewPropertyAnimatorCompat scaleXBy(float value) { 421 View view; 422 if ((view = mView.get()) != null) { 423 view.animate().scaleXBy(value); 424 } 425 return this; 426 } 427 428 /** 429 * This method will cause the View's <code>scaleY</code> property to be animated to the 430 * specified value. Animations already running on the property will be canceled. 431 * 432 * @param value The value to be animated to. 433 * @return This object, allowing calls to methods in this class to be chained. 434 */ scaleY(float value)435 public ViewPropertyAnimatorCompat scaleY(float value) { 436 View view; 437 if ((view = mView.get()) != null) { 438 view.animate().scaleY(value); 439 } 440 return this; 441 } 442 443 /** 444 * This method will cause the View's <code>scaleY</code> property to be animated by the 445 * specified value. Animations already running on the property will be canceled. 446 * 447 * @param value The amount to be animated by, as an offset from the current value. 448 * @return This object, allowing calls to methods in this class to be chained. 449 */ scaleYBy(float value)450 public ViewPropertyAnimatorCompat scaleYBy(float value) { 451 View view; 452 if ((view = mView.get()) != null) { 453 view.animate().scaleYBy(value); 454 } 455 return this; 456 } 457 458 /** 459 * Cancels all property animations that are currently running or pending. 460 */ cancel()461 public void cancel() { 462 View view; 463 if ((view = mView.get()) != null) { 464 view.animate().cancel(); 465 } 466 } 467 468 /** 469 * This method will cause the View's <code>x</code> property to be animated to the 470 * specified value. Animations already running on the property will be canceled. 471 * 472 * @param value The value to be animated to. 473 * @return This object, allowing calls to methods in this class to be chained. 474 */ x(float value)475 public ViewPropertyAnimatorCompat x(float value) { 476 View view; 477 if ((view = mView.get()) != null) { 478 view.animate().x(value); 479 } 480 return this; 481 } 482 483 /** 484 * This method will cause the View's <code>x</code> property to be animated by the 485 * specified value. Animations already running on the property will be canceled. 486 * 487 * @param value The amount to be animated by, as an offset from the current value. 488 * @return This object, allowing calls to methods in this class to be chained. 489 */ xBy(float value)490 public ViewPropertyAnimatorCompat xBy(float value) { 491 View view; 492 if ((view = mView.get()) != null) { 493 view.animate().xBy(value); 494 } 495 return this; 496 } 497 498 /** 499 * This method will cause the View's <code>y</code> property to be animated to the 500 * specified value. Animations already running on the property will be canceled. 501 * 502 * @param value The value to be animated to. 503 * @return This object, allowing calls to methods in this class to be chained. 504 */ y(float value)505 public ViewPropertyAnimatorCompat y(float value) { 506 View view; 507 if ((view = mView.get()) != null) { 508 view.animate().y(value); 509 } 510 return this; 511 } 512 513 /** 514 * This method will cause the View's <code>y</code> property to be animated by the 515 * specified value. Animations already running on the property will be canceled. 516 * 517 * @param value The amount to be animated by, as an offset from the current value. 518 * @return This object, allowing calls to methods in this class to be chained. 519 */ yBy(float value)520 public ViewPropertyAnimatorCompat yBy(float value) { 521 View view; 522 if ((view = mView.get()) != null) { 523 view.animate().yBy(value); 524 } 525 return this; 526 } 527 528 /** 529 * This method will cause the View's <code>translationX</code> property to be animated by the 530 * specified value. Animations already running on the property will be canceled. 531 * 532 * @param value The amount to be animated by, as an offset from the current value. 533 * @return This object, allowing calls to methods in this class to be chained. 534 */ translationXBy(float value)535 public ViewPropertyAnimatorCompat translationXBy(float value) { 536 View view; 537 if ((view = mView.get()) != null) { 538 view.animate().translationXBy(value); 539 } 540 return this; 541 } 542 543 /** 544 * This method will cause the View's <code>translationY</code> property to be animated by the 545 * specified value. Animations already running on the property will be canceled. 546 * 547 * @param value The amount to be animated by, as an offset from the current value. 548 * @return This object, allowing calls to methods in this class to be chained. 549 */ translationYBy(float value)550 public ViewPropertyAnimatorCompat translationYBy(float value) { 551 View view; 552 if ((view = mView.get()) != null) { 553 view.animate().translationYBy(value); 554 } 555 return this; 556 } 557 558 /** 559 * This method will cause the View's <code>translationZ</code> property to be animated by the 560 * specified value. Animations already running on the property will be canceled. 561 * 562 * <p>Prior to API 21, this method will do nothing.</p> 563 * 564 * @param value The amount to be animated by, as an offset from the current value. 565 * @return This object, allowing calls to methods in this class to be chained. 566 */ translationZBy(float value)567 public ViewPropertyAnimatorCompat translationZBy(float value) { 568 View view; 569 if ((view = mView.get()) != null) { 570 if (Build.VERSION.SDK_INT >= 21) { 571 view.animate().translationZBy(value); 572 } 573 } 574 return this; 575 } 576 577 /** 578 * This method will cause the View's <code>translationZ</code> property to be animated to the 579 * specified value. Animations already running on the property will be canceled. 580 * 581 * <p>Prior to API 21, this method will do nothing.</p> 582 * 583 * @param value The amount to be animated by, as an offset from the current value. 584 * @return This object, allowing calls to methods in this class to be chained. 585 */ translationZ(float value)586 public ViewPropertyAnimatorCompat translationZ(float value) { 587 View view; 588 if ((view = mView.get()) != null) { 589 if (Build.VERSION.SDK_INT >= 21) { 590 view.animate().translationZ(value); 591 } 592 } 593 return this; 594 } 595 596 /** 597 * This method will cause the View's <code>z</code> property to be animated to the 598 * specified value. Animations already running on the property will be canceled. 599 * 600 * <p>Prior to API 21, this method will do nothing.</p> 601 * 602 * @param value The amount to be animated by, as an offset from the current value. 603 * @return This object, allowing calls to methods in this class to be chained. 604 */ z(float value)605 public ViewPropertyAnimatorCompat z(float value) { 606 View view; 607 if ((view = mView.get()) != null) { 608 if (Build.VERSION.SDK_INT >= 21) { 609 view.animate().z(value); 610 } 611 } 612 return this; 613 } 614 615 /** 616 * This method will cause the View's <code>z</code> property to be animated by the 617 * specified value. Animations already running on the property will be canceled. 618 * 619 * <p>Prior to API 21, this method will do nothing.</p> 620 * 621 * @param value The amount to be animated by, as an offset from the current value. 622 * @return This object, allowing calls to methods in this class to be chained. 623 */ zBy(float value)624 public ViewPropertyAnimatorCompat zBy(float value) { 625 View view; 626 if ((view = mView.get()) != null) { 627 if (Build.VERSION.SDK_INT >= 21) { 628 view.animate().zBy(value); 629 } 630 } 631 return this; 632 } 633 634 /** 635 * Starts the currently pending property animations immediately. Calling <code>start()</code> 636 * is optional because all animations start automatically at the next opportunity. However, 637 * if the animations are needed to start immediately and synchronously (not at the time when 638 * the next event is processed by the hierarchy, which is when the animations would begin 639 * otherwise), then this method can be used. 640 */ start()641 public void start() { 642 View view; 643 if ((view = mView.get()) != null) { 644 view.animate().start(); 645 } 646 } 647 648 /** 649 * The View associated with this ViewPropertyAnimator will have its 650 * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to 651 * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. 652 * As stated in the documentation for {@link View#LAYER_TYPE_HARDWARE}, 653 * the actual type of layer used internally depends on the runtime situation of the 654 * view. If the activity and this view are hardware-accelerated, then the layer will be 655 * accelerated as well. If the activity or the view is not accelerated, then the layer will 656 * effectively be the same as {@link View#LAYER_TYPE_SOFTWARE}. 657 * 658 * <p>This state is not persistent, either on the View or on this ViewPropertyAnimator: the 659 * layer type of the View will be restored when the animation ends to what it was when this 660 * method was called, and this setting on ViewPropertyAnimator is only valid for the next 661 * animation. Note that calling this method and then independently setting the layer type of 662 * the View (by a direct call to 663 * {@link View#setLayerType(int, android.graphics.Paint)}) will result in some 664 * inconsistency, including having the layer type restored to its pre-withLayer() 665 * value when the animation ends.</p> 666 * 667 * <p>For API 14 and 15, this method will run by setting 668 * a listener on the ViewPropertyAnimatorCompat object, setting a hardware layer in 669 * the listener's {@link ViewPropertyAnimatorListener#onAnimationStart(View)} method, 670 * and then restoring the orignal layer type in the listener's 671 * {@link ViewPropertyAnimatorListener#onAnimationEnd(View)} method.</p> 672 * 673 * @see View#setLayerType(int, android.graphics.Paint) 674 * @return This object, allowing calls to methods in this class to be chained. 675 */ withLayer()676 public ViewPropertyAnimatorCompat withLayer() { 677 View view; 678 if ((view = mView.get()) != null) { 679 if (Build.VERSION.SDK_INT >= 16) { 680 view.animate().withLayer(); 681 } else { 682 mOldLayerType = view.getLayerType(); 683 setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this)); 684 } 685 } 686 return this; 687 } 688 689 /** 690 * Specifies an action to take place when the next animation runs. If there is a 691 * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the 692 * action will run after that startDelay expires, when the actual animation begins. 693 * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate 694 * choreographing ViewPropertyAnimator animations with other animations or actions 695 * in the application. 696 * 697 * <p>For API 14 and 15, this method will run by setting 698 * a listener on the ViewPropertyAnimatorCompat object and running the action 699 * in that listener's {@link ViewPropertyAnimatorListener#onAnimationStart(View)} method.</p> 700 * 701 * @param runnable The action to run when the next animation starts. 702 * @return This object, allowing calls to methods in this class to be chained. 703 */ withStartAction(Runnable runnable)704 public ViewPropertyAnimatorCompat withStartAction(Runnable runnable) { 705 View view; 706 if ((view = mView.get()) != null) { 707 if (Build.VERSION.SDK_INT >= 16) { 708 view.animate().withStartAction(runnable); 709 } else { 710 setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this)); 711 mStartAction = runnable; 712 } 713 } 714 return this; 715 } 716 717 /** 718 * Sets a listener for events in the underlying Animators that run the property 719 * animations. 720 * 721 * @param listener The listener to be called with AnimatorListener events. A value of 722 * <code>null</code> removes any existing listener. 723 * @return This object, allowing calls to methods in this class to be chained. 724 */ setListener(final ViewPropertyAnimatorListener listener)725 public ViewPropertyAnimatorCompat setListener(final ViewPropertyAnimatorListener listener) { 726 final View view; 727 if ((view = mView.get()) != null) { 728 if (Build.VERSION.SDK_INT >= 16) { 729 setListenerInternal(view, listener); 730 } else { 731 view.setTag(LISTENER_TAG_ID, listener); 732 setListenerInternal(view, new ViewPropertyAnimatorListenerApi14(this)); 733 } 734 } 735 return this; 736 } 737 setListenerInternal(final View view, final ViewPropertyAnimatorListener listener)738 private void setListenerInternal(final View view, final ViewPropertyAnimatorListener listener) { 739 if (listener != null) { 740 view.animate().setListener(new AnimatorListenerAdapter() { 741 @Override 742 public void onAnimationCancel(Animator animation) { 743 listener.onAnimationCancel(view); 744 } 745 746 @Override 747 public void onAnimationEnd(Animator animation) { 748 listener.onAnimationEnd(view); 749 } 750 751 @Override 752 public void onAnimationStart(Animator animation) { 753 listener.onAnimationStart(view); 754 } 755 }); 756 } else { 757 view.animate().setListener(null); 758 } 759 } 760 761 /** 762 * Sets a listener for update events in the underlying Animator that runs 763 * the property animations. 764 * 765 * <p>Prior to API 19, this method will do nothing.</p> 766 * 767 * @param listener The listener to be called with update events. A value of 768 * <code>null</code> removes any existing listener. 769 * @return This object, allowing calls to methods in this class to be chained. 770 */ setUpdateListener( final ViewPropertyAnimatorUpdateListener listener)771 public ViewPropertyAnimatorCompat setUpdateListener( 772 final ViewPropertyAnimatorUpdateListener listener) { 773 final View view; 774 if ((view = mView.get()) != null) { 775 if (Build.VERSION.SDK_INT >= 19) { 776 ValueAnimator.AnimatorUpdateListener wrapped = null; 777 if (listener != null) { 778 wrapped = new ValueAnimator.AnimatorUpdateListener() { 779 @Override 780 public void onAnimationUpdate(ValueAnimator valueAnimator) { 781 listener.onAnimationUpdate(view); 782 } 783 }; 784 } 785 view.animate().setUpdateListener(wrapped); 786 } 787 } 788 return this; 789 } 790 } 791