——》個(gè)人平時(shí)筆記,看到的同學(xué)歡迎指正錯(cuò)誤,文中多處摘錄于各大博主精華、書籍
1、在自定義View中,drawArc()是繪制弧形或者扇形的,drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint) ,繪制角度以X軸正方向即正右方為0度位置,順時(shí)針為繪制正角度,逆時(shí)針為負(fù);useCenter是否連接圓心,連接為繪畫扇形,不連接則繪制弧形。
path.AddCircle(x,y, radius, dir)+canvas.drawPath(path,paint)這種寫法,和直接使用canvas.drawCircle(x, y, radius,paint)的效果是一樣的,區(qū)別只是它的寫法更復(fù)雜。所以如果只畫一個(gè)圓,沒必要用Path,直接用drawCircle()就行了。drawPath()一般是在繪制組合圖形時(shí)才會(huì)用到的。
2、在自定義view中插值器(Interpolator)和估值器(TypeEvaluator)的關(guān)系:?
估值器依賴于插值器,一般依賴于系統(tǒng)給的默認(rèn)插值器,插值器返回的結(jié)果值就是回調(diào)給估值器中public Object?? evaluate(float fraction, Object startValue, Object endValue)方法中的fraction系數(shù),插值器動(dòng)態(tài)改變fraction系數(shù)從而影響改變估值器運(yùn)算后的具體結(jié)果值,最終通過估值器時(shí)時(shí)變化的結(jié)果值設(shè)置屬性動(dòng)畫的值,并時(shí)時(shí)刷新繪制UI控件,形成一個(gè)動(dòng)畫效果。如:該文插值器與估值器詳解 http://www.lxweimin.com/p/2f19fe1e3ca1
插值器影響動(dòng)畫的速度決定值的變化規(guī)律(勻速、加速等),即決定的是變化趨勢,比如非勻速動(dòng)畫就需要通過插值器來控制動(dòng)畫的播放過程。這個(gè)屬性可以不指定,默認(rèn)為@android:anim/accelerate_decelerate_interpolator,即加速減速插值器。
二者關(guān)系類似的可以比喻成一個(gè)物理位移公式: s=V0t+(at^2)/2,插值器為加速度a,估值器為位移s,插值器只是估值器計(jì)算中用到的一個(gè)屬性值。
3、MeasureSpec封裝了父布局ViewGroup傳遞給子View的布局要求。是要求而并非是強(qiáng)制的,如在子View的onMeasure()中還是可以設(shè)置setMeasuredDimension(Width, Height)的;
MeasureSpec通常翻譯為”測量規(guī)格”,它是一個(gè)32位的int數(shù)據(jù),其中高2位代表SpecMode即某種測量模式,低30位為SpecSize代表在該模式下的規(guī)格大小。
對(duì)于頂級(jí)View(即DecorView)和普通View來說,MeasureSpec的轉(zhuǎn)換過程略有不同。對(duì)于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams來共同確定;對(duì)于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定,MeasureSpec一旦確定后,onMeasure中就可以確定View的測量寬/高
鏈接:http://www.lxweimin.com/p/cf5092fa269,自定義View系列教程02--onMeasure源碼詳盡分析
當(dāng)View采用固定寬/高的時(shí)候,不管父容器的MeasureSpec是什么,View的MeasureSpec都是精確模式并且其大小遵循Layoutparams中的大小。當(dāng)View的寬/高是match_parent時(shí),如果父容器的模式是精準(zhǔn)模式,那么View也是精準(zhǔn)模式并且其大小是父容器的剩余空間;如果父容器是最大模式,那么View也是最大模式并且其大小不會(huì)超過父容器的剩余空間。當(dāng)View的寬/高是wrap_content時(shí),不管父容器的模式是精準(zhǔn)還是最大化,View的模式總是最大化并且大小不能超過父容器的剩余空間。UNSPECIFIED這個(gè)模式主要用于系統(tǒng)內(nèi)部多次Measure的情形,一般來說,我們不需要關(guān)注此模式。
ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),這之中會(huì)遍歷子View然后循環(huán)調(diào)用measureChild(),這之中會(huì)通過getChildMeasureSpec()方法中父ViewGroup的MeasureSpec+子View的LayoutParams一起獲取本子View最終生成的MeasureSpec,然后調(diào)用子View的child.measure(childWidthMeasureSpec,
childHeightMeasureSpec)到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize())這樣一個(gè)流程,getDefaultSize()默認(rèn)返回measureSpec的測量數(shù)值,所以繼承View進(jìn)行自定義的wrap_content需要重寫。
結(jié)合MeausreSpec1.png圖發(fā)現(xiàn)一個(gè)問題:在該圖的最后一行,如果子View在XML布局文件中對(duì)于大小的設(shè)置采用wrap_content,那么不管父ViewGroup的specMode是MeasureSpec.AT_MOST還是MeasureSpec.EXACTLY對(duì)于子View而言系統(tǒng)給它設(shè)置的specMode都是MeasureSpec.AT_MOST,并且其大小都是parentLeftSize即父ViewGroup目前剩余的可用空間。這時(shí)wrap_content就失去了原本的意義,變成了match_parent一樣了,所以自定義View在重寫onMeasure()的過程中應(yīng)該手動(dòng)處理View的寬或高為wrap_content的情況。
這個(gè)在《Android開發(fā)藝術(shù)探索》4.3.1節(jié)中完美解釋
第一種情況:如果在xml布局中View的寬和高均用wrap_content.那么需要設(shè)置View的寬和高為mWidth和mHeight.
第二種情況:如果在xml布局中View的寬或高其中一個(gè)為wrap_content,那么就將該值設(shè)置為默認(rèn)的寬或高,另外的一個(gè)值采用系統(tǒng)測量的specSize即可,代碼中設(shè)置如下,其中給mWidth、mHeight在自定義view中設(shè)定默認(rèn)值:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
?? super.onMeasure(widthMeasureSpec , heightMeasureSpec);
?? int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
?? int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);
?? int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
?? int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);
?if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
???????? setMeasuredDimension(mWidth, mHeight);
?}else if(widthSpecMode==MeasureSpec.AT_MOST){
???????? setMeasuredDimension(mWidth, heightSpceSize);
?}else if(heightSpecMode==MeasureSpec.AT_MOST){
???????? setMeasuredDimension(widthSpceSize, mHeight); }
}
上圖紅框?qū)嶋H不可能出現(xiàn)原因:
(1) 不可能出現(xiàn)一個(gè)這樣的View,其根View的大小為wrap_content且它的一個(gè)子View大小為match_parent。
(2) 從根View到這個(gè)子View的父ViewGroup都是wrap_content,而子View的大小為match_parent。這個(gè)極端情況也是不會(huì)的,可見情況1的分析.
(3)從根View到這個(gè)子View的父ViewGroup都是wrap_content,而子View大小也為wrap_content。這是個(gè)正常情況,所以我們用改良后的onMeasure()來專門處理的子View大小為wrap_content的情況。
4、getWidth方法是在layout方法完成后才有的值,所以說在自定義控件的時(shí)候在onLayout方法中一般采用getMeasuredWidth來獲得控件的寬度,因?yàn)間etMeasuredWidth在measure后就有了值,而getWidth在layout才有了值。除了onLayout方法中采用getMeasuredWidth方法外,在其他地方一般采用getWidth方法來獲取控件的寬度更準(zhǔn)確。
5、在自定義View中加載圖片資源Bitmap時(shí):我們可以通過設(shè)置繪制區(qū)域來控制顯示的圖片位置以及大小。
如下:由變量w,h來控制繪制結(jié)束的矩形dst右下角終點(diǎn)坐標(biāo),矩形dst的區(qū)域顯示圖片資源
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
?? mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash6);
?? // 指定圖片繪制區(qū)域,這里設(shè)置與圖片參數(shù)大小一致,繪制完整圖片
? ? Rect src =new Rect(0,0,mBgBitmap.getWidth(),mBgBitmap.getHeight());
???? //繪制區(qū)域
? ? Rect dst =new Rect(0,0,w,h);
??? // 繪制圖片
? ? canvas.drawBitmap(mBgBitmap, src, dst,null);
}
6、scrollBy()內(nèi)部是scrollTo()實(shí)現(xiàn)的累加位移,scrollTo()是相對(duì)于初始位置做的移動(dòng),注意是初始位置。
scrollTo()和scrollBy()時(shí)傳入的x,y為正值是反常的不是我們料想的那樣,以左上角為圓點(diǎn)“右正左負(fù),上負(fù)下正”,這是因?yàn)樵创a中如下
使用scrooler彈性滑動(dòng)來控制view的移動(dòng),實(shí)則是view自己讓自己移動(dòng)的。
調(diào)用invalidate()刷新界面,從而再次回到computeScroll(),回到在computeScroll()繼續(xù)處理滑動(dòng)事件。假如View的滑動(dòng)已經(jīng)停止了那就沒有必要再次執(zhí)行invalidate()了。說到底,不是Scroller讓View發(fā)生了滾動(dòng)而是View自己在滾動(dòng)。只不過在這個(gè)過程中Scroller在不停地追蹤View的滾動(dòng),而且提供了許多的輔助而已,比如:可以提供偏移量,耗時(shí),當(dāng)前位置等等信息。
代碼如下
7、官方不推薦通過無參的構(gòu)造方法生成一個(gè)canvas。如果要這么做那就需要調(diào)用setBitmap()為其設(shè)置一個(gè)Bitmap。為什么Canvas非要一個(gè)Bitmap對(duì)象呢?原因很簡單:缺少一個(gè)載體,Canvas需要一個(gè)Bitmap對(duì)象來保存像素,如果畫的東西沒有地方可以保存,又還有什么意義呢?
8、View繪制分三個(gè)步驟,順序是:onMeasure,onLayout,onDraw。經(jīng)代碼親測,log輸出顯示:調(diào)用invalidate方法只會(huì)執(zhí)行onDraw方法;調(diào)用requestLayout方法只會(huì)執(zhí)行onMeasure方法和onLayout方法,并不會(huì)執(zhí)行onDraw方法。所以當(dāng)我們進(jìn)行View更新時(shí),若僅View的顯示內(nèi)容發(fā)生改變且新顯示內(nèi)容不影響View的大小、位置,則只需調(diào)用invalidate方法;若View寬高、位置發(fā)生改變且顯示內(nèi)容不變,只需調(diào)用requestLayout方法;若兩者均發(fā)生改變,則需調(diào)用兩者,按照View的繪制流程,推薦先調(diào)用requestLayout方法再調(diào)用invalidate方法。
invalidate和postInvalidate:invalidate方法只能用于UI線程中,在非UI線程中,可直接使用postInvalidate方法,這樣就省去使用handler的煩惱。
activity和view都有onSaveInstanceState、onRestoreInstanceState,在activity中異常終止情況下這兩個(gè)方法都會(huì)執(zhí)行onSaveInstanceState->onDestory->onCreate->onRestoreInstanceState,而在按Home鍵或者啟動(dòng)新Activity仍然會(huì)單獨(dú)觸發(fā)onSaveInstanceState。
9、如若非使用Relativelayout,一般自定義組件的時(shí)候不會(huì)去繼承RelativeLayout,因?yàn)樗鼤?huì)進(jìn)行兩次繪制,影響繪制效率;故在能實(shí)現(xiàn)相同功能需求時(shí)更多的使用LinearLayout和FrameLayout。
總結(jié):LinearLayout和RelativeLayout的性能差別主要體現(xiàn)在onMeasure方法上,RelativeLayout始終要從豎直和水平兩個(gè)方向?qū)ψ覸iew進(jìn)行測量。而Linearlayout,當(dāng)我們沒有在子View中使用layout_weight屬性時(shí),LinearLayout只需對(duì)子View進(jìn)行一次測量,否則也需要對(duì)子View進(jìn)行兩次測量以確定最終大小。所以如果可以,我們盡量少用layout_weight屬性。在使用這兩個(gè)布局之前,我們可以先進(jìn)行衡量,如果需要實(shí)現(xiàn)的布局嵌套層次不深或者嵌套層次已經(jīng)固定了,可以考慮用LinearLayout,相對(duì)的,如果某個(gè)布局嵌套層次很深,此時(shí)應(yīng)該考慮使用RelativeLayout來減少嵌套層析,從而優(yōu)化布局的性能。
LinearLayout有兩個(gè)方向走向,一個(gè)是豎直方向,一個(gè)是水平方向。在onMeasure方法中會(huì)根據(jù)橫向還是縱向來調(diào)用measureVertical()或measureHorizontal()方法。我們都知道在LinearLayout中,我們可以使用layout_weight屬性,當(dāng)子View使用了layout_weight屬性時(shí)就會(huì)調(diào)用measureChildBeforeLayout()方法,這個(gè)方法的大概步驟是先不限制設(shè)置了weight屬性的子View的大小,只要容器剩余的空間還足夠就不對(duì)該子View作處理,但凡事都有個(gè)度,所以不可能每個(gè)設(shè)置了weight屬性的子View都無限大吧,所以當(dāng)所有子View測量完畢后,會(huì)對(duì)設(shè)置了weight屬性的子View再調(diào)用一次measure方法。
LinearLayout和RelativeLayout繪制過程的對(duì)比
10、getMeasuredWidth和getWidth:在View的默認(rèn)實(shí)現(xiàn)中,View的測量寬/高和最終寬/高一般情況下是相等的,只不過測量寬/高形成于View的measure過程,而最終寬/高形成于View的layout過程,即兩者的賦值時(shí)機(jī)不同,測量寬/高的賦值時(shí)機(jī)稍微早一些。
11、安卓動(dòng)畫:
動(dòng)畫分為View動(dòng)畫(視圖動(dòng)畫)和屬性動(dòng)畫,它們最大的區(qū)別是View動(dòng)畫改變的是視圖影像效果而不改變屬性值,屬性動(dòng)畫通過改變屬性值來構(gòu)成動(dòng)畫效果。在實(shí)際開發(fā)中,建議采用XML來定義View動(dòng)畫,這是因?yàn)閄ML格式的動(dòng)畫可讀性更好;建議采用代碼來實(shí)現(xiàn)屬性動(dòng)畫,這是因?yàn)橥ㄟ^代碼來實(shí)現(xiàn)比較簡單。
View動(dòng)畫:View動(dòng)畫改變的是視圖影像效果而不改變屬性值。例如一個(gè)平移動(dòng)畫,TextView坐標(biāo)(0,0)從左往右平移100px,平移后坐標(biāo)(100,0),而平移后我們看到TextView在(100,0)點(diǎn)上,實(shí)際上它還在(0,0)上,通過響應(yīng)點(diǎn)擊事件我們可以知道只有點(diǎn)擊原來的位置(0,0)才能響應(yīng)事件,而當(dāng)前看到的TextView點(diǎn)擊無響應(yīng)效果。View動(dòng)畫有TranslateAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation等。
幀動(dòng)畫也是屬于View動(dòng)畫,幀動(dòng)畫的使用比較簡單,但是比較容易引起OOM,所以在使用幀動(dòng)畫時(shí)應(yīng)盡量避免使用過多尺寸較大的圖片。
LayoutAnimation也是一個(gè)View動(dòng)畫,給ViewGroup的子元素加上出場效果。
使用View動(dòng)畫有時(shí)候會(huì)出現(xiàn)動(dòng)畫完成后View無法隱藏的現(xiàn)象,即setVisibility(View.GONE)失效了,這個(gè)時(shí)候只要調(diào)用view.clearAnimation()清除View動(dòng)畫即可解決此問題。
屬性動(dòng)畫:與View動(dòng)畫不同,它改變的是對(duì)象或者說控件的屬性值,平移到哪,當(dāng)前它的位置實(shí)際也就是在哪個(gè)點(diǎn)。屬性動(dòng)畫可以對(duì)任意對(duì)象的屬性進(jìn)行動(dòng)畫而不僅僅是View,動(dòng)畫默認(rèn)時(shí)間間隔300ms,默認(rèn)幀率10ms/幀,動(dòng)畫的每一幀都會(huì)回調(diào)onAnimationUpdate方法。其可以達(dá)到的效果是:在一個(gè)時(shí)間間隔內(nèi)完成對(duì)象從一個(gè)屬性值到另一個(gè)屬性值的改變。只要對(duì)象有這個(gè)屬性,它都能實(shí)現(xiàn)動(dòng)畫效果。屬性動(dòng)畫有ValueAnimator、ObjectAnimator和AnimatorSet。
屬性動(dòng)畫可以對(duì)任何對(duì)象做動(dòng)畫,甚至還可以沒有對(duì)象,屬性動(dòng)畫(ObjectAnimator)改變對(duì)象屬性,要求對(duì)象內(nèi)要提供該屬性的set和get方法(非ObjectAnimator可以沒有)。
例如:TextView? tvObjectAn; ObjectAnimator animator = ObjectAnimator.ofFloat(tvObjectAn,"rotation",360);??? 讓一個(gè)TextView旋轉(zhuǎn)360度,其中屬性"rotation"在TextView?父類View中就有提供setRotation() 與getRotation()方法。其中帶get***()、set***()的方法實(shí)體類在內(nèi)部估值器中被處理,實(shí)現(xiàn)對(duì)動(dòng)畫值的改變。
當(dāng)一個(gè)類沒有提供set、get方法時(shí),官方文檔上告訴我們有3種解決方法:
1.給你的對(duì)象加上get和set方法,如果你有權(quán)限的話,該解決方法幾乎不能實(shí)現(xiàn),因?yàn)槟阋话愣际菦]有權(quán)限;
2.用一個(gè)類來包裝原始對(duì)象,間接為其提供get和set方法,在set方法中自定義實(shí)現(xiàn)屬性的改變;
3.采用ValueAnimator,監(jiān)聽動(dòng)畫過程得到動(dòng)畫過程變動(dòng)值,通過這個(gè)變動(dòng)值自己實(shí)現(xiàn)屬性的改變。
12、在Drawable中StateListDrawable的selector標(biāo)簽,表示Drawable集合。selector中每個(gè)item對(duì)應(yīng)著一個(gè)具體的Drawable,系統(tǒng)按照從上到下的順序查找,直至查找到第一條匹配的item。一般來說,默認(rèn)的item都應(yīng)該放在selector的最后一條并且不附帶任何的狀態(tài)。當(dāng)上面沒有狀態(tài)匹配時(shí)就會(huì)匹配到默認(rèn)的item了。默認(rèn)的item不附帶狀態(tài),所以它可以匹配View的任何狀態(tài)。
<item android="@drawable="@drawable/icon" android:state_checked="false" android:state_pressed="false"/>
寫了多個(gè)狀態(tài)如state_checked和state_pressed便是且與的關(guān)系,需要同時(shí)滿足