美文网首页Android自定义View
requestLayout()引起的问题

requestLayout()引起的问题

作者: HWilliamgo | 来源:发表于2018-08-31 19:13 被阅读55次

    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

    相关文章

      网友评论

        本文标题:requestLayout()引起的问题

        本文链接:https://www.haomeiwen.com/subject/sweewftx.html