Fresco前传(3):之为什么图片不显示(坑爹的wrap_content)
2015-11-05 17:46
561 查看
前言
这叫一个坑,搞了半天图片都显示不出来。给出翻译的中文文档
正文
看了一下Fresco文档后你肯定欲血沸腾,想赶紧试试它有多么的强大。于是,你直接将文档中这段代码复制到了layout中。<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/my_image_view" android:layout_width="200dp" android:layout_height="200dp" fresco:placeholderImage="@drawable/my_drawable" />
显示的非常漂亮,内心BB一句,太爽了。然后你可能会这样想,要是能适应高度该多好,于是你把代码改成了这样。
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/my_image_view" android:layout_width="200dp" android:layout_height="wrap_content" fresco:placeholderImage="@drawable/my_drawable" />
一运行傻眼了,图片呢?如果换成
ImageView的话应该是能够正常显示的呀?
实际情况是
SimpleDraweeView已经被成功加载了,只不过高度为
0dp而已,所以你自然就看不到了。
这里先直接说一下结论,后面再慢慢分析。
结论:(一下几个要求要同时达到,才能显示图片)
宽度或者高度,两者至少一个以上的测量规格(
MeasureSpec)模式为
MeasureSpec.EXACTLY。换句话说,宽度或者高度,两个至少一个以上,被指定为
match_parent或者固定宽高值(例如:
100dp)
宽度或者高度,当两者其中有一个被指定为
warp_content时,必须在代码中为控件设置宽高比(
draweeView.setAspectRatio(0.5F);)
不要使用
0dp+
layout_weight=1的组合代替
warp_content,虽然在源码中有这样一句话// Note: wrap_content is supported for backwards compatibility, but should not be used.(warp_content是为了支持向后的兼容性,不应该被使用。)
如此这样,你的图片就能显示出来了:
draweeView.setAspectRatio(0.5F); draweeView.setImageURI(Uri.parse("...")); <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/draweeView" android:layout_width="match_parent" android:layout_height="wrap_content" />
分析
问题一 为什么不显示图片
问题来了,为什么在如下情况下图片没有正常显示(只剩下薄薄的一层)?这个肯定是和测量有关系了,看一下
SimpleDraweeView的
onMeasure()方法。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureSpec.width = widthMeasureSpec; mMeasureSpec.height = heightMeasureSpec; // 下面这局,在没有调用aspect为0时,不会执行。 AspectRatioMeasure.updateMeasureSpec( mMeasureSpec, mAspectRatio, getLayoutParams(), getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); super.onMeasure(mMeasureSpec.width, mMeasureSpec.height); }
并没有什么有价值的信息,跟到
super.onMeasure()中,会走到
ImageView的
onMeasure()方法,里面全部份大段的代码都是判断是否要根据比例来修改图片宽高,没有什么用,最后会执行以下的代码,我们好好分析一下:
int pleft = mPaddingLeft; int pright = mPaddingRight; int ptop = mPaddingTop; int pbottom = mPaddingBottom; ...省略 else { /* We are either don't want to preserve the drawables aspect ratio, or we are not allowed to change view dimensions. Just measure in the normal way. */ w += pleft + pright; h += ptop + pbottom; w = Math.max(w, getSuggestedMinimumWidth()); h = Math.max(h, getSuggestedMinimumHeight()); widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); } setMeasuredDimension(widthSize, heightSize);
可以看到,在
h和
getSuggestedMinimumHeight()中取最大值再赋给
h,而
h之前的值是
mPaddingBottom不会很大,而
getSuggestedMinimumHeight()的值貌似是
10dp(记不清除了),反正两者都不大,取最大值之后的
h值,自然也不会大到哪里去!
接着执行了以下代码,
heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); setMeasuredDimension(widthSize, heightSize);
简单的含义就是,从
h和高度的测量规格中,两者取小值,然后设置控件高度。
看到这里你就明白了为什么图片没有正常显示,一群小个子中取最小的,能高到哪里去?
问题二 为什么显示图片
那么,问题又来了,凭什么加上draweeView.setAspectRatio(0.5F);设置了宽高比之后就可以显示了呢?
先上代码和效果图:
draweeView.setAspectRatio(1F); draweeView.setImageURI(Uri.parse("...")); <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/draweeView" android:layout_width="match_parent" android:layout_height="wrap_content" />
同样的,图片先不显示肯定和
onMeasure()有关系,我就再贴一次代码(不要打我),至于为什么设置了宽高比就走
onMeasure()可以看我的前一篇文章:Fresco分析
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureSpec.width = widthMeasureSpec; mMeasureSpec.height = heightMeasureSpec; AspectRatioMeasure.updateMeasureSpec( mMeasureSpec, mAspectRatio, getLayoutParams(), getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); super.onMeasure(mMeasureSpec.width, mMeasureSpec.height); }
当宽高比不为0时,就会执行
AspectRatioMeasure.updateMeasureSpec()方法,这是一个根据宽高比重测宽高的方法。
方法代码如下:
public static void updateMeasureSpec( Spec spec, float aspectRatio, @Nullable ViewGroup.LayoutParams layoutParams, int widthPadding, int heightPadding) { ...省略代码 if (shouldAdjust(layoutParams.height)) { // 获取父控件期望的宽的测量宽度 int widthSpecSize = View.MeasureSpec.getSize(spec.width); // 根据父控件期望的宽的测量宽度和宽高比计算出咱们期望高的高度 int desiredHeight = (int) ((widthSpecSize - widthPadding) / aspectRatio + heightPadding); // 期望的高度与父控件期望的高度两者取小的 int resolvedHeight = View.resolveSize(desiredHeight, spec.height); // 最后重设高的测量规格 spec.height = View.MeasureSpec.makeMeasureSpec(resolvedHeight, View.MeasureSpec.EXACTLY); } ...省略代码 } private static boolean shouldAdjust(int layoutDimension) { // Note: wrap_content is supported for backwards compatibility, but should not be used. return layoutDimension == 0 || layoutDimension == ViewGroup.LayoutParams.WRAP_CONTENT; }
记住此时在layout我们控件的宽设置的是
wrap_content。首先会执行
shouldAdjust()方法,该方法在高度参数为0或者
wrap_content返回
true,这时会进入
if中。
在if中,首先拿到测量宽度,在根据比例拿到期望的高度。接下来是最关键的一句
View.resolveSize(desiredHeight, spec.height);,在期望的高度和父控件建议的高度之中,取最小的值。在
resolvesSize()代码中,可以看到在
MeasureSpec.AT_MOST分支中如果期望高度不大于父控件建议的高度,则将
size作为了最后的返回结果,即是将期望的高度作为最后的结果返回。
这时,宽度和高度就有了,剩下的就是布局事情了,相信不用我多说。最后,至于为什么会走到
MeasureSpec.AT_MOST分支,我只想告诉你,嘿嘿,你猜呀,你猜呀!
问题三 为什么设置0dp+layout_weight=1不好使?
在参看这篇文章以前,你肯定页看了一些其他的博客,无以不是告诉你,如果想使用宽高比,那么你应该使用0dp+
layout_weight=1的方式。
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/draweeView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
但是不行的是,没用呀,没用呀,完全没用呀,图片还是出不来。这里,我猜测的原因是
layout_weight参数并没有起到作用,导致高度为
0dp时,引起了一系列逗逼的结果。
假设,
layout_weight没生效,那么高度的值为
0dp,其测量模式是
MeasureSpec.EXACTLY,在
resolveSizeAndStat()中的
MeasureSpec.EXACTLY分支中,直接将
specSize作为结果了,而
specSize的值,恰恰是
0dp。悲剧。
最后
欢迎各位拍砖交流Me Github : https://github.com/biezhihua
相关文章推荐
- 《C Primer Plus》学习笔记之 函数
- iOS TableView实现QQ好友列表(三)
- 图形学实验Bezier曲线生成
- 练习!!标准体重
- 栈溢出原理与实践
- 最新版SDWebImage的使用
- UIViewController中edgesForExtendedLayout属性的画面切断现象
- zookeeper启动异常
- Android菜单详解——子菜单(SubMenu)
- 画二维图像
- (1)Two Sum--求数组中相加为指定值的两个数
- jQuery wrap wrapAll wrapInner使用
- 使用cookie解决微信不能存储localStorage的问题
- Android-05 Android的MVC设计框架浅析
- Visio 2013绘制时序图
- RSA加密(4.0)
- 水印控件
- 注意:只有xcode5.1创建的项目会自动适配iphone6,iphone6p
- 机器学习复习——Apriori
- 注意:只有xcode5.1创建的项目会自动适配iphone6,iphone6p