Glide target机制

首先给出一张类图:

ViewTarget

上面的Target是接口的定义,而BaseTarget只是对Target的抽象类,没啥分析的。

ViewTarget有两个知识点可以分析:

  • view大小(长宽)的计算
  • view状态的监听

view大小(长宽)的计算

这个就是SizeDeterminer内部类的事情了。

主要的代码:

private boolean isViewStateAndSizeValid(int width, int height) {
        return isDimensionValid(width) && isDimensionValid(height);
    }

    private int getTargetHeight() {
        int verticalPadding = view.getPaddingTop() + view.getPaddingBottom();
        LayoutParams layoutParams = view.getLayoutParams();
        int layoutParamSize = layoutParams != null ? layoutParams.height : PENDING_SIZE;
        return getTargetDimen(view.getHeight(), layoutParamSize, verticalPadding);
    }

    private int getTargetWidth() {
        int horizontalPadding = view.getPaddingLeft() + view.getPaddingRight();
        LayoutParams layoutParams = view.getLayoutParams();
        int layoutParamSize = layoutParams != null ? layoutParams.width : PENDING_SIZE;
        return getTargetDimen(view.getWidth(), layoutParamSize, horizontalPadding);
    }

    private int getTargetDimen(int viewSize, int paramSize, int paddingSize) {
        int adjustedParamSize = paramSize - paddingSize;
        if (adjustedParamSize > 0) {
            return adjustedParamSize;
        }


        if (waitForLayout && view.isLayoutRequested()) {
            return PENDING_SIZE;
        }


        int adjustedViewSize = viewSize - paddingSize;
        if (adjustedViewSize > 0) {
            return adjustedViewSize;
        }

        if (!view.isLayoutRequested() && paramSize == LayoutParams.WRAP_CONTENT) {
            if (Log.isLoggable(TAG, Log.INFO)) {
                Log.i(TAG, "Glide treats LayoutParams.WRAP_CONTENT as a request for an image the size of"
                        + " this device's screen dimensions. If you want to load the original image and are"
                        + " ok with the corresponding memory cost and OOMs (depending on the input size), use"
                        + " .override(Target.SIZE_ORIGINAL). Otherwise, use LayoutParams.MATCH_PARENT, set"
                        + " layout_width and layout_height to fixed dimension, or use .override() with fixed"
                        + " dimensions.");
            }
            return getMaxDisplayLength(view.getContext());
        }


        return PENDING_SIZE;
    }

    private boolean isDimensionValid(int size) {
        return size > 0 || size == SIZE_ORIGINAL;
    }

    // Use the maximum to avoid depending on the device's current orientation.
    private static int getMaxDisplayLength(@NonNull Context context) {
      if (maxDisplayLength == null) {
        WindowManager windowManager =
            (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = Preconditions.checkNotNull(windowManager).getDefaultDisplay();
        Point displayDimensions = new Point();
        display.getSize(displayDimensions);
        maxDisplayLength = Math.max(displayDimensions.x, displayDimensions.y);
      }
      return maxDisplayLength;
    }

整个的计算跟三个参数有关系:

  • view的padding值
  • LayoutParams里的width(或height)值
  • view的getWidth() 或者 getHeight()

如果在布局文件里宽高没有写固定的值,那么LayoutParams里的width(或height)就是-1(MATCH_PARENT) 或 -2(WRAP_CONTENT)。

计算的步骤:

  1. 先让LayoutParams里的值减去padding的值,大于0,则直接返回
  2. 是否等待layout之后,是就直接返回
  3. view的getXX()减去padding的值,大于0,则直接返回
  4. 计算屏幕大小了~~(以屏幕大小作为照片的大小)

view状态的监听

根据view是否展示,来决定请求是否继续还是暂停。

首先,注册监听:

public final ViewTarget<T, Z> clearOnDetach() {
    if (attachStateListener != null) {
      return this;
    }
    attachStateListener = new OnAttachStateChangeListener() {
      @Override
      public void onViewAttachedToWindow(View v) {
        resumeMyRequest();
      }

      @Override
      public void onViewDetachedFromWindow(View v) {
        pauseMyRequest();
      }
    };
    maybeAddAttachStateListener();
    return this;
  }

给view设置tag(为了能取消对应的view上的请求):

@Override
  public void setRequest(@Nullable Request request) {
    setTag(request);
  }

  private void setTag(@Nullable Object tag) {
    if (tagId == null) {
      isTagUsedAtLeastOnce = true;
      view.setTag(tag);
    } else {
      view.setTag(tagId, tag);
    }
  }

  @Nullable
  private Object getTag() {
    if (tagId == null) {
      return view.getTag();
    } else {
      return view.getTag(tagId);
    }
  }

在具体的事件下执行网络请求,或者暂停网络:

@SuppressWarnings("WeakerAccess")
  @Synthetic void resumeMyRequest() {
    Request request = getRequest();
    if (request != null && request.isPaused()) {
      request.begin();
    }
  }

  @SuppressWarnings("WeakerAccess")
  @Synthetic void pauseMyRequest() {
    Request request = getRequest();
    if (request != null && !request.isCancelled() && !request.isPaused()) {
      isClearedByUs = true;
      request.pause();
      isClearedByUs = false;
    }
  }

ImageViewTarget

这里主要的点是图片加载中的动画切换。

它有一个Animatable属性,在图片加载的各个周期中触发:

@Override
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

  @Override
  public void onLoadFailed(@Nullable Drawable errorDrawable) {
    super.onLoadFailed(errorDrawable);
    setResourceInternal(null);
    setDrawable(errorDrawable);
  }

  @Override
  public void onLoadCleared(@Nullable Drawable placeholder) {
    super.onLoadCleared(placeholder);
    if (animatable != null) {
      animatable.stop();
    }
    setResourceInternal(null);
    setDrawable(placeholder);
  }

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

ThumbnailImageViewTarget

专门针对小图的的target,主要代码:

@Override
  protected void setResource(@Nullable T resource) {
    ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
    Drawable result = getDrawable(resource);
    if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) {
      result = new FixedSizeDrawable(result, layoutParams.width, layoutParams.height);
    }

    view.setImageDrawable(result);
  }

  protected abstract Drawable getDrawable(T resource);

对于确定大小的Drawable是FixedSizeDrawable源码

SimpleTarget

可以指定大小的Target,这个在开发中应该能不用就尽量不要用

它有如下问题:

  • 必须指定大小,这样glide的内部根据图片展示计算大小的功能就没有了
  • 必须主动回收

建议的用法:

Target  target =
       Glide.with(fragment)
         .asBitmap()
         .load("http://somefakeurl.com/fakeImage.jpeg")
         .apply(fitCenterTransform())
         .into(new SimpleTarget (250, 250) {

            @Override
           public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
             // Do something with bitmap here.
           }

         });
   }

   // At some later point, clear the Target to release the resources, prevent load queues from
   // blowing out proportion, and to improve load times for future requests:
   Glide.with(fragment).clear(target);

NotificationTarget & AppWidgetTarget

这2个都是remoteviews上加载图片的时候使用

PreloadTarget

预加载,针对RequestBuilder public Target<TranscodeType> preload(int width, int height)使用

主要是为了加快显示,先下载下来。

results matching ""

    No results matching ""