一、概述
現在開發界的圖片加載使用框架真是越來越豐富了,例如ImageLoader、Glide、Picasso、Fresco等,選擇非常多,而且都還不錯。當然對於那些患有嚴重的“選擇恐懼症”的人來說,到底用那個是一個相當頭疼的問題。爲了減輕大家的選擇痛苦,我今天就選這裏面的其中一個ImageLoader講,如果大家覺得我封裝的這個很好用,那就用它吧。若不好的話,那我下次換一個講。
或許會有人問我,“這四個你爲什麼會選擇講ImageLoader而不是其它的呢?我來談談我選ImageLoader的理由
1、支持多線程下載圖片,圖片可以來源於網絡,文件系統,項目文件夾assets中以及drawable中等
2、高度的可定製性,支持隨意的配置ImageLoader,例如線程池,圖片下載器,內存緩存策略,硬盤緩存策略,圖片顯示選項以及其他的一些配置
3、強大的圖片緩存機制,支持圖片的內存緩存,文件系統緩存或者SD卡緩存
4、支持圖片下載過程的監聽
5、根據控件(ImageView)的大小對Bitmap進行裁剪,減少Bitmap佔用過多的內存
8、我只用過ImageLoader和Picasso,其它的沒有用過(這個理由說的我好尷尬、心虛)
二、開發實戰
前面說了點廢話,接下來進入正題。我們接下來的開發實戰分兩步走,首先是搭建ImageLoader使用環境,然後就是封裝ImageLoader。
1、搭建ImageLoader使用環境
這一步很好實現,就是在build.gradle文件中添加一句話。注意,這裏大家最好下載最新的。
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
還有就是要在manifest文件中添加兩個權限,分別是網絡訪問權限和寫文件權限,畢竟人家是可以加載網絡圖片且需要緩存的。
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2、封裝ImageLoader
先看一下我畫的封裝思路圖
對着這個思路圖咱們一步一步的實現,首先是默認參數配置及提供必要的參數配置。
對於自己的封裝的ImageLoader類,我們可以把它設置成爲單例的,例如:
public static ImageLoaderUtil getInstance(Context context) {
if (mInstance == null) {
synchronized (ImageLoaderUtil.class) {
if (mInstance == null) {
mInstance = new ImageLoaderUtil(context);
}
}
}
return mInstance;
}
接下來配置ImageLoader基本的默認參數,有了這些參數配置,下載、加載大圖片就基本可以避免OOM的問題了
/**
* 默認的參數值
*/
private static final int THREAD_COUNT = 4; // 標明UIL最多可以有多少條線程
private static final int PROPRITY = 2; // 標明圖片加載的一個優先級
private static final int DISK_CACHE_SIZE = 50 * 1024 * 1024; // 標明UIL可以最多緩存多少圖片(50M)
private static final int CONNECTION_TIME_OUT = 5 * 1000; // 連接的超時時間(5秒)
private static final int READ_TIME_OUT = 30 * 1000; // 讀取的超時時間(30秒)
上面只是設置,好麼有配置。接下來看看配置,這裏的配置分兩個部分,一個是ImageLoaderConfiguration,另一個是DisplayImageOptions.首先來配置ImageLoaderConfiguration,以下的配置是最基本的配置項,也是必須要的有的!註釋也都很詳盡,我就不羅嗦了。
/**
* 單例模式的私有構造方法
*
* @param context
*/
private ImageLoaderUtil(Context context) {
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration
.Builder(context)
.threadPoolSize(THREAD_COUNT) // 配置圖片下載線程的最大數量
.threadPriority(Thread.NORM_PRIORITY - PROPRITY) // 設置優先級
.denyCacheImageMultipleSizesInMemory() // 防止緩存多套尺寸的圖片到我們的內存中
.memoryCache(new WeakMemoryCache()) // 使用弱引用內存緩存
.diskCacheSize(DISK_CACHE_SIZE) // 分配硬盤緩存大小
.diskCacheFileNameGenerator(new Md5FileNameGenerator()) // 使用MD5命名文件
.tasksProcessingOrder(QueueProcessingType.LIFO) // 圖片下載順序
.defaultDisplayImageOptions(getDefaultOptions()) // 默認圖片加載的Options
.imageDownloader(new BaseImageDownloader(context, CONNECTION_TIME_OUT,
READ_TIME_OUT)) // 設置圖片下載器
.writeDebugLogs() // debug環境下會輸出日誌
.build();
// 初始化配置,創建ImageLoader實例
ImageLoader.getInstance().init(configuration);
mImageLoader = ImageLoader.getInstance();
}
接下來看看DisplayImageOptions的配置。
/**
* 實現我們的Options
*
* @return
*/
private DisplayImageOptions getDefaultOptions() {
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.xadsdk_img_error) // 在我們圖片地址爲空的時候加載的默認圖
.showImageOnFail(R.drawable.xadsdk_img_error) // 圖片下載失敗的時候要顯示的圖片
.cacheInMemory(true) // 設置圖片可以緩存到內存中
.cacheOnDisk(true) // 設置圖片可以緩存到硬盤中
.bitmapConfig(Bitmap.Config.RGB_565) // 使用的圖片解碼類型
.decodingOptions(new BitmapFactory.Options()) // 圖片的解碼配置
.build();
return options;
}
基本配置都搞定後,看看思路圖的第二步,對外提供接口。在前面我在提IamgeLoader的特性時提到了支持圖片下載過程的監聽,這是個什麼監聽?給大家看看源碼就明白了
public interface ImageLoadingListener { /** * Is called when image loading task was started * * @param imageUri Loading image URI * @param view View for image */ void onLoadingStarted(String imageUri, View view); /** * Is called when an error was occurred during image loading * * @param imageUri Loading image URI * @param view View for image. Can be <b>null</b>. * @param failReason {@linkplain com.nostra13.universalimageloader.core.assist.FailReason The reason} why image * loading was failed */ void onLoadingFailed(String imageUri, View view, FailReason failReason); /** * Is called when image is loaded successfully (and displayed in View if one was specified) * * @param imageUri Loaded image URI * @param view View for image. Can be <b>null</b>. * @param loadedImage Bitmap of loaded and decoded image */ void onLoadingComplete(String imageUri, View view, Bitmap loadedImage); /** * Is called when image loading task was cancelled because View for image was reused in newer task * * @param imageUri Loading image URI * @param view View for image. Can be <b>null</b>. */ void onLoadingCancelled(String imageUri, View view);這個接口裏聲明瞭四個方法,分別是onLoadingStarted()下載開始,onLoadingFailed()下載失敗,onLoadingComplete()下載完成,onLoadingCancelled()下載取消。因此我們只要實現了這四個方法,就可以做我們想要的操作,這裏我大家看一下我的實現:
public abstract class IamgeLoaderUtilsListener implements ImageLoadingListener {
@Override
public void onLoadingCancelled(String imageUri, View view) {
}
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
}
@Override
public abstract void onLoadingComplete(String imageUri, View view, Bitmap loadedImage);
}
這裏我定義了一個抽象類,並實現了ImageLoadingListener接口,我在項目中只想監聽圖片是否完成,所以就將onLoadingComplete()設置成爲抽象方法。在創建這個類時必須實現這個抽象方法,當然這個地方大家因各自的需求不同,可以自己設置。最後就是對外提供加載圖片的方法了,源碼中提供了displayImage()方法,這裏爲了適應各種顯示圖片的需求,我稍微封裝了一下。
/**
* 加載圖片API,然後重載幾個方法
* @param imageView
* @param url
* @param options
* @param listener
*/
public void displayImage(ImageView imageView, String url, DisplayImageOptions options,
IamgeLoaderUtilsListener listener) {
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,options,listener);
}
}
public void displayImage(ImageView imageView, String url,IamgeLoaderUtilsListener listener){
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,null,listener);
}
}
public void displayImage(String url,IamgeLoaderUtilsListener listener){
if(mImageLoader != null) {
mImageLoader.loadImage(url, null, null, listener);
}
}
public void displayImage(ImageView imageView, String url){
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,null,null);
}
}
好了,現在我將完整的代碼貼出來,讓大家瞭解全貌
public class ImageLoaderUtil {
/**
* 默認的參數值
*/
private static final int THREAD_COUNT = 4; // 標明UIL最多可以有多少條線程
private static final int PROPRITY = 2; // 標明圖片加載的一個優先級
private static final int DISK_CACHE_SIZE = 50 * 1024 * 1024; // 標明UIL可以最多緩存多少圖片(50M)
private static final int CONNECTION_TIME_OUT = 5 * 1000; // 連接的超時時間(5秒)
private static final int READ_TIME_OUT = 30 * 1000; // 讀取的超時時間(30秒)
private static ImageLoader mImageLoader = null;
private static ImageLoaderUtil mInstance = null;
public static ImageLoaderUtil getInstance(Context context) {
if (mInstance == null) {
synchronized (ImageLoaderUtil.class) {
if (mInstance == null) {
mInstance = new ImageLoaderUtil(context);
}
}
}
return mInstance;
}
/**
* 單例模式的私有構造方法
*
* @param context
*/
private ImageLoaderUtil(Context context) {
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration
.Builder(context)
.threadPoolSize(THREAD_COUNT) // 配置圖片下載線程的最大數量
.threadPriority(Thread.NORM_PRIORITY - PROPRITY) // 設置優先級
.denyCacheImageMultipleSizesInMemory() // 防止緩存多套尺寸的圖片到我們的內存中
.memoryCache(new WeakMemoryCache()) // 使用弱引用內存緩存
.diskCacheSize(DISK_CACHE_SIZE) // 分配硬盤緩存大小
.diskCacheFileNameGenerator(new Md5FileNameGenerator()) // 使用MD5命名文件
.tasksProcessingOrder(QueueProcessingType.LIFO) // 圖片下載順序
.defaultDisplayImageOptions(getDefaultOptions()) // 默認圖片加載的Options
.imageDownloader(new BaseImageDownloader(context, CONNECTION_TIME_OUT,
READ_TIME_OUT)) // 設置圖片下載器
.writeDebugLogs() // debug環境下會輸出日誌
.build();
// 初始化配置,創建ImageLoader實例
ImageLoader.getInstance().init(configuration);
mImageLoader = ImageLoader.getInstance();
}
/**
* 實現我們的Options
*
* @return
*/
private DisplayImageOptions getDefaultOptions() {
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.xadsdk_img_error) // 在我們圖片地址爲空的時候加載的默認圖
.showImageOnFail(R.drawable.xadsdk_img_error) // 圖片下載失敗的時候要顯示的圖片
.cacheInMemory(true) // 設置圖片可以緩存到內存中
.cacheOnDisk(true) // 設置圖片可以緩存到硬盤中
.bitmapConfig(Bitmap.Config.RGB_565) // 使用的圖片解碼類型
.decodingOptions(new BitmapFactory.Options()) // 圖片的解碼配置
.build();
return options;
}
/**
* 加載圖片API,然後重載幾個方法
* @param imageView
* @param url
* @param options
* @param listener
*/
public void displayImage(ImageView imageView, String url, DisplayImageOptions options,
IamgeLoaderUtilsListener listener) {
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,options,listener);
}
}
public void displayImage(ImageView imageView, String url,IamgeLoaderUtilsListener listener){
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,null,listener);
}
}
public void displayImage(String url,IamgeLoaderUtilsListener listener){
if(mImageLoader != null) {
mImageLoader.loadImage(url, null, null, listener);
}
}
public void displayImage(ImageView imageView, String url){
if (mImageLoader != null) {
mImageLoader.displayImage(url,imageView,null,null);
}
}
}
接下來最後一步,如何使用。非常方便,一句搞定ImageLoaderUtil.getInstance(mContext).displayImage(imageView, imageUrl);
如果大家想要監聽圖片下載是否完成,可以這樣ImageLoaderUtil.getInstance(getApplicationContext())
.displayImage(imageView,imageUrl, new IamgeLoaderUtilsListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
}
});
好了,IamgeLoader封裝就此結束。如果大家覺的我上面寫的有不對的地方,歡迎大家指正,非常感謝。