Heterogeneous GridLayout
Asked Answered
H

6

36

I am trying to implement the layout below:

Target Layout

I guess GridLayout is suitable for my needs but after 2 hours of struggle I couldn't create even a similar layout.. The layout is resizing itself wrongly, it exceeds the screen of the phone and it also does not span the specified rows and columns.

Here I selected a button so you can see how it exceeds the boundaries:

Fail

and here is the associated xml code: https://gist.github.com/2834492

I have reached a similar layout with nested linearlayouts but it's not possible to properly resize it for different screen sizes.


UPDATE - approximate LinearLayout implementation:

The XML code: https://gist.github.com/cdoger/2835887 However, the problem is it does not resize itself properly here some screenshots with different screen configurations:

enter image description here

enter image description here

enter image description here


TLDR: Can someone show me a heterogeneous layout implementation with GridLayout like in the first picture?

Hazlett answered 30/5, 2012 at 8:22 Comment(4)
I created a quick 'n dirty example a little while ago that might be of help to you. Like @Skies already pointed out though: I'm not aware of an xml-based approach to correctly scale the 'tiles', but then again, I didn't really spend time taking a good look around. From the sounds of it, you've already been at it way longer than I had back then.Randolphrandom
I have seen your answer while searching for some answers at first. However, I still cannot create a layout like in the first picture. I am pretty sure if there's no bug or sth with the GridLayout implementation it should be done via xml.Hazlett
If I remember correctly, the main problem I ran into with an xml-based approach is that GridLayout does not offer something like 'weights'. To an extend you can tell cells to fill up remaining space (with the gravity attribute), but you cannot use it to evenly distribute space. At least, in the short amount of time I spent on this, I wasn't able to find a way to create two equally-sized columns that fill the screen width, without using the device parameters at runtime. I'll keep an eye on this one - I'll be interested to see what others can come up with.Randolphrandom
Why didn't you specify a rowCount in your GridLayout?Ricer
H
5

So here is the solution I promised after one year =) It basically uses the ViewTreeObserver to get the dimensions of the parent layout and create custom views accordingly. Since this code is one year old ViewTreeObserver might not be the best way to get the dimensions dynamically.

You can find the full source code here: https://github.com/cdoger/Android_layout

I divided the screen into 8 equal widths and 6 equal heights. Here is a snapshot of how I laid out the views:

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

Here is the final screenshot:

enter image description here

Hazlett answered 20/1, 2014 at 12:13 Comment(3)
I am curious, would you still need to go through this whole dance with ViewTreeObserver, even in 2016 when we have the compat Design Support Library?Ricer
@Ricer haven't checked it since a long time. If you find something more useful and easier I would really like to see it =)Hazlett
Yes. The issue is same, and u still have to use ViewTreeObserver to calculate unit size.Hie
P
19

The issue you are facing is due to inappropriate use of the GridLayout. The GridLayout is made to show its children in a grid and you are trying to override that without extending the GridLayout. While what you want may be accomplished in code (utilizing numcolumns and columnsize), it will not be useful for multiple screen sizes without a heck of a lot of code.

The only adequate solution that won't require a ton of hacking is judicious use of both LinearLayout and RelativeLayout. LinearLayout should not be used exclusively as it is made to drop items in a line (horizontally or vertically only). This becomes especially apparent when you try and do the bottom four buttons. While the buttons above may be done with LinearLayout with very little effort, RelativeLayout is what you need for the bottom four buttons.

Note: RelativeLayout can be a little bit tricksy for those with little experience using them. Some pitfalls include: children overlapping, children moving off the screen, height and width rendering improperly applied. If you would like an example, let me know and I will edit my answer.

Final Note: I'm all for utilizing the current framework objects in unique ways, and genuinely prefer to provide the requested solution. The solution, however, is not viable given the constraints of the question.

(Revision) Solution 1

After some careful thought last night, this may be accomplished with a pure LinearLayout. While I do not like this solution, it should be multi-screen friendly and requires no tooling around from me. Caution should be used with too many LinearLayouts, as according to Google's developers, it can result in slow loading UIs due to the layout_weight property. A second solution utilizing RelativeLayout will be provided when I return home. Now Tested This provides the desired layout parameters on all screen-sizes and orientations.

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

Solution 1 Explanation

The key to LinearLayouts is to define your imperatives as separate Layouts and nest the others in them. As you apply constraints to more dimensions, more LinearLayouts must be added to encapsulate the others. For yours, it was crucial to have two more parents in order to maintain the proportion. A great indicator of when you should add another level is when you have to utilize layout_weight using anything other than an integer value. It simply becomes to hard to calculate properly. From there it was relatively simple to break it into columns.

Solution 2 (Failed)

While I was able to achieve desirable results utilizing RelativeLayout and "struts", I could only do so with layouts that were multiples of 2 buttons in height. Such a trick would be awesome as the levels of layout are greatly reduced, so I will work on a pure XML solution and post the answer here if and when I achieve it. In the meantime, the LinearLayout above should suit your needs perfectly.

