在我目前认知里,GridLayout有3种合并网格的方法:
- 在xml中写控件的时候通过属性合并;
- 动态加载GridLayout的情况,通过LayoutParams修改布局行为以及控件参数合并网格;
- 通过重写GridLayoutManager中的GetSpanSize方法实现;
1. XML中合并
GridLayout网格布局控制网格的属性主要有3类:
GridLayout属性 | 含义 |
---|---|
layout_rowCount | 网格布局最大行数 |
layout_columnCount | 网格布局最大列数 |
Item属性 | 含义 |
---|---|
layout_row | 显示在第几行 |
layout_column | 显示在第几列 |
layout_rowSpan | 纵向跨几行 |
layout_columnSpan | 横向跨几列 |
layout_rowWeight | 纵向剩余空间分配方式 |
layout_columnWeight | 横向剩余空间的分配方式 |
属性值 | 含义 |
---|---|
fill | 拉伸元素控件,填满所占格子 |
fill_veritical | 纵向拉伸填充 |
fill_horizontal | 横向拉伸填充 |
clip_veritical | 纵向裁剪超出格子的元素 |
clip_horizontal | 横向裁剪超出格子的元素 |
- 这里要注意,在使用layout_rowSpan和layout_columnSpan属性时要加上layout_gravity属性,否则没有效果。
xml合并网格方法很简单,下面贴一个计算器xml的代码自己复制尝试即可。
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:rowCount="7"
android:columnCount="4"
android:orientation="horizontal">
<TextView
android:layout_columnSpan="4"
android:layout_rowSpan="2"
android:layout_columnWeight="4"
android:background="@color/purple_200"
android:text="0"
android:textSize="40dp" />
<Button
android:layout_columnWeight="1"
android:text="AC"/>
<Button
android:layout_columnWeight="1"
android:text="+/-"/>
<Button
android:layout_columnWeight="1"
android:text="%"/>
<Button
android:layout_columnWeight="1"
android:text="←"/>
<Button
android:layout_columnWeight="1"
android:text="7"/>
<Button
android:layout_columnWeight="1"
android:text="8"/>
<Button
android:layout_columnWeight="1"
android:text="9"/>
<Button
android:layout_columnWeight="1"
android:text="÷"/>
<Button
android:layout_columnWeight="1"
android:text="4"/>
<Button
android:layout_columnWeight="1"
android:text="5"/>
<Button
android:layout_columnWeight="1"
android:text="6"/>
<Button
android:layout_columnWeight="1"
android:text="×"/>
<Button
android:layout_columnWeight="1"
android:text="1"/>
<Button
android:layout_columnWeight="1"
android:text="2"/>
<Button
android:layout_columnWeight="1"
android:text="3"/>
<Button
android:layout_columnWeight="1"
android:text="-"/>
<Button
android:layout_gravity="fill"
android:layout_columnSpan="2"
android:text="0"/>
<Button
android:layout_columnWeight="1"
android:text="."/>
<Button
android:layout_columnWeight="1"
android:layout_gravity="fill"
android:layout_rowSpan="2"
android:text="+"/>
<Button
android:layout_gravity="fill"
android:layout_columnSpan="3"
android:text="="/>
</GridLayout>
2. 通过LayoutParams合并网格
第2种主要是通过动态加载向网格中添加控件和网格参数来显示布局,网格参数就要用到LayoutParams了;
LayoutParams的作用主要有3:
- 控制View在布局中的位置;
- 控制View的大小;
- 控制View的布局行为;
我的理解LayoutParams就是为了方便在编码过程中修改控件属性的一个载体。
合并网格的话,GridLayout中有2个变量:
- rowSpec 为定义单元组的垂直特性的等级库;
- columnSpec 为定义单元组的水平特性的等级库;
/**
* The spec that defines the vertical characteristics of the cell group
*/
public Spec rowSpec = Spec.UNDEFINED;
/**
* The spec that defines the horizontal characteristics of the cell group
*/
public Spec columnSpec = Spec.UNDEFINED;
通过GridLayout中的静态方法spec来改变以上2个变量:
/**
* Equivalent to: {@code spec(start, 1, default_alignment, weight)} -
* where {@code default_alignment} is specified in
* {@link android.widget.GridLayout.LayoutParams}.
*
* @param start the start
* @param size the size
* @param weight the weight
*/
public static Spec spec(int start, int size, float weight) {
return spec(start, size, UNDEFINED_ALIGNMENT, weight);
}
在activity中的合并网格,首先要拿到GridLayout.LayoutParams这个载体,然后修改载体中的变量以达到修改GridLayout网格属性的目的;
val params = GridLayout.LayoutParams()
GridLayout.spec中会传3个参数:
第1个参数:网格起始位置;
第2个参数:网格占位;
第3个参数:网格权重;
以下代码合在一起的意思:网格位置(0,0),占1行4列,横向比重为3,纵向比重为1,在6行4列的表格中也就是第一行为一整行为一个网格,也就是计算器显示数值控件的位置。
params.rowSpec = GridLayout.spec(0, 1, 3f)
params.columnSpec = GridLayout.spec(0, 4, 1f)
动态加载计算器布局的完整代码如下:
某些网格需要特殊布局的情况,通过条件判断筛选出来要修改参数的网格,通过控件和布局载体params修改即可。
class Test : AppCompatActivity() {
private val mStrings = arrayOf("0", "AC", "退格", "/", "*", "7", "8", "9", "—", "4", "5", "6", "+", "1", "2", "3", "=", "%", "0", ".")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dynamic_loading)
val gridLayout = findViewById<GridLayout>(R.id.grid_layout)
gridLayout.rowCount = 6
gridLayout.columnCount = 4
for (i in mStrings.indices) {
val textView = TextView(this)
val params = GridLayout.LayoutParams()
params.width = 0
params.height = 0
if (i == 0) {
params.rowSpec = GridLayout.spec(0, 1, 3f)
params.columnSpec = GridLayout.spec(0, 4, 1f)
textView.gravity = Gravity.BOTTOM or Gravity.RIGHT
} else {
params.rowSpec = GridLayout.spec((i + 3) / 4, 1f)
params.columnSpec = GridLayout.spec((i + 3) % 4, 1f)
textView.setBackgroundColor(Color.WHITE)
if ("AC" == mStrings[i]) {
textView.setTextColor(Color.parseColor("#f68904"))
}
if ("=" == mStrings[i]) {
textView.setBackgroundColor(Color.parseColor("#f68904"))
textView.setTextColor(Color.WHITE)
params.rowSpec = GridLayout.spec((i + 3) / 4, 2, 1f)
}
textView.gravity = Gravity.CENTER
params.setMargins(2, 2, 2, 2)
}
textView.text = mStrings[i]
gridLayout.addView(textView, params)
}
}
}
计算器效果图如下:
3. 通过重写GetSpanSize合并网格
第3种方法使用的是RecyclerView + GridLayout,网格布局是在**《RecyclerView实现多种Item布局》的Activity的基础上进行修改的,有延用到重写的getItemViewType**方法,Activity的完整代码可以在上一篇找一下。
- GetSpanSize方法的源码是这样解释的:返回item在position所占用的跨度数;
/**
* Returns the number of span occupied by the item at <code>position</code>.
*
* @param position The adapter position of the item
* @return The number of spans occupied by the item at the provided position
*/
public abstract int getSpanSize(int position);
这个跨度是指按照排列方向的跨度,比如所有网格按照横向排列,那么重写getSpanSize方法会合并列;如果网格按照纵向排列,则会合并行。
重写getSpanSize(),通过调用adapter中的getItemViewType()获取相应position的viewType,然后判断:如果该位置是需要显示其他Item类型的position,就返回想要合并的跨度,这里3是指合并3个网格,也就是整行合并为一个网格,用来显示多Item布局,比如header(如果合并的网格和替换其他Item布局的网格不同,只需要多设置几个type常量判断即可);反之该position的view是正常单元格,return 1,占用一个单元格。
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
init()
val view = inflater.inflate(R.layout.addressbook_fragment, container, false)
var contactRecyclerView : RecyclerView = view.findViewById(R.id.addressbook_list)
val gridLayoutManager = GridLayoutManager(context,3, GridLayoutManager.HORIZONTAL, false)
val contactAdapter = AddressBookAdapter(this.requireContext())
// 重写setSpanSizeLookup中的getSpanSize方法
gridLayoutManager.setSpanSizeLookup(object : GridLayoutManager.SpanSizeLookup(){
override fun getSpanSize(position: Int): Int {
var type = contactRecyclerView.adapter?.getItemViewType(position)
if (type == contactAdapter.SEC_ITEM){
return 3
}else{
return 1
}
}
})
contactRecyclerView.layoutManager = gridLayoutManager
contactAdapter.setContactList(contactList)
contactRecyclerView.adapter = contactAdapter
return view
}
上面代码网格默认为为横向排列,合并效果为相邻两列的网格合并,如果想合并相邻两行的网格的话需要将网格的排列方向改为纵向排列:
val gridLayoutManager = GridLayoutManager(context,8, GridLayoutManager.HORIZONTAL, false)
这里的GridLayoutManager.HORIZONTAL文档中定义为RecyclerView.HORIZONTAL,我的理解是RecyclerView通过横向滑动来查看,纵向一列一列排满一页之后才能滑动,所以设置参数GridLayoutManager.HORIZONTAL可以将网格排列方式改为纵向排列。
-
合并后的效果图如下:
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)
PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题