Gilde使用小結-基礎


Glide是Google官方推薦使用的圖片加載庫。大哥已經表揚人家了,小弟們能不學習學習嗎?如果你現在還在使用Picasso,那么先參考這篇文章對比一下Picasso和Glide。

添加依賴:

dependencies {
  compile 'com.github.bumptech.glide:glide:3.7.0'  # 添加Glide依賴
  compile 'jp.wasabeef:glide-transformations:2.0.0' # 可選,添加Glide變換第三方實現依賴。如果你對顯示的圖片有什么模糊啊,圓角什么的變換,那就添加了吧,省很多事。
  compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' # 可選,沒用過,感興趣GitHub上看看吧.
}

應付95%使用場合:

不說廢話,直接上代碼:

Glide
        .with(context)
        .load(imageUrl)  # 也可以是來自resouceId、File、Uri代表的圖片資源。
        .placeHolder(resourceId) # 占位符,就是圖片從開始請求到最后完全加載,這段時間顯示的默認圖片。
        .error(erroResouceId) #請求圖片發生異常的錯誤圖片。
        .into(targetImageView)

That's it!如果我沒有估計錯,這幾行代碼足夠對付項目中大多數的圖片加載需求了。對于ListView、GridView、RecyclerView等也不用擔心,加載圖片也就是這些代碼,其它的什么條目不可見的時候自動取消加載圖片的請求等等Glide都已經替我們解決好了。

還用一點我想大聲對你說的就是Glide還可以加載gif。什么都不用改,只要你給的圖片資源是gif人家就能放。有時候服務器那邊腦子進水給你一個gif圖片的url,但實際上是張靜態圖片,邏輯上這個應該算是加載錯誤圖片資源,應該讓error()顯神威,不過Glide無法自動探知你就是想加載gif而其它類型都是錯誤,所以你得用asGif()明確告訴Glide:這里我就是要gif,其它類型的圖片都是錯誤

Glide  
    .with( context )
    .load( gifUrl )
    .asGif()
    .error( R.drawable.full_cake )
    .into( imageViewGif );

還有時候你只想顯示靜態圖片,但你是在怕了服務器那邊,搞不好又進水在需要靜態圖片的地方給了gif,沒關系,使用asBitmap()如果來的是一張gif只會顯示它的第一幀:

Glide  
    .with( context )
    .load( gifUrl )
    .asBitmap()
    .into( imageViewGifAsBitmap );

注意一點的就是你給with()方法的context實例,Glide的生命周期會綁定到這個context的實例上,Glide在context不同生命周期回調中有不同的處理,例如圖片請求在onStop()中會暫停,在onStart()中又會重新請求;gif在onStop()會暫停播放等等。所以Application、Activity、Fragment、Service等都可以提供context,你就要根據自己的情況慎重選擇適合的context實例了。

Glide還可以播放本地視頻,只是本地Android可以解碼的視頻類型哦!

String filePath = "/storage/emulated/0/Pictures/example_video.mp4";

Glide  
    .with( context )
    .load( Uri.fromFile( new File( filePath ) ) )
    .into( imageViewGifAsBitmap );

有時候一個頁面有很多圖片,但有主次之分。你希望主要的圖片先被加載出來,之后在加載次要的圖片。你可以通過指定請求優先級來實現這樣的需求,有一點需要銘記在心的是:指定優先級只不過讓優先級高的先請求,但由于圖片大小,圖片處理時間等等也需要時間,所以不能保證最后請求優先級高的一定最先顯示
??先來看一下,Glide都有哪些請求優先級別:

Priority.LOW
Priority.NORMAL
Priority.HIGH
Priority.IMMEDIATE

見名知義,不解釋了。最后用priority()指定請求優先級:

   Glide
        .with( context )
        .load( imageUrl )
        .priority( Priority.HIGH )
        .into( imageViewHero );

默認情況下根據給定的ImageView大小來決定要加載的圖片大小。當然,你也可以使用override(horizontalSize, verticalSize)指定要加載的圖片大小:

Glide
    .with(context)
    .load(imageUrl)
    .override(600, 200) 
    .into(imageViewResize);

不過這樣做可能會破壞原圖片的比例。所以你指定大小的時候,所以你還可以用fitCenter()或centerCrop()來縮放圖片,用以達到比較好的視覺效果。

Glide
    .with(context)
    .load(imageUrl)
    .override(600, 200)
    .centerCrop()  #縮放圖片以滿足ImageView的尺寸,超過ImageView的部分將會被裁剪掉,因此最終圖片可能不完全顯示。
    .into(imageViewResizeCenterCrop);
Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .override(600, 200)
    .fitCenter()  #保持原圖片的比例進行縮放,直到可以在ImageView中尺寸區域內完全顯示圖片。圖片能夠完全顯示,比例保持不變,但是可能圖片無法完全覆蓋ImageView的區域。
    .into(imageViewResizeFitCenter);

圖片從無到完全顯示,Glide默認是有過渡動畫的,還可用crossFade(int duration)來自定義過渡動畫的毫秒數。當然如果你就是喜歡嚇用戶,想要突然間把圖片加載出來了,可以用dontAnimate()取消過渡動畫:

