Loading

通过Glide加载不可见的图片

今天遇到一个需求,需要点击分享的时候生成图片以及二维码。

即:将带有图片以及二维码的布局文件生成Bitmap,当然这个布局文件是后台生成的,并不可见,这时候会发现使用Glide加载图片没有反应。

源码分析:

追踪到ViewTarget里面的getSize方法:

void getSize(@NonNull SizeReadyCallback cb) {
      int currentWidth = getTargetWidth();
      int currentHeight = getTargetHeight();
      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
        cb.onSizeReady(currentWidth, currentHeight);
        return;
      }

      // We want to notify callbacks in the order they were added and we only expect one or two
      // callbacks to be added a time, so a List is a reasonable choice.
      if (!cbs.contains(cb)) {
        cbs.add(cb);
      }
      if (layoutListener == null) {
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        observer.addOnPreDrawListener(layoutListener);
      }
    }

假如获取到的View的宽高不大于0,那么就不会走cb.onSizeReady(currentWidth, currentHeight);也就不会往下执行,而是会给ViewTreeObserver设置一个监听。

 

我们进去getTargetWidth()方法:

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) {
  // We consider the View state as valid if the View has non-null layout params and a non-zero
  // layout params width and height. This is imperfect. We're making an assumption that View
  // parents will obey their child's layout parameters, which isn't always the case.
  int adjustedParamSize = paramSize - paddingSize;
  if (adjustedParamSize > 0) {
    return adjustedParamSize;
  }
  // Since we always prefer layout parameters with fixed sizes, even if waitForLayout is true,
  // we might as well ignore it and just return the layout parameters above if we have them.
  // Otherwise we should wait for a layout pass before checking the View's dimensions.
  if (waitForLayout && view.isLayoutRequested()) {
    return PENDING_SIZE;
  }
  // We also consider the View state valid if the View has a non-zero width and height. This
  // means that the View has gone through at least one layout pass. It does not mean the Views
  // width and height are from the current layout pass. For example, if a View is re-used in
  // RecyclerView or ListView, this width/height may be from an old position. In some cases
  // the dimensions of the View at the old position may be different than the dimensions of the
  // View in the new position because the LayoutManager/ViewParent can arbitrarily decide to
  // change them. Nevertheless, in most cases this should be a reasonable choice.
  int adjustedViewSize = viewSize - paddingSize;
  if (adjustedViewSize > 0) {
    return adjustedViewSize;
  }
  // Finally we consider the view valid if the layout parameter size is set to wrap_content.
  // It's difficult for Glide to figure out what to do here. Although Target.SIZE_ORIGINAL is a
  // coherent choice, it's extremely dangerous because original images may be much too large to
  // fit in memory or so large that only a couple can fit in memory, causing OOMs. If users want
  // the original image, they can always use .override(Target.SIZE_ORIGINAL). Since wrap_content
  // may never resolve to a real size unless we load something, we aim for a square whose length
  // is the largest screen size. That way we're loading something and that something has some
  // hope of being downsampled to a size that the device can support. We also log a warning that
  // tries to explain what Glide is doing and why some alternatives are preferable.
  // Since WRAP_CONTENT is sometimes used as a default layout parameter, we always wait for
  // layout to complete before using this fallback parameter (ConstraintLayout among others).
  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());
  }
  // If the layout parameters are < padding, the view size is < padding, or the layout
  // parameters are set to match_parent or wrap_content and no layout has occurred, we should
  // wait for layout and repeat.
  return PENDING_SIZE;
}

即:当

layoutParamSize - padding 大于0的时候返回该接结果。

view.getWidth() - padding 大于0的时候返回该结果。

当我们后台加载的布局中ImageView的LayouParam没有设置确切数值的时候,返回的是PENDING_SIZE == 0,这时候像上面所说的,它就不会回调出去,而是会给ViewTreeObserver设置一个监听,addOnPreDrawListener,当测量完毕后开始绘制前会回调该监听。但是由于我们的布局是后台加载的,没有添加到界面上,所以该回调不会走,所以图片也就没法加载。

而我们平常使用的时候,则会回调该监听,重新获取View大小,并回调出去。

 

所以若要通过Glide加载不可见的图片,那么则需要设置有确切数值大小的LayouParam。

 (若分析有误欢迎指正)

转载请标明: https://www.cnblogs.com/tangZH/p/14691697.html

 

posted @ 2021-04-22 21:46  妖久  阅读(31)  评论(0编辑  收藏  举报