Android:如何在给定网址的情况下显示大型动画 gif?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/809878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android: How do a display a large animated gif given a url?
提问by rndmcnlly
Suppose I have the URL for large animated gif and I wanted to make a youtube like activity that displays the animation in a streaming way. How do I
假设我有大型动画 gif 的 URL,并且我想制作一个类似 youtube 的活动,以流媒体方式显示动画。我如何能
- stream in the image?
- get it do display with actual animation?
- 在图像中流?
- 让它以实际动画显示?
I know ImageView
is not the answer as it only shows the first frame.
我知道ImageView
这不是答案,因为它只显示第一帧。
A bonus would be having access to its buffering status so I can synchronize streaming sound as well -- this is part of a YTMNDviewer application. While I could create a service that transcodes the public gif files into a nicer format, I'd like the app to function without additional dependencies.
一个好处是可以访问它的缓冲状态,这样我也可以同步流声音——这是YTMND查看器应用程序的一部分。虽然我可以创建一个服务,将公共 gif 文件转码为更好的格式,但我希望该应用程序能够在没有额外依赖项的情况下运行。
采纳答案by rndmcnlly
The general sketch of the solution is to use employ custom View
which draws asks a Movie
to draw itself to the Canvas
periodically.
解决方案的一般草图是使用View
绘制请求的自定义绘制周期性地Movie
绘制自身Canvas
。
The first step is building the Movie
instance. There is factory called decodeStream
that can make a movie given an InputStream
but it isn't enough to use the stream from a UrlConnection
. If you try this you will get an IOException
when the movie loader tries to call reset
on the stream. The hack, unfortunate as it is, is to use a separated BufferedInputStream
with a manually-set mark
to tell it to save enough data that reset
won't fail. Luckily, the URLConnection
can tell us how much data to expect. I say this hack is unfortunate because it effectively requires the entire image to be buffered in memory (which is no problem for desktop apps, but it is a serious issue on a memory-constrained mobile device).
第一步是构建Movie
实例。有一个工厂叫做decodeStream
可以制作给定的电影,InputStream
但使用来自 的流是不够的UrlConnection
。如果您尝试此操作,您将IOException
在电影加载器尝试调用reset
流时得到一个。不幸的是,hack 是使用一个BufferedInputStream
带有手动设置的分隔符mark
来告诉它保存足够多的reset
不会失败的数据。幸运的是,它URLConnection
可以告诉我们需要多少数据。我说这个 hack 很不幸,因为它实际上需要将整个图像缓冲在内存中(这对于桌面应用程序来说没有问题,但在内存受限的移动设备上却是一个严重的问题)。
Here is a snip of the Movie
setup code:
这是Movie
设置代码的片段:
URL url = new URL(gifSource);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(conn.getContentLength());
Movie movie = Movie.decodeStream(bis);
bis.close();
Next, you need to create a view that will display this Movie
. A subclass of View
with a custom onDraw
will do the trick (assuming it has access to the Movie
you created with the previous code).
接下来,您需要创建一个将显示此Movie
. 一个View
带有自定义的子类onDraw
可以解决这个问题(假设它可以访问Movie
您用前面的代码创建的)。
@Override protected void onDraw(Canvas canvas) {
if(movie != null) {
long now = android.os.SystemClock.uptimeMillis();
int dur = Math.max(movie.duration(), 1); // is it really animated?
int pos = (int)(now % dur);
movie.setTime(pos);
movie.draw(canvas, x, y);
}
}
The view won't trigger itself to be redrawn without help, and blindly calling invalidate()
at the end of onDraw
is just an energy waste. In another thread (probably the one you used to download the image data), you can post messages to the main thread, asking for the view to be invalidated at a steady (but not insane) pace.
视图不会在没有帮助的情况下触发自身重绘,invalidate()
在末尾盲目调用onDraw
只是浪费能源。在另一个线程(可能是您用来下载图像数据的线程)中,您可以向主线程发布消息,要求以稳定(但不是疯狂)的速度使视图失效。
Handler handler = new Handler();
new Thread() {
@Override public void run() {
// ... setup the movie (using the code from above)
// ... create and display the custom view, passing the movie
while(!Thread.currentThread().isInterrupted()) {
handler.post(new Runnable() {
public void run(){
view.invalidate();
}
});
try {
Thread.sleep(50); // yields 20 fps
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}.start();
A really nice solution would have all sorts of sweet progress bars and error checking, but the core is here.
一个非常好的解决方案会有各种甜蜜的进度条和错误检查,但核心在这里。
回答by JRL
回答by Hitesh Sahu
Glideprove most easy and efficient way to achieve this with just one line of code :-
Glide证明了只需一行代码即可实现这一目标的最简单有效的方法:-
Glide.with(getApplicationContext())
.load(Uri.parse("https://media1.giphy.com/media/5ziaphcUWGKPu/200.gif"))
.asGif().placeholder(R.drawable.ic_launcher).crossFade()
.into(imageView);
Result is here :-
结果在这里:-
Gif taken from here http://giphy.com/search/big-gif/3
Gif 取自这里http://giphy.com/search/big-gif/3
Add jar from here :- https://github.com/bumptech/glide/releases
从这里添加 jar :- https://github.com/bumptech/glide/releases
回答by concept
Maybe AnimationDrawablecould work for you? EDIT: Not if you want to load from a URL like this post is about. Sorry
也许AnimationDrawable可以为您工作?编辑:如果你想从像这篇文章这样的 URL 加载,则不是。对不起
http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
Depending on the complexity of the GIF i.e. if it is a simple loading/progress indicator you could break the GIF apart, save each image separately and use the Android Framework's AnimationDrawable.
根据 GIF 的复杂性,即如果它是一个简单的加载/进度指示器,您可以将 GIF 分开,单独保存每个图像并使用 Android 框架的 AnimationDrawable。
For simple progress bars this may be less error prone, maybe even more performant.
对于简单的进度条,这可能不太容易出错,甚至可能更高效。