Android自定义View

requestLayout()引起的问题

2018-08-31  本文已影响55人  HWilliamgo

requestLayout()引起的问题

网上有大量写的很深入的requestLayout()源码分析的文章。故这里不再写了,只做一个实际情况下遇到的问题的分析。

起因:

自定义了一个CircleImageView,功能是调用setImage(Bitmap bitmap)后可以将图片以圆形加载。

本以为直接在setImage(Bitmap)的结尾直接调用requestLayout()即可。

这里从两个方面写:

xml中定义为wrap_content

LayoutParamswrap_content时,我处理的逻辑是:在onMeasure()中根据宽高的MeasureSpec是否等于MeasureSpec.AT_MOST,如果等于,那么在第一次绘制的时候,setMeasureDimension()都设置成0,而当调用了setBitmap()时,获取图片的宽高并保存,然后调用requestLayout(),此举引起onMeasure(),那么在此处将图片的宽高设置到setMeasureDimension()中,而整体的View的测量大小就是图片大小了。

此时我的onMeasuresetBitmap长这样:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.d(TAG, "onMeasure: ");
    int ws = MeasureSpec.getSize(widthMeasureSpec);
    int hs = MeasureSpec.getSize(heightMeasureSpec);
    int wm = MeasureSpec.getMode(widthMeasureSpec);
    int hm = MeasureSpec.getMode(heightMeasureSpec);
    //如果子view是wrap_content,那么view就设置成bitmap的大小。
    if (wm == MeasureSpec.AT_MOST) {
        ws = bitmapW;
    }
    if (hm == MeasureSpec.AT_MOST) {
        hs = bitmapH;
    }
    int resultW = MeasureSpec.makeMeasureSpec(ws, wm);
    int resultH = MeasureSpec.makeMeasureSpec(hs, hm);
    super.onMeasure(resultW, resultH);
}
public void setImage(Bitmap bitmap) {
    mBitmap = bitmap;
    bitmapH = mBitmap.getHeight();
    bitmapW = mBitmap.getWidth();
    requestLayout();//最后要调用一个requestLayout,引起onMeasure()和onLayout()。
}

大小不同的两张图片

此时为这个CircleImageView准备了两张分辨率不同的图片,点击按钮A加载图片A,点击按钮B加载图片B。

点击情况:

  1. 由显示A的情况下加载B,或者显示B的情况下加载A,或者从没有图片情况下点击加载A或B,分别引起了onMeasure(),onSizeChanged(),onLayout(),onDraw回调。成功地在onDraw重新drawBitmap()切换了图片。
  2. 当在A图片下点击按钮A,或者在B图片下点击按钮B。只引起了onMeasure()onLayout()回调。这里少了一个onSizeChanged()很好理解,因为在该情况下,setMeasureDimesion()传入的值和上一次一样,View中可以很容易通过这种判断而跳过onSizeChanged()回调,而至于为什么onDraw()回调没有引起,这点我也很疑惑。

大小相同的两张图片

此时我又准备了两张大小相同的图片。操作和上述一样。

点击情况:

  1. 从没有图片的情况下点击加载图片A:onMeasure(),onSizeChanged(),onLayout(),onDraw
  2. 从图片A点击加载图片B:加载失败,图片仍然停留在图片A,此时的回调是:onMeasure()和`onLayout()`
  3. 图片A点击加载图片A: 同2。

推论:(在wrap_content情况下)

  1. requestLayout()调用时,一定会引起onMeasure()onLayout()
  2. requestLayout()调用时,如果没有在setMeasureDimension()中传入和上次不同的测量值的话,一定不会引起onSizeChanged()onDraw()onSizeChanged()不被调用的原因很容易在onLayout()的源码中找到答案,而onDraw()不引起回调的原因目前还不明白。

xml中定义为精确值

  1. 情况:此时图片直接无法加载。仅仅在第一次实例化CircleImageView的时候会依次调用onMeasure(),onSizeChanged(),onLayout(),onDraw,伺候每一次调用setImage()都只会引起onMeasure()onLayout()

  2. 原因:因为在requestLayout()调用后,因为此时的测量模式是EXACTLY,因此setMeasuredDimension()中传入的值永远不变,永远都是xml中定义的那个精确值。而上文的推论中指出,setMeasuredDimension()传入的值等于原本的测量值的话,直接引起onSizeChanged()onDraw()无法调用。


结论

  1. 上文中的推论
  2. 不能依赖requestLayout来引起onDraw()回调,如果百分之百确定要绘制,直接调用invalidate()postInvalidate(),他们只会引起onDraw()的回调。
  3. CircleImageView源码:https://github.com/William619499149/anddroid-little-bubble/blob/master/CircleImageView.java
上一篇下一篇

猜你喜欢

热点阅读