尝试以编程方式均匀分布网格布局

Posted

技术标签:

【中文标题】尝试以编程方式均匀分布网格布局【英文标题】:Trying programmaticly to space a grid layout evenly 【发布时间】:2022-01-08 03:38:45 【问题描述】:

我有一个GridLayout,它应该显示均匀分布的 25 个按钮。为了能够设置onClickListener 而无需调用每个人,我想以编程方式执行此操作。 我用网格本身制作了一个布局资源文件来绑定它并能够膨胀它

activity.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"
    xmlns:grid="http://schemas.android.com/apk/res-auto"
    android:id="@+id/bingo_grid"
    android:layout_
    android:layout_
    android:layout_centerHorizontal="true"
    android:columnCount="5"
    android:rowCount="5"
    tools:context=".BingoActivity" />

现在我正在创建字段:

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        val bingoField = (1).rangeTo(25).toSet().toIntArray()
        binding = BingoActivityBinding.inflate(layoutInflater)

        setContentView(binding.root)
        binding.bingoGrid.alignmentMode = GridLayout.ALIGN_BOUNDS

        val bingoFieldGrid = binding.bingoGrid
        bingoFieldGrid.alignmentMode = GridLayout.ALIGN_BOUNDS
        bingoField.forEach 
            val button = createButton(it.toString())
            val gridLayoutParams = GridLayout.LayoutParams().apply 
                rowSpec = spec(GridLayout.UNDEFINED, GridLayout.CENTER, 1f)
                columnSpec = spec(GridLayout.UNDEFINED, GridLayout.CENTER, 1f)
                height = GridLayout.LayoutParams.WRAP_CONTENT
                width = GridLayout.LayoutParams.WRAP_CONTENT
            

            bingoFieldGrid.addView(button, gridLayoutParams)
        
    @RequiresApi(Build.VERSION_CODES.M)
    private fun createButton(buttonText: String): Button 
        var isCompleted = false
        return Button(baseContext).apply 
            setBackgroundColor(getColor(R.color.red))
            gravity = Gravity.CENTER
            text = buttonText
            setOnClickListener 
                isCompleted = if (!isCompleted) 
                    setBackgroundColor(getColor(R.color.green))
                    true
                 else 
                    setBackgroundColor(getColor(R.color.red))
                    false
                
            
        
    

所以,这些字段是自动生成的,没有问题,但是间距不对:

我对旧布局很陌生,有没有办法轻松实现?

【问题讨论】:

【参考方案1】:

您正在创建两种不同类型的 LayoutParams,这是没有意义的。根本不应该涉及 LinearLayout。

它们的工作方式是每个孩子都应该获得一组与其父 ViewGroup 使用的 LayoutParams 类型相匹配的 LayoutParams。所以在这种情况下,父级是 GridLayout,所以每个子级都应该使用 GridLayout.LayoutParams 的实例来添加。

GridLayout.LayoutParams 的工作方式是定义一个行规范和一个列规范来描述孩子应该如何占据单元格。我们希望他们采用单个下一个单元格,因此我们可以将第一个参数保留为 UNDEFINED。我们需要给它们一个相等的weight 大于 0,以便它们都均匀地共享剩余空间。我使用1f 来表示重量。

我对按钮使用大小为 0 的 FILL,以便它们填充它们的单元格。边距使它们之间产生了一些差距。

我将高度和宽度设置为 0 以防止它们过大。如果行或列变得太大而无法适应屏幕,布局就会变得太大。

您可能想要使用 MaterialButton 而不是普通的 Button,这样您就可以轻松地为背景色着色,而无需简单地将其设为静态纯色矩形。

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)

    binding = BingoBinding.inflate(layoutInflater)
    setContentView(binding.root)

    binding.bingoGrid.alignmentMode = GridLayout.ALIGN_BOUNDS

    for (num in 1..25) 
        val button = MaterialButton(this).apply 
            setBackgroundColor(resources.getColor(R.color.blue_500))
            gravity = Gravity.CENTER
            text = num.toString()
            setPadding(0)
        
        val params = GridLayout.LayoutParams().apply 
            rowSpec = spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f)
            columnSpec = spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f)
            width = 0
            height = 0
            setMargins((4 * resources.displayMetrics.density).toInt())
        
        binding.bingoGrid.addView(button, params)
    


AndroidStudio 对导入 spec 函数感到很厌烦。我必须在顶部手动添加:

import android.widget.GridLayout.Spec.*

【讨论】:

我尝试了您的解决方案,但在横向模式下它变得更糟。顺便提一句。它锁定在横向模式。 :) 我认为这与 rowSpec 和 columnSpec 有关,因为我看到了一些解决方案。但我不知道如何以编程方式设置它。您的解决方案也不知何故没有效果。就像没有它一样(我在上面编辑了我的主要代码,所以你可以看看有什么变化) 可能浏览量太高了。 48dp 可能太大,无法在小型手机上横向放置五行。一旦行的总高度超过屏幕可以容纳的高度,当您使用权重时,布局就会变得混乱。尝试使用我的新代码。 0 高度和宽度并填充单元格,以便它们始终适合。 如果我 1:1 复制您的代码,它只是空的,哈哈。 WTF 起初我认为 MaterialButton 有一些自己的约束规则。所以我修复了我可以使用的样式,但没有。它仍然是空的。您的 XML 文件看起来如何? 我将您的确切 xml 用于 GridLayout。它适用于我的 Button 或 MaterialButton。 啊,我忘了把android:layout_centerHorizontal="true"放回去!现在成功了,非常感谢!【参考方案2】:

你可以考虑谷歌 ConstraintLayout Flows:

要设置元素的数量,请使用app:flow_maxElementsWrap="5"

布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    android:layout_
    android:layout_>

    <androidx.constraintlayout.helper.widget.Flow
        android:id="@+id/flow"
        android:layout_
        android:layout_
        app:flow_horizontalGap="8dp"
        app:flow_maxElementsWrap="5"
        app:flow_verticalGap="8dp"
        app:flow_verticalStyle="packed"
        app:flow_wrapMode="chain" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后以编程方式将按钮添加到ConstraintLayout

val root = findViewById<ViewGroup>(R.id.root)
val size = 25
val array = IntArray(size)
for (i in 0 until size) 
    array[i] = i + 1
    val button = Button(this).apply 
        layoutParams = ViewGroup.LayoutParams(0, 0)
        id = i + 1
        text = (i + 1).toString()
    
    root.addView(button)

val flow = findViewById<Flow>(R.id.flow)
flow.referencedIds = array

提示:您可以使用WRAP_CONTENT 作为按钮高度以避免拉长按钮高度。

【讨论】:

以上是关于尝试以编程方式均匀分布网格布局的主要内容,如果未能解决你的问题,请参考以下文章

如何仅以编程方式在android中添加动态网格布局?

以编程方式创建网格视图

打开MASA Blazor的正确姿势4.3:Grid网格布局

Java AWT 图形界面编程LayoutManager 布局管理器 ④ ( GridLayout 网格布局 | GridBagLayout 网格包布局 )

Java AWT 图形界面编程LayoutManager 布局管理器 ④ ( GridLayout 网格布局 | GridBagLayout 网格包布局 )

Java图形化界面设计——布局管理器之GridLayout(网格布局)