今天遇到一个需求,需要点击分享的时候生成图片以及二维码。
即:将带有图片以及二维码的布局文件生成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。
(若分析有误欢迎指正)