如何在安卓设备上播放GIF

29

你好 stackoverflow,我正在尝试开发一个可以播放我的自己的 GIF 的安卓应用程序,以下是代码片段:

MainActivity.java

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}
}

AnimationView.java

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;

public class AnimationView extends View {
private Movie mMovie;
private long mMovieStart;
private static final boolean DECODE_STREAM = true;

private int mDrawLeftPos;
private int mHeight, mWidth;

private static byte[] streamToBytes(InputStream is) {
    ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
    byte[] buffer = new byte[1024];
    int len;
    try {
        while ((len = is.read(buffer)) >= 0) {
            os.write(buffer, 0, len);
        }
    } catch (java.io.IOException e) {
    }
    return os.toByteArray();
}

public AnimationView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setFocusable(true);
    java.io.InputStream is;
    is = context.getResources().openRawResource(R.drawable.scanning);
    if (DECODE_STREAM) {
        mMovie = Movie.decodeStream(is);
    } else {
        byte[] array = streamToBytes(is);
        mMovie = Movie.decodeByteArray(array, 0, array.length);
    }
}

@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{   
    int p_top = this.getPaddingTop(), p_bottom = this.getPaddingBottom();

    mWidth = mMovie.width();
    mHeight = mMovie.height();
    // Calculate new desired height
    final int desiredHSpec = MeasureSpec.makeMeasureSpec( mHeight + p_top + p_bottom , MeasureSpec.EXACTLY );

    setMeasuredDimension( widthMeasureSpec, desiredHSpec );
    super.onMeasure( widthMeasureSpec, desiredHSpec );

    // Update the draw left position
    mDrawLeftPos = Math.max( ( this.getWidth() - mWidth ) / 2, 0) ;
}

@Override
public void onDraw(Canvas canvas) {
    long now = android.os.SystemClock.uptimeMillis();
    if (mMovieStart == 0) { // first time
        mMovieStart = now;
    }
    if (mMovie != null) {
        int dur = mMovie.duration();
        if (dur == 0) {
            dur = 3000;
        }
        int relTime = (int) ((now - mMovieStart) % dur);
        // Log.d("", "real time :: " +relTime);
        mMovie.setTime(relTime);
        mMovie.draw(canvas, mDrawLeftPos, this.getPaddingTop());
        invalidate();
    }
}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >

<com.example.androidgifwork.AnimationView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true" />
</LinearLayout>

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidgifwork"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="19" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name="com.example.androidgifwork.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

当我运行上述代码片段时,GIF 并没有运行,但是当我删除 android:targetSdkVersion="19" 后,GIF 就可以运行了,请帮助我解决这个谜题。

谢谢


我移除了android:targetSdkVersion="18",然后它就可以工作了。有人解决了这个问题吗?我也遇到了同样的问题。 - Akanksha Rathore
@Akanksha 是的,在4.4API 18及以上版本中存在GIF问题,但如果你真的想播放GIF,是可以实现的。我不能在这里发布播放任何版本Android中GIF的完整源代码,请发送私信。谢谢。 - Chethan Shetty
我怎样可以给你发送私信?你能把你的代码发给我吗? - Akanksha Rathore
9个回答

83

2017年更新的答案

要在Android中播放GIF,请使用Glide库加载任何图像或GIF。

Glide.with(context)
.load(YOUR_GIF)
.into(YOUR_IMAGE_VIEW);

使用 Glide 加载普通图片、来自服务器的图片,甚至可以加载 GIF。还可以看看与 Glide 类似的Picasso Android 图片加载库,但截至目前(2017年4月16日),Picasso 尚不支持在 Android 中加载 GIF


######################################################################

旧回答

