前言
開始意識(shí)到犀变,知識(shí)"縫合"的重要性膳算,只要沒有開始學(xué)會(huì)表述,看書再多弛作,終究會(huì)忘記涕蜂。
ok,這一篇映琳,用來記錄View的相關(guān)小知識(shí)點(diǎn)机隙,持續(xù)更新與整理。
關(guān)于 View 繪制流程
- onMeasure(): 尺寸大小
- onLayout(): 擺放位置
- onDraw(): 圖層繪制
簡單來說萨西,自定義 View 時(shí)有鹿,最重要的就是這三個(gè)方法。個(gè)人認(rèn)為谎脯,其中 onDraw() 是最關(guān)鍵的葱跋,也是最難的,其中牽扯到一大堆坐標(biāo)的計(jì)算源梭,暈~
簡單說一下娱俺,自己的理解:
- onMeasure()
- 繼承自 View 的話,就只是計(jì)算自己的尺寸废麻。
- 繼承自 ViewGroup 的話荠卷,就先要計(jì)算每一個(gè)子 View 的寬高,并為其 mMeasuredWidth 和 mMeasuredHeight 這兩個(gè)屬性賦值烛愧,最近才計(jì)算出自己的尺寸油宜。
- 為了適配的話掂碱,可以嘗試使用 resolveSize()。
- onSizeChanged()
- 提一嘴這個(gè)慎冤,個(gè)人比較常用疼燥,它在構(gòu)造方法、onMeasure() 之后蚁堤,又在 onDraw() 之前悴了,此時(shí)已經(jīng)完成全局變量初始化,也得到了控件的寬高违寿;
- 所以可以在這個(gè)方法中確定一些與寬高有關(guān)的數(shù)值,比如這個(gè) View 的寬高半徑熟空,Padding 等藤巢,方便繪制的時(shí)候計(jì)算大小和位置。
- onLayout()
- 這個(gè)方法本身息罗,其實(shí)是為了控制子 View 在自己中的擺放位置掂咒。
- 繼承自 View 的話,onLayout() 方法為空實(shí)現(xiàn)迈喉,畢竟沒娃 = =
- 繼承自 ViewGroup 的話绍刮,需要復(fù)寫,因?yàn)橛泻⒆影
- onDraw()
- 其實(shí) View 最終都是調(diào)用 draw() 方法進(jìn)行繪制的挨摸,然而平常都是復(fù)寫onDraw()方法進(jìn)行繪制孩革,沒懂這兩者之間的關(guān)系 = =
- 需要先調(diào)用 super.draw() 完成系統(tǒng)的繪制,再進(jìn)行自定義的繪制得运。
- ViewGroup 中膝蜈,dispatchDraw() 發(fā)起對(duì)子視圖的繪制,具體沒研究過熔掺。
附圖一張饱搏,能夠非常清晰的說明,整個(gè)繪制流程置逻。同時(shí)推沸,也說明了,invalidate() 和 requestLayout() 的區(qū)別券坞。
奉上原圖鏈接: https://lh3.googleusercontent.com/-TTZ-LN41ygA/UIQt75DBSEI/AAAAAAAAFAY/v8jAR9WWg5w/w829-h1463-p-rw/android.png
關(guān)于 View 的刷新機(jī)制
所謂的刷新機(jī)制鬓催,一句話說清楚,就是:子通知父開始刷新恨锚,父控制子的刷新操作深浮。
常用的有兩種刷新方式,invalidate()眠冈,requestLayout();
其實(shí)區(qū)別飞苇,再上圖中已經(jīng)說明了菌瘫,
** requestLayout() 比 invalidate() 多走了 measure 與 layout 這兩個(gè)流程 **
因此:
- requestLayout() 常用于當(dāng) View 發(fā)生位移,旋轉(zhuǎn)等 LayoutParams 發(fā)生變化的場景布卡;
- invalidate() 則只是對(duì)當(dāng)前 View 進(jìn)行重繪雨让,不會(huì)進(jìn)行測量、布局流程
額外提一句忿等,
** postInvalidate() 是在非UI線程中調(diào)用栖忠,invalidate() 則是在UI線程中調(diào)用。**
關(guān)于 onDraw() 再多BB兩句
- 要對(duì)各個(gè)坐標(biāo)很熟悉贸街,這個(gè)很重要庵寞,詳情請(qǐng)參考 http://blog.csdn.net/jason0539/article/details/42743531
- 要對(duì) Canvas(畫布) 和 Paint(畫筆)的各個(gè)方法比較熟悉,比如** drawBitmap()薛匪,drawOval()捐川,drawArc() **等
我真正想說的是,drawText()逸尖,這個(gè)很奇葩古沥,吃了不少苦頭,先看看官方描述:
** 注意娇跟,關(guān)于 y 的描述岩齿,有一個(gè)詞 baseline ** ,Android中苞俘,對(duì)文字有顯示盹沈,劃分了五條線,有點(diǎn)像小時(shí)候?qū)懫匆魰r(shí)的那個(gè)吃谣,其中第三條線襟诸,叫做 baseline,也就是說基协,xy參數(shù)歌亲,并不是常用的左上角坐標(biāo) = =
另外再說一句,在使用drawText()澜驮,處理文本(單個(gè)字符)居中時(shí)陷揪,有時(shí)候總是對(duì)不齊,請(qǐng)檢查杂穷,** 文字的寬度悍缠,和,外接矩形的寬度是否一致 **耐量,找不到特別合適的圖飞蚓,將就一下吧 = =
textSmallWidth 是數(shù)字3的真實(shí)寬度,而 mTextRect.height() 是外接矩形的高度廊蜒,
額趴拧,這個(gè)解釋一下溅漾,mTextRect.height() 是大于等于 mTextRect.width() 的,當(dāng)內(nèi)容是單個(gè)字符的時(shí)候著榴,height = width
mTextPaint.getTextBounds("3", 0, timeText.length(), mTextRect);
這段代碼可以獲取到添履,數(shù)字3繪制時(shí)的真實(shí)寬度。
對(duì)自定義View進(jìn)行控制
這一段是在別人的博客上看到的脑又,我覺得很有用暮胧,摘抄至此。盡管我都知道问麸,但是沒有總結(jié)過往衷。
如果想控制 View 在屏幕上的渲染效果,就在重寫 onDraw() 方法严卖,在里面進(jìn)行相應(yīng)的處理席舍。
如果想要控制用戶與 View 之間的交互操作,則在 onTouchEvent() 方法中對(duì)手勢進(jìn)行控制處理妄田。
如果想要控制 View 中內(nèi)容在屏幕上顯示的尺寸大小,就重寫 onMeasure() 方法中進(jìn)行處理驮捍。
在 XML文件中設(shè)置自定義View的XML屬性疟呐。
如果想避免失去 View 的相關(guān)狀態(tài)參數(shù)的話,就在 onSaveInstanceState() 和 onRestoreInstanceState() 方法中保存有關(guān)View的狀態(tài)信息东且。
總結(jié)
第一次開始寫启具,沒有想象中的難,只要一開始寫珊泳,就會(huì)不斷面臨選擇從而最終達(dá)到目的鲁冯,最可怕的就是,永遠(yuǎn)停留在開始前的那一刻色查,遲遲不肯動(dòng)筆薯演。
整個(gè)過程,花了很久秧了,整理自己的思路跨扮,找合適的圖,本以為30分鐘就該搞定了验毡,= =
果然衡创,項(xiàng)目實(shí)際時(shí)間 = 預(yù)估時(shí)間 * 5;
寫的有錯(cuò)的地方晶通,望各位大牛指正璃氢,可能邏輯不夠清晰,語言有些啰嗦狮辽,奈何小學(xué)語文沒畢業(yè)~