异构网格布局

Posted

技术标签:

【中文标题】异构网格布局【英文标题】:Heterogeneous GridLayout 【发布时间】:2012-06-04 11:13:43 【问题描述】:

我正在尝试实现以下布局:

我猜GridLayout 适合我的需求,但经过 2 小时的努力,我什至无法创建类似的布局。布局错误地调整自身大小,它超过了手机的屏幕,并且它也不跨越指定的行和列。

在这里我选择了一个按钮,以便您可以看到它是如何超出边界的:

这里是相关的 xml 代码:https://gist.github.com/2834492

我已经使用嵌套的线性布局实现了类似的布局,但无法针对不同的屏幕尺寸正确调整大小


更新 - 近似 LinearLayout 实现:

XML 代码:https://gist.github.com/cdoger/2835887 但是,问题是它没有正确调整自己的大小,这里有一些屏幕配置不同的屏幕截图:


TLDR: 有人可以向我展示一个像第一张图片中那样使用 GridLayout 的异构布局实现吗?

【问题讨论】:

我不久前创建了一个quick 'n dirty example,它可能对您有所帮助。就像@Skies 已经指出的那样:我不知道有一种基于 xml 的方法来正确缩放“图块”,但话又说回来,我并没有真正花时间好好看看周围。从它的声音来看,你已经比我那时更久了。 我在最初搜索一些答案时看到了您的答案。但是,我仍然无法创建像第一张图片中那样的布局。我很确定如果 GridLayout 实现没有错误或某事,它应该通过 xml 完成。 如果我没记错的话,我在使用基于 xml 的方法时遇到的主要问题是 GridLayout 不提供类似“权重”的东西。在扩展方面,您可以告诉单元格填充剩余空间(使用重力属性),但您不能使用它来均匀分配空间。至少,在我花在这上面的很短的时间里,我无法找到一种方法来创建两个相同大小的列来填充屏幕宽度,而无需在运行时使用设备参数。我会密切关注这个——我很想看看其他人能想出什么。 为什么不在 GridLayout 中指定 rowCount 【参考方案1】:

您面临的问题是由于不当使用 GridLayout。 GridLayout 用于在网格中显示其子项,您试图在不扩展 GridLayout 的情况下覆盖它。虽然您想要的可以在代码中完成(利用 numcolumns 和 columnsize),但如果没有大量代码,它对于多种屏幕尺寸将没有用处。

唯一不需要大量黑客攻击的适当解决方案是明智地使用 LinearLayout 和 RelativeLayout。不应仅使用 LinearLayout,因为它用于将项目放在一行中(仅水平或垂直)。当您尝试执行底部的四个按钮时,这一点变得尤为明显。虽然上面的按钮可以通过 LinearLayout 轻松完成,但对于底部的四个按钮,RelativeLayout 是您所需要的。

注意: 对于那些没有使用它们的经验的人来说,RelativeLayout 可能有点棘手。一些陷阱包括:孩子重叠、孩子离开屏幕、高度和宽度渲染应用不当。如果您想要一个示例,请告诉我,我将编辑我的答案。

最后说明: 我完全赞成以独特的方式利用当前的框架对象,并且真的更喜欢提供所要求的解决方案。然而,鉴于问题的限制,该解决方案是不可行的。

(修订)解决方案 1

