Android使用AnimationDrawable将URI列表以GIF形式展示问题
Hey there! Let's sort out this problem you're having. Your core idea of using AnimationDrawable to create a GIF-like effect from JPEG URIs is totally on the right track—you just hit a snag with how you're trying to use Glide alongside it.
Why the Glide Exception Happens
Glide is built to load and display individual image resources (bitmaps, drawables, native GIFs) efficiently, with caching and lifecycle handling. It doesn't support directly loading an AnimationDrawable because that's an Android system container for frame-by-frame animations, not a single image resource. Trying to pass it to Glide's into() method confuses the library, hence the exception.
Correct Implementation Steps
Instead, use Glide to load each individual URI into a Drawable, then assemble those Drawables into your AnimationDrawable manually, and finally attach it directly to your ImageView. Here's how to do it properly:
1. Initialize Your Animation and Frame Storage
First, set up the containers to hold your loaded frames and the animation itself:
private val loadedFrames = mutableListOf<Drawable>() private val frameAnimation = AnimationDrawable() private lateinit var targetImageView: ImageView
2. Load Frames with Glide and Build the Animation
Loop through your URI list, use Glide to load each one asynchronously, and add the loaded Drawables to your AnimationDrawable as frames:
val jpegUris: List<Uri> = // Your list of JPEG URIs targetImageView = findViewById(R.id.your_image_view_id) // Set frame duration (adjust this to control animation speed, e.g., 200ms per frame) val frameDurationMs = 200 jpegUris.forEachIndexed { index, uri -> Glide.with(this) // Optional: Resize images to avoid OOM with large frames .override(500, 500) .load(uri) .into(object : CustomTarget<Drawable>() { override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) { // Add the loaded drawable as a frame to the animation frameAnimation.addFrame(resource, frameDurationMs) loadedFrames.add(resource) // Once all frames are loaded, attach and start the animation if (loadedFrames.size == jpegUris.size) { targetImageView.setImageDrawable(frameAnimation) // Set to loop infinitely (set to true for one-time play) frameAnimation.isOneShot = false frameAnimation.start() } } override fun onLoadCleared(placeholder: Drawable?) { // Handle any cleanup if the load is canceled } override fun onLoadFailed(errorDrawable: Drawable?) { // Optional: Handle failed frame loads (skip or show placeholder) loadedFrames.add(errorDrawable ?: ColorDrawable(Color.GRAY)) if (loadedFrames.size == jpegUris.size) { targetImageView.setImageDrawable(frameAnimation) frameAnimation.isOneShot = false frameAnimation.start() } } }) }
3. Handle Lifecycle for Animation
Don't forget to start/stop the animation with your component's lifecycle to avoid unnecessary resource usage:
override fun onResume() { super.onResume() if (::frameAnimation.isInitialized) { frameAnimation.start() } } override fun onPause() { super.onPause() if (::frameAnimation.isInitialized) { frameAnimation.stop() } }
Key Notes
- Order Consistency: The above code maintains the order of your URI list since we process them in sequence and add frames as they load. If you need strict sequential loading (e.g., ensure frame 1 loads before frame 2), you can chain the Glide requests instead of launching them all at once.
- Memory Management: If you have a large number of high-resolution frames, use Glide's
override()method to resize them, or add adiskCacheStrategyto manage memory usage. - Error Handling: The added
onLoadFailed()override ensures your animation still plays even if some frames fail to load—you can customize this behavior to fit your needs.
This approach leverages Glide's strengths (reliable asynchronous image loading) while using AnimationDrawable for what it's designed for: frame-by-frame animation playback.
内容的提问来源于stack exchange,提问作者Shaxib

