在咱們的實際應用中, 常常須要用到自定義控件,好比自定義圓形頭像,自定義計步器等等。但有時咱們不只須要自定義控件,舉個例子,FloatingActionButton 你們都很經常使用,因此你們也很常常會有一種需求,點擊某個 FloatingActionButton 彈出更多 FloatingActionButton ,這個需求的通常思路是寫 n 個 button 而後再一個個的去設置動畫效果。但這實在是太麻煩了,因此網上有個 FloatingActionButtonMenu 這個開源庫,這就是利用到了自定義佈局 「ViewGroup」,如今就讓我給他家介紹下,如何自定義佈局 「layout」。html
相比於自定義 View ,自定義 ViewGroup 的難點在於,子控件位置的肯定和佈局大小的肯定。不像 單個 View 子要花粉好模式,測量好寬度就搞定了,ViewGroup 的長寬根據子 View 的數量和單個的大小變化而變化。這就是最大的坎,因此該如何肯定 ViewGroup 的大小呢?java
這裏 我爲你們設計一個 相似 LinearLayout 線性佈局的 ViewGroup 做爲範例。android
首先,若是是一個 LinearLayout 那麼當設置 wrap_content 時,他就會以子空間中最寬的那個爲它的寬度。同時在高度方面會是全部子控件高度的總和。因此咱們先寫兩個方法,分別用於測量 ViewGroup 的寬度和高度。編程
private int getMaxWidth(){
int count = getChildCount();
int maxWidth = 0;
for (int i = 0 ; i < count ; i ++){
int currentWidth = getChildAt(i).getMeasuredWidth();
if (maxWidth < currentWidth){
maxWidth = currentWidth;
}
}
return maxWidth;
}
private int getTotalHeight(){
int count = getChildCount();
int totalHeight = 0;
for (int i = 0 ; i < count ; i++){
totalHeight += getChildAt(i).getMeasuredHeight();
}
return totalHeight;
}
複製代碼
對於 ViewGroup 而言咱們能夠粗略的分爲兩種模式:固定長寬模式(match_parent),自適應模式(wrap_content),根據這兩種模式,就能夠對 ViewGroup 的繪製進行劃分。這裏關於 measureChildren 這個方法,他是用於將全部的子 View 進行測量,這會觸發每一個子 View 的 onMeasure 函數,可是你們要注意要與 measureChild 區分,measureChild 是對單個 view 進行測量ide
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode= MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
int groupWidth = getMaxWidth();
int groupHeight= getTotalHeight();
setMeasuredDimension(groupWidth, groupHeight);
}else if (widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(getMaxWidth(), height);
}else if (heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(width, getTotalHeight());
}
}
複製代碼
整完上面這些東西,咱們的佈局大小七十九已經出來了,然咱們在活動的佈局文件裏面加上它,並添加上幾個子 View 而後運行一下,先看看效果:函數
<com.entry.android_view_user_defined_first.views.MyLinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorAccent">
<Button android:layout_width="100dp" android:layout_height="50dp" android:text="qwe"/>
<Button android:layout_width="250dp" android:layout_height="150dp" android:text="qwe"/>
<Button android:layout_width="200dp" android:layout_height="75dp" android:text="qwe"/>
</com.entry.android_view_user_defined_first.views.MyLinearLayout>
複製代碼
咱們看見佈局出來了,大小好像也沒啥問題,可是子 View 呢??! 這麼沒看見子 View 在看看代碼,系統以前然咱們重寫的 onLayout() 仍是空着的呀!!也就是說,子 View 的大小和位置根本就尚未進行過設定!讓咱們來重寫下 onLayout() 方法。佈局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
int currentHeight = 0;
for (int i = 0 ; i < count ; i++){
View view = getChildAt(i);
int height = view.getMeasuredHeight();
int width = view.getMeasuredWidth();
view.layout(l, currentHeight, l + width, currentHeight + height);
currentHeight += height;
}
}
複製代碼
成功了有木有!學習
因爲本文是本身學習過程的總結,若是文中有錯誤,但願你們能在評論區指出動畫
最後,祝你們編程愉快!spa