GridLayout使用要点总结
FannieChang
8年前
<p>GridLayout是在Android4.0中引进的新布局,使用它的理由有两个:1,减少布局嵌套层次从而提高性能;2,对需要复杂对齐的布局,非他莫属。不使用它的理由:由于太灵活导致学习难度比较大。本文是我研究GridLayout的心得要点。</p> <h2><strong>一、GridLayout如何确定子View的坐标</strong></h2> <p>对于一个n行m列的GridLayout,每个cell都有唯一的坐标(i,j),其中,i的取值区间为[0,n-1],j的取值坐标为[0,m-1]。所谓确定子View的坐标就是确定该子View所在的cell的坐标,如果子View占据了一个cell group,子View的坐标由cell group左上角的cell的坐标来确定,如图所示:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b86ca0a6e63976d8c69cda416fea4aeb.png"></p> <p style="text-align:center">coordinate-of-child-view.png</p> <p>最简单的情况就是利用子view的属性android:layout_row和android:layout_column,这两个属性已经明白无误地告诉GridLayout当前子View的坐标是什么。这没什么好说的。</p> <p>如果一个子View是如下三种情况之一,GridLayout就要帮助我们来确定子View的坐标:</p> <ul> <li> <p>子View上既没有android:layout_row属性,也没有android:layout_column属性</p> </li> <li> <p>子View上只有android:layout_row属性</p> </li> <li> <p>子View上只有android:layout_column属性</p> </li> </ul> <p>google官方只有对第一种情况的说明:</p> <p>Automatic Index Allocation</p> <p>As children are added to a GridLayout, it maintains a cursor position and a “high-water mark” that it uses to place widgets in cells that don’t yet have anything in them.</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/bf1453386faca7c5ee8298c5fc91c861.png"></p> <p style="text-align:center">milne-3.png</p> <p>When GridLayout’s <a href="/misc/goto?guid=4959720457199379444" rel="nofollow,noindex">orientation</a> property is horizontal</p> <p>and a <a href="/misc/goto?guid=4959720457293349561" rel="nofollow,noindex">columnCoun</a> t has been set (to 8 in this example) the high-water mark (shown above in red) is maintained as a separate height value for each column. When indices need to be created, GridLayout first determines the size of the cell group (by looking at the <a href="/misc/goto?guid=4959720457379630058" rel="nofollow,noindex">rowSpan</a> and <a href="/misc/goto?guid=4959720457464935943" rel="nofollow,noindex">columnSpan</a> parameters of the new widget) and then, starting at the cursor, goes through the available locations from: left to right, top to bottom, so as to find the row and column indices of the first location that’s free.</p> <p>When GridLayout’s orientation is vertical</p> <p>, all of the same principles apply, except that the roles of the horizontal and vertical axes are exchanged.</p> <p>If you want multiple views to be placed in the same cell, you have to define the indices explicitly, as the default allocation procedure above is designed to place widgets in separate cells.</p> <p>说的简单,可是实际使用起来,完全不是这么回事!!</p> <p>比如上文最后一句:</p> <p>the default allocation procedure above is designed to place widgets in separate cells.</p> <p>我实际测试中就发现了例外情况,并且在stackoverflow上提出来了。</p> <p>如果有人能解决这个问题,希望能告诉我,谢谢!</p> <p>另外,官方只说了GridLayout会给每一列维护一个high-water mark,并且维护一个全局的cursor position,可cursor position 到底怎么确定?有什么变化规律?经我实际测试,没有什么规律,很容易搞乱。</p> <p>最佳实践:在实际使用中, <strong>强烈建议</strong> 每一个子View都明确设置android:layout_row和android:layout_column,不要使用GridLayout的自动确定子View坐标功能。</p> <h2><strong>二、GridLayout如何确定cell的宽高</strong></h2> <p>假设我们给每一个子View都明确设置了row和column,还有rowSpan和columnSpan,此时GridLayout就可以确定每一行的高和每一列的宽,每一个cell的宽高也随之确定。</p> <p>GridLayout确定的宽高的算法是(以第1行和第1列为例):</p> <ul> <li> <p>第1行的高:</p> <p>测量第1行中所有子View的高,取最大值作为该行的高,如果该行没有子View,行高设为0。</p> </li> <li> <p>第1列的宽:</p> <p>测量第1列中所有子View的宽,取最大值作为该列的宽,如果该列没有子View,列宽设为0。</p> </li> </ul> <p>这里有必要提示一下:GridLayout的子View不需要设置layout_width和layout_height属性,因为GridLayout会把所有的子View的这两个属性设置为WRAP_CONTENT,所以你设置了也没有用。</p> <h2><strong>三、GridLayout如何利用剩余的空间</strong></h2> <p>假如GridLayout布局完之后还有剩余的空间,如何分配这些空间呢?</p> <p>多余的空间分两种情况:横向和纵向,我们以纵向为例:</p> <p>假设分配完之后的布局是这样的:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/27549f18bed666b0e4585534f77f94f6.png"></p> <p style="text-align:center">redunt.PNG</p> <p>显然,屏幕右边还剩余一些空间。假设要把这些空间分给其他列,我们分三种情况讨论:</p> <ul> <li> <p>分给其中只有一个子View的单列</p> </li> <li> <p>分给其中有多个子View 的单列</p> </li> <li> <p>分给2个以上的列</p> </li> </ul> <p>1、 分给其中只有一个子View的单列</p> <p>假设我们想把这些剩余的空间分配给“0505”(它实际上是一个TextView)所在的列。有两个办法:</p> <ul> <li> <p>我们可以在TextView “0505”上添加这样的设置:</p> <p>android:layout_gravity="fill_horizontal"</p> </li> </ul> <p>说明:此处的fill_horizontal还可以是center、center_horizontal、right、left,不能是其他值。</p> <ul> <li> <p>我们还可以在TextView “0505”上添加这样的设置:</p> <p>android:layout_columnWeight="1"</p> </li> </ul> <p>其中,weight的值可以随意写,反正只有这一列占用剩余的空间。</p> <p>不管是用上面哪一种办法,还是两种都用,我们都可以达到目的,效果是这样的:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/70487dbeceb9fbf380860875523f8316.png"></p> <p style="text-align:center">noredunt.PNG</p> <p>2、 分给其中有多个子View 的单列</p> <p>如果我们想把多余的空间分配给“0404”和“1515”所在的列。那么对于每一个子View,即“0404”和“1515”。我们要不为它们添加android:layout_gravity="fill_horizontal"。</p> <p>说明:此处的fill_horizontal仍然只能取center、center_horizontal、right、left。</p> <p>要不为它们添加android:layout_columnWeight="1",其中,weight的值可以随意写,反正只有这一列占用剩余的空间。</p> <p>也就是说,要在该列所有的子View上都添加上layout_gravity属性或者layout_columnWeight属性。</p> <p>这里有一点容易让人迷惑,就是如果我两个子View都添加了layout_columnWeight,并且它们的值不一样,这时会怎么样呢?</p> <p>如果是将剩余的空间全分配给这一列,那么两个子View的column_Weight值是否相同是无所谓的。</p> <p>但是对于第3种情况,也就是将多余的空间分给2个以上的列时,GridLayout只考虑两个column_Weight中比较大的值。</p> <p>最后的效果是这样的:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/f71c470f3cf0088cef83f3d9ad50676f.png"></p> <p style="text-align:center">multi_child_view.PNG</p> <p>3、 分给2个以上的列</p> <p>如果我们想把剩余的空间同时分给“0404”和“1515”所在列和“0505”所在列。这时情况就有点微妙了。这两列不仅要满足上面1和2中的条件,还有一个额外的限制:</p> <p>每一列中至少有一个子View上设置了column_Weight属性。</p> <p>拿“0505”来说,此时它就不能只设置layout_gravity了,而是首先要设置column_Weight,layout_gravity是可加可不加的。</p> <p>对于“0404”和“1515”所在列,就不能两个子View都设置layout_gravity了,而是至少有一个上要设置column_Weight,当然可以两个子View都设置column_Weight,此时只有二者中较大的值有效。</p> <p>这个要求其实很好理解,因为我们要把多余的空间分给两个列,所以我们必须告诉GridLayout,给各个列分配多余空间时的比例划分。</p> <h2><strong>4、Demo</strong></h2> <p>使用GridLayout高仿的简书首页文章列表:</p> <pre> <code class="language-java"><TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="match_parent" android:layout_height="wrap_content"> <GridLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:columnCount="4" android:rowCount="3"> <com.example.milter.forgridlayoutblog.AvatarView android:id="@+id/author_avatar" android:layout_width="26.0dip" android:layout_height="26.0dip" android:layout_gravity="center" android:scaleType="centerCrop" android:src="@drawable/avatar"/> <TextView android:id="@+id/author_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginLeft="8.0dip" android:ellipsize="end" android:maxWidth="200.0dip" android:paddingBottom="10dp" android:singleLine="true" android:text="milter" android:textColor="#ff4093c6" android:textSize="12.0sp"/> <TextView android:id="@+id/last_compiled_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginLeft="8.0dp" android:paddingBottom="10dp" android:singleLine="true" android:text="2小时前" android:textColor="@color/text_color_b1" android:textSize="10.0sp"/> <TextView android:id="@+id/title" android:layout_column="0" android:layout_columnSpan="4" android:layout_row="1" android:ellipsize="end" android:lineSpacingExtra="2.0dip" android:maxLines="2" android:text="为什么要用GridLayout以及怎样用GridLayout,为什么要用GridLayout以及怎样用GridLayout" android:textColor="@android:color/holo_purple" android:textSize="16.0sp"/> <LinearLayout android:layout_column="0" android:layout_columnSpan="4" android:layout_row="2"> <TextView android:id="@+id/collection_tag" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@drawable/shape_oval_h20_thin_theme" android:gravity="center" android:maxLength="10" android:maxWidth="72dip" android:minWidth="36.0dip" android:text="程序员" android:textColor="@color/theme_color" android:textSize="10.0sp"/> <TextView android:id="@+id/extra_info" android:layout_width="0dip" android:layout_height="match_parent" android:layout_marginLeft="8dp" android:layout_weight="1" android:drawablePadding="5.0dip" android:ellipsize="marquee" android:gravity="center" android:lineSpacingExtra="2.0dip" android:maxLines="1" android:text="100次阅 • 10评论 • 100喜欢 • 100打赏" android:textColor="@color/text_color_b1" android:textSize="10.0sp" /> </LinearLayout> </GridLayout> <ImageView android:id="@+id/image" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="center" android:layout_marginLeft="5.0dip" android:scaleType="centerCrop" android:src="@drawable/image" android:visibility="visible"/> </TableRow> <View android:layout_width="match_parent" android:layout_height="0.39999998dip" android:layout_alignParentBottom="true" android:background="@android:color/holo_purple" android:paddingBottom="2dp"/> </TableLayout> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="match_parent" android:layout_height="wrap_content"> <GridLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:columnCount="4" android:rowCount="3"> <com.example.milter.forgridlayoutblog.AvatarView android:id="@+id/author_avatar1" android:layout_width="26.0dip" android:layout_height="26.0dip" android:layout_gravity="center" android:scaleType="centerCrop" android:src="@drawable/avatar"/> <TextView android:id="@+id/author_name1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginLeft="8.0dip" android:ellipsize="end" android:maxWidth="200.0dip" android:paddingBottom="10dp" android:singleLine="true" android:text="milter" android:textColor="#ff4093c6" android:textSize="12.0sp"/> <TextView android:id="@+id/last_compiled_time1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginLeft="8.0dp" android:paddingBottom="10dp" android:singleLine="true" android:text="2小时前" android:textColor="@color/text_color_b1" android:textSize="10.0sp"/> <TextView android:id="@+id/title1" android:layout_column="0" android:layout_columnSpan="4" android:layout_row="1" android:ellipsize="end" android:lineSpacingExtra="2.0dip" android:maxLines="2" android:text="为什么要用GridLayout以及怎样用GridLayout,为什么要用GridLayout以及怎样用GridLayout" android:textColor="@android:color/holo_purple" android:textSize="16.0sp"/> <LinearLayout android:layout_column="0" android:layout_columnSpan="4" android:layout_row="2"> <TextView android:id="@+id/collection_tag1" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="@drawable/shape_oval_h20_thin_theme" android:gravity="center" android:maxLength="10" android:maxWidth="72dip" android:minWidth="36.0dip" android:text="程序员" android:textColor="@color/theme_color" android:textSize="10.0sp"/> <TextView android:id="@+id/extra_info1" android:layout_width="0dip" android:layout_height="match_parent" android:layout_marginLeft="8dp" android:layout_weight="1" android:drawablePadding="5.0dip" android:ellipsize="marquee" android:gravity="center" android:lineSpacingExtra="2.0dip" android:maxLines="1" android:text="100次阅 • 10评论 • 100喜欢 • 100打赏" android:textColor="@color/text_color_b1" android:textSize="10.0sp" /> </LinearLayout> </GridLayout> <ImageView android:id="@+id/image1" android:layout_width="80dp" android:layout_height="80dp" android:layout_gravity="center" android:layout_marginLeft="5.0dip" android:scaleType="centerCrop" android:src="@drawable/image" android:visibility="visible"/> </TableRow> <View android:layout_width="match_parent" android:layout_height="0.39999998dip" android:layout_alignParentBottom="true" android:background="@android:color/holo_purple" android:paddingBottom="2dp"/> </TableLayout></code></pre> <p>效果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/aad2eca6b7a535db5d5b12cdf589fe77.png"></p> <p style="text-align:center">demo.PNG</p> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/441d60be7d8a</p> <p> </p>