经过昨晚的深思熟虑,这可能是用一个纯线性布局来完成的。虽然我不喜欢这个解决方案,但它应该是多屏友好的,并且不需要我使用任何工具。应谨慎使用过多的 LinearLayout,因为根据 Google 的开发人员的说法,由于 layout_weight 属性,它可能会导致加载缓慢的 UI。当我回到家时,将提供第二个使用 RelativeLayout 的解决方案。 现已测试这可在所有屏幕尺寸和方向上提供所需的布局参数。

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_ 
    android:layout_ 
    android:orientation="vertical" > 
    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:orientation="vertical">
        <LinearLayout 
            android:layout_ 
            android:layout_ 
            android:layout_weight="1" 
            android:orientation="horizontal"> 
            <Button 
                android:id="@+id/Button01" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="1" 
                android:text="Button" />     
            <Button 
                android:id="@+id/Button02" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="1" 
                android:text="Button" />     
        </LinearLayout> 
        <Button 
            android:id="@+id/button3" 
            android:layout_ 
            android:layout_
            android:layout_weight="1" 
            android:text="Button" />   
        <LinearLayout 
            android:layout_ 
            android:layout_ 
            android:layout_weight="1.00"
            android:orientation="horizontal">  
            <Button 
                android:id="@+id/button1" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="1" 
                android:text="Button" />   
            <Button 
                android:id="@+id/button2" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="1" 
                android:text="Button" />     
        </LinearLayout>
    </LinearLayout>    
    <LinearLayout 
        android:layout_ 
        android:layout_ 
        android:layout_weight="1" 
        android:orientation="horizontal">     
        <LinearLayout 
            android:layout_ 
            android:layout_ 
            android:layout_weight="1" 
            android:orientation="vertical" >    
            <Button 
                android:id="@+id/button4" 
                android:layout_ 
                android:layout_
                android:layout_weight="1" 
                android:text="Button" />     
            <Button 
                android:id="@+id/button5" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="2" 
                android:text="Button" />     
        </LinearLayout>     
        <LinearLayout 
            android:layout_ 
            android:layout_ 
            android:layout_weight="1" 
            android:orientation="vertical" > 
            <Button 
                android:id="@+id/button6" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="2" 
                android:text="Button" /> 
            <Button 
                android:id="@+id/button7" 
                android:layout_ 
                android:layout_ 
                android:layout_weight="1" 
                android:text="Button" /> 
        </LinearLayout> 
    </LinearLayout> 
</LinearLayout> 

解决方案 1 说明

LinearLayouts 的关键是将您的命令定义为单独的布局并将其他布局嵌套在其中。当您将约束应用于更多维度时,必须添加更多 LinearLayout 来封装其他维度。对你来说,为了保持这个比例,再多两个父母是至关重要的。当您使用除整数值之外的任何值来利用 layout_weight 时,一个很好的指示何时应该添加另一个级别。它变得很难正确计算。从那里将其分成几列相对简单。

解决方案 2(失败)

虽然我能够使用 RelativeLayout 和“struts”实现理想的结果,但我只能使用高度为 2 个按钮的倍数的布局来实现。这样的技巧会很棒,因为布局级别大大降低,所以我将研究一个纯 XML 解决方案,并在我实现它时在此处发布答案。同时,上面的 LinearLayout 应该很适合你的需求。

【讨论】:

