每次的探索都源於好奇,每次的收穫都是堅持的結果。
一、簡介
本文章介紹三種主流的方法去播放gif圖片,並介紹優劣勢方便今後使用的時候更快更容易的找到自己需要使用的播放gif的方法,後續可能有對應原理的解析,此篇只用於介紹使用。
二、可收穫
- 瞭解三種gif的播放方式,以及如何使用
- 知道三種gif播放的優劣勢
- 瞭解如何從網絡下載gif,知道一些rxjava和retrofit的知識。
三、android播放gif的方法
1)使用Movie播放Gif
- 基本原理:使用Movie類,將圖片加載到Movie內,通過不斷的繪製,不斷的切換時間,達到播放Gif的目的。
- 基本操作:
– 首先自定義一個GifView用於使用Movie和接收Gif圖片。(使用前先在對應位置放好Gif文件)
–代碼如下
public class GifView extends View {
private Movie mMovie;
private long mMovieStart = 0;
public GifView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void startGif(String path) {
//此方法用於加載drawable內的gif圖片
// mMovie = Movie.decodeStream(getResources().openRawResource(R.drawable.test));
byte[] array = new byte[0];
try {
//用於加載本地文件gif圖片
array = streamToBytes(new FileInputStream(path));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
mMovie = Movie.decodeByteArray(array, 0, array.length);
invalidate();
}
//關鍵代碼,原理已經說過
@Override
public void onDraw(Canvas canvas) {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas, 0, 0);
invalidate();
}
}
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();
}
}
結論:此方法我測試過,速度很慢,此方法也可以控制gif的播放,可以通過是否重繪進行控制,或者可以使用ValueAnimator ,根據對應的Gif時長生成對應長度的value,實時根據value設置time,用於控制播放暫停等操作,但根本原因加載太慢不推薦使用。
2)使用glide加載gif
基本原理:glide內部會識別你是用的是什麼類型的圖片,如果是gif圖片,內部會轉變爲GifDrawable用於播放。
- 代碼:
-首先配置build.gradle
-compile 'com.github.bumptech.glide:glide:3.7.0'
-配置倉庫
buildscript {
repositories {
jcenter()
mavenCentral()
}
allprojects {
repositories {
jcenter()
mavenCentral()
}
}
-使用:
ImageView imageView = (ImageView) findViewById(R.id.test_img);
//load方法內可以傳入資源id也可以傳入下載鏈接
Glide.with(this).load(GifUtils.localPath).into(imageView);
- 結論:該方法首次加載也比較慢,但Glide內有緩存,無需考慮數據的來源(本地或者網絡),不需要考慮數據的緩存。剛開始以爲加載速度慢是因爲下載的問題,後面加載本地的速度依然很慢,具體原因後面探索,初步懷疑是java解析的速度慢。想輕鬆一點的話可以使用此方法,如果追求速度,請看下面的。
3)使用android-gif-drawable
- 基本原理:內部轉化爲gifdrawable,類似於使用動畫一樣進行播放,內部使用c++的方法逐幀播放。內部原理還需探究。
-代碼如下:
-非下載的使用方式:
GifImageView gifImageView= (GifImageView) findViewById(R.id.test_img);
gifImageView.setImageDrawable(new GifDrawable(new File(GifUtils.localPath)));
-下載的方式:
首先定義下載:
public interface ServiceApi {
//下載文件
@GET
Observable<ResponseBody> downloadPicFromNet(@Url String fileUrl);
}
其次定義下載和顯示:
public class DownloadPicUtils {
public static void getPicFromNet(final GifImageView gifImageView) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.baidu.com/")//此處是無效基地址,但是不能寫空字符串
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //添加Rxjava
.addConverterFactory(GsonConverterFactory.create()) // 默認添加後返回gson格式,但是首先他會根據你返回的類型去判斷默認的是否可以,默認的就是返回responseBody,如果滿足不執行其他的,反之繼續遍歷轉換器,找到符合的
.build();
ServiceApi serviceApi = retrofit.create(ServiceApi.class);
serviceApi.downloadPicFromNet(GifUtils.netPath)
.subscribeOn(Schedulers.newThread())//在新線程中實現該方法
.map(new Func1<ResponseBody, String>() {
@Override
public String call(ResponseBody arg0) {
return saveImgFromNet(arg0);
}
})
.observeOn(AndroidSchedulers.mainThread())//在Android主線程中展示
.subscribe(new Subscriber<String>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable arg0) {
Log.v("info", "error:" + arg0.toString());
}
@Override
public void onNext(String arg0) {
try {
gifImageView.setImageDrawable(new GifDrawable(new File(GifUtils.localPath)));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private static String saveImgFromNet(ResponseBody arg0) {
try {
FileHelper.saveBytesToFile(arg0.bytes(), GifUtils.localPath);
} catch (IOException e) {
e.printStackTrace();
}
return GifUtils.localPath;
}
}
文件存儲輔助類:
public class FileHelper {
public static byte[] getBytesFromStream(InputStream is) throws IOException {
int len;
int size = 1024;
byte[] buf;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
buf = new byte[size];
while ((len = is.read(buf, 0, size)) != -1) {
bos.write(buf, 0, len);
}
buf = bos.toByteArray();
return buf;
}
public static void saveBytesToFile(byte[] bytes, String path) throws IOException {
FileOutputStream fileOuputStream = null;
try {
fileOuputStream = new FileOutputStream(path);
fileOuputStream.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOuputStream != null) {
fileOuputStream.close();
}
}
}
}
使用方式:
GifImageView gifImageView= (GifImageView) findViewById(R.id.test_img);
DownloadPicUtils.getPicFromNet(gifImageView);
- 此方法加載gif速度很快,即使加上下載的速度仍比其他的兩種快,就是內部沒有緩存和網絡模塊,需要自己去實現,不過如果追求性能,這個是屬於最推薦的方式