Glide
    .with(context)
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .placeholder(R.mipmap.ic_launcher) 
    .error(R.mipmap.future_studio_launcher) 
    .dontAnimate()
    .into(imageViewFade);
Glide緩存使用

不用說,Glide當然會在內存和本地緩存已經請求過的圖片。但有時一個url對應的圖片內容變動比較大,不需要把它緩存在內存或者本地。那么可以使用skipMemoryCache(true):

Glide  
    .with( context )
    .load( imageUrl)
    .skipMemoryCache( true )
    .into( imageViewInternet );

當然這只是告訴Glide圖片不要在內存中緩存,Glide在本地還會照樣緩存請求過的圖片的。可以用diskCacheStrategy()取消本地緩存。要明白一點的是Glide對于一張從網絡請求來的圖片,Glide除了會在本地存儲原始尺寸的圖片,還會緩存加載到ImageView的較小尺寸的圖片。因此diskCacheStrategy()可以接受:

  • DiskCacheStrategy.NONE 本地什么都不緩存
  • DiskCacheStrategy.SOURCE 本地緩存原始的全尺寸圖片
  • DiskCacheStrategy.RESULT 本地緩存加載到ImageView上的較小尺寸的圖片
  • DiskCacheStrategy.ALL 默認緩存全部尺寸的圖片
Glide  
    .with( context )
    .load( imageUrl)
    .diskCacheStrategy( DiskCacheStrategy.SOURCE )
    .into( imageViewFile );
自定義變換:

有時候在圖片顯示之前,會需要對圖片進行一些變換,最常出現的需求就是將圖片模糊。自定義變換主要就是實現Transformation接口,要知道我們的Glide還可以比方gif和視頻,因此實現這個接口難度會大很多。當然如果只是針對靜態圖片,實現BitmapTransformation會容易很多:

public class BlurTransformation extends BitmapTransformation {

    private RenderScript rs;

    public BlurTransformation(Context context) {
        super( context );

        rs = RenderScript.create( context );
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );
        Allocation input = Allocation.createFromBitmap(
            rs, 
            blurredBitmap, 
            Allocation.MipmapControl.MIPMAP_FULL, 
            Allocation.USAGE_SHARED
        );
        Allocation output = Allocation.createTyped(rs, input.getType());
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setInput(input);
        script.setRadius(10);
        script.forEach(output);

        output.copyTo(blurredBitmap);

        toTransform.recycle();

        return blurredBitmap;
    }

    @Override
    public String getId() {
        return "blur";
    }
}

我們在transform方法里可以拿到要顯示的圖片toTransform以及ta的尺寸(outWidth, outHeight),接下來就可以發揮想象力處理圖片然后把處理后的圖片返回就像。關于上面圖片模糊具體可以看這篇文章。然后就可以用transform()、或者bitmapTransform()方法來使用我們自定義的變換了:

Glide  
    .with( context )
    .load( imageUrl)
    .transform( new BlurTransformation( context ) )
    //.bitmapTransform( new BlurTransformation( context ) )  #這個方法接受的變換只針對bitmap。
    .into( imageView1 );

如果你要進行多種變換,transform()和bitmapTransform()可接受多個參數:

Glide  
    .with( context )
    .load( imageUrl )
    .transform( new GreyscaleTransformation( context ), new BlurTransformation( context ) )
    .into( imageView2 );

在最開始添加依賴時說過已經有好人實現了許多圖片變換,看看這個glide-transformations項目。是不是感覺瞬間輕松許多。

自定義動畫:

之前已經提到的crossFade()是個漸變動畫,現在我們還想要自己定義的更帥的動畫。可以使用animate()方法,ta可以接受一個動畫的資源文件:

#R.anim.scale
<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android"  
     android:fillAfter="true">

    <scale
        android:duration="@android:integer/config_longAnimTime"
        android:fromXScale="0.1"
        android:fromYScale="0.1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1"
        android:toYScale="1"/>

</set>  
Glide  
    .with( context )
    .load( eatFoodyImages[0] )
    .animate( android.R.anim.slide_in_left ) 
    .into( imageView1 );ava

另外animate()還接受一個ViewPropertyAnimation.Animator實現:

ViewPropertyAnimation.Animator animationObject = new ViewPropertyAnimation.Animator() {  
    @Override
    public void animate(View view) {

        view.setAlpha( 0f );

        ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
        fadeAnim.setDuration( 2500 );
        fadeAnim.start();
    }
};

只要實現一個animate方法就可以啦。animate方法中的view就是傳入into方法里的ImageView對象:

Glide  
    .with( context )
    .load( imageUrl )
    .animate( animationObject )
    .into( imageView2 );

我這里好像發現當調用了crossFade()方法,animate()方法定義的動畫好像不會被調用,也就是沒有效果。另外好像過渡動畫只對ImageView對象有效果,當傳入into()方法的ViewTarget類型對象,crossFade()和animate()好像都不會調用。這個還有待繼續驗證。

回調:SimpleTarget和ViewTarget:

