Android 是否可以让新的ImageDecoder类手动一帧接一帧地返回位图? 背景

Android 是否可以让新的ImageDecoder类手动一帧接一帧地返回位图? 背景,android,animated-gif,imagedecoder,animated-webp,Android,Animated Gif,Imagedecoder,Animated Webp,我正在尝试手动(逐帧)浏览动画GIF和WEBP文件的位图,这样它不仅适用于视图,也适用于其他情况(如实时壁纸) 问题 只有Android p使用ImageDecoder API支持动画GIF/WEBP文件(示例) 对于GIF,我想尝试使用Glide来完成任务,但我失败了,所以我尝试通过使用允许加载它们的库(,解决方案)来克服这一点。我认为它很好用 对于WebP,我认为我已经找到了另一个可以在较旧的Android版本(,made fork)上工作的库,但在某些情况下它似乎不能很好地处理WebP文件

我正在尝试手动(逐帧)浏览动画GIF和WEBP文件的位图,这样它不仅适用于视图,也适用于其他情况(如实时壁纸)

问题 只有Android p使用ImageDecoder API支持动画GIF/WEBP文件(示例)

对于GIF,我想尝试使用Glide来完成任务,但我失败了,所以我尝试通过使用允许加载它们的库(,解决方案)来克服这一点。我认为它很好用

对于WebP,我认为我已经找到了另一个可以在较旧的Android版本(,made fork)上工作的库,但在某些情况下它似乎不能很好地处理WebP文件(报告)。我试图找出问题所在以及解决方法,但没有成功

因此,假设有一天谷歌将通过支持库(他们编写的)为旧版Android版本支持GIF和WEBP动画,我决定尝试使用ImageDecoder来完成这项任务

问题是,从ImageDecoder的整个API来看,我们应该如何使用它是非常有限的。我不知道如何克服它的局限性

我发现了什么 这就是如何使用ImageDecoder在ImageView上显示动画WebP(当然,只是一个示例):

class MainActivity:AppCompatActivity(){
@SuppressLint(“StaticFieldLeak”)
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val source=ImageDecoder.createSource(参考资料,R.raw.test)
对象:AsyncTask(){
覆盖有趣的背景(vararg参数:Void?):可绘制{
回击{
ImageDecoder.decodeDrawable(源)
}捕获(e:例外){
无效的
}
}
重写onPostExecute(结果:可绘制?){
super.onPostExecute(结果)
imageView.setImageDrawable(结果)
如果(结果为动画可绘制){
result.start()
}
}
}.execute()
}
}
我试着阅读了和的所有文档,也查看了它的代码,但我不知道如何手动检查每一帧,并在它们之间留出需要等待的时间

