Android 進階自定義 ViewGroup 自定義佈局

前言

在咱們的實際應用中, 常常須要用到自定義控件,好比自定義圓形頭像,自定義計步器等等。但有時咱們不只須要自定義控件,舉個例子,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());
        }
    }

重寫 onLayout

整完上面這些東西,咱們的佈局大小七十九已經出來了,然咱們在活動的佈局文件裏面加上它,並添加上幾個子 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;
        }
    }

再運行一下看看:

成功了有木有!學習

因爲本文是本身學習過程的總結,若是文中有錯誤,但願你們能在評論區指出動畫

最後,祝你們編程愉快!設計

相關文章
相關標籤/搜索