这是 GridLayout 顺便说一句不是 GridView 我猜这是一个错字。我按照您的建议管理了所需的布局,我将在有时间时放置代码。我是用纯代码而不是 xml 完成的。 你是对的。我不知道为什么我一直在输入 GridView。 :( -1。无论如何,我会在 MST 下午 2 点之前为您提供一份工作。这对您来说将是一个纯 XML 解决方案 只是为了确保我理解正确...前 5 个按钮必须占据屏幕的 1/2,而底部的四个按钮必须占据屏幕的 1/2? 没错,在我的解决方案中,我将屏幕划分为 6 个单位高度和 2 个单位宽度的部分,在运行时测量屏幕并相应地生成布局。【参考方案2】:

我阅读了这个帖子并意识到我想要一个比线性布局更扁平的解决方案。经过一番研究,我最终制作了自己的布局。它受到 GridLayout 的启发,但略有不同。

请注意,如果您要复制粘贴代码,则需要在某些地方更改包名称。

这个布局有 4 个布局参数,孩子们用来定位自己。这些是 layout_left、layout_top、layout_right、layout_bottom。 ICGridLayout 本身有两个属性:layout_spacing 和 columns

Columns 告诉布局您希望它包含多少列。然后它将计算高度与宽度相同的单元格的大小。这将是布局宽度/列。

间距是您希望每个孩子之间的间距。

layout_left|top|right|bottom 属性是每一边的坐标。布局不进行计算以避免碰撞或任何事情。它只是把孩子带到他们想去的地方。

如果你想要更小的方块,你只需要增加 columns 属性。

请记住,这是一个快速原型,我会继续努力,当我觉得它准备好了时,我会将它上传到 Github 并在此处发表评论。

我下面的所有代码都应该产生以下结果:

*****编辑***** 为孩子们添加了测量的调用,第一次忘记了。 结束编辑 ICGridLayout.java:

package com.risch.evertsson.iclib.layout;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.risch.evertsson.iclib.R;

/**
 * Created by johanrisch on 6/13/13.
 */
public class ICGridLayout extends ViewGroup 
    private int mColumns = 4;
    private float mSpacing;

    public ICGridLayout(Context context) 
        super(context);
    

    public ICGridLayout(Context context, AttributeSet attrs) 
        super(context, attrs);
        init(attrs);
    

    public ICGridLayout(Context context, AttributeSet attrs, int defStyle) 
        super(context, attrs, defStyle);
        init(attrs);
    

    private void init(AttributeSet attrs) 
        TypedArray a = getContext().obtainStyledAttributes(
                attrs,
                R.styleable.ICGridLayout_Layout);
        this.mColumns = a.getInt(R.styleable.ICGridLayout_Layout_columns, 3);
        this.mSpacing = a.getDimension(R.styleable.ICGridLayout_Layout_layout_spacing, 0);
        a.recycle();
    

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) 
        if (changed) 
            int width = (int) (r - l);
            int side = width / mColumns;
            int children = getChildCount();
            View child = null;
            for (int i = 0; i < children; i++) 
                child = getChildAt(i);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                int left = (int) (lp.left * side + mSpacing / 2);
                int right = (int) (lp.right * side - mSpacing / 2);
                int top = (int) (lp.top * side + mSpacing / 2);
                int bottom = (int) (lp.bottom * side - mSpacing / 2);
                child.layout(left, top, right, bottom);
            
        
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        measureVertical(widthMeasureSpec, heightMeasureSpec);

    

    private void measureVertical(int widthMeasureSpec, int heightMeasureSpec) 
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int width = 0;
        int height = 0;


        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.EXACTLY) 
            width = MeasureSpec.getSize(widthMeasureSpec);
         else 
            throw new RuntimeException("widthMeasureSpec must be AT_MOST or " +
                    "EXACTLY not UNSPECIFIED when orientation == VERTICAL");
        


        View child = null;
        int row = 0;
        int side = width / mColumns;
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) 
            child = getChildAt(i);

            LayoutParams lp = (LayoutParams) child.getLayoutParams();

            if (lp.bottom > row) 
                row = lp.bottom;
            



            int childHeight = (lp.bottom - lp.top)*side;
            int childWidth = (lp.right-lp.left)*side;
            int heightSpec = MeasureSpec.makeMeasureSpec(childHeight, LayoutParams.MATCH_PARENT);
            int widthSpec = MeasureSpec.makeMeasureSpec(childWidth, LayoutParams.MATCH_PARENT);

            child.measure(widthSpec, heightSpec);
        
        height = row * side;
        // TODO: Figure out a good way to use the heightMeasureSpec...

        setMeasuredDimension(width, height);
    

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) 
        return new ICGridLayout.LayoutParams(getContext(), attrs);
    

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) 
        return p instanceof ICGridLayout.LayoutParams;
    

    @Override
    protected ViewGroup.LayoutParams
            generateLayoutParams(ViewGroup.LayoutParams p) 
        return new ICGridLayout.LayoutParams(p);
    

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() 
        return new LayoutParams();
    

    public static class LayoutParams extends ViewGroup.MarginLayoutParams 
        int right = 1;
        int bottom = 1;
        int top = 0;
        int left = 0;
        int width = -1;
        int height = -1;

        public LayoutParams() 
            super(MATCH_PARENT, MATCH_PARENT);
            top = 0;
            left = 1;
        

        public LayoutParams(int width, int height) 
            super(width, height);
            top = 0;
            left = 1;
        

        public LayoutParams(Context context, AttributeSet attrs) 
            super(context, attrs);
            TypedArray a = context.obtainStyledAttributes(
                    attrs,
                    R.styleable.ICGridLayout_Layout);
            left = a.getInt(R.styleable.ICGridLayout_Layout_layout_left, 0);
            top = a.getInt(R.styleable.ICGridLayout_Layout_layout_top, 0);
            right = a.getInt(R.styleable.ICGridLayout_Layout_layout_right, left + 1);
            bottom = a.getInt(R.styleable.ICGridLayout_Layout_layout_bottom, top + 1);
            height = a.getInt(R.styleable.ICGridLayout_Layout_layout_row_span, -1);
            width = a.getInt(R.styleable.ICGridLayout_Layout_layout_col_span, -1);
            if (height != -1) 
                bottom = top + height;
            
            if (width != -1) 
                right = left + width;
            

            a.recycle();
        

        public LayoutParams(ViewGroup.LayoutParams params) 
            super(params);
        

    


