android進階3step4:Android 拓展學習——Gif介紹
GIF是什麼
GIF(圖形交換格式)的原義是“影象互換格式”,是CompuServe公司公司在1987年開發的影象檔案格式。
GIF檔案的資料,是一種基於LZW演算法的連續色調的無失真壓縮格式。其壓縮率一般在50%左右,它不屬於任何應用程式。
跨平臺
GIF的特點
GIF格式的特點是其在一個GIF檔案中可以存多幅彩色影象,如果把存於一個檔案中的多幅影象資料逐幅讀出並顯示到螢幕上,就可構成一種最簡單的動畫。
多張靜態圖組成簡單動畫
GIF的版本
GIF主要分為兩個版本,即GIF 89a和GIF 87a
GIF 87a:是在1987年制定的版本
GIF 89a:是1989年制定的版本。在這個版本中,為GIF文件擴充了圖形控制區塊,備註,說明,應用程式程式設計介面等四個區塊,並提供了對透明色和多幀動畫的支援
如何在安卓上實現GIF
方法1:電影類
安卓我們提供了一個方便的工具:android.graphics.Movie
package android.graphics;直接繼承自Object,直接繼承自Object的基本上都是工具類。
Android的ImageView無法直接載入Gif圖片,通過Android中的電影類把一個gif圖片當作一個原始的資源載入到電影,然後電影將其解析為電影幀進行載入
。電影其實管理著GIF動畫中的多個幀,只需要通過setTime()一下就可以讓它在draw()的時候繪出相應的那幀影象,通過當前時間與duration之間的換算關係,便可實現GIF動起來的效果。對於比較小的GIF圖片使用此方法還是可以的,要是大的話,建議還是把GIF圖片轉換成一幀一幀的圖片為png,然後通過動畫播放。
簡單的利用電影播放GIF圖的控制元件
GifView.java自定義gif類
import java.io.File; import android.content.Context; import android.graphics.Canvas; import android.graphics.Movie; import android.os.Build; import android.os.SystemClock; import android.view.View; /** * 自定義可以迴圈播放gif動畫的View,可以像使用其他控制元件一樣使用 **/ public class GifView extends View { private int mResId; private Movie mMovie; private long mStartTime; public GifView(Context context) { super(context); ////android sdk>16 必須關閉硬體加速 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { setLayerType(View.LAYER_TYPE_SOFTWARE, null); } } //設定展示這個gif的viewgroup的id 在使用的使用呼叫 public void setMovieResource(int resId) { mResId = resId; //建立Movie物件 mMovie = Movie.decodeStream(getResources().openRawResource(resId)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mMovie != null) { //設定viewgroup的寬高為gif圖片的寬高 setMeasuredDimension(mMovie.width(), mMovie.height()); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mMovie != null) { //動畫開始的時間 long now = SystemClock.uptimeMillis(); //第一次播放 if (mStartTime == 0) { mStartTime = now; } //動畫持續的時間,也就是完成一次動畫的時間 int dur = mMovie.duration(); if (dur == 0) { dur = 1000; } //注意這是取餘操作,這才能算出當前這次重複播放的第一幀的時間 //假設有10幀的gif,第一幀為100ms now=100 mStartTime=100 則取餘播放第0幀圖片 //第二幀now=200 mStartTime=100 取餘則播放第1幀的圖片 int time = (int)((now - mStartTime) % dur); //設定相對本次播放第一幀時間,根據這個時間來決定顯示第幾幀 mMovie.setTime(time); mMovie.draw(canvas, 0, 0); //會驅動View的重新整理,view一重新整理就好呼叫此方法 invalidate(); } } }
ManiActivity.java中呼叫 注意要在res資料夾下建立raw檔案來放置你的gif圖片
import android.app.Activity;
import android.view.ViewGroup;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GifView gifView = new GifView(this);
//這是放gif的ViewGroup控制元件 可以說relativelayout等
((ViewGroup)findViewById(R.id.layout_holder)).addView(gifView);
gifView.setMovieResource(R.raw.ppt);
}
這是activity_main.xml中中
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
</RelativeLayout>
完成。
載入網路的GIF圖片
1.新增網路許可權
<uses-permission android:name="android.permission.INTERNET" />
2.(android 6.0之後不提供org.apache.http。*)在專案的build.gradle中新增否則導不了包
android {
compileSdkVersion 28
defaultConfig {
...
useLibrary 'org.apache.http.legacy'
...
}
3.建立GifDownload.java類下載gif回撥給主介面
import android.util.Log;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/12/6 15:04<p>
* <p>更改時間:2018/12/6 15:04<p>
* <p>版本號:1<p>
*/
public class GifDownload extends Thread {
private String mUrl;
private DownloadListener mListener;
//傳入回撥物件的構造
public GifDownload(String url, DownloadListener listener) {
mUrl = url;
mListener = listener;
}
@Override
public void run() {
//新建資料夾存放下載後的圖片
File file = new File("/data/data/com.example.mygif/cache/", "test.gif");
//多級目錄上的父目錄不存在則建立
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//如果已經存在該檔案則刪除
if (file.exists()) {
file.delete();
}
/***
* 網路獲取gif檔案
* 傳入url
*/
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(mUrl);
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//如果訪問成功
//建立檔案輸出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
//拿到檔案輸入流讀取資料
InputStream inputStream = httpResponse.getEntity().getContent();
//每次讀的大小
byte[] buffer = new byte[1024 * 8];
int len = 0;
//讀到-1為空
while ((len = inputStream.read(buffer)) != -1) {
//檔案輸出流寫人資料
fileOutputStream.write(buffer, 0, len);
}
//清空緩衝區
fileOutputStream.flush();
//關閉流
fileOutputStream.close();
inputStream.close();
}
//關閉網路連線
httpClient.getConnectionManager().shutdown();
} catch (Exception exception) {
Log.e("GifDownload", "run", exception);
}
//下載完成之後回撥給主介面
mListener.onFinish(file);
}
//回撥介面
public static interface DownloadListener {
void onFinish(File file);
}
}
4.GiifView.java中多加了一個setFile方法
//直接設定檔案來構造電影
public void setFile(File file) { mMovie = Movie.decodeFile(file.getAbsolutePath()); }
import java.io.File;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.os.SystemClock;
import android.view.View;
/**
* 自定義可以迴圈播放gif動畫的View,可以像使用其他控制元件一樣使用
**/
public class GifView extends View {
private int mResId;
private Movie mMovie;
private long mStartTime;
public GifView(Context context) {
super(context);
////android sdk>16 必須關閉硬體加速
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
//設定展示這個gif的viewgroup的id 在使用的使用呼叫
public void setMovieResource(int resId) {
mResId = resId;
//建立Movie物件
mMovie = Movie.decodeStream(getResources().openRawResource(resId));
}
//直接設定file來構造Movie
public void setFile(File file) {
mMovie = Movie.decodeFile(file.getAbsolutePath());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mMovie != null) {
//設定viewgroup的寬高為gif圖片的寬高
setMeasuredDimension(mMovie.width(), mMovie.height());
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mMovie != null) {
//動畫開始的時間
long now = SystemClock.uptimeMillis();
//第一次播放
if (mStartTime == 0) {
mStartTime = now;
}
//動畫持續的時間,也就是完成一次動畫的時間
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
//注意這是取餘操作,這才能算出當前這次重複播放的第一幀的時間
//假設有10幀的gif,第一幀為100ms now=100 mStartTime=100 則取餘播放第0幀圖片
//第二幀now=200 mStartTime=100 取餘則播放第1幀的圖片
int time = (int)((now - mStartTime) % dur);
//設定相對本次播放第一幀時間,根據這個時間來決定顯示第幾幀
mMovie.setTime(time);
mMovie.draw(canvas, 0, 0);
//會驅動View的重新整理,view一重新整理就好呼叫此方法
invalidate();
}
}
}
5.MainActivity.java接受下載返回的檔案設定給GifView進行載入顯示
import java.io.File;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.ViewGroup;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final GifView gifView = new GifView(this);
//放gifview的容器 可以是relativelayout等
((ViewGroup)findViewById(R.id.layout_holder)).addView(gifView);
//url檔案地址
new
GifDownload("https://cdn.duitang.com/uploads/item/201411/16/20141116232126_yXFLS.gif",
new GifDownload.DownloadListener() {
@Override
public void onFinish(File file) {
if (file.exists()) {
//設定資源給gifview,來展示
gifView.setFile(file);
}
}
}).start();
}
}
多張靜態圖實現輪放(實現GIF效果)
GIF的原理也是多張靜態圖顯示,來實現一種動畫的效果
1.新增SD卡讀取許可權(圖片是放在手機上,具體需求具體加許可權)
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2.佈局檔案activity_main.xml裝gifView的容器
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/layout_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
</RelativeLayout>
3.GifView的實現
控制每一幀每一幀顯示,實現輪播
/**
* <p>檔案描述:<p>
* <p>作者:Mr-Donkey<p>
* <p>建立時間:2018/12/6 15:24<p>
* <p>更改時間:2018/12/6 15:24<p>
* <p>版本號:1<p>
*/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
public class GifView extends FrameLayout {
private ImageView mIvImageView; //放在ImageView上顯示,輪放
private String[] mImages; //圖片路徑集合
private int mEndIndex;//記錄最後一張圖片的下標
private int mRate = 1000;//設定圖片播放的速度
public GifView(Context context) {
super(context);
//新建ImageView
mIvImageView = new ImageView(context);
mIvImageView.setScaleType(ScaleType.FIT_XY);
addView(mIvImageView);
}
//對外提供設定每一張圖片播放的時間(速度)
public void setRate(int rate) {
mRate = rate;
}
//對外設定圖片陣列的資源
public void setImages(String[] images) {
mImages = images;
//最後一張圖片的下標
mEndIndex = mImages.length - 1;
//先載入第一張圖片
Bitmap bitmap = BitmapFactory.decodeFile(mImages[0]);
//設定父佈局的寬高為圖片的寬高
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(bitmap.getWidth(),
bitmap.getHeight());
mIvImageView.setLayoutParams(params);
//把bitmap釋放掉
bitmap.recycle();
}
/**對外設定播放的方法
* 一幀一幀載入圖片
* 線上程中執行
*/
public void play() {
new Thread(new Runnable() {
@Override
public void run() {
int index = 0;
while (index < mEndIndex) {
//記錄開始的時間
long stime = java.lang.System.currentTimeMillis();
Message message = mHandler.obtainMessage(1);
message.obj = BitmapFactory.decodeFile(mImages[index]);
mHandler.sendMessage(message);
//拿到載入完這張圖片的時間間隔
//末尾-開始=間隔
long interval = java.lang.System.currentTimeMillis() - stime;
//設定需要延遲的時間(由傳入的時間決定了)
long offset = mRate - interval;
if (offset > 0) {
try {
Thread.sleep(offset);
} catch (InterruptedException e) {
}
}
index++;
//如果下標等於最後一張,又重新賦值為0一直迴圈載入
if (index == mEndIndex) {
index = 0;
}
}
}
}).start();
}
//通過Handler實現主執行緒的UI更新,接受子執行緒發來的資料,進行顯示
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
mIvImageView.setImageBitmap((Bitmap)msg.obj);
}
};
}
4.MainActivity.java中
import java.io.File;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.ViewGroup;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GifView gifView = new GifView(this);
((ViewGroup)findViewById(R.id.layout_holder)).addView(gifView);
//圖片所在的路徑
File dir = new File("/storage/emulated/0/images/");
File[] files = dir.listFiles();
String[] images = new String[files.length];
for (int i = 0; i < images.length; i++) {
//拿到圖片的絕對路徑,存放到images中來
images[i] = files[i].getAbsolutePath();
}
//gifview設定image資料來源(路徑)
gifView.setImages(images);
//每一圖片顯示的時間
gifView.setRate(200);
//開啟圖片輪播
gifView.play();
}
}
思考:單個gif檔案和多張靜態圖的gif優缺點?
單個gif優點:
- 優點:單檔案直接播放使用,邏輯簡單;
- 缺點: 檔案大,圖片質量低
多張靜態圖優點:
- 優點:整體檔案小,圖片質量高;
- 缺點:程式碼邏輯複雜
方法2:使用的GitHub上的框架的Android的GIF抽拉
以下內容來自官方:https://github.com/koral--/android-gif-drawable
通過JNI捆綁的GIFLib用於渲染幀。這種方式比應該類WebView
或更高效Movie
。
如何使用?
1,新增依賴
以下將項依賴插入build.gradle
專案的檔案中。或者直接在專案結構中新增依賴
dependencies {
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.16'
}
請注意,在整個工程的build.gradle
,新增: mavenCentral()
buildscript {
repositories {
mavenCentral()
}
}
allprojects {
repositories {
mavenCentral()
}
要求(要求)
- Android 4.2+(API級別17+)
- 用於
GifTextureView
硬體加速渲染 - 適用於
GifTexImage2D
OpenGL ES 2.0+
然後就可以在XML中使用控制元件了
最簡單的方法是使用GifImageView
(或GifImageButton
)像普通法一樣ImageView
<pl.droidsonroids.gif.GifImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/src_anim"
android:background="@drawable/bg_anim"
/>
如果由android:src和/
或android:background
GIF檔案宣告的繪製,它們則自動將識別為GifDrawable
小號動畫狀語從句:。
如果給定繪製不是GIF,那麼提到瀏覽普通就像ImageView
狀語從句ImageButton
。
更多的實現詳看官方GitHub: https ://github.com/koral--/android-gif-drawable