1. android 自定義View:為什麼你設置的wrap_content不起作用
在使用自定義View時,View寬 / 高的 wrap_content 屬性不起自身應有的作用,而且是起到與 match_parent 相同作用。
其實這里有兩個問題:
請分析 & 解決問題之前,請先看自定義View原理中 (2)自定義View Measure過程 - 最易懂的自定義View原理系列
問題出現在View的寬 / 高設置,那我們直接來看自定義View繪制中第一步對View寬 / 高設置的過程:measure過程中的 onMeasure() 方法
繼續往下看 getDefaultSize()
從上面發現:
那麼有人會問:wrap_content和match_parent具有相同的效果,為什麼是填充父容器的效果呢?
我們知道,子View的MeasureSpec值是根據子View的布局參數(LayoutParams)和父容器的MeasureSpec值計算得來,具體計算邏輯封裝在getChildMeasureSpec()里。
接下來,我們看生成子View MeasureSpec的方法: getChildMeasureSpec() 的源碼分析:
getChildMeasureSpec()
從上面可以看出,當子View的布局參數使用 match_parent 或 wrap_content 時:
所以: wrap_content 起到了和 match_parent 相同的作用:等於父容器當前剩餘空間大小
當自定義View的布局參數設置成wrap_content時時,指定一個默認大小(寬 / 高)。
這樣,當你的自定義View的寬 / 高設置成wrap_content屬性時就會生效了。
網上流傳著這么一個解決方案:
答: 是,當父View為 AT_MOST 、View為 match_parent 時,該View的 match_parent 的效果就等於 wrap_content 。上述方法存在邏輯錯誤,但由於這種情況非常特殊的,所以導致最終的結果沒有錯誤。具體分析請看下面例子:
從上面的效果可以看出,View大小 = 默認值
我再將子View的屬性改為 wrap_content :
從上面的效果可以看出,View大小還是等於默認值。
相信看到這里你已經看懂了:
為了更好的表示判斷邏輯,我建議你們用本文提供的解決方案,即根據布局參數判斷默認值的設置
不定期分享關於 安卓開發 的干貨,追求 短、平、快 ,但 卻不缺深度 。
2. Android 自定義View 寬高總是充滿父容器,怎麼讓它自適應保持對應寬高
自定義View,想要自定義給定寬和高,你要寫自定義屬性,然後在xml文件中指定寬高才會有效,同時當給定的寬和高的值是wrap_content 或 fill_parent 這類的,這時需要在自定義View中重櫻茄寫onMeasure方衡頌慧法咐答,進行控制項的寬高測量。
3. Android自定義View
View的構造函數:共有4個
系統自帶的View可以在xml中配置屬性,對於寫的好的自定義View同樣可以在xml中配置屬性,為了使自定義的View的屬性可以在xml中配置,需要以下4個步驟:
一定要記住:無論是measure過程、layout過程還是draw過程,永遠都是從View樹的根節點開始測量或計算(即從樹的頂端開始),一層一層、一個分支一個分支地進行(即樹形遞歸),最終計算整個View樹中各個View,最終確定整個View樹的相關屬性。
Android的坐標系定義為:
View的位置由4個頂點決定的 4個頂點的位置描述分別由4個值決定:
View的位置是通過view.getxxx()函數進行獲取:(以Top為例)
與MotionEvent中 get()和getRaw()的區別
MarginLayoutParams是和外間距有關的。事實也確實如此,和LayoutParams相比,MarginLayoutParams只是增加了對上下左右外間距的支持。實際上大部分LayoutParams的實現類都是繼承自MarginLayoutParams,因為基本所有的父容器都是支持子View設置外間距的。
1. 創建自定義屬性
2. 繼承MarginLayout
3. 重寫ViewGroup中幾個與LayoutParams相關的方法
在為View設置LayoutParams的時候需要根據它的父容器選擇對應的LayoutParams,否則結果可能與預期不一致,這里簡單羅列一些常見的LayoutParams子類:
測量規格,封裝了父容器對 view 的布局上的限制,內部提供了寬高的信息( SpecMode 、 SpecSize ),SpecSize是指在某種SpecMode下的參考尺寸,其中SpecMode 有如下三種:
針對上表,這里再做一下具體的說明
一般getIntrinsicWidth/Height能獲得內部寬/高 圖片Drawable其內部寬高就是圖
片的寬高 顏色Drawable沒有內部寬高的概念 內部寬高不等同於它的大小,一般
Drawable沒有大小概念(作為View背景時,會被拉伸至View的大小)
4. android 自定義view 的高度,寬度怎麼設置
Android中View是顯示控制項,需要用一個layout布局來裝載,layout尺寸大寫的設置如下:
1. 盡量使用wrap_content、match_parent、weight 來規定layout的大小。
2. 要確保布局的靈活性並適應各種尺寸的屏幕,應使用 「wrap_content」 和 「match_parent」 控制某些視圖組件的寬度和高度。
3. 使用 「wrap_content」,系統就會將視圖的寬度或高度設置成所需的最小尺寸以適應視圖中的內容,而 「match_parent」(在低於 API 級別 8 的級別中稱為「fill_parent」)則會展開組件以匹配其父視圖的尺寸。
4. 如果使用 「wrap_content」 和 「match_parent」 尺寸值而不是硬編碼的尺寸,視圖就會相應地僅使用自身所需的空間或展開以填滿可用空間。此方法可讓布局正確適應各種屏幕尺寸和屏幕方向。
5. 如果某些地方必須要規定控制項的大小,那麼要使用dp 或者 dip單位。
5. Android 自定義View之Layout過程
系列文章:
在上篇文章: Android 自定義View之Measure過程 ,我們分析了Measure過程,本次將會掀開承上啟下的Layout過程神秘面紗,
通過本篇文章,你將了解到:
在上篇文章的比喻里,我們說過:
該ViewGroup 重寫了onMeasure(xx)和onLayout(xx)方法:
同時,當layout 執行結束,清除PFLAG_FORCE_LAYOUT標記,該標記會影響Measure過程是否需要執行onMeasure。
該View 重寫了onMeasure(xx)和onLayout(xx)方法:
MyViewGroup里添加了MyView、Button兩個控制項,最終運行的效果如下:
可以看出,MyViewGroup 里子布局的是橫向擺放的。我們重點關注Layout過程。實際上,MyViewGroup里我們只重寫了onLayout(xx)方法,MyView也是重寫了onLayout(xx)方法。
接下來,分析View Layout過程。
與Measure過程類似,連接ViewGroup onLayout(xx)和View onLayout(xx)之間的橋梁是View layout(xx)。
可以看出,最終都調用了setFrame(xx)方法。
對於Measure過程在onMeasure(xx)里記錄了尺寸的值,而對於Layout過程則在layout(xx)里記錄了坐標值,具體來說是在setFrame(xx)里,該方法兩個重點地方:
View.onLayout(xx)是空實現
從layout(xx)和onLayout(xx)聲明可知,這兩個方法都是可以被重寫的,接下來看看ViewGroup是否重寫了它們。
ViewGroup.layout(xx)雖然重寫了layout(xx),但是僅僅做了簡單判斷,最後還是調用了View.layout(xx)。
這重寫後將onLayout變為抽象方法,也就是說繼承自ViewGroup的類必須重寫onLayout(xx)方法。
我們以FrameLayout為例,分析其onLayout(xx)做了什麼。
FrameLayout.onLayout(xx)為子布局Layout的時候,起始坐標都是以FrameLayout為基準,並沒有記錄上一個子布局佔了哪塊位置,因此子布局的擺放位置可能會重疊,這也是FrameLayout布局特性的由來。而我們之前的Demo在水平方向上記錄了上一個子布局的擺放位置,下一個擺放時只能在它之後,因此就形成了水平擺放的功能。
由此類推,我們常說的某個子布局在父布局裡的哪個位置,決定這個位置的即是ViewGroup.onLayout(xx)。
上邊我們分析了View.layout(xx)、View.onLayout(xx)、ViewGroup.layout(xx)、ViewGroup.onLayout(xx),這四者什麼關系呢?
View.layout(xx)
View.onLayout(xx)
ViewGroup.layout(xx)
ViewGroup.onLayout(xx)
View/ViewGroup 子類需要重寫哪些方法:
用圖表示:
通過上述的描述,我們發現Measure過程和Layout過程里定義的方法比較類似:
它倆的套路比較類似:measure(xx)、layout(xx)一般不需要我們重寫,measure(xx)里調用onMeasure(xx),layout(xx)為調用者設置坐標值。
若是ViewGroup:onMeasure(xx)里遍歷子布局,並測量每個子布局,最後將結果匯總,設置自己測量的尺寸;onLayout(xx)里遍歷子布局,並設置每個子布局的坐標。
若是View:onMeasure(xx)則測量自身,並存儲測量尺寸;onLayout(xx)不需要做什麼。
Measure過程雖然比Layout過程復雜,但仔細分析後就會發現其本質就是為了設置兩個成員變數:
而Layout過程雖然比較簡單,其本質是為了設置坐標值
將Measure設置的變數和Layout設置的變數聯系起來:
此外,Measure過程通過設置PFLAG_LAYOUT_REQUIRED 標記來告訴需要進行onLayout,而Layout過程通過清除 PFLAG_FORCE_LAYOUT來告訴Measure過程不需要執行onMeasure了。
這就是Layout的承上作用
我們知道View的繪制需要依靠Canvas繪制,而Canvas是有作用區域限制的。例如我們使用:
Cavas繪制的起點是哪呢?
對於硬體繪制加速來說:正是通過Layout過程中設置的RenderNode坐標。
而對於軟體繪制來說:
關於硬體繪制加速/軟體繪制 後續文章會分析。
這就是Layout的啟下作用
以上即是Measure、Layout、Draw三者的內在聯系。
當然Layout的"承上"還需要考慮margin、gravity等參數的影響。具體用法參見最開始的Demo。
getMeasuredWidth()/getMeasuredHeight 與 getWidth/getHeight區別
我們以獲取width為例,分別來看看其方法:
getMeasuredWidth():獲取測量的寬,屬於"臨時值"
getWidth():獲取View真實的寬
在Layout過程之前,getWidth() 默認為0
何時可以獲取真實的寬、高
下篇將分析Draw()過程,我們將分析"一切都是draw出來的"道理
本篇基於 Android 10.0
6. android 自定義view 怎麼設置其寬度和高度
自定義View 可以設和數蠢畢空置iji的布局,
1.在布局中設置寬高
2.在ondraw方法中繪制的view設置寬高
3.LayoutInflater設置寬喚陪高
7. android自定義view不隨系統顯示設置變大
設置屬性動畫改變scaleX和scaleY值。
變大變小可以用view、setScaleX和setScaleY,賀纖使用前分別設置軸心(setPivotX)。要動畫而不是直接突然變大的話用屬性動畫,屬性動畫改變scaleX和scaleY值。
補間動畫在這里不好用,不需要動畫的話,禪前仿這里可以用把其他View都gone掉更悔橋好,用線性布局和weight去1/4平分空間,gone掉別的view的時候這個就自然變大占滿了或者應該播放時上面蒙層一個View專門處理播放。
8. android 自定義view 怎麼設置其寬度和高度
自定義view的父類肯定是View或者View的子類。所以也是支持layout_width,layout_height屬性的,如果想另外控制的話,可以重寫View的onDraw方法。