Androidの画像読み込みライブラリUniversal Image Loader for Androidがすごい便利

Androidアプリを開発している時に何かと必要になってくる画像読み込み
URLから画像を表示したい時とか画像をキャッシュしておきたいとか色々とやりたいけど、実装が大変だし面倒。

そんな中見つけたライブラリが[Universal Image Loader for Android](https://github.com/nostra13/Android-Universal-Image-Loader)だったのですが、これが中々便利でした。

検索してみると情報が少なかったり古かったりしていたので、ちょっと細かく紹介していこうと思います。

# 特徴
- Android 2.0以降から使える
- 導入めっちゃ簡単
- 非同期で画像を読み込み可能
- ディスクキャッシュやメモリキャッシュも簡単実装
- キャッシュする時はディレクトリの容量やファイル数で制限できる
- 読み込み時のリスナーをセットできるので、失敗時の画像を表示したりも可能
- 表示時の画像の拡大・縮小もやってくれる
- 割りと細かいカスタマイズできそう(Bitmap変換とかそこらへん)

# 導入
jarで取り込む方法と[Maven](http://maven.apache.org/)から入れる方法の2つがあるみたいです。

## jarで入れる方法
1. GitHubのReadmeにjarのダウンロードリンクが貼られてるのでそこからuniversal-image-loader-1.8.4.jarあたりをダウンロードする。
https://github.com/nostra13/Android-Universal-Image-Loader#downloads
2. 自分のプロジェクト内のlibsフォルダにダウンロードしたjarを置く

これだけでライブラリが使えます、便利ですね。

## Mavenで入れる方法
1. これをpom.xmlに書く

```pom.xml
<dependency>
    <groupId>com.nostra13.universalimageloader</groupId>
    <artifactId>universal-image-loader</artifactId>
    <version>1.8.4</version>
</dependency>
```
これだけでライブラリが使えます、便利ですね。

# 使い方
## 大まかな流れ
1. ImageLoaderConfiguration.BuilderからImageLoaderConfigurationを生成する
2. DisplayImageOptions.BuilderからDisplayImageOptionsを生成する
3. ImageLoader.displayImageまたはImageLoader.loadImageで画像の読み込みをする

## 細かい使い方
AndroidManifestにパーミッションを追加する

```AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<!-- SDカードにキャッシュしたい場合 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```

ImageLoaderの初期化をApplicationで行いたいのでAndroidManifestに宣言する。

```AndroidManifest.xml
<application android:name="com.hoge.MyApplication">
    ...
</application>
```

AndroidManifestに宣言したMyApplicationを作る

```MyApplication.java
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // グローバル設定の生成と初期化を行う
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
            .memoryCache(new LruMemoryCache(2 * 1024 * 1024))
            .memoryCacheSize(2 * 1024 * 1024)
            ... // ImageLoaderConfigurationの設定をメソッドチェインで繋いでいく
            .build();
        ImageLoader.getInstance().init(config);
    }
}
```

ちなみにこの初期化を行わないとエラーで落ちるので注意
http://stackoverflow.com/questions/15627031/how-to-use-android-universal-image-loader

### ImageLoaderConfiguration.Builderの設定項目

- **memoryCacheExtraOptions** - メモリキャッシュのサイズを制限できる
- **discCacheExtraOptions** - ディスクキャッシュの設定ができる
  - 引数で渡せるCompressFormatはこれ
  - http://developer.android.com/reference/android/graphics/Bitmap.CompressFormat.html
- **taskExecutor** - 処理を行うためのExecutor
  - 用途に応じてAsyncTask.SERIAL_EXECUTORもしくはAsyncTask.THREAD_POOL_EXECUTORあたりを渡すと良さそう
- **taskExecutorForCachedImages** - キャッシュしている画像を処理する時のExecutor
- **threadPoolSize** - 非同期で処理する数
- **threadPriority** - 非同期処理の優先順位
- **tasksProcessingOrder** - これよくわかってない。キューに積まれたタスクをどう処理するかみたいのだろうか
- **denyCacheImageMultipleSizesInMemory** - メモリキャッシュ時に、サイズの違う同じ画像を格納するのを許容しないようにする
- **memoryCache** - メモリキャッシュの設定
  - FIFOLimitedMemoryCache - キャッシュの制限サイズを超えた時に、最近読み込んだ画像を削除する
  - LRULimitedMemoryCache - キャッシュの制限サイズを超えた時に、一番古い画像を削除する
  - LargestLimitedMemoryCache - キャッシュの制限サイズを超えた時に、一番サイズが大きい画像を削除する
  - UsingFreqLimitedMemoryCache - キャッシュの制限サイズを超えた時に、使用頻度が低い画像を削除する
  - LimitedAgeMemoryCache - オブジェクトの有効期限が過ぎたら更新される？
  - LruMemoryCache - 制限サイズ以下になるまで画像を削除する
- **memoryCacheSize** - メモリキャッシュの制限サイズ
- **discCache** - ディスクキャッシュの設定
  - FileCountLimitedDiscCache - ファイル数で制限する。制限を超えた場合は古いファイルから削除される
  - LimitedAgeDiscCache - 指定された期間を過ぎたファイルが削除される
  - TotalSizeLimitedDiscCache - 最大サイズで制限する。制限を超えた場合は使用頻度の低いファイルから削除される
  - UnlimitedDiscCache - 制限なし
- **discCacheSize** - ディスクキャッシュの制限サイズ
- **discCacheFileCount** - ディスクキャッシュのファイル数
- **discCacheFileNameGenerator** - キャッシュファイルの名前を生成するクラスを設定
  - FileNameGenerator - URIからファイル名を生成
  - HashCodeFileNameGenerator - ハッシュからファイル名を生成
  - Md5FileNameGenerator - MD5からファイル名を生成
- **imageDownloader** - ダウンローダーをセットする
  - BaseImageDownloader - 基本
  - NetworkDeniedImageDownloader - ネットワークを遮断する(Exceptionが飛ぶ)
  - SlowNetworkImageDownloader
- **imageDecoder** - Bitmapに変換する処理のクラスをセットする
  - BaseImageDecoder - 基本
- **defaultDisplayImageOptions** - 後で説明するDisplayImageOptionsをセットする
- **enableLogging** - ログを出力する
- **build** - ImageLoaderConfigurationを生成する

### DisplayImageOptions.Builderの設定項目
- **showStubImage** - 読込中の画像を設定する
- **showImageForEmptyUri** - URIがnullだった時の画像を設定する
- **showImageOnFail** - 読み込み失敗時の画像を設定する
- **resetViewBeforeLoading** - 読み込み前にViewをリセットする
- **delayBeforeLoading** - 読み込みのタスクを開始するまでの遅延時間を設定する
- **cacheInMemory** - メモリキャッシュさせる
- **cacheOnDisc** - ディスクキャッシュさせる
- **preProcessor** - あんまりよくわかってない
- **postProcessor** - あんまりよくわかってない
- **extraForDownloader** - あんまりよくわかってない
- **imageScaleType** - 画像の拡大縮小を設定する
  - NONE - 何もしない
  - IN_SAMPLE_POWER_OF_2 - 小さい画像が生成されるまでのステップを二倍軽減？
  - IN_SAMPLE_INT - inSampleSizeで拡大縮小する http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
  - EXACTLY - ターゲットサイズに対して正確に縮小させる。画像がターゲットサイズよりも小さかった場合は拡大しない
  - EXACTLY_STRETCHED - ターゲットサイズに対して正確に拡大縮小させる
- **bitmapConfig** - Bitmap.Configをセットできる http://developer.android.com/reference/android/graphics/Bitmap.Config.html
- **decodingOptions** - あんまりよくわかってない
- **displayer** - 表示方法を設定する
  - SimpleBitmapDisplayer - 基本
  - FadeInBitmapDisplayer - フェードインのアニメーションをかける
  - FakeBitmapDisplayer - 何も表示しない
  - RoundedBitmapDisplayer - 角丸
- **handler** - Handlerをセットできる
- **build** - DisplayImageOptionsを生成する

実際に画像を読み込みたい時はImageLoaderのインスタンスを取得して、loadImageまたはdisplayImageを呼べば良い

```HogeActivity.java
@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.hoge);
    ImageView imageView = (ImageView) findViewById(R.id.fuga);
    String imageUrl = "http://hoge.com"
    ImageLoader loader = ImageLoader.getInstance();

    // loadImageを使う場合
    loader.loadImage(imageUrl, new SimpleImageLoadingListener() {
        @Override
        public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
            imageView.setImageBitmap(loadedImage);
        }
    });

    // displayImageを使う場合
    loader.displayImage(imageUrl, imageView);
    
    ...
}
```

これだけで画像読み込み周りの処理が大体できちゃうのは便利。

この他の使い方などはこちらに色々載ってた
https://github.com/nostra13/Android-Universal-Image-Loader#usage