


Use Handler to implement infinite loop and timed pause of view animation in Android
Oct 12, 2025 am 08:33 AMUnderstand the limitations of traditional animation loops
In Android, we usually define Animation objects through XML or code to add dynamic effects to views. For example, a simple rotation animation might look like this:
<!-- res/anim/shake_animation.xml --> <set xmlns:android="http://schemas.android.com/apk/res/android"> <rotate android:duration="30" android:fromdegrees="-2" android:pivotx="50%" android:pivoty="50%" android:repeatcount="20" android:repeatmode="reverse" android:todegrees="2"></rotate> </set>
In this shake_animation.xml definition:
- android:duration="30": Indicates that the duration of a single rotation (from -2 degrees to 2 degrees, or from 2 degrees to -2 degrees) is 30 milliseconds.
- android:repeatCount="20": Indicates that the animation will repeat 20 half-cycles. Since rotate goes from -2 to 2 in one half cycle, and from 2 to -2 in another half cycle, repeatCount="20" actually means 10 complete shakes (from -2 to 2 and back to -2).
- android:repeatMode="reverse": Indicates that the animation will play in reverse every time it is repeated.
When we apply this animation to a view, for example through viewToAnimate.startAnimation(shake);, it will immediately perform these 10 shakes, with a total duration of 30ms * 20 = 600ms. However, traditional Animation mechanisms (such as repeatCount) can only control the number of times the animation is repeated within a single startAnimation() call. It cannot realize the need to pause for a period of time after the animation is played, and then restart the entire animation cycle. If we need to wait for a long time (for example, 5 seconds) before restarting the animation after each complete playback, the function of Animation itself will be insufficient.
Core solution: Handler and postDelayed
In order to achieve infinite looping and regular pause of animation, we need to introduce Android's Handler and Runnable mechanisms. Handler allows us to send tasks (Runnable) to the message queue and execute these tasks after a specified time. Combined with the postDelayed() method, we can precisely control the startup timing of the animation.
The basic idea is:
- Define a Runnable that contains the code to load and start the animation.
- Use Handler.postDelayed() to schedule this Runnable for the first time and set the delay for the first animation.
- Inside the Runnable, after the animation starts, it uses Handler.postDelayed() to schedule itself again, thereby achieving infinite loops and pauses between each loop.
Implement infinite loops and timed pauses
The following is a code example that implements an infinite loop shaking animation of a view with a timed pause:
import android.content.Context; import android.os.Handler; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; public class AnimationScheduler { private final Context context; private final View viewToAnimate; private Handler animationHandler; private Runnable animationRunnable; // Assume the total animation duration is 600ms (30ms * 20 repeatCount) private static final long ANIMATION_DURATION = 600; // Pause time after the animation ends private static final long PAUSE_DURATION = 5000; // 5 seconds // Delay before the first animation starts private static final long INITIAL_DELAY = 5000; // 5 seconds public AnimationScheduler(Context context, View viewToAnimate) { this.context = context; this.viewToAnimate = viewToAnimate; this.animationHandler = new Handler(); initAnimationRunnable(); } private void initAnimationRunnable() { animationRunnable = new Runnable() { @Override public void run() { // 1. Load animation Animation shakeAnimation = AnimationUtils.loadAnimation(context, R.anim.shake_animation); // 2. Start animation viewToAnimate.startAnimation(shakeAnimation); // 3. Schedule the start of the next animation // The start time of the next animation = the current animation start time, the total animation duration, and the pause duration // Or simply, start from the current Runnable execution, delay (the total animation duration, pause duration) and then execute the Runnable again animationHandler.postDelayed(this, ANIMATION_DURATION PAUSE_DURATION); } }; } /** *Start infinite loop animation*/ public void startInfiniteAnimation() { // First dispatch, start animation after initial delay animationHandler.postDelayed(animationRunnable, INITIAL_DELAY); } /** * Stop infinite loop animation to prevent memory leaks */ public void stopInfiniteAnimation() { if (animationHandler != null && animationRunnable != null) { animationHandler.removeCallbacks(animationRunnable); } } }
Code explanation:
- AnimationScheduler class: Encapsulates animation scheduling logic in a class for easy management and reuse.
- ANIMATION_DURATION and PAUSE_DURATION:
- ANIMATION_DURATION is the total duration of actual playback of the animation defined in shake_animation.xml (30ms * 20 = 600ms).
- PAUSE_DURATION is the number of milliseconds we want the view to pause after each animation ends (for example, 5000ms = 5 seconds).
- Handler animationHandler = new Handler();: Create a Handler instance, which will be associated with the message queue of the thread that created it (usually the UI main thread).
- Runnable animationRunnable:
- Animation shakeAnimation = AnimationUtils.loadAnimation(context, R.anim.shake_animation);: Load and create a new animation instance every time Runnable is executed.
- viewToAnimate.startAnimation(shakeAnimation);: Start animation.
- animationHandler.postDelayed(this, ANIMATION_DURATION PAUSE_DURATION);: This is the key to implementing the loop. It tells the Handler to execute the current animationRunnable again after ANIMATION_DURATION PAUSE_DURATION milliseconds.
- ANIMATION_DURATION ensures that the animation has enough time to complete.
- PAUSE_DURATION provides additional pause time after the animation ends.
- Therefore, the total interval from the start of one animation to the start of the next is ANIMATION_DURATION PAUSE_DURATION. The actual pause time is PAUSE_DURATION.
- startInfiniteAnimation(): Call postDelayed() for the first time to start the entire loop. It uses INITIAL_DELAY to set the wait time before the first animation starts.
- stopInfiniteAnimation(): Provides a method to stop loop animation. Calling removeCallbacks(animationRunnable) can remove all pending animationRunnable tasks from the Handler's message queue, thereby stopping subsequent scheduling.
Usage example:
In an Activity or Fragment, you can use AnimationScheduler like this:
public class MyActivity extends AppCompatActivity { private AnimationScheduler scheduler; private View myItemView; // Assuming this is the view you want to shake @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myItemView = findViewById(R.id.my_item_view); // Replace with your view ID scheduler = new AnimationScheduler(this, myItemView); scheduler.startInfiniteAnimation(); } @Override protected void onDestroy() { super.onDestroy(); // Very important: stop animation when Activity/Fragment is destroyed to prevent memory leaks if (scheduler != null) { scheduler.stopInfiniteAnimation(); } } }
Things to note and best practices
-
Handler memory leak:
- The Handler will hold a reference to the Activity or Fragment in which it is located. If the Runnable contains references to these components and the Runnable is delayed, then when the Activity or Fragment is destroyed, the Handler will still hold their references, causing a memory leak.
- Solution: Be sure to call handler.removeCallbacks(runnable) in Activity's onDestroy() or Fragment's onDestroyView() to remove all pending tasks. In the AnimationScheduler example above, the stopInfiniteAnimation() method serves this purpose.
- A safer approach is to use WeakReference to reference the Context or View to avoid memory leaks caused by direct references.
-
Precise control of animation duration and scheduling intervals:
- As mentioned before, the delay in handler.postDelayed(runnable, delay) is calculated from when postDelayed is called.
- If you want the animation to pause for exactly X milliseconds after it ends, the second argument to postDelayed should be X the total duration of the animation.
- In AnimationScheduler we have achieved this via ANIMATION_DURATION PAUSE_DURATION.
-
Application in RecyclerView:
- If you want to achieve this effect on multiple list items of RecyclerView, you can create and start an AnimationScheduler instance for each ViewHolder that needs animation in the onBindViewHolder() method of RecyclerView.Adapter.
- Performance considerations: Running complex animations on a large number of visible items simultaneously may impact performance. Make sure the animation itself is lightweight enough, and stop the corresponding animation when the list item scrolls off the screen to save resources. You can call scheduler.stopInfiniteAnimation() in onViewRecycled().
- For unified animation of all visible items, you can consider triggering it through a global Handler outside RecyclerView, and then traverse the currently visible ViewHolder and start the animation, instead of maintaining an independent Handler for each ViewHolder.
-
Stop animation:
- In addition to stopping animations at the end of the component's life cycle, you may also want to stop animations when user interaction or other events are triggered. Just call scheduler.stopInfiniteAnimation().
-
Alternatives: ObjectAnimator and ValueAnimator:
- For more complex animation needs, especially when you need to animate property values ??rather than view transformations, ObjectAnimator and ValueAnimator provide more powerful control capabilities. They can directly manipulate the properties of the view (such as rotation, alpha, translationX, etc.), and can more easily implement animation monitoring and chained animations.
- Although this tutorial uses traditional Animation, Handler combined with ObjectAnimator can also achieve similar infinite loop and pause effects, and is usually more flexible.
Summarize
By cleverly combining the Handler and postDelayed methods, we can overcome the limitations of traditional Android Animation in loop control and achieve infinite loop playback of view animations with custom pause intervals. This approach provides a powerful tool for creating more interactive and engaging user interfaces. When using it, be sure to pay attention to the memory leak problem of Handler and choose the most appropriate animation implementation method according to actual needs.
The above is the detailed content of Use Handler to implement infinite loop and timed pause of view animation in Android. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

ArtGPT
AI image generator for creative art from text prompts.

Stock Market GPT
AI powered investment research for smarter decisions

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Use the -cp parameter to add the JAR to the classpath, so that the JVM can load its internal classes and resources, such as java-cplibrary.jarcom.example.Main, which supports multiple JARs separated by semicolons or colons, and can also be configured through CLASSPATH environment variables or MANIFEST.MF.

UseFile.createNewFile()tocreateafileonlyifitdoesn’texist,avoidingoverwriting;2.PreferFiles.createFile()fromNIO.2formodern,safefilecreationthatfailsifthefileexists;3.UseFileWriterorPrintWriterwhencreatingandimmediatelywritingcontent,withFileWriterover

JavaSPI is a built-in service discovery mechanism in JDK, and implements interface-oriented dynamic expansion through ServiceLoader. 1. Define the service interface and create a file with the full name of the interface under META-INF/services/, and write the fully qualified name of the implementation class; 2. Use ServiceLoader.load() to load the implementation class, and the JVM will automatically read the configuration and instantiate it; 3. The interface contract should be clarified during design, support priority and conditional loading, and provide default implementation; 4. Application scenarios include multi-payment channel access and plug-in verification; 5. Pay attention to performance, classpath, exception isolation, thread safety and version compatibility; 6. In Java9, provide can be used in combination with module systems.

Use the implements keyword to implement the interface. The class needs to provide specific implementations of all methods in the interface. It supports multiple interfaces and is separated by commas to ensure that the methods are public. The default and static methods after Java 8 do not need to be rewrite.

Javagenericsprovidecompile-timetypesafetyandeliminatecastingbyallowingtypeparametersonclasses,interfaces,andmethods;wildcards(?,?extendsType,?superType)handleunknowntypeswithflexibility.1.UseunboundedwildcardwhentypeisirrelevantandonlyreadingasObject

This article explores in-depth the mechanism of sending multiple HTTP requests on the same TCP Socket, namely, HTTP persistent connection (Keep-Alive). The article clarifies the difference between HTTP/1.x and HTTP/2 protocols, emphasizes the importance of server-side support for persistent connections, and how to correctly handle Connection: close response headers. By analyzing common errors and providing best practices, we aim to help developers build efficient and robust HTTP clients.

This tutorial details how to efficiently process nested ArrayLists containing other ArrayLists in Java and merge all its internal elements into a single array. The article will provide two core solutions through the flatMap operation of the Java 8 Stream API: first flattening into a list and then filling the array, and directly creating a new array to meet the needs of different scenarios.

The answer is to use Thread.currentThread().getStackTrace() to get the call method name, and obtain the someMethod name of the call anotherMethod through index 2. Since index 0 is getStackTrace, 1 is the current method, and 2 is the caller, the example output is "Calledbymethod:someMethod", which can also be implemented by Throwable, but attention should be paid to performance, obfuscation, security and inline impact.