Parol answered 7/6, 2012 at 8:52 Comment(4)
It's GridLayout btw not GridView I guess it's a typo. I managed the desired layout as you suggested I will put the code when I got the time. I have done it with pure code not xml.Hazlett
You're right. I don't know why I kept typing GridView. :( -1. Anyhow, I'll provide a work-up for you by 2pm MST. It will be a pure XML solution for youParol
Just to make sure I get it correct... The top 5 buttons must take 1/2 the screen and the bottom four, the other 1/2?Parol
That's correct, in my solution I have divided the screen to 6 unit height and 2 unit width pieces, measure the screen on run-time and generate the layouts accordingly.Hazlett
M
16

I read this thread and realised that I wanted a flatter solution than those with linear layout. After some research I ended up making my own layout. It is inspired by a GridLayout but differs a bit.

Please note that if you are going to copy-paste the code you'll need to change package names in some places.

This layout has 4 layout parameters that children use to position themselves.These are layout_left, layout_top, layout_right, layout_bottom. The ICGridLayout itself has two attributes: layout_spacing and columns.

Columns tells the layout how many columns you want it to contain. It will then calculate the size of a cell with the same height as width. Which will be the layouts width/columns.

The spacing is the amount of space you want between each child.

The layout_left|top|right|bottom attributes are the coordinates for each side. The layout does no calculations in order to avoid collision or anything. It just puts the children where they want to be.

If you'd like to have smaller squares you just have to increase the columns attribute.

Keep in mind that this is a quick prototype, I will continue working on it and when I feel that it's ready I'll upload it to Github and put a comment here.

All of my code below should produce the following result: Layout when using my provided code

*****EDIT***** Added the call to measure for the children, forgot that the first time around. END EDIT 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 is pretty straight forward. It takes the values provided by the children and lays them out. 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_width="match_parent"
    android:layout_height="match_parent" >

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


        <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            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_width="match_parent"
            android:layout_height="match_parent"
            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_width="match_parent"
            android:layout_height="match_parent"
            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_width="match_parent"
            android:layout_height="match_parent"
            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_width="match_parent"
            android:layout_height="match_parent"
            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_width="match_parent"
            android:layout_height="match_parent"
            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>

-- Johan Risch

P.S This is my first long answer, I've tried to do it in a correct way. If I've failed please tell me without flaming :) D.S

Mertz answered 14/6, 2013 at 1:9 Comment(0)
K
10

Like this ?

enter image description here

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

</LinearLayout>
Kingfisher answered 30/5, 2012 at 8:35 Comment(4)
It looks like a good approach but does it resize itself well for all screen sizes? e.g. I tried it with a 3.2" configuration and button get corrupted 2.7" or 4.65" configurations also does not have the desired affect.(Screenshot: goo.gl/ka1hN) I think LinearLayout is not really suitable for this, is it?Hazlett
I don't know how to do it in XML so you can try to do it programmatically. Use the WindowManager to get the screen size and affect each part with a percentage of screenSize.Kingfisher
I'm trying a solution with rowSpan and columnSpan option.Kingfisher
But this isn't a GridLayout. The OP specifically conveyed his intention to use GridLayout...Ricer
M
8

As many have said, nested linear layouts seem the only way to win here. Some of the solutions have not used the layout parameters in the most flexible manner. Code below seeks to do that, and in a way that's robust with aspect ratio changes. Details are in the comments.

enter image description here Landscape

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

    <!-- First row. -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

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

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="A" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="B" />
    </LinearLayout>

    <!-- Second row. -->

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="C" />

    <!-- Third row. -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

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

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="D" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="E" />
    </LinearLayout>

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:baselineAligned="false" >

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

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            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_width="match_parent"
                android:layout_height="wrap_content"
                android:text="F" />

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

            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="G" />
        </LinearLayout>

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

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical" >

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

            <Button
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:text="H" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="I" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>
Morea answered 8/6, 2012 at 3:59 Comment(0)
H
5

So here is the solution I promised after one year =) It basically uses the ViewTreeObserver to get the dimensions of the parent layout and create custom views accordingly. Since this code is one year old ViewTreeObserver might not be the best way to get the dimensions dynamically.

You can find the full source code here: https://github.com/cdoger/Android_layout

I divided the screen into 8 equal widths and 6 equal heights. Here is a snapshot of how I laid out the views:

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

Here is the final screenshot:

enter image description here

Hazlett answered 20/1, 2014 at 12:13 Comment(3)
I am curious, would you still need to go through this whole dance with ViewTreeObserver, even in 2016 when we have the compat Design Support Library?Ricer
@Ricer haven't checked it since a long time. If you find something more useful and easier I would really like to see it =)Hazlett
Yes. The issue is same, and u still have to use ViewTreeObserver to calculate unit size.Hie
P
3

Embed your GridLayout in LinearLayout as below and try it worked for me.

enter image description here

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

<GridLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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_width="94dp"
        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>
Phago answered 2/6, 2012 at 11:1 Comment(1)
Ok it seems to solve the borders issue but still it does not have the layout in the first picture. BTW, I tried it and buttons still exceed the borders.Hazlett

© 2022 - 2024 — McMap. All rights reserved.