ICGridLayout.java 非常简单。它采用孩子们提供的价值观并将其展示出来。 attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ICGridLayout_Layout">
        <attr name="columns" format="integer"/>
        <attr name="layout_left" format="integer"/>
        <attr name="layout_top" format="integer"/>
        <attr name="layout_right" format="integer"/>
        <attr name="layout_bottom" format="integer"/>
        <attr name="layout_col_span" format="integer"/>
        <attr name="layout_row_span" format="integer"/>
        <attr name="layout_spacing" format="dimension"/>
    </declare-styleable>

</resources>

example_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.rischit.projectlogger"
    android:id="@+id/scroller"
    android:layout_
    android:layout_ >

    <com.risch.evertsson.iclib.layout.ICGridLayout
        android:id="@+id/ICGridLayout1"
        android:layout_
        android:layout_
        app:layout_spacing="4dp"
        app:columns="4" >


        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="1"
            app:layout_left="0"
            app:layout_right="4"
            app:layout_top="0"
            android:background="#ff0000"
            android:text="TextView" />

        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="3"
            app:layout_left="3"
            app:layout_right="4"
            app:layout_top="1"
            android:background="#00ff00"
            android:text="TextView" />

        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="4"
            app:layout_left="0"
            app:layout_right="3"
            app:layout_top="1"
            android:background="#0000ff"
            android:text="TextView" />

        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="4"
            app:layout_left="3"
            app:layout_right="4"
            app:layout_top="3"
            android:background="#ffff00"
            android:text="TextView" />

        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="6"
            app:layout_left="0"
            app:layout_right="1"
            app:layout_top="4"
            android:background="#ff00ff"
            android:text="TextView" />

        <TextView
            android:id="@+id/textView1"
            android:layout_
            android:layout_
            app:layout_bottom="6"
            app:layout_left="1"
            app:layout_right="4"
            app:layout_top="4"
            android:background="#ffffff"
            android:text="TextView" />
    </com.risch.evertsson.iclib.layout.ICGridLayout>

</ScrollView>

-- 约翰·里施

附言 这是我的第一个长答案,我试图以正确的方式做到这一点。如果我失败了,请告诉我不要发火:) D.S

【讨论】:

【参考方案3】:

