我想在一个 android 应用程序中显示一个动画 GIF 图像,如下图所示。我试过webview,但没有成功。如何在应用程序中显示动画 gif?
11 回答
您还可以使用此库轻松支持gifDrawable。
只需使用 GifImageView 而不是普通的 ImageView:
<pl.droidsonroids.gif.GifImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/your_anim"/>
并在 src attr 下找到您的 gif 文件。就这样!
您可以使用滑翔:
ImageView imageView = (ImageView) findViewById(R.id.imageView);
GlideDrawableImageViewTarget imageViewTarget = new GlideDrawableImageViewTarget(imageView);
Glide.with(this).load(R.raw.sample_gif).into(imageViewTarget);
经过长时间的谷歌搜索,我知道没有对 GIF 图像的原生支持。在应用程序中显示动画 gif 没有适当的解决方案。您可以查看此解决方案
使动画 gif 在布局中播放。
我尝试了很多库来使用 Animated gif 。但是每个图书馆都落后和不堪重负。但是现在,经过一两天的研究,我有了一个使用动画 gif 的想法,并且性能非常好,不滞后不压碎。
解决方案是使用Glide
请按照以下步骤操作。
在 xml 文件中。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#62b849"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="@+id/splace_image_view"
android:layout_centerInParent="true"/>
</RelativeLayout>
在你的java
文件中
ImageView imageView = findViewById(R.id.splace_image_view);
Glide
.with(this)
.load(R.drawable.football)
.into(imageView);
在 Android 中显示 GIF 图像的最佳和最简单的解决方案,它将完美运行:
- 打开 build.gradle(模块:app)
- 放入依赖项: compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.+'
打开布局文件夹并将此代码放在要显示 GIF 图像的位置:例如 activity_main.xml
<pl.droidsonroids.gif.GifImageView android:layout_width="150dp" android:layout_height="wrap_content" android:src="@drawable/your_gif_file_name"/>
android:src="@drawable/your_gif_file_name", 将 'your_gif_file_name' 替换为你想要的 gif 图像文件
您不需要任何库,只需使用以下代码:
第 1 步: 创建一个名为 GIFView.java 的文件
package com.thigale.testproject;
/**
* Created by Thigale Sameer on 11-12-16.
*/
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;
import java.io.InputStream;
public class GifView extends View {
public Movie mMovie;
public long movieStart;
private int gifId;
public GifView(Context context) {
super(context);
}
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}
private void initializeView(final int id) {
InputStream is = getContext().getResources().openRawResource(id);
mMovie = Movie.decodeStream(is);
this.gifId = id;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
long now = android.os.SystemClock.uptimeMillis();
if (movieStart == 0) {
movieStart = now;
}
if (mMovie != null) {
int relTime = (int) ((now - movieStart) % mMovie.duration());
mMovie.setTime(relTime);
mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());
this.invalidate();
}
}
public void setGIFResource(int resId) {
this.gifId = resId;
initializeView(this.gifId);
}
public int getGIFResource() {
return this.gifId;
}
}
第 2 步: 在 res/attrs.xml 中添加以下行
<declare-styleable name="GIFView">
<attr name="src" format="reference" />
</declare-styleable>
第 3 步: 将此行添加到您的 AndroidManifest.xml 特定活动中
android:hardwareAccelerated="false"
第 4 步: 在您的 XML 中创建此视图:
<com.thigale.testproject.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
components:src="@drawable/loading" />
步骤 5 在您创建视图的父活动中,添加以下行:
xmlns:components="http://schemas.android.com/apk/res-auto"
我也尝试过这样做,但 Android 不显示带有动画的 gif 图像。如果你想达到同样的效果,那么你需要拍摄几帧动画图像,然后逐帧使用动画。
您可以在下面的链接中参考。 http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
试试这种方式:
Movie movie,movie1;
InputStream is=null,is1=null;
long moviestart;
long moviestart1;
public GIFView(Context context) {
super(context);
is=context.getResources().openRawResource(R.drawable.hxps);
is1=context.getResources().openRawResource(R.drawable.cartoon);
movie=Movie.decodeStream(is);
movie1=Movie.decodeStream(is1);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
super.onDraw(canvas);
long now=android.os.SystemClock.uptimeMillis();
System.out.println("now="+now);
if (moviestart == 0) { // first time
moviestart = now;
}
if(moviestart1==0)
{
moviestart1=now;
}
System.out.println("\tmoviestart="+moviestart);
int relTime = (int)((now - moviestart) % movie.duration()) ;
int relTime1=(int)((now - moviestart1)% movie1.duration());
System.out.println("time="+relTime+"\treltime="+movie.duration());
movie.setTime(relTime);
movie1.setTime(relTime1);
movie.draw(canvas,0,0);
movie1.draw(canvas,10,300);
this.invalidate();
}
你可以试试这个库GifImageView。它非常简单易用。以下示例代码来自该项目的 README。
@Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState);
gifView = new GifImageView(context); gifView.setBytes(bitmapData); setContentView(gifView);
}
@Override protected void onStart() { super.onStart(); gifView.startAnimation(); }
@Override protected void onStop() { super.onStop(); gifView.stopAnimation(); }
更新:
在android arsenal和GIFView的 GitHub 页面中有一个最新版本。
这是当有人要求我帮助他展示 GIF 时我所做的一件小事。我在网上找到的大部分东西都是第三方库和解决方案,它们使用 UI 线程来处理在我的手机上运行不佳的 gif,所以我决定在 android 的 Movie API 的帮助下自己做。我特意让它扩展了 ImageView,这样我们就可以使用 scaleType 之类的属性。这支持从 url 或资产目录中检索 gif。我记录了一切。
如何使用它:
在 xml 布局文件中使用它的简单示例:
<[package].GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/gif_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
gif_view:gif_src="asset:gif1" />
编码:
GIF.java:
/**
* Class that represents a gif instance.
*/
public class GIF {
private static final Bitmap.Config DEF_VAL_CONFIG = Bitmap.Config.RGB_565;
private static final int DEF_VAL_DELAY_IN_MILLIS = 33;
// the gif's frames are stored in a movie instance
private Movie movie;
// the canvas of this gif
private Canvas canvas;
// the bitmap of this gif
private Bitmap bitmap;
// the start time of the gif
private long gifStartTime;
// the executor of the gif's thread
private ScheduledExecutorService executor;
// the main runnable of the gif
private Runnable mainRunnable;
// delay in millis between frames
private int delayInMillis;
private OnFrameReadyListener onFrameReadyListener;
private Handler listenerHandler;
private Runnable listenerRunnable;
/**
* Creates Gif instance based on the passed InputStream.
*
* @param in the InputStream
* @throws InputStreamIsNull if in is null
* @throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
*/
public GIF(InputStream in) {
this(in, DEF_VAL_CONFIG);
}
/**
* Creates Gif instance based on the passed InputStream and the config.
*
* @param in the InputStream
* @param config the Config
* @throws NullPointerException if config is null
* @throws InputStreamIsNull if in is null
* @throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
*/
public GIF(InputStream in, Bitmap.Config config) {
if (in == null)
throw new InputStreamIsNull("the input stream is null");
this.movie = Movie.decodeStream(in);
if (movie == null)
throw new InputStreamIsEmptyOrUnavailableException("the input steam is empty or unavailable");
this.bitmap = Bitmap.createBitmap(movie.width(), movie.height(), config);
// associates the canvas with the bitmap
this.canvas = new Canvas(bitmap);
this.mainRunnable = new Runnable() {
@Override
public void run() {
draw();
invokeListener();
}
};
setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);
}
/**
* Register a callback to be invoked when the gif changed a frame.
* Invokes methods from a special thread.
*
* @param onFrameReadyListener the listener to attach
*/
public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener) {
setOnFrameReadyListener(onFrameReadyListener, null);
}
/**
* Register a callback to be invoked when the gif changed a frame.
* Invokes methods from the specified handler.
*
* @param onFrameReadyListener the listener to attach
* @param handler the handler
*/
public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener, Handler handler) {
this.onFrameReadyListener = onFrameReadyListener;
listenerHandler = handler;
if (listenerHandler != null)
listenerRunnable = new Runnable() {
@Override
public void run() {
GIF.this.onFrameReadyListener.onFrameReady(bitmap);
}
};
else
listenerRunnable = null;
}
/**
* Sets the delay in millis between every calculation of the next frame to be set.
*
* @param delayInMillis the delay in millis
* @throws IllegalArgumentException if delayInMillis is non-positive
*/
public void setDelayInMillis(int delayInMillis) {
if (delayInMillis <= 0)
throw new IllegalArgumentException("delayInMillis must be positive");
this.delayInMillis = delayInMillis;
}
/**
* Starts the gif.
* If the gif is already running does nothing.
*/
public void startGif() {
if (executor != null)
return;
executor = Executors.newSingleThreadScheduledExecutor();
final int INITIAL_DELAY = 0;
executor.scheduleWithFixedDelay(mainRunnable, INITIAL_DELAY,
delayInMillis, TimeUnit.MILLISECONDS);
}
/**
* Stops the gif.
* If the gif is not running does nothing.
*/
public void stopGif() {
if (executor == null)
return;
executor.shutdown();
// waits until the thread is finished
while (true) {
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
break;
} catch (InterruptedException ignored) {
}
}
executor = null;
}
// calculates the frame and draws it to the bitmap through the canvas
private void draw() {
// if gifStartTime == 0 inits it for the first time
if (gifStartTime == 0)
gifStartTime = SystemClock.uptimeMillis();
long timeElapsed = SystemClock.uptimeMillis() - gifStartTime;
int timeInGif = (int) (timeElapsed % movie.duration());
movie.setTime(timeInGif);
movie.draw(canvas, 0, 0);
}
// invokes the listener
private void invokeListener() {
if (onFrameReadyListener == null)
return;
// if handler was given invokes from it, otherwise invokes from this thread
if (listenerHandler != null)
listenerHandler.post(listenerRunnable);
else
onFrameReadyListener.onFrameReady(bitmap);
}
/**
* Interface definition for a callback to be invoked when the gif changed a frame.
*/
public interface OnFrameReadyListener {
/**
* Called when the gif changed a frame.
* <p>
* Note: If a handler was given with the listener this method
* invokes from the handler, otherwise this method
* invokes from a special thread.
* <p>
* Note: This bitmap is mutable and used by the gif instance
* thus it is not recommended to mutate it.
*
* @param bitmap the new bitmap of the gif
*/
void onFrameReady(Bitmap bitmap);
}
/**
* Definition of a runtime exception class to throw when the inputStream is null.
*/
public static class InputStreamIsNull extends NullPointerException {
/**
* Creates a new instance.
*/
public InputStreamIsNull() {
super();
}
/**
* * Creates a new instance with a message.
*
* @param message the message
*/
public InputStreamIsNull(String message) {
super(message);
}
}
/**
* Definition of a runtime exception class to throw when the inputStream is empty or unavailable.
*/
public static class InputStreamIsEmptyOrUnavailableException extends RuntimeException {
/**
* Creates a new instance.
*/
public InputStreamIsEmptyOrUnavailableException() {
super();
}
/**
* * Creates a new instance with a message.
*
* @param message the message
*/
public InputStreamIsEmptyOrUnavailableException(String message) {
super(message);
}
}
}
GIFView.java:
/**
* A view that can show gifs.
* <p>
* XML Attributes:
* <p>
* gif_src:
* A string that represents the gif's source.
* <p>
* - If you want to get the gif from a url
* concatenate the string "url:" with the full url.
* <p>
* - if you want to get the gif from the assets directory
* concatenate the string "asset:" with the full path of the gif
* within the assets directory. You can exclude the .gif extension.
* <p>
* for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
* the string should be: "asset:ex_dir/ex_gif"
* <p>
* delay_in_millis:
* A positive integer that represents how many milliseconds
* should pass between every calculation of the next frame to be set.
*/
public class GIFView extends ImageView {
public static final String RESOURCE_PREFIX_URL = "url:";
public static final String RESOURCE_PREFIX_ASSET = "asset:";
private static final int DEF_VAL_DELAY_IN_MILLIS = 33;
// the gif instance
private GIF gif;
// keeps track if the view is in the middle of setting the gif
private boolean settingGif;
private GIF.OnFrameReadyListener gifOnFrameReadyListener;
private OnSettingGifListener onSettingGifListener;
// delay in millis between frames
private int delayInMillis;
/**
* Creates a new instance in the passed context.
*
* @param context the context
*/
public GIFView(Context context) {
super(context);
init(null);
}
/**
* Creates a new instance in the passed context with the specified set of attributes.
*
* @param context the context
* @param attrs the attributes
*/
public GIFView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
// inits the view
private void init(AttributeSet attrs) {
this.gifOnFrameReadyListener = new GIF.OnFrameReadyListener() {
@Override
public void onFrameReady(Bitmap bitmap) {
setImageBitmap(bitmap);
}
};
setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);
if (attrs != null)
initAttrs(attrs);
}
// inits the view with the specified attributes
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(
attrs, R.styleable.gif_view,
0, 0);
try {
// gets and sets the delay in millis.
int delayInMillis = typedArray.getInt(R.styleable.gif_view_delay_in_millis,
DEF_VAL_DELAY_IN_MILLIS);
if (delayInMillis != DEF_VAL_DELAY_IN_MILLIS)
setDelayInMillis(delayInMillis);
// gets the source of the gif and sets it
String string = typedArray.getString(R.styleable.gif_view_gif_src);
if (string != null)
setGifResource(typedArray.getString(R.styleable.gif_view_gif_src));
} finally {
typedArray.recycle();
}
}
/**
* Register callbacks to be invoked when the view finished setting a gif.
*
* @param onSettingGifListener the listener to attach
*/
public void setOnSettingGifListener(OnSettingGifListener onSettingGifListener) {
this.onSettingGifListener = onSettingGifListener;
}
/**
* Sets the delay in millis between every calculation of the next frame to be set.
*
* @param delayInMillis the delay in millis
* @throws IllegalArgumentException if delayInMillis is non-positive
*/
public void setDelayInMillis(int delayInMillis) {
if (delayInMillis <= 0)
throw new IllegalArgumentException("delayInMillis must be positive");
this.delayInMillis = delayInMillis;
if (gif != null)
gif.setDelayInMillis(delayInMillis);
}
/**
* Returns true if the view is in the process of setting the gif, false otherwise.
*
* @return true if the view is in the process of setting the gif, false otherwise
*/
public boolean isSettingGif() {
return settingGif;
}
/**
* Sets the gif of this view and starts it.
* <p>
* Note that every exception while setting the gif is only sent to the
* OnSettingGifListener instance attached to this view.
* <p>
* If the view has already begun setting another gif, does nothing.
* You can query this state with isSettingGif().
* <p>
* The string passed must be in the following format:
* <p>
* - If you want to get the gif from a url
* concatenate the string "url:" with the full url.
* <p>
* - if you want to get the gif from the assets directory
* concatenate the string "asset:" with the full path of the gif
* within the assets directory. You can exclude the .gif extension.
* <p>
* You can use the Constants:
* <p>
* GIFView.RESOURCE_PREFIX_URL = "url:"
* GIFView.RESOURCE_PREFIX_ASSET = "asset:"
* <p>
* for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
* invoke the method like this: setGifResource(GIFView.RESOURCE_PREFIX_ASSET + "ex_dir/ex_gif");
*
* @param string the string
* @throws IllegalArgumentException if the string format is invalid
*/
public void setGifResource(String string) {
if (settingGif)
return;
// stops the gif if it is running
if (gif != null)
gif.stopGif();
// defines some finals for readability
final int URL_START_INDEX = RESOURCE_PREFIX_URL.length();
final int ASSET_START_INDEX = RESOURCE_PREFIX_ASSET.length();
final String GIF_EXTENSION = ".gif";
if (string.startsWith(RESOURCE_PREFIX_URL)) {
// notifies setting gif has started
settingGif = true;
// gets the url
String url = string.substring(URL_START_INDEX);
new AsyncSettingOfGif() {
@Override
protected InputStream getGifInputStream(String url) throws Exception {
// gets the input stream from the url
return (InputStream) new URL(url).getContent();
}
}.execute(url);
} else if (string.startsWith(RESOURCE_PREFIX_ASSET)) {
// notifies setting gif has started
settingGif = true;
// gets the asset path
String assetPath = string.substring(ASSET_START_INDEX)
.replaceAll("[\\\\/]", File.separator); // replacing file separators
if (!assetPath.endsWith(GIF_EXTENSION))
assetPath += GIF_EXTENSION;
new AsyncSettingOfGif() {
@Override
protected InputStream getGifInputStream(String assetPath) throws Exception {
// gets the input stream from the assets directory
return GIFView.this.getResources().getAssets().open(assetPath);
}
}.execute(assetPath);
// if string format is invalid
} else {
throw new IllegalArgumentException("string format is invalid");
}
}
/**
* Called when the view finished to set the gif
* or an exception has occurred.
* If there are no exceptions e is null.
* <p>
* Note that the gif can be initialized properly
* and one or more exceptions can be caught in the way.
*
* @param e the Exception
*/
protected void onFinishSettingGif(Exception e) {
// notifies setting the gif has finished
settingGif = false;
if (gif != null)
onSuccess();
else
onFailure(e);
}
// on finish setting the gif
private void onSuccess() {
gif.setOnFrameReadyListener(gifOnFrameReadyListener, getHandler());
gif.setDelayInMillis(delayInMillis);
startGif();
if (onSettingGifListener != null)
onSettingGifListener.onSuccess(this);
}
// when an exception has occurred while trying to set the gif
private void onFailure(Exception e) {
if (onSettingGifListener != null)
onSettingGifListener.onFailure(this, e);
}
/**
* Starts the gif.
* If the gif is already running does nothing.
*
* @throws IllegalStateException if the gif has not been initialized yet
*/
public void startGif() {
if (gif == null || settingGif)
throw new IllegalStateException("the gif has not been initialized yet");
gif.startGif();
}
/**
* Stops the gif.
* If the gif is not running does nothing.
*
* @throws IllegalStateException if the gif has not been initialized yet
*/
public void stopGif() {
if (gif == null || settingGif)
throw new IllegalStateException("the gif has not been initialized yet");
gif.stopGif();
}
/**
* Interface definition for callbacks to be invoked when setting a gif.
*/
public interface OnSettingGifListener {
/**
* Called when a gif has successfully set.
*
* @param view the GIFView
*/
void onSuccess(GIFView view);
/**
* Called when a gif cannot be set.
*
* @param view the GIFView
* @param e the Exception
*/
void onFailure(GIFView view, Exception e);
}
/**
* Definition of an Exception class to throw when the view cannot initialize the gif.
*/
public static class CannotInitGifException extends Exception {
/**
* Creates a new instance.
*/
public CannotInitGifException() {
super();
}
/**
* * Creates a new instance with a message.
*
* @param message the message
*/
public CannotInitGifException(String message) {
super(message);
}
}
/**
* A sub-class of AsyncTask to easily perform an async task of setting a gif.
* <p>
* The default implementation of AsyncSettingOfGif.doInBackground() is to try and init the gif
* from the input stream returned from AsyncSettingOfGif.getGifInputStream() and notify
* GIFView.onFinishSettingGif() sending to it the exception, if occurred, or null.
* <p>
* Implementations of this class should override AsyncSettingOfGif.getGifInputStream()
* to return the right input stream for the gif based on the string argument.
* The string argument can be, for example, a url to retrieve the input stream from.
*/
protected abstract class AsyncSettingOfGif extends AsyncTask<String, Void, Exception> {
@Override
protected Exception doInBackground(String... string) {
CannotInitGifException exceptionToSend = null;
try (InputStream in = getGifInputStream(string[0])) {
// tries to init the gif
gif = new GIF(in);
} catch (Exception e) {
// prepares the message of the exception
String message = e.getMessage();
if (e instanceof FileNotFoundException)
message = "file not found: " + message;
// prepares the exception to send back
exceptionToSend = new CannotInitGifException(message);
}
return exceptionToSend;
}
/**
* Override this method to return the right input stream for the gif based on the string argument.
* The string argument can be, for example, a url to retrieve the input stream from.
*
* @param string the string
* @return an InputStream of a gif
* @throws Exception if an exception has occurred
*/
protected abstract InputStream getGifInputStream(String string) throws Exception;
@Override
protected void onPostExecute(Exception e) {
onFinishSettingGif(e);
}
}
}
资源/值/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="gif_view">
<attr name="gif_src" format="string" />
<attr name="delay_in_millis" format="integer" />
</declare-styleable>
</resources>
您可以添加一个 webview 并在其中显示一个 gif 图像,例如:
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2c/Rotating_earth_(large).gif/200px-Rotating_earth_(large).gif");
这将在您的应用程序中显示 gif 图像。
希望能帮助到你!祝你好运!