Android GIF 播放解决方案

题外话

先推荐一个安卓动画解决方案,如果公司的设计师可以使用AE设计APP内需求的动画效果(配合bodymovin插件导出含有动画内容的json文件),开发者就可以使用lottie框架实现复杂酷炫的动画效果。此方案兼顾了移动端Android和IOS双平台以及React Native,接入方便且播放效果好,故极力推荐。

Adobe After Effects CC 2017.png

Lottie

Bodymovin

接下来进入正题了

在公司项目的开发中遇到了控制gif播放,计算gif播放次数,监听播放完成等使用场景。首先想到的是开发项目中已经引入的图片加载框架Glide附带的gif播放功能。

Glide

github地址

  • 首先在项目的build.gradle文件中声明Glide版本
   ...
    ext.glide_version = '3.8.0'
    ...
    repositories {
        google()
        jcenter()
    }
    dependencies {
        ...
    }
}```

-  在app中的build.gradle中引入Glide

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
...
compile "com.github.bumptech.glide:glide:$glide_version" //Glide
...
}


- 简单使用Glide播放GIF动画

Glide.with(ctx).load(R.drawable.cat).asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE).into(gif_view)


- 在Kotlin中使用Glide控制播放GIF

Glide.with(ctx).load(R.drawable.cat_1).asGif()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.listener(object : RequestListener<Int, GifDrawable> {
override fun onException(e: Exception?, model: Int?,
target: Target<GifDrawable>?,
isFirstResource: Boolean): Boolean {
return false
}

                override fun onResourceReady(resource: GifDrawable?, 
                                             model: Int?,
                                             target: Target<GifDrawable>?,
                                             isFromMemoryCache: Boolean,
                                             isFirstResource: Boolean): Boolean {
                    Observable.just(resource).flatMap(Function<GifDrawable?,
                            Observable<GifDrawable>> {
                        val decoder = it.decoder
                        val frameCount = it.frameCount
                        //计算GIF时长
                        val duration = (1..frameCount)
                                .map { decoder.getDelay(it).toLong() }
                                .sum()
                        return@Function Observable.just(it)
                                .delay(duration * loopCount,
                                TimeUnit.MILLISECONDS).repeat(1)
                    }).subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe({
                                //播放完成进行后续操作
                                it.stop()
                                toast("Play Over")
                            })
                    return false
                }
            }).into(gif_view)
Glide个人感觉是当前最流行的图片加载框架,首推使用,不过我在公司项目的开发中发现加固后可能会出现GIF掉帧的情况,暂未解决。


###Fresco

[中文文档](https://www.fresco-cn.org) 
[github地址](https://github.com/facebook/fresco)

- 首先在项目的build.gradle文件中声明Fresco版本

```buildscript {
   ...
    ext.fresco_version = '1.3.0'
    ...
    repositories {
        google()
        jcenter()
    }
    dependencies {
        ...
    }
}```

-  在app中的build.gradle中引入Fresco

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
...
//fresco
compile "com.facebook.fresco:fresco:$fresco_version"
compile "com.facebook.fresco:animated-base-support:$fresco_version"//支持API14以下
compile "com.facebook.fresco:animated-gif:$fresco_version"//支持本地GIF
compile "com.facebook.fresco:animated-webp:$fresco_version"//支持网络GIF
...
}


- 使用Fresco播放GIF

 val controller = Fresco.newDraweeControllerBuilder()
            .setUri(Uri.parse("res:///" + R.drawable.cat_2))
            .setAutoPlayAnimations(true)
            .setControllerListener(object : BaseControllerListener<ImageInfo?>() {
                override fun onFinalImageSet(id: String?,
                                           imageInfo: ImageInfo?,
                                           animatable: Animatable?) {
                    Observable.just(animatable).flatMap(Function<Animatable?
                            , Observable<String?>> {
                        var duration: Long = 0
                        if (animatable is AbstractAnimatedDrawable) {
                            duration = animatable.duration.toLong()
                            animatable.start()
                        }
                        return@Function Observable.just("Play Over")
                                .delay(duration * loopCount,
                                TimeUnit.MILLISECONDS).repeat(1)
                    }).subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe({
                                //播放完成进行后续操作
                                toast(it)
                                animatable?.stop()
                            })
                }
            }).build()
    gif_view.controller = controller

我在实际使用Fresco开发的时候感觉是比Glide的效果要好的,但是由于Fresco必须在布局中使用SimpleDraweeView,导致项目在替换图片加载框架时会很困难,同时Fresco的体积相对其他框架也更大,故需谨慎使用。

###android-gif-drawable
[github地址](https://github.com/koral--/android-gif-drawable/)

- 首先在项目的build.gradle文件中声明android-gif-drawable版本

```buildscript {
   ...
    ext.gif_drawable_version = '1.2.7'
    ...
    repositories {
        google()
        jcenter()
    }
    dependencies {
        ...
    }
}```

-  在app中的build.gradle中引入android-gif-drawable

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
...
compile "pl.droidsonroids.gif:android-gif-drawable:$gif_drawable_version"
...
}


- 使用android-gif-drawable播放GIF

val gifDrawable: GifDrawable = GifDrawable(resources, R.drawable.cat)
gifDrawable.loopCount = loop
gifDrawable.addAnimationListener {
if (it >= loop - 1) {
//播放完成后续操作
toast("Play Over")
}
}
gif_view.setImageDrawable(gifDrawable)


这个框架是gif播放中效果最好,最流畅的加载框架,但是由于目前版本仅支持本地gif文件的播放,如需要加载网络GIF图片以及缓存则需要自己拓展功能。


如有疑问可留言互相探讨学习。
附上Demo的github地址:
[GifPlayMaster](https://github.com/qlfabc/GifPlayMaster)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,431评论 25 707
  • 一.榜单介绍 排行榜包括四大类: 单一框架:仅提供路由、网络层、UI层、通信层或其他单一功能的框架 混合开发框架:...
    伟子男阅读 5,239评论 0 161
  • 框架:提供一定能力的小段程序 http://www.cnblogs.com/jincheng-yangchaofa...
    姑娘请别为难小僧阅读 7,184评论 0 132
  • 第一次使用手机APP 绘画,画的不是很好请大家指正 这款APP 有很多功能,仿纸效果也不错,自由发挥的程度也很大。...
    岚子墨阅读 312评论 0 0
  • 沙漠蓝月国, 读到这个名字,立刻就想起鸣沙山,月牙泉。 最近有新闻,曾经消失的月牙泉又有水了,真是令人欣慰,而且看...
    林少含阅读 213评论 0 0