GridLayout是一个非常强大的网格类布局,它不但能像TableLayout那样,实现网格类布局,但它更为强大的地方在于每个Cell的大小可以横向或者纵向拉伸,每个Cell的对齐方式也有很多种,而且不像TableLayout,需要一个TableRow,GridLayout可以通过指定Cell的坐标位置就能实现Cell的拉伸,从而实现,大小不一致的风格卡片式布局。
基本概念
GridLayout把页面分成m行和n列,使用m+1条线和n+1条线,把页面共分成n*m个Cell。指定位置时行坐标是从0到m,列坐标是从0到n。每一个子View占一个或多个Cell。比如(0, 0)到(0, 1)就是占第一个Cell的区域。(0, 0), (0, 2)就是占第一行的2个Cell的区域(横向拉伸).
使用方法
主要介绍一下如何添加Cell,以及设置Cell的位置和拉伸。其他的跟普通的ViewGroup没什么区别的,也没啥好说的。
GridLayout的基本设置
首先需要给GridLayout设置行数和列数:
- android:columnCount 整数,最多的列数
- android:rowCount 整数,最多的行数
在添加Cell就需要注意,不能超过设置的最大行数和列数,否则在添加Cell时会有异常。
元素Cell的位置控制
添加Cell时需要指定其位置
- android:layout_column 整数n,在哪一列开始显示n=[0, 最大列-1]
- android:layout_columnSpan 整数k,指定元素横跨几列,需要注意保证n+k <= 最大列数
- android:layout_row 指定从哪一行开始显示,规则同列数
- android:layout_rowSpan 纵向跨几行,规则同列
行高和列宽的确定
每一行的高度是由这一行中Cell的最大高度决定的,以及每一列的宽度是由每一列中最大的宽度决定的,小于行高和列宽的元素可以设置其对齐方式和填充方式。
填充方式
通过Cell的android:layout_gravity
参数来指定,Cell的填充方式,注意仅当Cell元素本身的尺寸小于它所占格子的大小时才有效,比如元素本身尺寸小于行高和列宽,或者当它占多行,或者占多列时:
- center – 不改变元素的大小,仅居中
- center_horizontal – 不改变大小,水平居中
- center_vertical – 不改变大小,垂直居中
- top – 不改变大小,置于顶部
- left – 不改变大小,置于左边
- bottom – 不改变大小,置于底部
- right – 不改变大小,置于右边
- start – 不改变大小,置于开头(这个是与RTL从右向左读的文字有关的,如果使用start/end,那么当LTR文字时start=left,end=right,当RTL时start=right,end=left,也就是说系统会自动处理了)
- end – 不改变大小,置于结尾
- fill – 拉伸元素控件,填满其应该所占的格子
- fill_vertical – 仅垂直方向上拉伸填充
- fill_horizontal – 仅水平方向上拉伸填充
- clip_vertical – 垂直方向上裁剪元素,仅当元素大小超过格子的空间时
- clip_horizontal – 水平方向上裁剪元素,仅当元素大小超过格子的空间时
需要注意的是这些值是可以组合的,比如:
android:layout_gravity="center_vertical|clip_horizontal"
Cell之间的间距如何控制
默认间距
可以使用默认的间距android:useDefaultMargins=“true”
或者GridLayout#setUseDefaultMargins()
。这个属性默认值是“false”。
另外一种方式就是跟普通布局管理器一样,给每个Cell设置其margins
通常如果不满意系统的默认间距,就可以设置useDefaultMargins=“false”
,然后通过给Cell设置margin来控制间距。
居中方法
仅有一个Cell或者仅有一行,或者仅有一列时
当仅有一个子View时或者仅有一行或者一列的时候,可以把每个Cell设置其android:layout_gravitiy=“center”
(相应代码为LayoutParams#Gravity为CENTER),就可以让其在GridLayout中居中。
让一行居中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <GridLayout android:layout_width="wrap_content" android:layout_height="200dip" android:useDefaultMargins="true" android:background="@android:color/white" android:rowCount="1" android:columnCount="2"> <Button android:layout_column="0" android:layout_row="0" android:text="Left Button" android:layout_gravity="fill_horizontal|center_vertical"/> <Button android:layout_column="1" android:layout_row="0" android:text="Right Button" android:layout_gravity="fill_horizontal|center_vertical"/> </GridLayout>
|
让一个元素居中:
1 2 3 4 5 6 7 8 9 10 11 12
| <GridLayout android:layout_width="200dip" android:layout_height="200dip" android:useDefaultMargins="true" android:background="@android:color/white" android:rowCount="1" android:columnCount="1"> <Button android:layout_column="0" android:layout_row="0" android:text="Left Button" android:layout_gravity="center"/> </GridLayout>
|
其他情况
其他情况,设置子View的Gravity就不再起作用了,这时最好的办法就是让GridLayout的高度是WRAP_CONTENT,然后让GridLayout在其父布局中居中。
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
| <LinearLayout android:layout_width="match_parent" android:orientation="vertical" android:gravity="center" android:background="@android:color/darker_gray" android:layout_height="200dip"> <GridLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:useDefaultMargins="true" android:background="@android:color/white" android:rowCount="2" android:columnCount="2"> <Button android:layout_column="0" android:layout_row="0" android:text="Left Button" android:layout_gravity="fill_horizontal|center_vertical"/> <Button android:layout_column="1" android:layout_row="0" android:text="Right Button" android:layout_gravity="fill_horizontal|center_vertical"/> <Button android:layout_column="1" android:layout_row="1" android:text="Right Button 2" android:layout_gravity="fill_horizontal|center_vertical"/> </GridLayout> </LinearLayout>
|
适用场景
GridLayout虽然强大,可以当作LinearLayout使用,也可以当作RelativeLayout使用,甚至也能当FrameLayout使用。但是,我们不可以滥用,对于任意布局都一样,不能是它能实现需求就使用它,而是要根据实际的需求,选择最简单,最方便的,同时也要考虑性能。
通常对于类似于网格的布局就可以考虑用GridLayout来实现,或者用LinearLayout横七竖八的套了好几层时也要考虑使用GridLayout。
GridLayout vs GridView or RecyclerView
当要实现网格布局,或者非均匀风格布局时,可能首先想到的就是GridView,但是这也要看实际的情况而定。GridView,ListView以及RecyclerView是用于无限长度列表或者网格的场景,它们最大的特点是无限长度,因此这几个组件的重点在于如何复用Cell以提升性能,以及处理手势事件(Fling)等。所以,每当遇到列表或者网格的时候,先想一下这个长度大概会是多少,如果是在百个以内,且不会随时增长,这时就可以考虑使用静态(非动态复用)的组件比如LinearLayout或者GridLayout来实现。
实例
说的太多都是废话,来一个实例感觉一下子是最直接的:
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| <GridLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@android:color/white" android:alignmentMode="alignMargins" android:useDefaultMargins="true" android:columnCount="4" android:rowCount="5" android:visibility="visible"> <Button android:layout_column="0" android:layout_row="0" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="1"/> <Button android:layout_column="1" android:layout_row="0" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="2"/> <Button android:layout_column="2" android:layout_row="0" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="3"/> <Button android:layout_column="0" android:layout_row="1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="4"/> <Button android:layout_column="1" android:layout_row="1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="5"/> <Button android:layout_column="2" android:layout_row="1" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="6"/> <Button android:layout_column="0" android:layout_row="2" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="7"/> <Button android:layout_column="1" android:layout_row="2" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="8"/> <Button android:layout_column="2" android:layout_row="2" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="9"/> <Button android:layout_column="0" android:layout_row="3" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="0"/> <Button android:layout_column="1" android:layout_row="3" android:layout_gravity="fill_horizontal" android:layout_columnSpan="2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dip" android:text="Delete"/> <Button android:layout_column="0" android:layout_row="4" android:layout_columnSpan="2" android:layout_gravity="fill_horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:text="Clear"/> <Button android:layout_column="2" android:layout_row="4" android:layout_columnSpan="2" android:layout_gravity="fill_horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dip" android:text="="/> <Button android:layout_column="3" android:layout_row="0" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="fill" android:padding="10dip" android:text="+"/> <Button android:layout_column="3" android:layout_row="1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="fill" android:padding="10dip" android:text="-"/> <Button android:layout_column="3" android:layout_row="2" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="fill" android:padding="10dip" android:text="*"/> <Button android:layout_column="3" android:layout_row="3" android:layout_columnSpan="1" android:layout_gravity="fill" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dip" android:text="/"/> </GridLayout>
|
GridLayout和GridView的区别
GridView是一种适配器布局,它的继承关系是ViewGroup–>AdapterView–>AbsListView–>GridView,他是从一个adapter中取出内容填充到GridView中的每一个子View。
GridLayout是一个布局,它大大简化了对复杂布局的处理,提高了性能。他直接继承自ViewGroup,和LinearLayout这种是类似的。
我感觉GridView和GridLayout就和ListView和LinearLayout的区别类似。
那么什么时候用GridView什么时候用GridLayout呢?
类似九宫格这种可以采用GridView。但是像Android系统自带的计算器的界面就不可能通过GridView实现,因为有些按键不一样大,如果使用GridLayout实现那就很简单了。
另外GridLayout比RelativeLayout更好用,例如:
一个登陆界面举例:登陆界面应该有登陆界面的标题、用户名和密码的label和输入框及确认登陆按钮,如果用相对布局实现肯定是可以实现的,需要相对布局里面控件与控件间的位置关系就复杂繁琐了。Android框架在将你定义的相对布局xml文件渲染给屏幕的时候,需要处理这么复杂的控件间的关系,肯定性能会受一定程度的影响。当然,用相对布局会比用多个线性布局嵌套性能要好一些。
但是,用GridLayout的话,渲染性能会比用相对布局好很多。因为GridLayout中的控件间的位置关系没那么复杂,就是指定将某个控件放到某行某列,占几行或占几列,布局xml写起来也非常简洁。
参考资料
Android 布局之GridLayout
New Layout Widgets: Space and GridLayout
GridLayout-网格布局