前言
Weex是目前跨端方案中相对比较成熟的一个,但是开源版本的Weex在实际使用过程中还是碰到不少问题,今天就来说说一个最近碰到的Android端有关于图片加载的坑。
Weex的image标签在一般的使用场景下都是预先指定宽高的,然而在一些特殊场景下,我们需要根据网络图片的宽高来动态指定image标签的宽高。于是乎,坑就来了,在加载常规的png/jpg图片时,一切都是正常的,但是在加载gif时,就会出现无法正常获取宽高的问题。
Weex官方的处理速度一直很捉急,那么只能从源码入手一步步自己排查问题。
问题排查
Weex提供了IWXImgLoaderAdapter接口让用户自己可以自定义图片加载库,我们这边则使用了Glide来加载图片。
public interface IWXImgLoaderAdapter {
void setImage(String var1, ImageView var2, WXImageQuality var3, WXImageStrategy var4);
}
在setImage方法中自定义图片加载:
Glide.with(context).load(temp).listener(new RequestListener() {
@Override
public boolean onException(Exception e, String model, Target target,
boolean isFirstResource) {
if (strategy != null && strategy.getImageListener() != null) {
strategy.getImageListener().onImageFinish(url, view, false, null);
}
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target target,
boolean isFromMemoryCache, boolean isFirstResource) {
if (strategy != null && strategy.getImageListener() != null) {
strategy.getImageListener().onImageFinish(url, view, true, null);
}
return false;
}
}).into(view);
其中WXImageStrategy的ImageListener接口最终在com.taobao.weex.ui.component.WXImage的setRemoteSrc被实现。
public class WXImageStrategy {
/** @deprecated */
@Deprecated
public boolean isClipping;
public boolean isSharpen;
public int blurRadius;
public String placeHolder;
WXImageStrategy.ImageListener imageListener;
public WXImageStrategy() {
}
public WXImageStrategy.ImageListener getImageListener() {
return this.imageListener;
}
public void setImageListener(WXImageStrategy.ImageListener imageListener) {
this.imageListener = imageListener;
}
public interface ImageListener {
void onImageFinish(String var1, ImageView var2, boolean var3, Map var4);
}
}
private void setRemoteSrc(Uri rewrited, int blurRadius) {
WXImageStrategy imageStrategy = new WXImageStrategy();
imageStrategy.isClipping = true;
WXImageSharpen imageSharpen = this.getDomObject().getAttrs().getImageSharpen();
imageStrategy.isSharpen = imageSharpen == WXImageSharpen.SHARPEN;
imageStrategy.blurRadius = Math.max(0, blurRadius);
this.mBlurRadius = blurRadius;
imageStrategy.setImageListener(new ImageListener() {
public void onImageFinish(String url, ImageView imageView, boolean result, Map extra) {
if (WXImage.this.getDomObject() != null && WXImage.this.getDomObject().getEvents().contains("load")) {
Map params = new HashMap();
Map size = new HashMap(2);
if (imageView != null && imageView instanceof WXImage.Measurable) {
size.put("naturalWidth", ((WXImage.Measurable)imageView).getNaturalWidth());
size.put("naturalHeight", ((WXImage.Measurable)imageView).getNaturalHeight());
} else {
size.put("naturalWidth", 0);
size.put("naturalHeight", 0);
}
if (WXImage.this.getDomObject() != null && WXImage.this.containsEvent("load")) {
params.put("success", result);
params.put("size", size);
WXImage.this.fireEvent("load", params);
}
}
}
});
String placeholder = null;
if (this.getDomObject().getAttrs().containsKey("placeholder")) {
placeholder = (String)this.getDomObject().getAttrs().get("placeholder");
} else if (this.getDomObject().getAttrs().containsKey("placeHolder")) {
placeholder = (String)this.getDomObject().getAttrs().get("placeHolder");
}
if (!TextUtils.isEmpty(placeholder)) {
imageStrategy.placeHolder = this.getInstance().rewriteUri(Uri.parse(placeholder), "image").toString();
}
IWXImgLoaderAdapter imgLoaderAdapter = this.getInstance().getImgLoaderAdapter();
if (imgLoaderAdapter != null) {
imgLoaderAdapter.setImage(rewrited.toString(), (ImageView)this.getHostView(), this.getDomObject().getAttrs().getImageQuality(), imageStrategy);
}
}
在setRemoteSrc方法中通过com.taobao.weex.ui.view.WXImageView的getNaturalWidth以及getNaturalHeight方法来获取最终的宽高,然后问题就是出来这两个方法的实现上,我们继续看:
public int getNaturalWidth() {
Drawable drawable = this.getDrawable();
if (drawable != null) {
if (drawable instanceof ImageDrawable) {
return ((ImageDrawable)drawable).getBitmapWidth();
}
if (drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
if (bitmap != null) {
return bitmap.getWidth();
}
WXLogUtils.w("WXImageView", "Bitmap on " + drawable.toString() + " is null");
} else {
WXLogUtils.w("WXImageView", "Not supported drawable type: " + drawable.getClass().getSimpleName());
}
}
return -1;
}
com.taobao.weex.ui.view.WXImageView实际是继承自android.widget.ImageView,在getNaturalWidth以及getNaturalHeight中通过拿到ImageView的Drawable来拿到宽高,然而Gif格式的图片拿到的却是GifDrawable。所以最终自然就没办法拿到实际宽高了。
解决方案
在不修改Weex源码前提下,需要先用Glide将Gif图加载成File,然后通过判断文件头或者调用系统的BitmapFactory.Options类的outMimeType等方式获取图片宽高设置给image,然后再次调用glide将图片设置到最终的ImageView中。
修改Weex的com.taobao.weex.ui.view.WXImageView类中的getNaturalWidth以及getNaturalHeight方法,当发现是GifDrawable时候,调用Drawable类的getIntrinsicWidth以及getIntrinsicHeight来获取宽高。
PS
Weex的image标签如果需要动态设置宽高还有一个坑,就还是需要预先给image标签设置一个初试宽高,通常会设置1px,不然依然无法正常显示……