requestLayout()引起的問題
網(wǎng)上有大量寫的很深入的
requestLayout()
源碼分析的文章或杠。故這里不再寫了滨彻,只做一個實際情況下遇到的問題的分析搔确。起因:
自定義了一個
CircleImageView
绝页,功能是調(diào)用setImage(Bitmap bitmap)
后可以將圖片以圓形加載民泵。本以為直接在
setImage(Bitmap)
的結(jié)尾直接調(diào)用requestLayout()
即可迄汛。
這里從兩個方面寫:
xml中定義為wrap_content
當(dāng)LayoutParams
是wrap_content
時捍壤,我處理的邏輯是:在onMeasure()
中根據(jù)寬高的MeasureSpec
是否等于MeasureSpec.AT_MOST
,如果等于鞍爱,那么在第一次繪制的時候鹃觉,setMeasureDimension()
都設(shè)置成0,而當(dāng)調(diào)用了setBitmap()
時硬霍,獲取圖片的寬高并保存帜慢,然后調(diào)用requestLayout()
,此舉引起onMeasure()
,那么在此處將圖片的寬高設(shè)置到setMeasureDimension()
中粱玲,而整體的View的測量大小就是圖片大小了躬柬。
此時我的onMeasure
和setBitmap
長這樣:
@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就設(shè)置成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();//最后要調(diào)用一個requestLayout允青,引起onMeasure()和onLayout()。
}
大小不同的兩張圖片
此時為這個CircleImageView
準(zhǔn)備了兩張分辨率不同的圖片卵沉,點擊按鈕A加載圖片A颠锉,點擊按鈕B加載圖片B。
點擊情況:
- 由顯示A的情況下加載B史汗,或者顯示B的情況下加載A琼掠,或者從沒有圖片情況下點擊加載A或B,分別引起了
onMeasure()
,onSizeChanged()
,onLayout()
,onDraw
回調(diào)停撞。成功地在onDraw
重新drawBitmap()
切換了圖片瓷蛙。 - 當(dāng)在A圖片下點擊按鈕A,或者在B圖片下點擊按鈕B戈毒。只引起了
onMeasure()
和onLayout()
回調(diào)艰猬。這里少了一個onSizeChanged()
很好理解,因為在該情況下埋市,setMeasureDimesion()
傳入的值和上一次一樣冠桃,View中可以很容易通過這種判斷而跳過onSizeChanged()
回調(diào),而至于為什么onDraw()
回調(diào)沒有引起道宅,這點我也很疑惑食听。
大小相同的兩張圖片
此時我又準(zhǔn)備了兩張大小相同的圖片。操作和上述一樣污茵。
點擊情況:
- 從沒有圖片的情況下點擊加載圖片A:
onMeasure()
,onSizeChanged()
,onLayout()
,onDraw
- 從圖片A點擊加載圖片B:加載失敗碳蛋,圖片仍然停留在圖片A,此時的回調(diào)是:
onMeasure()
和`onLayout()` - 圖片A點擊加載圖片A: 同2省咨。
推論:(在wrap_content情況下)
- 當(dāng)
requestLayout()
調(diào)用時,一定會引起onMeasure()
和onLayout()
玷室。 - 當(dāng)
requestLayout()
調(diào)用時零蓉,如果沒有在setMeasureDimension()
中傳入和上次不同的測量值的話,一定不會引起onSizeChanged()
和onDraw()
穷缤。onSizeChanged()
不被調(diào)用的原因很容易在onLayout()
的源碼中找到答案敌蜂,而onDraw()
不引起回調(diào)的原因目前還不明白。
xml中定義為精確值
情況:此時圖片直接無法加載津肛。僅僅在第一次實例化CircleImageView的時候會依次調(diào)用
onMeasure()
,onSizeChanged()
,onLayout()
,onDraw
章喉,伺候每一次調(diào)用setImage()
都只會引起onMeasure()
和onLayout()
。原因:因為在
requestLayout()
調(diào)用后,因為此時的測量模式是EXACTLY
秸脱,因此setMeasuredDimension()
中傳入的值永遠(yuǎn)不變落包,永遠(yuǎn)都是xml中定義的那個精確值。而上文的推論中指出摊唇,setMeasuredDimension()
傳入的值等于原本的測量值的話咐蝇,直接引起onSizeChanged()
和onDraw()
無法調(diào)用。
結(jié)論
- 上文中的推論
- 不能依賴
requestLayout
來引起onDraw()
回調(diào)巷查,如果百分之百確定要繪制有序,直接調(diào)用invalidate()
或postInvalidate()
,他們只會引起onDraw()
的回調(diào)岛请。 - CircleImageView源碼:https://github.com/William619499149/anddroid-little-bubble/blob/master/CircleImageView.java