Glide加載圖檔、加載進度監聽
- 前言
- 正文
- 一、項目配置
- 二、顯示網絡圖檔
- 三、添加設定資源監聽
- 四、添加設定資源監聽
- 五、添加加載進度條
- 六、封裝工具類
- 七、源碼
- 總結
前言
在日常開發中使用url加載圖檔是常見的。這也是Glide圖檔加載架構這麼受歡迎的原因。當然本文如果隻是簡單的加載一個圖檔出來那就完全沒有必要了,自然要搞點花裡胡哨的事情才行。補充知識:Glide (音譯:哥來德)
正文
再搞事情之前首先建立一個項目,就命名為GlideDemo吧。
一、項目配置
建立好之後,在app子產品下build.gradle的dependencies閉包中添加如下依賴:
//glide
//glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
然後Sync同步一下。
之後在res下建立一個xml檔案夾,檔案夾下建立一個network_config.xml檔案,裡面的代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true"
為什麼要加這個呢?因為在Android9.0以後通路網絡預設使用密文位址,也就是https通路,加上這個就可以通路http了,當然你還需要在AndroidManifest.xml中配置才行。
同時,别忘了添加網絡通路權限,否則你是無法加載網絡url圖檔的。
<uses-permission android:name="android.permission.INTERNET"
下面進入activity_main.xml中,修改布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
android:scaleType="centerCrop"
二、顯示網絡圖檔
這裡我隻是增加了一個圖檔控件,用于顯示網絡圖檔。
下面進入到MianActivity。
//網絡圖檔URL
private String imgUrl = "http://cn.bing.com/th?id=OHR.LargestCave_ZH-CN2069899703_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp";
//圖檔控件
private ImageView ivBg;
網絡圖檔是使用必應的圖檔,然後在onCreate中進行配置顯示。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//綁定id
ivBg = findViewById(R.id.iv_bg);
//顯示圖檔
Glide.with(this).load(imgUrl).into(ivBg);
}
下面運作一下,你可以選擇模拟器或者真機。
嗯,這就加載出來了,我相信你平常也是這麼來使用Glide的。
三、添加設定資源監聽
但如果你的圖檔很大,網絡又不是很好的情況下,就會讓使用者有一種不好的體驗,比如,當你在地鐵站裡浏覽資訊時,此時網絡環境很差,你加載圖檔沒有反應,而使用者也無法感覺,此時就會認為你的軟體有問題,是以你應該告訴使用者目前的圖檔加載情況。
加載狀态監聽
private static final String TAG = "MainActivity";
然後将
Glide.with(this).load(imgUrl).into(ivBg);
改成
//顯示圖檔
Glide.with(this).load(imgUrl).into(new ImageViewTarget<Drawable>(ivBg) {
//圖檔開始加載
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
Log.d(TAG,"圖檔開始加載");
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.d(TAG,"圖檔加載失敗");
}
//圖檔加載完成
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
super.onResourceReady(resource, transition);
// 圖檔加載完成
ivBg.setImageDrawable(resource);
Log.d(TAG,"圖檔加載完成");
}
@Override
protected void setResource(@Nullable Drawable resource) {
Log.d(TAG,"設定資源");
}
});
這裡使用了ImageViewTarget,它裡面傳入ImageView,這裡預設是要你實作一個方法,那就是setResource,不過要是想實作這個狀态的監聽,則還需要實作onLoadStarted、onLoadFailed、onResourceReady這三個方法。現在我在上面列印了日志,下面重新運作一下,待圖檔加載出來之後,看一下日志。
這裡可以看到,這是正常加載的情況,下面你可以把網絡關掉,然後解除安裝剛才安裝的應用,重新安裝。
你會發現關閉網絡之後圖檔确實沒有加載出來,但是日志也沒有看到有失敗的字樣。
這裡你就要多重考慮一下了,因為加載網絡圖檔實際上是分為兩步的,第一步請求網絡資源,第二步緩存資源顯示出來,剛才把網絡關閉了,那麼我們就應該對網絡請求增加監聽才對。
四、添加設定資源監聽
改動代碼如下所示。
//顯示圖檔
Glide.with(this)
.load(imgUrl)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Log.d(TAG,"網絡通路失敗,請檢查是否開始網絡或者增加http的通路許可");
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
Log.d(TAG,"網絡通路成功,可以顯示圖檔");
return false;
}
})
.into(new ImageViewTarget<Drawable>(ivBg) {
//圖檔開始加載
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
Log.d(TAG, "圖檔開始加載");
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.d(TAG, "圖檔加載失敗");
}
//圖檔加載完成
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
super.onResourceReady(resource, transition);
// 圖檔加載完成
ivBg.setImageDrawable(resource);
Log.d(TAG, "圖檔加載完成");
}
@Override
protected void setResource(@Nullable Drawable resource) {
Log.d(TAG, "設定資源");
}
});
可以看到我又增加了一個listener,裡面有對網絡通路的傳回,成功和失敗,網絡狀态不好的情況下才會失敗,像剛才我們沒有開始網絡就根本不會發起網絡請求,自然不會有請求的傳回。
下面開啟網絡,運作試一下。
這樣你就完成了加載網絡圖檔是網絡狀态的監聽。
五、添加加載進度條
如果你還想加上一點變化的話可以這樣,修改activity_main.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"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
android:scaleType="centerCrop" />
<ProgressBar
android:id="@+id/progressBar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
這裡我修改了根布局為RelativeLayout ,然後加上了一個ProgressBar,它預設是隐藏的,下面回到MainActivity中。
//進度條
private ProgressBar progressBar;
然後在onCreate中
= findViewById(R.id.progressBar);
然後在圖檔設定資源時,開始時顯示加載進度條,完成時隐藏進度條然後顯示圖檔。
運作的效果就像下面這樣。
當然這個加載速度取決于你的網速,快的話就是一閃而過。
現在你回頭看這個Glide的加載,如果要同時滿足網絡加載和圖檔資源設定的監聽,代碼量就會比較多,如果我一個頁面有多個地方要加載網絡圖檔呢?我總不能寫這麼多重複的代碼吧。是以我們可以寫一個工具類來幫助我們做這一步。
六、封裝工具類
建立一個GlideUtil類。
在裡面寫入如下代碼。
/**
* Glide工具類
* @author llw
*/
public class GlideUtil {
//上下文
private static Context context;
public static void init(Context context) {
GlideUtil.context = context;
}
/**
* 顯示網絡Url圖檔
* @param url
* @param imageView
*/
public static void loadImg(String url, ImageView imageView) {
Glide.with(context).load(url).into(imageView);
}
/**
* 顯示資源圖檔
* @param recourseId 資源圖檔
* @param imageView
*/
public static void loadImg(Integer recourseId, ImageView imageView) {
Glide.with(context).load(recourseId).into(imageView);
}
/**
* 顯示bitmap圖檔
* @param bitmap
* @param imageView
*/
public static void loadImg(Bitmap bitmap, ImageView imageView) {
Glide.with(context).load(bitmap).into(imageView);
}
/**
* 顯示drawable圖檔
* @param drawable
* @param imageView
*/
public static void loadImg(Drawable drawable, ImageView imageView) {
Glide.with(context).load(drawable).into(imageView);
}
}
目前這個代碼很簡單,通過init方法擷取上下文,然後通過多參數方法來顯示圖檔,當然這個可以根據實際需求來進行增減,這樣寫其實就減少了一步操作,可以在程式初始化的時候擷取應用的上下文即可,你應該知道是什麼了,沒錯就是Application,你如果不自己寫則會使用預設的,但日常開發中都會自己自定義一個Application,在裡面完成一些應用的初始化配置,比如資料庫的建立,一個資源庫的初始化。
下面建立一個MyApplication,然後內建Application,重寫onCreate方法,在裡面通過GildeUtil的init方法擷取上下文。
/**
* 自定義應用
* @author llw
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
GlideUtil.init(this);
}
}
然後你還需要到AndroidManifest.xml的application标簽下進行配置,如下圖所示:
下面你就可以使用這個工具類來加載圖檔了。
修改MainActivity中onCreate中的代碼。
//顯示圖檔
loadImg(imgUrl, ivBg);
然後你可以運作了,雖然這種封裝方式并不是很高明,但是起碼代碼很簡潔不是嗎。它可以讓你選擇不同的圖檔資源類型,根據需求選擇。
當然這隻是普通的顯示,如果我在知道網絡請求的情況呢?
在GlideUtil中添加
private static final String TAG = "GlideUtil";
//圖檔加載網絡監聽
private static RequestListener<Drawable> requestListener = new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
Log.d(TAG,"網絡通路失敗,請檢查是否開始網絡或者增加http的通路許可");
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
Log.d(TAG,"網絡通路成功,可以顯示圖檔");
return false;
}
};
然後增加一個方法。
/**
* 顯示網絡Url圖檔 附帶加載網絡監聽
* @param url
* @param imageView
*/
public static void loadImgListener(String url, ImageView imageView) {
Glide.with(context)
.load(url)
.listener(requestListener)
.into(imageView);
}
然後在MainActivity中修改代碼。
下面運作一下,然後看日志是否有列印。
這樣就可以了。那如果我也要知道這個設定圖檔資源的監聽呢?依葫蘆畫瓢就行了。
在GlideUtil中增加一個方法。
/**
* 擷取ImageViewTarget
*
* @param imageView
* @return
*/
private static ImageViewTarget<Drawable> getImageViewTarget(final ImageView imageView) {
ImageViewTarget<Drawable> imageViewTarget = new ImageViewTarget<Drawable>(imageView) {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
Log.d(TAG, "開始加載圖檔");
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.d(TAG, "加載圖檔失敗");
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
super.onResourceReady(resource, transition);
// 圖檔加載完成
imageView.setImageDrawable(resource);
Log.d(TAG, "加載圖檔完成");
}
@Override
protected void setResource(@Nullable Drawable resource) {
}
};
return imageViewTarget;
}
然後修改loadImgListener方法。
再運作一下,看日志。
當然這種寫法還是不夠人性化,可能你隻需要其中一個,或者都需要,或者都不需要,那如果要滿足這個需求就還需要改動一下這個loadImgListener方法。
改動如下:
/**
* 顯示網絡Url圖檔 附帶加載網絡監聽和設定資源監聽
* @param url 網絡圖檔url
* @param imageView 圖檔控件
* @param needNetListener 是否需要網絡監聽
* @param needResourceListener 是否需要設定資源監聽
*/
public static void loadImgListener(String url, ImageView imageView,
boolean needNetListener, boolean needResourceListener) {
if (needResourceListener) {
Glide.with(context)
.load(url)
.listener(needNetListener ? requestListener : null)
.into(getImageViewTarget(imageView));
} else {
Glide.with(context)
.load(url)
.listener(needNetListener ? requestListener : null)
.into(imageView);
}
}
這裡我增加了兩個參數,用于控制是否需要網絡監聽和設定圖檔資源監聽,然後改動一下MainActvity中的代碼調用。
下面運作一下,你會發現日志都會列印。
然後都設定為false,這時候是不會有日志列印的,我就不截圖了。
下面設定一個為true一個為false。
運作看看。
OK,到這一步是不是就沒有問題了呢?
那麼還有一個問題,就是如果我要顯示加載進度條呢?
那麼我們可以自定義一個這樣的彈窗,
首先你需要一個加載圖檔。如果圖檔是黑色背景的話,那麼使用白色的加載圖示無疑是很好的選擇。
圖示名稱:icon_loading.png
這個圖示可以從我的源碼裡面去拿,或者自己從網絡上下載下傳。
"loading_dialog" parent="android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">#000</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
在styles.xml中設定彈窗樣式
然後新增一個動畫樣式代碼。
首先在res下建立一個anim檔案夾,然後建立一個loading_animation.xml檔案,裡面的代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<set android:shareInterpolator="false" xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="+360"
android:duration="1500"
android:startOffset="-1"
android:repeatMode="restart"
android:repeatCount="-1"/>
</set>
下面增加一個彈窗,在layout下建立loading_dialog.xml,布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_view"
android:orientation="vertical"
android:layout_width="120dp"
android:layout_height="120dp"
android:gravity="center"
android:padding="10dp">
<ImageView
android:id="@+id/iv_loading"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/icon_loading" />
<TextView
android:visibility="gone"
android:id="@+id/tv_loading_tx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:maxLines="1"
android:text="玩命加載中..."
android:textColor="#FFF"
android:textSize="14sp"
這裡我把文字隐藏了。下面自定義一個加載彈窗,建立一個LoadingDialog類,繼承Dialog。裡面的代碼如下:
/**
* 自定義加載彈窗
* @author llw
*/
public class LoadingDialog extends Dialog {
TextView tvLoadingTx;
ImageView ivLoading;
public LoadingDialog(Context context) {
this(context, R.style.loading_dialog, "玩命加載中...");
}
public LoadingDialog(Context context, String string) {
this(context, R.style.loading_dialog, string);
}
protected LoadingDialog(Context context, int theme, String string) {
super(context, theme);
setCanceledOnTouchOutside(true);//點選其他區域時 true 關閉彈窗 false 不關閉彈窗
setContentView(R.layout.loading_dialog);//加載布局
tvLoadingTx = findViewById(R.id.tv_loading_tx);
tvLoadingTx.setText(string);
ivLoading = findViewById(R.id.iv_loading);
// 加載動畫
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(
context, R.anim.loading_animation);
// 使用ImageView顯示動畫
ivLoading.startAnimation(hyperspaceJumpAnimation);
getWindow().getAttributes().gravity = Gravity.CENTER;//居中顯示
getWindow().getAttributes().dimAmount = 0.5f;//背景透明度 取值範圍 0 ~ 1
}
//關閉彈窗
@Override
public void dismiss() {
super.dismiss();
}
然後最後一步就是在GlideUtil中去使用了,可以新寫一個方法。
//加載彈窗
private static LoadingDialog loadingDialog;
新增loadImgListenerNeedDialog方法。
/**
* 顯示網絡Url圖檔 附帶加載網絡監聽和設定資源監聽 顯示加載彈窗
* @param context 顯示在哪個Activity/Fragment上
* @param url 網絡圖檔url
* @param imageView 圖檔控件
* @param needNetListener 是否需要網絡監聽
* @param needResourceListener 是否需要設定資源監聽
*/
public static void loadImgListenerNeedDialog(Context context,String url, ImageView imageView,
boolean needNetListener, boolean needResourceListener) {
loadingDialog = new LoadingDialog(context);
if (needResourceListener) {
Glide.with(context)
.load(url)
.listener(needNetListener ? requestListener : null)
.into(getImageViewTarget(imageView));
} else {
Glide.with(context)
.load(url)
.listener(needNetListener ? requestListener : null)
.into(imageView);
}
}
然後在getImageViewTarget中顯示,這裡因為,需要或者不需要彈窗的監聽都是會調用getImageViewTarget,是以對彈窗進行顯示和隐藏式,判斷是否為空,避免程式空指針崩潰。
下面進入MainActivity中調用這個方法。
//顯示圖檔并監聽網絡加載情況
loadImgListenerNeedDialog(this,imgUrl,ivBg,false,true);
運作一下: