Android 百分比布局库(percent-support-lib) 解析与扩展

转自:http://blog.csdn.net/lmj623565791/article/details/46695347


摘抄:

一、概述

周末游戏打得过猛,于是周天熬夜码代码,周一早上浑浑噩噩的发现android-percent-support-lib-sample这个项目,Google终于开始支持百分比的方式布局了,瞬间脉动回来,啊咧咧。对于这种历史性的时刻,不出篇博客难以表达我内心的激动。

还记得不久前,发了篇博客:Android 屏幕适配方案,这篇博客以Web页面设计引出一种适配方案,最终的目的就是可以通过百分比控制控件的大小。当然了,存在一些问题,比如:

  • 对于没有考虑到屏幕尺寸,可能会出现意外的情况;
  • apk的大小会增加;

当然了android-percent-support这个库,基本可以解决上述问题,是不是有点小激动,稍等,我们先描述下这个support-lib。

这个库提供了:

  • 两种布局供大家使用:
    PercentRelativeLayoutPercentFrameLayout,通过名字就可以看出,这是继承自FrameLayoutRelativeLayout两个容器类;

  • 支持的属性有:

layout_widthPercentlayout_heightPercent
layout_marginPercentlayout_marginLeftPercent
layout_marginTopPercentlayout_marginRightPercent
layout_marginBottomPercentlayout_marginStartPercentlayout_marginEndPercent

可以看到支持宽高,以及margin。

也就是说,大家只要在开发过程中使用PercentRelativeLayoutPercentFrameLayout替换FrameLayoutRelativeLayout即可。

是不是很简单,不过貌似没有LinearLayout,有人会说LinearLayout有weight属性呀。但是,weight属性只能支持一个方向呀~~哈,没事,刚好给我们一个机会去自定义一个PercentLinearLayout

好了,本文分为3个部分:

  • PercentRelativeLayoutPercentFrameLayout的使用
  • 对上述控件源码分析
  • 自定义PercentLinearLayout

二、使用

关于使用,其实及其简单,并且github上也有例子,android-percent-support-lib-sample。我们就简单过一下:

首先记得在build.gradle添加:

<code class="language-xml hljs  has-numbering"> compile 'com.android.support:percent:22.2.0'</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

(一)PercentFrameLayout
<code class="language-xml hljs  has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag"><<span class="hljs-title">android.support.percent.PercentFrameLayout
</span>    <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attribute">xmlns:app</span>=<span class="hljs-value">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
    <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_gravity</span>=<span class="hljs-value">"left|top"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#44ff0000"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:30%,height:20%"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"20%"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"30%"</span>/></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_gravity</span>=<span class="hljs-value">"right|top"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#4400ff00"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:70%,height:20%"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"20%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"70%"</span>/></span>


    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_gravity</span>=<span class="hljs-value">"bottom"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#770000ff"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:100%,height:10%"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"10%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"100%"</span>/></span>


<span class="hljs-tag"></<span class="hljs-title">android.support.percent.PercentFrameLayout</span>></span>
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li></ul>

3个TextView,很简单,直接看效果图:


(二) PercentRelativeLayout
<code class="language-xml hljs  has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span>
<span class="hljs-tag"><<span class="hljs-title">android.support.percent.PercentRelativeLayout
</span>    <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attribute">xmlns:app</span>=<span class="hljs-value">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
    <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>
    <span class="hljs-attribute">android:clickable</span>=<span class="hljs-value">"true"</span>></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/row_one_item_one"</span>
        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_alignParentTop</span>=<span class="hljs-value">"true"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#7700ff00"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"w:70%,h:20%"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"20%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"70%"</span>/></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/row_one_item_two"</span>
        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_toRightOf</span>=<span class="hljs-value">"@+id/row_one_item_one"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#396190"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"w:30%,h:20%"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"20%"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"30%"</span>/></span>


    <span class="hljs-tag"><<span class="hljs-title">ImageView
</span>        <span class="hljs-attribute">android:id</span>=<span class="hljs-value">"@+id/row_two_item_one"</span>
        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:src</span>=<span class="hljs-value">"@drawable/tangyan"</span>
        <span class="hljs-attribute">android:scaleType</span>=<span class="hljs-value">"centerCrop"</span>
        <span class="hljs-attribute">android:layout_below</span>=<span class="hljs-value">"@+id/row_one_item_one"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#d89695"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"70%"</span>/></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_below</span>=<span class="hljs-value">"@id/row_two_item_one"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#770000ff"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:100%,height:10%"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"10%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"100%"</span>/></span>


<span class="hljs-tag"></<span class="hljs-title">android.support.percent.PercentRelativeLayout</span>></span>

</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li></ul>

ok,依然是直接看效果图:

使用没什么好说的,就是直观的看一下。


三、源码分析

其实细想一下,Google只是对我们原本熟悉的RelativeLayout和FrameLayout进行的功能的扩展,使其支持了percent相关的属性。

那么,我们考虑下,如果是我们添加这种扩展,我们会怎么做:

  • 通过LayoutParams获取child设置的percent相关属性的值
  • onMeasure的时候,将child的width,height的值,通过获取的自定义属性的值进行计算(eg:容器的宽 * fraction ),计算后传入给child.measure(w,h);

ok,有了上面的猜想,我们直接看PercentFrameLayout的源码。

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PercentFrameLayout</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FrameLayout</span> {</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> PercentLayoutHelper mHelper = <span class="hljs-keyword">new</span> PercentLayoutHelper(<span class="hljs-keyword">this</span>);

    <span class="hljs-comment">//省略了,两个构造方法</span>

    <span class="hljs-keyword">public</span> <span class="hljs-title">PercentFrameLayout</span>(Context context, AttributeSet attrs) {
        <span class="hljs-keyword">super</span>(context, attrs);
    }



    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> LayoutParams <span class="hljs-title">generateLayoutParams</span>(AttributeSet attrs) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> LayoutParams(getContext(), attrs);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">if</span> (mHelper.handleMeasuredStateTooSmall()) {
            <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onLayout</span>(<span class="hljs-keyword">boolean</span> changed, <span class="hljs-keyword">int</span> left, <span class="hljs-keyword">int</span> top, <span class="hljs-keyword">int</span> right, <span class="hljs-keyword">int</span> bottom) {
        <span class="hljs-keyword">super</span>.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LayoutParams</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">FrameLayout</span>.<span class="hljs-title">LayoutParams</span>
            <span class="hljs-keyword">implements</span> <span class="hljs-title">PercentLayoutHelper</span>.<span class="hljs-title">PercentLayoutParams</span> {</span>
        <span class="hljs-keyword">private</span> PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

        <span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(Context c, AttributeSet attrs) {
            <span class="hljs-keyword">super</span>(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }
        <span class="hljs-comment">//省略了一些代码...</span>

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> PercentLayoutHelper.PercentLayoutInfo <span class="hljs-title">getPercentLayoutInfo</span>() {
            <span class="hljs-keyword">return</span> mPercentLayoutInfo;
        }

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setBaseAttributes</span>(TypedArray a, <span class="hljs-keyword">int</span> widthAttr, <span class="hljs-keyword">int</span> heightAttr) {
            PercentLayoutHelper.fetchWidthAndHeight(<span class="hljs-keyword">this</span>, a, widthAttr, heightAttr);
        }
    }
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li></ul>

代码是相当的短,可以看到PercentFrameLayout里面首先重写了generateLayoutParams方法,当然了,由于支持了一些新的layout_属性,那么肯定需要定义对应的LayoutParams。


(一)percent相关属性的获取

可以看到PercentFrameLayout.LayoutParams在原有的FrameLayout.LayoutParams基础上,实现了PercentLayoutHelper.PercentLayoutParams接口。

这个接口很简单,只有一个方法:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PercentLayoutParams</span> {</span>
        PercentLayoutInfo getPercentLayoutInfo();
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

而,这个方法的实现呢,也只有一行:return mPercentLayoutInfo;,那么这个mPercentLayoutInfo在哪完成赋值呢?

看PercentFrameLayout.LayoutParams的构造方法:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(Context c, AttributeSet attrs) {
            <span class="hljs-keyword">super</span>(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>

可以看到,将attrs传入给getPercentLayoutInfo方法,那么不用说,这个方法的内部,肯定是获取自定义属性的值,然后将其封装到PercentLayoutInfo对象中,最后返回。

代码如下:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> PercentLayoutInfo <span class="hljs-title">getPercentLayoutInfo</span>(Context context,
            AttributeSet attrs) {
        PercentLayoutInfo info = <span class="hljs-keyword">null</span>;
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
        <span class="hljs-keyword">float</span> value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent width: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.widthPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent height: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.heightPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>, -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.leftMarginPercent = value;
            info.topMarginPercent = value;
            info.rightMarginPercent = value;
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent left margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.leftMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent top margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.topMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent right margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.rightMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent bottom margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.bottomMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent start margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.startMarginPercent = value;
        }
        value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                -<span class="hljs-number">1</span>f);
        <span class="hljs-keyword">if</span> (value != -<span class="hljs-number">1</span>f) {
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, <span class="hljs-string">"percent end margin: "</span> + value);
            }
            info = info != <span class="hljs-keyword">null</span> ? info : <span class="hljs-keyword">new</span> PercentLayoutInfo();
            info.endMarginPercent = value;
        }
        array.recycle();
        <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, <span class="hljs-string">"constructed: "</span> + info);
        }
        <span class="hljs-keyword">return</span> info;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li></ul>

是不是和我们平时的取值很类似,所有的值最终封装到PercentLayoutInfo对象中。

ok,到此我们的属性获取就介绍完成,有了这些属性,是不是onMeasure里面要进行使用呢?


(二) onMeasue中重新计算child的尺寸
<code class="language-java hljs  has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">if</span> (mHelper.handleMeasuredStateTooSmall()) {
            <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

可以看到onMeasure中的代码页很少,看来核心的代码都被封装在mHelper的方法中,我们直接看mHelper.adjustChildren方法。

<code class="language-java hljs  has-numbering"><span class="hljs-javadoc">/**
     * Iterates over children and changes their width and height to one calculated from percentage
     * values.
     *<span class="hljs-javadoctag"> @param</span> widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
     *<span class="hljs-javadoctag"> @param</span> heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">adjustChildren</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) {
        <span class="hljs-comment">//...</span>
        <span class="hljs-keyword">int</span> widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
        <span class="hljs-keyword">int</span> heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();

            <span class="hljs-keyword">if</span> (params <span class="hljs-keyword">instanceof</span> PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, <span class="hljs-string">"using "</span> + info);
                }
                <span class="hljs-keyword">if</span> (info != <span class="hljs-keyword">null</span>) {
                    <span class="hljs-keyword">if</span> (params <span class="hljs-keyword">instanceof</span> ViewGroup.MarginLayoutParams) {
                        info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,
                                widthHint, heightHint);
                    } <span class="hljs-keyword">else</span> {
                        info.fillLayoutParams(params, widthHint, heightHint);
                    }
                }
            }
        }
    }
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li></ul>

通过注释也能看出,此方法中遍历所有的孩子,通过百分比的属性重新设置其宽度和高度。

首先在widthHint、heightHint保存容器的宽、高,然后遍历所有的孩子,判断其LayoutParams是否是PercentLayoutParams类型,如果是,通过params.getPercentLayoutInfo拿出info对象。

是否还记得,上面的分析中,PercentLayoutInfo保存了percent相关属性的值。

如果info不为null,则判断是否需要处理margin;我们直接看fillLayoutParams方法(处理margin也是类似的)。

<code class="language-java hljs  has-numbering"> <span class="hljs-javadoc">/**
         * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
         */</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">fillLayoutParams</span>(ViewGroup.LayoutParams params, <span class="hljs-keyword">int</span> widthHint,
                <span class="hljs-keyword">int</span> heightHint) {
            <span class="hljs-comment">// Preserve the original layout params, so we can restore them after the measure step.</span>
            mPreservedParams.width = params.width;
            mPreservedParams.height = params.height;

            <span class="hljs-keyword">if</span> (widthPercent >= <span class="hljs-number">0</span>) {
                params.width = (<span class="hljs-keyword">int</span>) (widthHint * widthPercent);
            }
            <span class="hljs-keyword">if</span> (heightPercent >= <span class="hljs-number">0</span>) {
                params.height = (<span class="hljs-keyword">int</span>) (heightHint * heightPercent);
            }
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, <span class="hljs-string">"after fillLayoutParams: ("</span> + params.width + <span class="hljs-string">", "</span> + params.height + <span class="hljs-string">")"</span>);
            }
        }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>

首先保存原本的width和height,然后重置params的width和height为(int) (widthHint * widthPercent)(int) (heightHint * heightPercent);

到此,其实我们的百分比转换就结束了,理论上就已经实现了对于百分比的支持,不过Google还考虑了一些细节。

我们回到onMeasure方法:

<code class="language-java hljs  has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec) {
        mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">if</span> (mHelper.handleMeasuredStateTooSmall()) {
            <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

下面还有个mHelper.handleMeasuredStateTooSmall的判断,也就是说,如果你设置的百分比,最终计算出来的MeasuredSize过小的话,会进行一些操作。
代码如下:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">handleMeasuredStateTooSmall</span>() {
        <span class="hljs-keyword">boolean</span> needsSecondMeasure = <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, <span class="hljs-string">"should handle measured state too small "</span> + view + <span class="hljs-string">" "</span> + params);
            }
            <span class="hljs-keyword">if</span> (params <span class="hljs-keyword">instanceof</span> PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                <span class="hljs-keyword">if</span> (info != <span class="hljs-keyword">null</span>) {
                    <span class="hljs-keyword">if</span> (shouldHandleMeasuredWidthTooSmall(view, info)) {
                        needsSecondMeasure = <span class="hljs-keyword">true</span>;
                        params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                    <span class="hljs-keyword">if</span> (shouldHandleMeasuredHeightTooSmall(view, info)) {
                        needsSecondMeasure = <span class="hljs-keyword">true</span>;
                        params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                    }
                }
            }
        }
        <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, <span class="hljs-string">"should trigger second measure pass: "</span> + needsSecondMeasure);
        }
        <span class="hljs-keyword">return</span> needsSecondMeasure;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li></ul>

