1. 程式人生 > >Android Glide設定預設圖片、異常圖片為圓形圖片

Android Glide設定預設圖片、異常圖片為圓形圖片

Android Glide4 非同步圖片框架

前言

在專案開發中,是需要經常用到圓形圖片的,設定預設圖片,設定資源圖片等等。

若是,異常圖片,預設圖片都需要美工妹子做成圓形圖片,無疑增加了美工妹子的工作量。本著當活雷鋒的思想,程式設計師能搞定的事情,絕不麻煩美工妹子。

要說圖片非同步載入框架,現今最流行的非Glide莫屬,連谷歌I/O App都在使用,可見它的強大之處。

根據原始碼走向找到,設定預設圖片,異常圖片的方法

眾所周知,Glide的開啟載入是通過呼叫GlideApp.with().....into()或者preload().

1. 首先,進入RequestBuilder類#into():

  /**
   * Set the target the resource will be loaded into.
   *
   * @param target The target to load the resource into.
   * @return The given target.
   * @see RequestManager#clear(Target)
   */
  public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if
(!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } Request previous = target.getRequest(); if (previous != null) { requestManager.clear(target); } requestOptions.lock(); Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request); return
target; }

2. 接下,原始碼來走向到RequestManager類中track():

  void track(Target<?> target, Request request) {
    targetTracker.track(target);
    requestTracker.runRequest(request);
  }

3. 在接下來,原始碼走向到RequestTracker類中runRequest():

  /**
   * Starts tracking the given request.
   */
  public void runRequest(Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }

4. 最終走向到SingleRequest類中begin():

@Override
public void begin() {
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
      onLoadFailed(new GlideException("Received null model"), logLevel);
      return;
    }

    status = Status.WAITING_FOR_SIZE;
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight);
    } else {
      target.getSize(this);
    }

    if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
        && canNotifyStatusChanged()) {
      target.onLoadStarted(getPlaceholderDrawable());
    }
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
}

可以從原始碼target.onLoadStarted(getPlaceholderDrawable())發覺,最後是通過Target物件的onLoadStarted()方法來設定預設空白圖片的。

SingleRequest類中設定異常圖片的原始碼

  private void setErrorPlaceholder() {
    if (!canNotifyStatusChanged()) {
      return;
    }

    Drawable error = null;
    if (model == null) {
      error = getFallbackDrawable();
    }
    // Either the model isn't null, or there was no fallback drawable set.
    if (error == null) {
      error = getErrorDrawable();
    }
    // The model isn't null, no fallback drawable was set or no error drawable was set.
    if (error == null) {
      error = getPlaceholderDrawable();
    }
    target.onLoadFailed(error);
  }

可以從原始碼target.onLoadFailed(error)可知,是通過Target物件的onLoadFailed()方法來設定異常圖片的。

長征路已經走完一半,已經找到了最後的原始碼走向。剩下來,只需要自定義一個Target子類。

Glide中已經存在一個ImageViewTarget類,檢視原始碼如下

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter {

  @Nullable
  private Animatable animatable;

  public ImageViewTarget(ImageView view) {
    super(view);
  }

  /**
   * Returns the current {@link android.graphics.drawable.Drawable} being displayed in the view
   * using {@link android.widget.ImageView#getDrawable()}.
   */
  @Override
  @Nullable
  public Drawable getCurrentDrawable() {
    return view.getDrawable();
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param drawable {@inheritDoc}
   */
  @Override
  public void setDrawable(Drawable drawable) {
    view.setImageDrawable(drawable);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param placeholder {@inheritDoc}
   */
  @Override
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param errorDrawable {@inheritDoc}
   */
  @Override
  public void onLoadFailed(@Nullable Drawable errorDrawable) {
    super.onLoadFailed(errorDrawable);
    setResourceInternal(null);
    setDrawable(errorDrawable);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param placeholder {@inheritDoc}
   */
  @Override
  public void onLoadCleared(@Nullable Drawable placeholder) {
    super.onLoadCleared(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

  @Override
  public void onResourceReady(Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }

  @Override
  public void onStart() {
    if (animatable != null) {
      animatable.start();
    }
  }

  @Override
  public void onStop() {
    if (animatable != null) {
      animatable.stop();
    }
  }

  private void setResourceInternal(@Nullable Z resource) {
    maybeUpdateAnimatable(resource);
    setResource(resource);
  }

  private void maybeUpdateAnimatable(@Nullable Z resource) {
    if (resource instanceof Animatable) {
      animatable = (Animatable) resource;
      animatable.start();
    } else {
      animatable = null;
    }
  }

  protected abstract void setResource(@Nullable Z resource);
}

從原始碼可知,無論是空白圖片,還是異常圖片都是最後走向到setDrawable(Drawable drawable)

原始碼尋找之路已經走完,馬上就可以實現自己需要的需求,繼續擼起。

定義一個預設,異常,資源圖片都為圓形的 ImageViewTarget子類

分析

  • 定義ImageViewTarget子類:
  • 重寫setDrawable(Drawable drawable),異常,預設空白圖片設定為圓形。
  • 實現抽象方法setResource( Bitmap resource),正常載入的資源圖片設定為圓形

程式碼編寫如下

/**
 *
 * 設定 預設圖片,異常圖片,指定路徑的圖片為圓角圖片
 */

public class CircleBitmapTarget extends ImageViewTarget<Bitmap> {
    public CircleBitmapTarget(ImageView view) {
        super(view);
    }

    /**
     *  從指定路徑載入的Bitmap
     * @param resource
     */
    @Override
    protected void setResource(@Nullable Bitmap resource) {
         bindCircleBitmapToImageView(resource);
    }
    /**
     *
     * onLoadFailed()和onLoadStarted呼叫該方法,用於設定預設的圖片和異常圖片
     * 設定預設圖片
     * @param drawable
     */
    @Override
    public void setDrawable(Drawable drawable) {
        if (drawable instanceof BitmapDrawable){
           Bitmap bitmap1= ((BitmapDrawable) drawable).getBitmap();
          bindCircleBitmapToImageView(bitmap1);
        }else{
            view.setImageDrawable(drawable);
        }
    }

    /**
     * 通過RoundedBitmapDrawable繪製圓形Bitmap,且載入ImageView.
     * @param bitmap
     */
    private void bindCircleBitmapToImageView(Bitmap bitmap){
        RoundedBitmapDrawable bitmapDrawable=  RoundedBitmapDrawableFactory.create(view.getContext().getResources(),bitmap);
        bitmapDrawable.setCircular(true);
        view.setImageDrawable(bitmapDrawable);
    }
}

最後使用方式

  GlideRequest<Bitmap> glideRequest = GlideApp.with(context).asBitmap();
        glideRequest.load(imageUrl).error(errorResourceId)//異常時候顯示的圖片
                .placeholder(placeResourceId)//載入成功前顯示的圖片
                .fallback(nullResourceId)//url為空的時候,顯示的圖片
                .into(new CircleBitmapTarget(imageView));//在RequestBuilder<Bitmap> 中使用自定義的ImageViewTarget