像这样?

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical" >
    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="0.54" >
        <Button
            android:id="@+id/Button01"
            android:layout_
            android:layout_
            android:layout_weight="1.00"
            android:text="Button" />    
        <Button
            android:id="@+id/Button02"
            android:layout_
            android:layout_
            android:layout_weight="1.00"
            android:text="Button" />    
    </LinearLayout>
    <Button
        android:id="@+id/button3"
        android:layout_
        android:layout_
        android:text="Button" />  
    <LinearLayout
        android:layout_
        android:layout_ > 
        <Button
            android:id="@+id/button1"
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="Button" />  
        <Button
            android:id="@+id/button2"
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="Button" />    
    </LinearLayout>   
    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="1" >    
        <LinearLayout
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:orientation="vertical" >   
            <Button
                android:id="@+id/button4"
                android:layout_
                android:layout_
                android:text="Button" />    
            <Button
                android:id="@+id/button5"
                android:layout_
                android:layout_
                android:text="Button" />    
        </LinearLayout>    
        <LinearLayout
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:orientation="vertical" >
            <Button
                android:id="@+id/button6"
                android:layout_
                android:layout_
                android:text="Button" />
            <Button
                android:id="@+id/button7"
                android:layout_
                android:layout_
                android:text="Button" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

【讨论】:

这看起来是个不错的方法,但它是否能很好地适应所有屏幕尺寸?例如我尝试使用 3.2" 配置和按钮损坏 2.7" 或 4.65" 配置也没有预期的效果。(屏幕截图:goo.gl/ka1hN)我认为 LinearLayout 不太适合这个,是吗? 我不知道如何在 XML 中执行此操作,因此您可以尝试以编程方式执行此操作。使用 WindowManager 获取屏幕大小,并以 screenSize 的百分比影响每个部分。 我正在尝试使用 rowSpan 和 columnSpan 选项的解决方案。 但这不是GridLayout。 OP 明确表达了他使用 GridLayout 的意图...【参考方案4】:

正如许多人所说,嵌套线性布局似乎是在这里获胜的唯一方法。一些解决方案没有以最灵活的方式使用布局参数。下面的代码试图做到这一点,并且以一种对纵横比变化具有鲁棒性的方式。详细信息在 cmets 中。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:orientation="vertical" >

    <!-- First row. -->

    <LinearLayout
        android:layout_
        android:layout_ >

        <!-- Equal weights cause two columns of equal width. -->

        <Button
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="A" />

        <Button
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="B" />
    </LinearLayout>

    <!-- Second row. -->

    <Button
        android:layout_
        android:layout_
        android:text="C" />

    <!-- Third row. -->

    <LinearLayout
        android:layout_
        android:layout_ >

        <!-- Equal weights cause two columns of equal width. -->

        <Button
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="D" />

        <Button
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:text="E" />
    </LinearLayout>

    <!-- Uneven fourth and fifth rows. -->

    <LinearLayout
        android:layout_
        android:layout_
        android:layout_weight="1"
        android:baselineAligned="false" >

        <!-- Left column. Equal weight with right column gives them equal width. -->

        <LinearLayout
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:orientation="vertical" >

            <!--
                 The use of weights below assigns all extra space to G. There
                 are other choices. LinearLayout computes sizes along its
                     axis as given, then divides the remaining extra space using
                 weights.  If a component doesn't have a weight, it keeps
                 the specified size exactly.
            -->


            <!-- Fill width of layout and use wrap height (because there's no weight). -->

            <Button
                android:layout_
                android:layout_
                android:text="F" />

            <!-- Fill width of layout and put all the extra space here. -->

            <Button
                android:layout_
                android:layout_
                android:layout_weight="1"
                android:text="G" />
        </LinearLayout>

        <!-- Right column. Equal weight with left column gives them equal width. -->

        <LinearLayout
            android:layout_
            android:layout_
            android:layout_weight="1"
            android:orientation="vertical" >

            <!-- Same as above except top button gets all the extra space. -->

            <Button
                android:layout_
                android:layout_
                android:layout_weight="1"
                android:text="H" />

            <Button
                android:layout_
                android:layout_
                android:text="I" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

【讨论】:

【参考方案5】:

所以这是我在一年后承诺的解决方案 =) 它基本上使用ViewTreeObserver 来获取父布局的尺寸并相应地创建自定义视图。由于这段代码是一年前的,ViewTreeObserver 可能不是动态获取维度的最佳方式。

您可以在此处找到完整的源代码: https://github.com/cdoger/Android_layout

我将屏幕分成 8 个等宽和 6 个等高。这是我如何布置视图的快照:

final RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.main_layout);
ViewTreeObserver vto = mainLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() 
    @Override
    public void onGlobalLayout() 
        final int oneUnitWidth = mainLayout.getMeasuredWidth() / 8;
        final int oneUnitHeight = mainLayout.getMeasuredHeight() / 6;
        /**
         * 1
         ***************************************************************/
        final RelativeLayout.LayoutParams otelParams = new RelativeLayout.LayoutParams(
                oneUnitWidth * 4, oneUnitHeight);
        otelParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        otelParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
        // otelParams.setMargins(0, 0, 2, 0);
        View1.setLayoutParams(otelParams);
        /***************************************************************/

        /**
         * 2
         ***************************************************************/
        final RelativeLayout.LayoutParams otherParams = new RelativeLayout.LayoutParams(
                oneUnitWidth * 4, oneUnitHeight);
        otherParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        otherParams.addRule(RelativeLayout.RIGHT_OF, View1.getId());
        otherParams.setMargins(2, 0, 0, 0);
        View2.setLayoutParams(otherParams);
        /***************************************************************/
//... goes on like this

这是最终的截图:

【讨论】:

我很好奇,即使在 2016 年我们有 compat 设计支持库时,您是否还需要与 ViewTreeObserver 一起完成整个舞蹈? @IgorGanapolsky 很久没有检查过了。如果您发现更有用和更容易的东西,我真的很想看到它 =) 是的。问题是一样的,你仍然必须使用ViewTreeObserver 来计算单位大小。【参考方案6】:

如下所示将您的 GridLayout 嵌入到 LinearLayout 中并尝试它对我有用。

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_
     android:layout_
     android:orientation="horizontal" >

<GridLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:columnCount="2" >

    <Button
        android:layout_column="0"
        android:layout_columnSpan="1"
        android:layout_gravity="start|end"
        android:layout_row="0"
        android:text="ASDFASDF" />

    <Button
        android:layout_column="1"
        android:layout_gravity="start|end"
        android:layout_row="0"
        android:text="SDAVDFBDFB" />

    <Button
        android:layout_column="0"
        android:layout_columnSpan="2"
        android:layout_gravity="fill|center"
        android:layout_row="1"
        android:text="ASDVADFBFDAFEW" />

    <Button
        android:layout_column="0"
        android:layout_columnSpan="1"
        android:layout_gravity="fill|center"
        android:layout_row="2"
        android:text="FWEA AWFWEA" />

    <Button
        android:layout_column="1"
        android:layout_columnSpan="1"
        android:layout_gravity="fill"
        android:layout_row="2"
        android:text="BERWEfasf" />

    <Button
        android:layout_
        android:layout_column="0"
        android:layout_columnSpan="1"
        android:layout_gravity="fill|center"
        android:layout_row="3"
        android:text="SDFVBFAEVSAD" />

    <Button
        android:layout_column="0"
        android:layout_columnSpan="1"
        android:layout_gravity="fill|center"
        android:layout_row="4"
        android:layout_rowSpan="2"
        android:text="GVBAERWEFSD" />

    <Button
        android:layout_column="1"
        android:layout_columnSpan="1"
        android:layout_gravity="fill|center"
        android:layout_row="3"
        android:layout_rowSpan="2"
        android:text="VSDFAVE SDFASDWA SDFASD" />

    <Button
        android:layout_column="1"
        android:layout_columnSpan="1"
        android:layout_gravity="fill|center"
        android:layout_row="5"
        android:text="FWEWEGAWEFWAE"/>
</GridLayout>

    </LinearLayout>

【讨论】:

好的,它似乎解决了边框问题,但仍然没有第一张图片中的布局。顺便说一句,我试过了,按钮仍然超出了边界。

以上是关于异构网格布局的主要内容,如果未能解决你的问题,请参考以下文章

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

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

【PyQt】网格布局 QGridLayout

CSS网格布局(Grid)教程

网格布局

supermap布局设定地图网格及布局网格