对于所有想在应用中播放 GIF 的人,请查找下面的代码 PlayGifView.java

 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Movie;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;

 public class PlayGifView extends View{

     private static final int DEFAULT_MOVIEW_DURATION = 1000;

     private int mMovieResourceId;
     private Movie mMovie;

     private long mMovieStart = 0;
     private int mCurrentAnimationTime = 0;

     @SuppressLint("NewApi")
     public PlayGifView(Context context, AttributeSet attrs) {
         super(context, attrs);

        /**
         * Starting from HONEYCOMB have to turn off HardWare acceleration to draw
         * Movie on Canvas.
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    }

     public void setImageResource(int mvId){
         this.mMovieResourceId = mvId;
    mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
    requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if(mMovie != null){
        setMeasuredDimension(mMovie.width(), mMovie.height());
    }else{
        setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
    }
}

@Override
protected void onDraw(Canvas canvas) {
    if (mMovie != null){
        updateAnimtionTime();
        drawGif(canvas);
        invalidate();
    }else{
        drawGif(canvas);
    }
}

private void updateAnimtionTime() {
    long now = android.os.SystemClock.uptimeMillis();

    if (mMovieStart == 0) {
        mMovieStart = now;
    }
    int dur = mMovie.duration();
    if (dur == 0) {
        dur = DEFAULT_MOVIEW_DURATION;
    }
    mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}

private void drawGif(Canvas canvas) {
    mMovie.setTime(mCurrentAnimationTime);
    mMovie.draw(canvas, 0, 0);
    canvas.restore();
}

}

在您的活动类中使用以下代码播放 GIF

PlayGifView pGif = (PlayGifView) findViewById(R.id.viewGif);
pGif.setImageResource(<Your GIF file name Eg: R.drawable.infinity_gif>);

XML布局

<yourPacckageName.PlayGifView
            android:id="@+id/viewGif"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />

1
@SagarDevanga 使用android:layout_width="match_parent"android:layout_height="match_parent"使其全屏,并确保GIF视图的父级是全屏。 - Chethan Shetty
我能否使用同一个PlayGifView来显示图像而不是gif?如果可以,怎么做? - suresh cheemalamudi
2
@sureshcheemalamudi 答案是肯定的,但你需要在 PlayGifView 中重写 setImageResource(resourceID, resourceType) 方法,该方法可以接受新参数 resourceType,用于指定它是 GIF 还是图片。但我不建议这样做,因为加载图像非常直接和容易实现,除非你有特定的要求,否则你可以采用我提到的方法。 - Chethan Shetty
1
@ChethanShetty 有没有办法减慢 GIF 动画的速度? - ML.
1
谢谢@ChethanShetty,它起作用了。在我的情况下,我正在使用一个gif作为加载器,同时从API获取数据,glide的问题是,它并不总是从头开始加载gif,它使用最后加载的状态并执行进一步的gif动画(因为它维护缓存),这不符合我的要求。PlayGifView在我的情况下非常好用。谢谢 :) - Kavita Patil
显示剩余9条评论

7
使用Glide可以将项目中的任何gif从raw文件夹或外部URL加载到ImageView,ImageButtons或类似的[Glide targets][2]中。
Glide.with(getActivity()).load(R.raw.alarm).asGif().into(btnAlert);

根据您的提及,已更新答案。 - Chethan Shetty
1
asGif()现在不存在。 - bebosh
@bebosh,它是存在的。顺序必须是Glide.with(context).asGif().load().into(),而不是Glide.with().load().asGif().into()。 - c-an
根据 Glide Wiki,Gif() 应该在 load() 之后使用。: https://github.com/bumptech/glide/wiki/Transformations & https://github.com/bumptech/glide/wiki - Hitesh Sahu

1
如果您使用WebView,GIF将会直接在其上播放。但我不确定,在您的情况下是否想要将其放在WebView中。

2
我尝试过使用webview @Sushil,但在KitKat中WebView不可预测,GIF无法播放。 - Chethan Shetty

1
将以下内容添加到您的依赖项中(build.gradle):
 allprojects {
    repositories {
        mavenCentral()
    }
}
dependencies {
    implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
}

使用这个在 XML 中展示你的 GIF。
 <pl.droidsonroids.gif.GifTextView
                    android:id="@+id/gifTextView2"
                    android:layout_width="230dp"
                    android:layout_height="200dp"
                    android:layout_weight="1"
                    android:src="@drawable/dev_option_gif"
                    />

更多信息请查看:https://github.com/koral--/android-gif-drawable


0

源代码 https://drive.google.com/open?id=0BzBKpZ4nzNzUZy1BVlZSbExvYUU

**在清单文件中添加 android:hardwareAccelerated="false"**

package com.keshav.gifimageexampleworking;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity
{
    private GifImageView gifImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);

        gifImageView.setGifImageResource(R.drawable.success1);

    }

 @Override
 protected void onResume() 
 {
    super.onResume();

    //refresh long-time task in background thread
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //dummy delay for 2 second
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //update ui on UI thread
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    gifImageView.setGifImageResource(R.drawable.success);
                }
            });

        }
    }).start();
   }
}


package com.keshav.gifimageexampleworking;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.net.Uri;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.io.FileNotFoundException;
import java.io.InputStream;


public class GifImageView extends View {

    private InputStream mInputStream;
    private Movie mMovie;
    private int mWidth, mHeight;
    private long mStart;
    private Context mContext;

    public GifImageView(Context context) {
        super(context);
        this.mContext = context;
    }

    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        if (attrs.getAttributeName(1).equals("background")) {
            int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
            setGifImageResource(id);
        }
    }


    private void init() {
        setFocusable(true);
        mMovie = Movie.decodeStream(mInputStream);
        mWidth = mMovie.width();
        mHeight = mMovie.height();

        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        long now = SystemClock.uptimeMillis();

        if (mStart == 0) {
            mStart = now;
        }

        if (mMovie != null) {

            int duration = mMovie.duration();
            if (duration == 0) {
                duration = 1000;
            }

            int relTime = (int) ((now - mStart) % duration);

            mMovie.setTime(relTime);

            mMovie.draw(canvas, 10, 10);
            invalidate();
        }
    }

    public void setGifImageResource(int id) {
        mInputStream = mContext.getResources().openRawResource(id);
        init();
    }

    public void setGifImageUri(Uri uri) {
        try {
            mInputStream = mContext.getContentResolver().openInputStream(uri);
            init();
        } catch (FileNotFoundException e) {
            Log.e("GIfImageView", "File not found");
        }
    }
}

很好,还可以参考使用Glide库播放GIF的更新答案,代码更简洁。 - Chethan Shetty

0
Android 中的动态 GIF 图片是一个困难的主题。这个问题已经被广泛讨论,但开发者仍然很难将 GIF 图片应用到实际中。使用 glidepl.droidsonroids.gif:android-gif-drawable 可能会在构建 gradle 时出现问题。因此,我找到了一种不需要任何依赖项的方法。我使用了这种方法来加载 gif 图片到我的视图中:

GifAnimView.java:

package com.app.samplegif;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.graphics.Paint;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;

import com.app.samplegif.R;

import java.io.IOException;
import java.io.InputStream;

@SuppressLint("NewApi")
public class GifAnimView extends View {
    private Context _context;
    private  Movie gifMovie;
    private String gifName = "not set";
    private InputStream gifStream;

    private int width;
    private int height;

    private long startTime;
private Paint paint;


public GifAnimView(Context context, AttributeSet attrs) {
    super(context,attrs);
    initGif(context,attrs);
    _context = context;
 }

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (gifStream == null || gifMovie == null) {
        canvas.drawColor(Color.WHITE);
        canvas.drawText(gifName,width/2, height /2,paint);
        return;
    }
    long now = SystemClock.uptimeMillis();
    int relTime = (int) ((now - startTime) % gifMovie.duration());
    gifMovie.setTime(relTime);
    gifMovie.draw(canvas, 0, 0);
    this.invalidate();
}

private void initGif(Context context, AttributeSet attrs) {
    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.GifAnimView, 0, 0);
            gifName = a.getString(R.styleable.GifAnimView_gifSrc);

    try {
        gifStream =context.getAssets().open(gifName);
        gifMovie = Movie.decodeStream(gifStream);
        startTime = SystemClock.uptimeMillis();
    } catch (IOException e) {
        e.printStackTrace();
    }

    paint = new Paint();
    paint.setTextSize(40);
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setStyle(Paint.Style.FILL);
    a.recycle();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    this.width = w;
    this.height = h;
    super.onSizeChanged(w, h, oldw, oldh);
}  

}

创建 res/values 文件夹中的 attrs.xml
<?xml version="1.0" encoding="utf-8"?>
  <resources>
    <declare-styleable name="GifAnimView">
        <attr name="gifSrc" format="string" localization="suggested" />
    </declare-styleable>
  </resources>

your.gif文件复制到src/main/assets/文件夹中

activity_main.xml: 您可以使用app:gifSrc属性作为存在于资产文件夹中的gif的名称。

// ....
 <com.app.samplegif.GifAnimView
    android:id="@+id/gif_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_marginLeft="30dp"
    android:layout_marginTop="20dp"
    app:gifSrc="your.gif" />

 // ....

0

0

你也可以使用Coil库,它比Glide对GIF图像的控制略微减少。但是,如果您只想加载一次GIF并显示简单的实现,则仍然可以提供。可能,如果您想重复这样做,您需要再次触发可绘制对象或只调用一次加载函数。无论如何,即使再次调用load函数,资源也应该在后台缓存(需要进行调查,不确定100%)。 您所需的一切是添加依赖项:

 implementation "io.coil-kt:coil-gif:1.0.0"

并指定您的解码机制:

val imageLoader = ImageLoader.Builder(context)
    .componentRegistry {
        if (SDK_INT >= 28) {
            add(ImageDecoderDecoder())
        } else {
            add(GifDecoder())
        }
    }
    .build()

现在可以直接在图像视图中加载图片/动态图了:

app_logo.load("needed_gif_url")

不要忘记在需要使用解码器的每个请求中传递所需的图像加载器,例如GIF。或者将您的自定义图像加载器设置为每个请求的默认值:

Coil.setImageLoader(imageLoader)

0

对于运行 API 29(Pie) 及以上版本的设备,您可以使用AnimatedImageDrawable播放 GIF 和 WebP 动画图像。

这里是一个示例:

ImageView imgGif = findViewById(R.id.imgGif);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            drawable = ImageDecoder.decodeDrawable(ImageDecoder.createSource(getResources(), R.drawable.giphy));
            imgGif.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

使用这个可以提供更多控制和功能,比如:

  • 检查gif是否正在运行
  • 启动和停止播放。
  • 应用颜色过滤器
  • 改变图像形状

    以及更多其他功能


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接