【轉載】自定義控件知識儲備-LayoutParams的那些事

這是我轉載的,原作地址:http://blog.csdn.net/yisizhu/article/details/51582622


LayoutParams是什麼?

LayoutParams,顧名思義,就是Layout Parameters :佈局參數。 
很久很久以前,我就知道LayoutParams了,並且幾乎天天見面。那時候在佈局文件XML裏,寫的最多的肯定是Android:layout_width = "match_parent"之類的了。比如:

常見佈局文件的栗子

<TextView
    style="@style/text_flag_01"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_marginLeft="10dp"
    android:layout_gravity="center"
    android:gravity="center"
    android:text="英明神武蘑菇君"
    android:textColor="@color/white"
    android:background="@color/colorAccent"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我們都知道layout_widthlayout_height這兩個屬性是爲View指定寬度的。不過,當時年輕的我心裏一直有個疑問:爲什麼要加上”layout_”前綴修飾呢?其它的描述屬性,如textColorbackground,都很正常啊!講道理,應該用widthheight描述寬高才對啊?

後來呀,我遇到了LayoutParams,它說layout_width是它的屬性而非View的,並且不只是針對這一個,而是所有以”layout_”開頭的屬性都與它有關!所以,它的東西當然要打上自己的標識”layout_”。(呵呵,囂張個啥,到頭來你自己還不是屬於View的一部分( ̄┰ ̄*))

既然layout_width這樣的屬性是LayoutParams定義的,那爲何會出現在描述View的xml屬性裏呢?View和LayoutParams之間有什麼恩怨糾纏呢?

不吹不黑,咱們來看看官方文檔是怎麼說的:

  1. LayoutParams are used by views to tell their parents how they want to be laid out. 
    – LayoutParams是View用來告訴它的父控件如何放置自己的。

  2. The base LayoutParams class just describes how big the view wants to be for both width and height. 
    – 基類LayoutParams(也就是ViewGroup.LayoutParams)僅僅描述了這個View想要的寬度和高度。

  3. There are subclasses of LayoutParams for different subclasses of ViewGroup. 
    – 不同ViewGroup的繼承類對應着不同的ViewGroup.LayoutParams的子類。