首先遍历所有的孩子,拿出孩子的layoutparams,如果是PercentLayoutParams实例,则取出info。如果info不为null,调用shouldHandleMeasuredWidthTooSmall判断:

<code class="language-java hljs  has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">shouldHandleMeasuredWidthTooSmall</span>(View view, PercentLayoutInfo info) {
        <span class="hljs-keyword">int</span> state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
        <span class="hljs-keyword">return</span> state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= <span class="hljs-number">0</span> &&
                info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

这里就是判断,如果你设置的measuredWidth或者measureHeight过小的话,并且你在布局文件中layout_w/h 设置的是WRAP_CONTENT的话,将params.width / height= ViewGroup.LayoutParams.WRAP_CONTENT,然后重新测量。

哈,onMeasure终于结束了~~~现在我觉得应该代码结束了吧,尺寸都设置好了,还需要干嘛么,but,你会发现onLayout也重写了,我们又不改变layout规则,在onLayout里面干什么毛线:

<code class="language-java hljs  has-numbering"><span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onLayout</span>(<span class="hljs-keyword">boolean</span> changed, <span class="hljs-keyword">int</span> left, <span class="hljs-keyword">int</span> top, <span class="hljs-keyword">int</span> right, <span class="hljs-keyword">int</span> bottom) {
        <span class="hljs-keyword">super</span>.onLayout(changed, left, top, right, bottom);
        mHelper.restoreOriginalParams();
    }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

继续看mHelper.restoreOriginalParams

<code class="language-java hljs  has-numbering"> <span class="hljs-javadoc">/**
     * Iterates over children and restores their original dimensions that were changed for
     * percentage values. Calling this method only makes sense if you previously called
     * {@link PercentLayoutHelper#adjustChildren(int, int)}.
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">restoreOriginalParams</span>() {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>, N = mHost.getChildCount(); i < N; i++) {
            View view = mHost.getChildAt(i);
            ViewGroup.LayoutParams params = view.getLayoutParams();
            <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, <span class="hljs-string">"should restore "</span> + view + <span class="hljs-string">" "</span> + params);
            }
            <span class="hljs-keyword">if</span> (params <span class="hljs-keyword">instanceof</span> PercentLayoutParams) {
                PercentLayoutInfo info =
                        ((PercentLayoutParams) params).getPercentLayoutInfo();
                <span class="hljs-keyword">if</span> (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, <span class="hljs-string">"using "</span> + info);
                }
                <span class="hljs-keyword">if</span> (info != <span class="hljs-keyword">null</span>) {
                    <span class="hljs-keyword">if</span> (params <span class="hljs-keyword">instanceof</span> ViewGroup.MarginLayoutParams) {
                        info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
                    } <span class="hljs-keyword">else</span> {
                        info.restoreLayoutParams(params);
                    }
                }
            }
        }
    }
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li></ul>

噗,原来是重新恢复原本的尺寸值,也就是说onMeasure里面的对值进行了改变,测量完成后。在这个地方,将值又恢复成如果布局文件中的值,上面写的都是0。恢复很简单:

<code class="language-java hljs  has-numbering">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">restoreLayoutParams</span>(ViewGroup.LayoutParams params) {
            params.width = mPreservedParams.width;
            params.height = mPreservedParams.height;
        }</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

你应该没有忘在哪存的把~忘了的话,麻烦Ctrl+F ‘mPreservedParams.width’ 。

也就是说,你去打印上面写法,布局文件中view的v.getLayoutParams().width,这个值应该是0。

这里感觉略微不爽~这个0没撒用处呀,还不如不重置~~

好了,到此就分析完了,其实主要就几个步骤:

  • LayoutParams中属性的获取
  • onMeasure中,改变params.width为百分比计算结果,测量
  • 如果测量值过小且设置的w/h是wrap_content,重新测量
  • onLayout中,重置params.w/h为布局文件中编写的值

可以看到,有了RelativeLayout、FrameLayout的扩展,竟然没有LinearLayout几个意思。好在,我们的核心代码都由PercentLayoutHelper封装了,自己扩展下LinearLayout也不复杂。


三、实现PercentLinearlayout

可能有人会说,有了weight呀,但是weight能做到宽、高同时百分比赋值嘛?

好了,代码很简单,如下:


(一)PercentLinearLayout
<code class="language-java hljs  has-numbering"><span class="hljs-keyword">package</span> com.juliengenoud.percentsamples;

<span class="hljs-keyword">import</span> android.content.Context;
<span class="hljs-keyword">import</span> android.content.res.TypedArray;
<span class="hljs-keyword">import</span> android.support.percent.PercentLayoutHelper;
<span class="hljs-keyword">import</span> android.util.AttributeSet;
<span class="hljs-keyword">import</span> android.view.ViewGroup;
<span class="hljs-keyword">import</span> android.widget.LinearLayout;

<span class="hljs-javadoc">/**
 * Created by zhy on 15/6/30.
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PercentLinearLayout</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LinearLayout</span>
{</span>

    <span class="hljs-keyword">private</span> PercentLayoutHelper mPercentLayoutHelper;

    <span class="hljs-keyword">public</span> <span class="hljs-title">PercentLinearLayout</span>(Context context, AttributeSet attrs)
    {
        <span class="hljs-keyword">super</span>(context, attrs);

        mPercentLayoutHelper = <span class="hljs-keyword">new</span> PercentLayoutHelper(<span class="hljs-keyword">this</span>);
    }


    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onMeasure</span>(<span class="hljs-keyword">int</span> widthMeasureSpec, <span class="hljs-keyword">int</span> heightMeasureSpec)
    {
        mPercentLayoutHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        <span class="hljs-keyword">if</span> (mPercentLayoutHelper.handleMeasuredStateTooSmall())
        {
            <span class="hljs-keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onLayout</span>(<span class="hljs-keyword">boolean</span> changed, <span class="hljs-keyword">int</span> l, <span class="hljs-keyword">int</span> t, <span class="hljs-keyword">int</span> r, <span class="hljs-keyword">int</span> b)
    {
        <span class="hljs-keyword">super</span>.onLayout(changed, l, t, r, b);
        mPercentLayoutHelper.restoreOriginalParams();
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> LayoutParams <span class="hljs-title">generateLayoutParams</span>(AttributeSet attrs)
    {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> LayoutParams(getContext(), attrs);
    }


    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LayoutParams</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">LinearLayout</span>.<span class="hljs-title">LayoutParams</span>
            <span class="hljs-keyword">implements</span> <span class="hljs-title">PercentLayoutHelper</span>.<span class="hljs-title">PercentLayoutParams</span>
    {</span>
        <span class="hljs-keyword">private</span> PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

        <span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(Context c, AttributeSet attrs)
        {
            <span class="hljs-keyword">super</span>(c, attrs);
            mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
        }

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> PercentLayoutHelper.PercentLayoutInfo <span class="hljs-title">getPercentLayoutInfo</span>()
        {
            <span class="hljs-keyword">return</span> mPercentLayoutInfo;
        }

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setBaseAttributes</span>(TypedArray a, <span class="hljs-keyword">int</span> widthAttr, <span class="hljs-keyword">int</span> heightAttr)
        {
            PercentLayoutHelper.fetchWidthAndHeight(<span class="hljs-keyword">this</span>, a, widthAttr, heightAttr);
        }

        <span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(<span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height) {
            <span class="hljs-keyword">super</span>(width, height);
        }


        <span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(ViewGroup.LayoutParams source) {
            <span class="hljs-keyword">super</span>(source);
        }

        <span class="hljs-keyword">public</span> <span class="hljs-title">LayoutParams</span>(MarginLayoutParams source) {
            <span class="hljs-keyword">super</span>(source);
        }

    }

}

</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li></ul>

如果你详细看了上面的源码分析,这个代码是不是没撒解释的了~


(二)测试布局
<code class="language-xml hljs  has-numbering"><span class="hljs-pi"><?xml version="1.0" encoding="utf-8"?></span>


<span class="hljs-tag"><<span class="hljs-title">com.juliengenoud.percentsamples.PercentLinearLayout
</span>    <span class="hljs-attribute">xmlns:android</span>=<span class="hljs-value">"http://schemas.android.com/apk/res/android"</span>
    <span class="hljs-attribute">xmlns:app</span>=<span class="hljs-value">"http://schemas.android.com/apk/res-auto"</span>
    <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
    <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"match_parent"</span>
    <span class="hljs-attribute">android:orientation</span>=<span class="hljs-value">"vertical"</span>></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#ff44aacc"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:60%,height:5%"</span>
        <span class="hljs-attribute">android:textColor</span>=<span class="hljs-value">"#ffffff"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"5%"</span>
        <span class="hljs-attribute">app:layout_marginBottomPercent</span>=<span class="hljs-value">"5%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"60%"</span>/></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#ff4400cc"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:textColor</span>=<span class="hljs-value">"#ffffff"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:70%,height:10%"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"10%"</span>
        <span class="hljs-attribute">app:layout_marginBottomPercent</span>=<span class="hljs-value">"5%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"70%"</span>/></span>
    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#ff44aacc"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:80%,height:15%"</span>
        <span class="hljs-attribute">android:textColor</span>=<span class="hljs-value">"#ffffff"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"15%"</span>
        <span class="hljs-attribute">app:layout_marginBottomPercent</span>=<span class="hljs-value">"5%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"80%"</span>/></span>
    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#ff4400cc"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:90%,height:5%"</span>
        <span class="hljs-attribute">android:textColor</span>=<span class="hljs-value">"#ffffff"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"20%"</span>
        <span class="hljs-attribute">app:layout_marginBottomPercent</span>=<span class="hljs-value">"10%"</span>
        <span class="hljs-attribute">app:layout_widthPercent</span>=<span class="hljs-value">"90%"</span>/></span>

    <span class="hljs-tag"><<span class="hljs-title">TextView
</span>        <span class="hljs-attribute">android:layout_width</span>=<span class="hljs-value">"match_parent"</span>
        <span class="hljs-attribute">android:layout_height</span>=<span class="hljs-value">"0dp"</span>
        <span class="hljs-attribute">android:background</span>=<span class="hljs-value">"#ff44aacc"</span>
        <span class="hljs-attribute">android:gravity</span>=<span class="hljs-value">"center"</span>
        <span class="hljs-attribute">android:text</span>=<span class="hljs-value">"width:100%,height:25%"</span>
        <span class="hljs-attribute">android:textColor</span>=<span class="hljs-value">"#ffffff"</span>
        <span class="hljs-attribute">app:layout_heightPercent</span>=<span class="hljs-value">"25%"</span>
        <span class="hljs-attribute">app:layout_marginBottomPercent</span>=<span class="hljs-value">"5%"</span>
        /></span>


<span class="hljs-tag"></<span class="hljs-title">com.juliengenoud.percentsamples.PercentLinearLayout</span>></span>

</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li></ul>

我们纵向排列的几个TextView,分别设置宽/高都为百分比,且之间的间隔为5%p。


(三)效果图

ok,到此,我们使用、源码分析、扩展PercentLinearLayout就结束了。

添加PercentLinearLayout后的地址:点击查看

扩展下载:android-percent-support-extend 包含android studio, eclipse项目,以及上述源码。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值