Target接口代表Glide中資源最終被加載到的地方并且可以毀掉Glide中的生命周期方法。Target可以回調的生命周期方法有:

  • onLoadStarted
  • onResourceReady
  • onLoadCleared
  • onLoadFailed
    ??典型的生命周期是:onLoadStarted -> onResourceReady 或者 onLoadFailed -> onLoadCleared。實際可能某些方法不會調用,例如加入直接從內存緩存中加載資源,onLoadStarted方法便不會被調用了。
    ??這里介紹兩個Target實現類,同時也代表著兩個常見的需求。
    ??有時候我們只是希望獲得一張圖片,而不想把它加載到什么地方上顯示,對于這個需求使用SimpleTarget最合適不過了。
private SimpleTarget target = new SimpleTarget<Bitmap>() {  
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {

       //在這里我們就可以獲得加載的資源了,當然這里是一個bitmap。
    }
};

private void loadImageSimpleTarget() {  
    Glide
        .with( context )
        .load( imageUrl)
        .asBitmap()
        .into( target ); //使用Target。
}

這里有點要在強調一下,就是關于傳給with()方法的context實例。要記得Glide的生命周期會和這個context實例的生命周期相綁定。所以可能會有這樣的場景:在activityA中獲得圖片,你把這個activityA傳給with()方法,然后還沒獲得圖片,用戶跳轉到activityB,在activityB要顯示這個圖片。很顯然activityA在回調onStop()的時候Glide也停止請求那張圖片,所以在activityB中也就沒有圖片可顯示啦。

還可以指定SimpleTarget獲得的資源的尺寸:

private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {  
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
        imageView2.setImageBitmap( bitmap );
    }
};

另一個需求那就更常見了,而且也更迫切。當你自定義一個view的時候,同時這個view還不是繼承ImageView但是ta也有顯示圖片的需要,怎么辦?用ViewTarget來辦:

//自定義的一個ViewGroup
public class CustomView extends FrameLayout {  
    ImageView iv;
    TextView tv;

    public void initialize(Context context) {
        inflate( context, R.layout.custom_view_, this );

        iv = (ImageView) findViewById( R.id.custom_view_image );
        tv = (TextView) findViewById( R.id.custom_view_text );
    }

    public CustomView(Context context, AttributeSet attrs) {
        super( context, attrs );
        initialize( context );
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super( context, attrs, defStyleAttr );
        initialize( context );
    }

    public void setImage(Drawable drawable) {
        iv = (ImageView) findViewById( R.id.custom_view_image );

        iv.setImageDrawable( drawable );
    }
}

    CustomView customView = (CustomView) findViewById( R.id.custom_view );

    //定義一個ViewTarget,注意傳入自定義View對象,還有ViewTarget的泛型。
    viewTarget = new ViewTarget<CustomView, GlideDrawable>( customView ) {
        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
          //這里我們獲得傳入的自定義View,在這個自定義View中我們寫了該View設置圖片的方法。
            this.view.setImage( resource.getCurrent() );
        }
    };

    Glide
        .with( context.getApplicationContext() ) 
        .load( eatFoodyImages[2] )
        .into( viewTarget ); //使用Target。
調試和錯誤處理:

可以使用ADB命令來查看資源請求時的日志:

adb shell setprop log.tag.GenericRequest DEBUG [還可選VERBOSE,INFO,WARN,ERROR日志級別] 

雖然有error()幫我們處理請求圖片出錯時的情況,但有時我們也想自己作一些處理,最起碼得看看發生了什么是吧。用listener()注冊一個RequestListener便可以得到請求資源時的一些關鍵回調方法:

private RequestListener<String, GlideDrawable> requestListener = new RequestListener<String, GlideDrawable>() {  
    @Override
    public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        //獲得異常你想怎么處理隨你的便利。
        return false;  //如果返回true,Glide認為你已經處理好了這個異常,那么error()方法也就不會調用了。
    }

    @Override
    public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        return false;
    }
};

Glide  
    .with( context )
    .load(UsageExampleListViewAdapter.eatFoodyImages[0])
    .listener( requestListener ) //注冊監聽器。
    .error( R.drawable.cupcake )
    .into( imageViewPlaceholder );

當然Glide還有其它的監聽器也很有用,例如LifecycleListener就可以在Glide里監聽Fragment和Activity的onStart()、onStop()和onDestroy()回調。

集成自己的網絡庫:

你可以使用自己的網絡庫,但這個細講起來還比較復雜。這里介紹當你的工程使用的是OkHttp或者Volley的情況,稍微配置一下build.gradle,其它什么都不用做了:

dependencies {  
    // 其它配置

    
    compile 'com.github.bumptech.glide:glide:3.7.0'

    // 特別注意這個,就是ta讓你可以使用OkHttp作為自己的網絡請求庫。
    compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'
    compile 'com.squareup.okhttp:okhttp:2.7.5'
    
    // 針對Volley的配置 。
   //compile 'com.github.bumptech.glide:volley-integration:1.4.0@aar'
  //compile 'com.mcxiaoke.volley:library:1.0.8'

    //針對OkHttp3的配置。
   //compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
  //compile 'com.squareup.okhttp3:okhttp:3.2.0'
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容