看着我妙到巔峯的翻譯,想必大家都看懂了<( ̄▽ ̄)/。看不懂?那我再來畫蛇添足稍微解釋一下:

  1. 上面我們提到過,描述View直接用它們自己的屬性就好了,如textColorbackground等等,爲什麼還需要引入LayoutParams呢?在我看來,textColorbackground這樣的屬性都是隻與TextView自身有關的,無論這個TextView處於什麼環境,這些屬性都是不變的。而layout_widthlayout_marginLeft這樣的屬性是與它的父控件息息相關的,是父控件通過LayoutParams提供這些”layout_”屬性給孩子們用的;是父控件根據孩子們的要求(LayoutParams)來決定怎麼測量,怎麼安放孩子們的;是父控件……(寫不下去了,我都快被父控件感動了,不得不再感慨一句,當父母的都不容易啊(′⌒`)) )。所以,View的LayoutParams離開了父控件,就沒有意義了。

  2. 基類LayoutParams是ViewGroup類裏的一個靜態內部類(看吧,這就證明了LayoutParams是與父控件直接相關的),它的功能很簡單,只提供了widthheight兩個屬性,對應於xml裏的layout_widthlayout_height。所以,對任意系統提供的容器控件或者是自定義的ViewGroup,其chid view總是能寫layout_widthlayout_height屬性的。

  3. 自從有了ViewGroup.LayoutParams後,我們就可以在自定義ViewGroup時,根據自己的邏輯實現自己的LayoutParams,爲孩子們提供更多的佈局屬性。不用說,系統裏提供給我們的容器控件辣麼多,肯定也有很多LayoutParams的子類啦。let us see see:

ViewGroup.LayoutParams的截圖

果然,我們看到了很多ViewGroup.LayoutParams的子類,裏面大部分我們應該都比較熟悉。如果你覺得和它們不熟,那就是你一廂情願啦,你早就“偷偷摸摸”的用過它們好多次了→_→

ViewGroup.MarginLayoutParams

我們首先來看看ViewGroup.MarginLayoutParams,看名字我們也能猜到,它是用來提供margin屬性滴。margin屬性也是我們在佈局時經常用到的。看看這個類裏面的屬性:

public static class MarginLayoutParams extends ViewGroup.LayoutParams {

        public int leftMargin;

        public int topMargin;

        public int rightMargin;

        public int bottomMargin;

        private int startMargin = DEFAULT_MARGIN_RELATIVE;

        private int endMargin = DEFAULT_MARGIN_RELATIVE;

        ...
    }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

前面4個屬性是我們以前在佈局文件裏常用的,而後面的startMarginendMargin是爲了支持RTL設計出來代替leftMarginrightMargin的。

一般情況下,View開始部分就是左邊,但是有的語言目前爲止還是按照從右往左的順序來書寫的,例如阿拉伯語。在Android 4.2系統之後,Google在Android中引入了RTL佈局,更好的支持了從右往左文字佈局的顯示。爲了更好的兼容RTL佈局,google推薦使用MarginStart和MarginEnd來替代MarginLeft和MarginRight,這樣應用可以在正常的屏幕和從右往左顯示文字的屏幕上都保持一致的用戶體驗。

我們除了在佈局文件裏用layout_marginLeftlayout_marginTop這樣的屬性來指定單個方向的間距以外,還會用layout_margin來表示四個方向的統一間距。我們來通過源碼看看這一過程:

 public MarginLayoutParams(Context c, AttributeSet attrs) {
            super();

            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
            setBaseAttributes(a,
                    R.styleable.ViewGroup_MarginLayout_layout_width,
                    R.styleable.ViewGroup_MarginLayout_layout_height);

            int margin = a.getDimensionPixelSize(
                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
            if (margin >= 0) {
                leftMargin = margin;
                topMargin = margin;
                rightMargin= margin;
                bottomMargin = margin;
            } else {
                leftMargin = a.getDimensionPixelSize(
                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
                        UNDEFINED_MARGIN);
                if (leftMargin == UNDEFINED_MARGIN) {
                    mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
                    leftMargin = DEFAULT_MARGIN_RESOLVED;
                }
                ... 
            }
            ...
    }        
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在這個MarginLayoutParams的構造函數裏,將獲取到的xml佈局文件裏的屬性轉化成了leftMagrinrightMagrin等值。先獲取xml裏的layout_margin值,如果未設置,則再去獲取layout_marginLeftlayout_marginRight等值。所以從這裏可以得出一個小結論:

在xml佈局裏,layout_margin屬性的值會覆蓋layout_marginLeftlayout_marginRight等屬性的值。

以前我還很傻很天真的猜測,屬性寫在後面,就會覆蓋前面的屬性。雖然經過實踐,也能發現上述的結論,但是自己瞭解了背後的原理,再去看看源碼實現,自然就有更深刻的印象了。<( ̄ˇ ̄)/

揭開隱藏的LayoutParams

在上文中提到,我們初學Android的時候經常在“偷偷摸摸”的使用着LayoutParams,而自己卻還 
一臉懵逼。 
因爲我們常用它的方式是在XML佈局文件裏,使用容器控件的LayoutParams裏的各種屬性來給孩子們佈局。這種方式直觀方便,直接就能在預覽界面看到效果,但是同時佈局也被我們寫死了,無法動態改變。想要動態變化,那還是得不怕麻煩,使用代碼來寫。(實際上,我們寫的XML佈局最終也是通過代碼來解析滴)

好的,那還是讓我們通過源碼來揭開隱藏在ViewGroup裏的LayoutParams吧!<( ̄︶ ̄)↗[Go!]……等會,我們該從哪裏開始看源碼呢?我認爲有句名言說的在理:

脫離場景談源碼,都是在耍流氓 ——英明神武蘑菇君

上文提到,LayoutParams其實是父控件提供給child view的,好讓child view選擇如何測量和放置自己。所以肯定在child view添加到父控件的那一刻,child view就應該有LayoutParams了。我們來看看幾個常見的添加View的方式:


LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
// 1.直接添加一個“裸”的TextView,不主動指定LayoutParams
TextView textView = new TextView(this);
textView.setText("紅色蘑菇君");
textView.setTextColor(Color.RED);
parent.addView(textView);

// 2.先手動給TextView設定好LayoutParams,再添加
textView = new TextView(this);
textView.setText("綠色蘑菇君");
textView.setTextColor(Color.GREEN);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(300,300);
textView.setLayoutParams(lp);
parent.addView(textView);

// 3.在添加的時候傳遞一個創建好的LayoutParams
textView = new TextView(this);
textView.setText("藍色蘑菇君");
textView.setTextColor(Color.BLUE);
LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,300);
parent.addView(textView, lp2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

上面代碼展示的是3種往LinearLayout裏動態添加TextView的方式,其中都涉及到了addView這個方法。我們來看看addView的幾個重載方法:

//這3個方法都來自於基類ViewGroup

 public void addView(View child) {
        addView(child, -1);
    }

 /*
  * @param child the child view to add
  * @param index the position at which to add the child    
  /
public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }

public void addView(View child, LayoutParams params) {
        addView(child, -1, params);
    }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

可以看出addView(View child)是調用了addView(View child, int index)方法的,在這個裏面對child的LayoutParams做了判斷,如果爲null的話,則調用了generateDefaultLayoutParams方法爲child生成一個默認的LayoutParams。這也合情合理,畢竟現在這個社會呀,像蘑菇君我這麼懶的人太多,你要是不給個默認的選項,那別說友誼的小船了,就算泰坦尼克,那也說翻就翻!<( ̄︶ ̄)>……好的,那讓我們看看LinearLayout爲我們這羣懶人生成了怎樣的默認LayoutParams:

@Override
protected LayoutParams generateDefaultLayoutParams() {
        if (mOrientation == HORIZONTAL) {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        } else if (mOrientation == VERTICAL) {
            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
        return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

顯然,LinearLayout是重寫了基類ViewGroup裏的generateDefaultLayoutParams方法的:如果佈局是水平方向,則孩子們的寬高都是WRAP_CONTENT,而如果是垂直方向,高仍然是WRAP_CONTENT,但寬卻變成了MATCH_PARENT。所以,這一點大家得注意,因爲很有可能因爲我們的懶,導致佈局效果和我們理想中的不一樣。因此呢,第1種添加View的方式是不推薦滴,像第2或第3種方式,添加的時候指定了LayoutParams,不僅明確,而且易修改。(果然還是勤勞致富呀…)

上面三個重載的addView方法最終都調用了addView(View child, int index, LayoutParams params)這個參數最多的方法:

public void addView(View child, int index, LayoutParams params) {
        ...
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {
        ...

        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }

        if (preventRequestLayout) {
            child.mLayoutParams = params;
        } else {
            child.setLayoutParams(params);
        }

        ...
    }    

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

addView方法又調用了方法addViewInner,在這個私有方法裏,又幹了哪些偷偷摸摸的事呢?接着來看看:

//這兩個方法都重寫了基類ViewGroup裏的方法
// Override to allow type-checking of LayoutParams.
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof LinearLayout.LayoutParams;
}

@Override
  protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    return new LayoutParams(p);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

checkLayoutParams方法的作用是檢查傳遞進來的LayoutParams是不是LinearLayout的LayoutParam。如果不是呢?再通過generateLayoutParams方法根據你傳遞的LayoutParams的屬性構造一個LinearLayout的LayoutParams。不得不再次感慨父容器控件的不容易:我們懶得設置child view的LayoutParams,甚至是設置了錯誤的LayoutParams,父控件都在竭盡所能的糾正我們的錯誤,只爲了給孩子提供一個舒適的環境。(╥╯^╰╥)

不過呀,雖然父控件可以在添加View時幫我們糾正部分錯誤,但我們在其他情況下錯誤的修改child View的LayoutParams,那父控件也愛莫能助了。比如下面這種情況:

LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
textView = new TextView(this);
textView.setText("此處有BUG");
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(200,200);
parent.addView(textView, lp);

textView.setLayoutParams(new ViewGroup.LayoutParams(100,100));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

會直接報ClassCastException

java.lang.ClassCastException: android.view.ViewGroup$LayoutParams cannot be cast to android.widget.LinearLayout$LayoutParams
  • 1
  • 1

上面這種異常熟悉麼?反正我是相當熟悉的〒▽〒……原因就是上面代碼裏的textView是LinearLayout的孩子,而我們調用textView的setLayoutParams方法強行給它設置了一個ViewGroup的LayoutParams,所有在LinearLayout重新進行繪製流程的時候,在onMeasure方法裏,會進行強制類型轉換操作:

LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
  • 1
  • 1

所以App斯巴達了。也許你會說,我纔不會這麼傻,我知道textView的父控件是LinearLayout了,我肯定會給它設置相應的LayoutParams的!這是當然的啦,在這種明確的情況下,我們當然不會這麼傻。但是,很不幸的是,有很多時候我們並不能一眼就看出來一個View的LayoutParams是什麼類型的LayoutParams,這就需要動用你的智慧去分析分析啦,希望這篇文章能給你一些幫助。♪(^∀^●)ノ

自定義LayoutParams

在本文的開頭就提到過:每個容器控件幾乎都會有自己的LayoutParams實現,像LinearLayout、FrameLayout和RelativeLayout等等。所以,我們在自定義ViewGroup時,幾乎都要自定義相應的LayoutParams。這一節呢,就是對如何自定義LayoutParams進行一個總結。

我以一個簡單的流佈局FlowLayout爲例,流佈局的簡單定義如下:

FlowLayout:添加到此容器的控件自左往右依次排列,如果當前行的寬度不足以容納下一個控件,就會將此控件放置到下一行。

假設這個FlowLayout可以給它的孩子們提供一個gravity屬性,效果就是讓孩子能在某一行的垂直方向上選擇三個位置:top(處於頂部)、center(居中)、bottom(處於底部)。咦?這個效果是不是和LinearLayout提供給孩子的layout_gravity屬性很像?那好,我們來參考一下LinearLayout裏的LayoutParams源碼:

public static class LayoutParams extends ViewGroup.MarginLayoutParams {

        public float weight;

        public int gravity = -1;

        public LayoutParams(Context c, AttributeSet attrs) {

            super(c, attrs);
            TypedArray a =
            c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
            weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
            gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);

            a.recycle();
        }


        public LayoutParams(ViewGroup.LayoutParams p) {
            super(p);
        }


        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
        }  

         public LayoutParams(LayoutParams source) {
            super(source);

            this.weight = source.weight;
            this.gravity = source.gravity;
        }
    }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

首先,LinearLayout裏的靜態內部類LayoutParams是繼承ViewGroup.MarginLayoutParams的,所以它的孩子們都可以用margin屬性。事實上,絕大部分容器控件都是直接繼承ViewGroup.MarginLayoutParams而非ViewGroup.LayoutParams。所以我們的FlowLayout也直接繼承ViewGroup.MarginLayoutParams

其次,LinearLayout支持兩個屬性weightgravity,這兩個屬性在xml對應的就是layout_weightlayout_gravity。在它的構造函數LayoutParams(Context c, AttributeSet attrs)裏,將獲取到的xml佈局文件裏的屬性轉化成了weightgravity的值。不過com.android.internal.R.styleable.LinearLayout_Layout這個東西是什麼鬼?其實這是系統在xml屬性文件裏配置的declare-styleable,好讓系統知道LinearLayout能爲它的孩子們提供哪些屬性支持。我們在佈局的時候IDE也會給出這些快捷提示。而對於自定義的FlowLayout來說,模仿LinearLayout的寫法,可以在attrs.xml文件裏這麼寫:

<declare-styleable name="FlowLayout_Layout">
        <attr name="android:layout_gravity"/>
    </declare-styleable>
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

而剩下的幾個構造方法起的作用就是從傳遞的LayoutParams參數裏克隆屬性了。

依葫蘆畫瓢,FlowLayout的LayoutParams如下:

public static class LayoutParams extends ViewGroup.MarginLayoutParams {

        public int gravity = -1;

        public LayoutParams(Context c, AttributeSet attrs) {

            super(c, attrs);
            TypedArray a =
            c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
            weight = a.getFloat(R.styleable.FlowLayout_Layout, 0);
            gravity = a.getInt(R.styleable.FlowLayout_Layout_android_layout_gravity, -1);

            a.recycle();
        }

         public LayoutParams(ViewGroup.LayoutParams p) {
            super(p);
        }


        public LayoutParams(ViewGroup.MarginLayoutParams source) {
            super(source);
        }  


         public LayoutParams(LayoutParams source) {
            super(source);
            this.gravity = source.gravity;
        }

    }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

看起來還是挺簡單的吧?好,那我們這篇文章到此結束……等一等!好像忘記了點什麼……

image

如果對上面分析ViewGroup的addView方法的流程還有印象,可能你會注意ViewGroup裏的這幾個方法:

 public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return p;
    }

protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return  p != null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

爲了能在添加child view時給它設置正確的LayoutParams,我們還需要重寫上面幾個方法(還問爲啥要重寫?快翻到前面再see see)。同樣的,我們還是先來看看LinearLayout是怎麼處理的吧:

 @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LinearLayout.LayoutParams(getContext(), attrs);
    }


    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        if (mOrientation == HORIZONTAL) {
            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        } else if (mOrientation == VERTICAL) {
            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        }
        return null;
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }


    // Override to allow type-checking of LayoutParams.
    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LinearLayout.LayoutParams;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

那FlowLayout該如何重寫上面的幾個方法呢?相信聰明的你已經知道了。(๑•̀ㅂ•́)و✧

總結

這一篇文章從自定義控件的角度,並結合源碼和表情包生動形象的談了談我所理解的LayoutParams。(生動,形象?真不要臉…(¯﹃¯))。不得不說,結合源碼來學習某個知識點,的確是能起到事半功倍的作用。蘑菇君初來乍到,文章裏如有錯誤和疏漏之處,歡迎指正和補充。

預告

下一篇文章打算記錄一個簡單的自定義ViewGroup:流佈局FlowLayout的實現過程,將自定義控件知識儲備-View的繪製流程裏的知識點和本篇文章的LayoutParams結合起來。

PS:寫博客的初始階段果然是有些艱辛,腦海裏想寫的很多,而真到了要以文字表達出來時,卻有一種“愛你在心口難開”的尷尬。不過,感覺到艱難也就意味着自己在走上坡路,堅持下去,希望能給自己和大家帶來更多的幫助。

我是蘑菇君,我爲自己帶鹽

加油,加油


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章