问题
  • 有没有一种方法可以使用ImageDecoderAPI手动检查每一帧,得到一个位图来绘制,并知道帧之间需要等待多长时间?有解决办法吗?甚至可以使用AnimatedImageDrawable

  • 我想在旧的Android版本上也这么做。可能吗?如果是,怎么做?可能在不同的API/库上?谷歌写了一篇文章,试图在旧的Android版本上使用ImageDecoder,但我看不到任何地方提到它(除了我提供的链接)。可能还没准备好。。。安卓P甚至还没有达到0.1%的用户。。。也许你能做到?我也尝试过在那里检查它,但我看不出它也能做这样的事情,而且它是一个巨大的库,仅用于此任务,所以我宁愿使用不同的库来代替。。。我也知道libwebp是可用的,但它是C/C++的,不确定它是否适合Android,也不确定它在Android的Java/Kotlin上是否有端口


  • 编辑:

    因为我想我得到了我想要的,对于第三方库和ImageDecoder,能够从动画WebP中获取位图,我仍然想知道如何使用ImageDecoder获取帧计数和当前帧,如果可能的话。我尝试使用
    ImageDecoder.decodeDrawable(来源,对象:ImageDecoder.OnHeaderDecodeListener…
    ,但它不提供帧计数信息,而且在API中我无法看到我可以转到特定的帧索引并从那里开始,或者知道特定帧需要多长时间才能转到下一帧。因此我对这些进行了重新测试

    遗憾的是,我也找不到谷歌有适用于较旧Android版本的ImageDecoder


    同样有趣的是,如果有什么方法可以像我对相对较新的HEIC动画文件所做的那样。目前它只在Android p上受支持。

    好的,我得到了一个可能的解决方案,使用,和

    我不确定这是否是最好的方法,但我认为它应该工作得很好。下一段代码显示了如何在我创建的位图实例中为动画需要显示的每个帧绘制可绘制的绘图。这不完全是我要求的,但它可能会帮助其他人

    以下是代码(项目可用):

    CallbackEx.kt

    abstract class CallbackEx : Drawable.Callback {
        override fun unscheduleDrawable(who: Drawable, what: Runnable) {}
        override fun invalidateDrawable(who: Drawable) {}
        override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {}
    }
    
    @GlideModule
    class MyAppGlideModule : AppGlideModule()
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: WebpDrawable? = null
        var gifDrawable: GifDrawable? = null
        var callback: Drawable.Callback? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            useFrameByFrameDecoding()
    //        useNormalDecoding()
        }
    
        fun useNormalDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    //                .load(R.raw.test)
                    //                .load(R.raw.fast)
                    .load(R.raw.example2)
    
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            imageView.setImageDrawable(drawable)
                            when (drawable) {
                                is GifDrawable -> {
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        fun useFrameByFrameDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    .load(R.raw.test)
                    //                .load(R.raw.fast)
                    //                .load(R.raw.example2)
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            //                        val callback
                            when (drawable) {
                                is GifDrawable -> {
                                    gifDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(GifDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    webpDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(WebpDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        override fun onStart() {
            super.onStart()
            gifDrawable?.start()
            gifDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            Log.d("AppLog", "onStop")
            webpDrawable?.stop()
            gifDrawable?.stop()
        }
    
    }
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: AnimatedImageDrawable? = null
    
        @SuppressLint("StaticFieldLeak")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val source = ImageDecoder.createSource(resources, R.raw.test)
            object : AsyncTask<Void, Void, Drawable?>() {
                override fun doInBackground(vararg params: Void?): Drawable? {
                    return try {
                        ImageDecoder.decodeDrawable(source)
                    } catch (e: Exception) {
                        null
                    }
                }
    
                override fun onPostExecute(drawable: Drawable?) {
                    super.onPostExecute(drawable)
    //                imageView.setImageDrawable(result)
                    if (drawable is AnimatedImageDrawable) {
                        webpDrawable = drawable
                        val bitmap =
                            Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                        val canvas = Canvas(bitmap)
                        drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                        drawable.repeatCount = AnimatedImageDrawable.REPEAT_INFINITE
                        drawable.callback = object : Drawable.Callback {
                            val handler = Handler()
                            override fun unscheduleDrawable(who: Drawable, what: Runnable) {
                                Log.d("AppLog", "unscheduleDrawable")
                            }
    
                            override fun invalidateDrawable(who: Drawable) {
                                who.draw(canvas)
                                imageView.setImageBitmap(bitmap)
                                Log.d("AppLog", "invalidateDrawable")
                            }
    
                            override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
                                Log.d("AppLog", "scheduleDrawable next frame in ${`when` - SystemClock.uptimeMillis()} ms")
                                handler.postAtTime(what, `when`)
                            }
                        }
                        drawable.start()
                    }
                }
            }.execute()
        }
    
        override fun onStart() {
            super.onStart()
            webpDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            webpDrawable?.stop()
        }
    
    }
    
    MyAppGlideModule.kt

    abstract class CallbackEx : Drawable.Callback {
        override fun unscheduleDrawable(who: Drawable, what: Runnable) {}
        override fun invalidateDrawable(who: Drawable) {}
        override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {}
    }
    
    @GlideModule
    class MyAppGlideModule : AppGlideModule()
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: WebpDrawable? = null
        var gifDrawable: GifDrawable? = null
        var callback: Drawable.Callback? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            useFrameByFrameDecoding()
    //        useNormalDecoding()
        }
    
        fun useNormalDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    //                .load(R.raw.test)
                    //                .load(R.raw.fast)
                    .load(R.raw.example2)
    
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            imageView.setImageDrawable(drawable)
                            when (drawable) {
                                is GifDrawable -> {
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        fun useFrameByFrameDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    .load(R.raw.test)
                    //                .load(R.raw.fast)
                    //                .load(R.raw.example2)
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            //                        val callback
                            when (drawable) {
                                is GifDrawable -> {
                                    gifDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(GifDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    webpDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(WebpDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        override fun onStart() {
            super.onStart()
            gifDrawable?.start()
            gifDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            Log.d("AppLog", "onStop")
            webpDrawable?.stop()
            gifDrawable?.stop()
        }
    
    }
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: AnimatedImageDrawable? = null
    
        @SuppressLint("StaticFieldLeak")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val source = ImageDecoder.createSource(resources, R.raw.test)
            object : AsyncTask<Void, Void, Drawable?>() {
                override fun doInBackground(vararg params: Void?): Drawable? {
                    return try {
                        ImageDecoder.decodeDrawable(source)
                    } catch (e: Exception) {
                        null
                    }
                }
    
                override fun onPostExecute(drawable: Drawable?) {
                    super.onPostExecute(drawable)
    //                imageView.setImageDrawable(result)
                    if (drawable is AnimatedImageDrawable) {
                        webpDrawable = drawable
                        val bitmap =
                            Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                        val canvas = Canvas(bitmap)
                        drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                        drawable.repeatCount = AnimatedImageDrawable.REPEAT_INFINITE
                        drawable.callback = object : Drawable.Callback {
                            val handler = Handler()
                            override fun unscheduleDrawable(who: Drawable, what: Runnable) {
                                Log.d("AppLog", "unscheduleDrawable")
                            }
    
                            override fun invalidateDrawable(who: Drawable) {
                                who.draw(canvas)
                                imageView.setImageBitmap(bitmap)
                                Log.d("AppLog", "invalidateDrawable")
                            }
    
                            override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
                                Log.d("AppLog", "scheduleDrawable next frame in ${`when` - SystemClock.uptimeMillis()} ms")
                                handler.postAtTime(what, `when`)
                            }
                        }
                        drawable.start()
                    }
                }
            }.execute()
        }
    
        override fun onStart() {
            super.onStart()
            webpDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            webpDrawable?.stop()
        }
    
    }
    
    MainActivity.kt

    abstract class CallbackEx : Drawable.Callback {
        override fun unscheduleDrawable(who: Drawable, what: Runnable) {}
        override fun invalidateDrawable(who: Drawable) {}
        override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {}
    }
    
    @GlideModule
    class MyAppGlideModule : AppGlideModule()
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: WebpDrawable? = null
        var gifDrawable: GifDrawable? = null
        var callback: Drawable.Callback? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            useFrameByFrameDecoding()
    //        useNormalDecoding()
        }
    
        fun useNormalDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    //                .load(R.raw.test)
                    //                .load(R.raw.fast)
                    .load(R.raw.example2)
    
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            imageView.setImageDrawable(drawable)
                            when (drawable) {
                                is GifDrawable -> {
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        fun useFrameByFrameDecoding() {
            //webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
            Glide.with(this)
                    .load(R.raw.test)
                    //                .load(R.raw.fast)
                    //                .load(R.raw.example2)
                    //                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
                    .into(object : SimpleTarget<Drawable>() {
                        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
                            //                        val callback
                            when (drawable) {
                                is GifDrawable -> {
                                    gifDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(GifDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                                is WebpDrawable -> {
                                    webpDrawable = drawable
                                    val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                                    val canvas = Canvas(bitmap)
                                    drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                                    drawable.setLoopCount(WebpDrawable.LOOP_FOREVER)
                                    callback = object : CallbackEx() {
                                        override fun invalidateDrawable(who: Drawable) {
                                            who.draw(canvas)
                                            imageView.setImageBitmap(bitmap)
                                            Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('@')} ${drawable.frameIndex}/${drawable.frameCount}")
                                        }
                                    }
                                    drawable.callback = callback
                                    drawable.start()
                                }
                            }
                        }
                    })
        }
    
        override fun onStart() {
            super.onStart()
            gifDrawable?.start()
            gifDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            Log.d("AppLog", "onStop")
            webpDrawable?.stop()
            gifDrawable?.stop()
        }
    
    }
    
    class MainActivity : AppCompatActivity() {
        var webpDrawable: AnimatedImageDrawable? = null
    
        @SuppressLint("StaticFieldLeak")
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val source = ImageDecoder.createSource(resources, R.raw.test)
            object : AsyncTask<Void, Void, Drawable?>() {
                override fun doInBackground(vararg params: Void?): Drawable? {
                    return try {
                        ImageDecoder.decodeDrawable(source)
                    } catch (e: Exception) {
                        null
                    }
                }
    
                override fun onPostExecute(drawable: Drawable?) {
                    super.onPostExecute(drawable)
    //                imageView.setImageDrawable(result)
                    if (drawable is AnimatedImageDrawable) {
                        webpDrawable = drawable
                        val bitmap =
                            Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
                        val canvas = Canvas(bitmap)
                        drawable.setBounds(0, 0, bitmap.width, bitmap.height)
                        drawable.repeatCount = AnimatedImageDrawable.REPEAT_INFINITE
                        drawable.callback = object : Drawable.Callback {
                            val handler = Handler()
                            override fun unscheduleDrawable(who: Drawable, what: Runnable) {
                                Log.d("AppLog", "unscheduleDrawable")
                            }
    
                            override fun invalidateDrawable(who: Drawable) {
                                who.draw(canvas)
                                imageView.setImageBitmap(bitmap)
                                Log.d("AppLog", "invalidateDrawable")
                            }
    
                            override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
                                Log.d("AppLog", "scheduleDrawable next frame in ${`when` - SystemClock.uptimeMillis()} ms")
                                handler.postAtTime(what, `when`)
                            }
                        }
                        drawable.start()
                    }
                }
            }.execute()
        }
    
        override fun onStart() {
            super.onStart()
            webpDrawable?.start()
        }
    
        override fun onStop() {
            super.onStop()
            webpDrawable?.stop()
        }
    
    }
    

    首先需要创建一个源,包括:

    // source from file
    val source = ImageDecoder.createSource(file)
    
    // source from byte buffer
    val source = ImageDecoder.createSource(byteBuffer)
    
    // source from resource
    val source = ImageDecoder.createSource(resources, resId)
    
    // source from URI
    val source = ImageDecoder.createSource(contentResolver, uri)
    
    // source from asset file
    val source = ImageDecoder.createSource(assetManager, assetFileName)
    
    // create bitmap
    val bitmap = ImageDecoder.decodeBitmap(source)
    
    // create drawable
    val drawable = ImageDecoder.decodeDrawable(source)
    
    然后用以下任一方法进行解码:

    // source from file
    val source = ImageDecoder.createSource(file)
    
    // source from byte buffer
    val source = ImageDecoder.createSource(byteBuffer)
    
    // source from resource
    val source = ImageDecoder.createSource(resources, resId)
    
    // source from URI
    val source = ImageDecoder.createSource(contentResolver, uri)
    
    // source from asset file
    val source = ImageDecoder.createSource(assetManager, assetFileName)
    
    // create bitmap
    val bitmap = ImageDecoder.decodeBitmap(source)
    
    // create drawable
    val drawable = ImageDecoder.decodeDrawable(source)
    
    更新:问题是,结果没有两种方法:正如
    AnimationDrawable
    所指出的那样。正如@androiddeveloper所指出的……我搞乱了两个不同的类。我仔细检查了文档,似乎没有办法。使用
    gifimagerreader
    仍然可以提取():

    ArrayList getFrames(文件gif)引发IOException{
    ArrayList frames=新的ArrayList();
    ImageReader ir=新的GIFImageReader(新的GIFImageReaderSpi());
    ir.setInput(ImageIO.createImageInputStream(gif));
    for(int i=0;i

    刚刚尝试将其转换为Kotlin,但Android上没有
    javax.imageio.imageio

    几年前我玩过GIF图像。我的想法是将GIF图像解码为帧,将帧转换为位图添加创建可从位图绘制的动画并延迟下注