Search Unity

[Solved]How to make Grid Layout group Cell Size X Auto expand

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by Collin_Patrick, Dec 29, 2016.

  1. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    I am trying to make a list of UI elements. I want there to always be 2 columns and a potentially infinite number of rows. I want each cell to expand their x size constraint to completely fill the allocated space without any empty space on the sides. How can I achieve this?

    This is what I want it to look like:
    What I want.PNG
    This is what I get upon changing resolutions:
    What I get.PNG
     
    ToonLeaderBacon and ina like this.
  2. Collin_Patrick

    Collin_Patrick

    Joined:
    Sep 24, 2016
    Posts:
    83
    I found a small hack that let me do what I want. I took the source code of the grid layout group and changed a few lines to fit my needs.

    Found answer here:
    http://answers.unity3d.com/questions/989697/grid-layout-group-scalable-content.html

    Full script, just create a new script copy/paste the code and make sure it is named properly and id should work
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. namespace UnityEngine.UI
    5. {
    6.     [AddComponentMenu("Layout/Auto Expand Grid Layout Group", 152)]
    7.     public class AutoExpandGridLayoutGroup : LayoutGroup
    8.     {
    9.         public enum Corner { UpperLeft = 0, UpperRight = 1, LowerLeft = 2, LowerRight = 3 }
    10.         public enum Axis { Horizontal = 0, Vertical = 1 }
    11.         public enum Constraint { Flexible = 0, FixedColumnCount = 1, FixedRowCount = 2 }
    12.  
    13.         [SerializeField]
    14.         protected Corner m_StartCorner = Corner.UpperLeft;
    15.         public Corner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value); } }
    16.  
    17.         [SerializeField]
    18.         protected Axis m_StartAxis = Axis.Horizontal;
    19.         public Axis startAxis { get { return m_StartAxis; } set { SetProperty(ref m_StartAxis, value); } }
    20.  
    21.         [SerializeField]
    22.         protected Vector2 m_CellSize = new Vector2(100, 100);
    23.         public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value); } }
    24.  
    25.         [SerializeField]
    26.         protected Vector2 m_Spacing = Vector2.zero;
    27.         public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } }
    28.  
    29.         [SerializeField]
    30.         protected Constraint m_Constraint = Constraint.Flexible;
    31.         public Constraint constraint { get { return m_Constraint; } set { SetProperty(ref m_Constraint, value); } }
    32.  
    33.         [SerializeField]
    34.         protected int m_ConstraintCount = 2;
    35.         public int constraintCount { get { return m_ConstraintCount; } set { SetProperty(ref m_ConstraintCount, Mathf.Max(1, value)); } }
    36.  
    37.         protected AutoExpandGridLayoutGroup()
    38.         { }
    39.  
    40. #if UNITY_EDITOR
    41.         protected override void OnValidate()
    42.         {
    43.             base.OnValidate();
    44.             constraintCount = constraintCount;
    45.         }
    46. #endif
    47.  
    48.         public override void CalculateLayoutInputHorizontal()
    49.         {
    50.             base.CalculateLayoutInputHorizontal();
    51.  
    52.             int minColumns = 0;
    53.             int preferredColumns = 0;
    54.             if (m_Constraint == Constraint.FixedColumnCount)
    55.             {
    56.                 minColumns = preferredColumns = m_ConstraintCount;
    57.             }
    58.             else if (m_Constraint == Constraint.FixedRowCount)
    59.             {
    60.                 minColumns = preferredColumns = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f);
    61.             }
    62.             else
    63.             {
    64.                 minColumns = 1;
    65.                 preferredColumns = Mathf.CeilToInt(Mathf.Sqrt(rectChildren.Count));
    66.             }
    67.  
    68.             SetLayoutInputForAxis(
    69.                 padding.horizontal + (cellSize.x + spacing.x) * minColumns - spacing.x,
    70.                 padding.horizontal + (cellSize.x + spacing.x) * preferredColumns - spacing.x,
    71.                 -1, 0);
    72.         }
    73.  
    74.         public override void CalculateLayoutInputVertical()
    75.         {
    76.             int minRows = 0;
    77.             if (m_Constraint == Constraint.FixedColumnCount)
    78.             {
    79.                 minRows = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f);
    80.             }
    81.             else if (m_Constraint == Constraint.FixedRowCount)
    82.             {
    83.                 minRows = m_ConstraintCount;
    84.             }
    85.             else
    86.             {
    87.                 float width = rectTransform.rect.size.x;
    88.                 int cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x)));
    89.                 minRows = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX);
    90.             }
    91.  
    92.             float minSpace = padding.vertical + (cellSize.y + spacing.y) * minRows - spacing.y;
    93.             SetLayoutInputForAxis(minSpace, minSpace, -1, 1);
    94.         }
    95.  
    96.         public override void SetLayoutHorizontal()
    97.         {
    98.             SetCellsAlongAxis(0);
    99.         }
    100.  
    101.         public override void SetLayoutVertical()
    102.         {
    103.             SetCellsAlongAxis(1);
    104.         }
    105.  
    106.         private void SetCellsAlongAxis(int axis)
    107.         {
    108.             // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis
    109.             // and only vertical values when invoked for the vertical axis.
    110.             // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis.
    111.             // Since we only set the horizontal position and not the size, it shouldn't affect children's layout,
    112.             // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout.
    113.  
    114.             if (axis == 0)
    115.             {
    116.                 // Only set the sizes when invoked for horizontal axis, not the positions.
    117.                 for (int i = 0; i < rectChildren.Count; i++)
    118.                 {
    119.                     RectTransform rect = rectChildren[i];
    120.  
    121.                     m_Tracker.Add(this, rect,
    122.                         DrivenTransformProperties.Anchors |
    123.                         DrivenTransformProperties.AnchoredPosition |
    124.                         DrivenTransformProperties.SizeDelta);
    125.  
    126.                     rect.anchorMin = Vector2.up;
    127.                     rect.anchorMax = Vector2.up;
    128.                     rect.sizeDelta = cellSize;
    129.                 }
    130.                 return;
    131.             }
    132.  
    133.             float width = rectTransform.rect.size.x;
    134.             float height = rectTransform.rect.size.y;
    135.  
    136.             int cellCountX = 1;
    137.             int cellCountY = 1;
    138.             if (m_Constraint == Constraint.FixedColumnCount)
    139.             {
    140.                 cellCountX = m_ConstraintCount;
    141.                 cellCountY = Mathf.CeilToInt(rectChildren.Count / (float)cellCountX - 0.001f);
    142.             }
    143.             else if (m_Constraint == Constraint.FixedRowCount)
    144.             {
    145.                 cellCountY = m_ConstraintCount;
    146.                 cellCountX = Mathf.CeilToInt(rectChildren.Count / (float)cellCountY - 0.001f);
    147.             }
    148.             else
    149.             {
    150.                 if (cellSize.x + spacing.x <= 0)
    151.                     cellCountX = int.MaxValue;
    152.                 else
    153.                     cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x)));
    154.  
    155.                 if (cellSize.y + spacing.y <= 0)
    156.                     cellCountY = int.MaxValue;
    157.                 else
    158.                     cellCountY = Mathf.Max(1, Mathf.FloorToInt((height - padding.vertical + spacing.y + 0.001f) / (cellSize.y + spacing.y)));
    159.             }
    160.  
    161.             int cornerX = (int)startCorner % 2;
    162.             int cornerY = (int)startCorner / 2;
    163.  
    164.             int cellsPerMainAxis, actualCellCountX, actualCellCountY;
    165.             if (startAxis == Axis.Horizontal)
    166.             {
    167.                 cellsPerMainAxis = cellCountX;
    168.                 actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildren.Count);
    169.                 actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis));
    170.             }
    171.             else
    172.             {
    173.                 cellsPerMainAxis = cellCountY;
    174.                 actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildren.Count);
    175.                 actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildren.Count / (float)cellsPerMainAxis));
    176.             }
    177.  
    178.             Vector2 requiredSpace = new Vector2(
    179.                     actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x,
    180.                     actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y
    181.                     );
    182.             Vector2 startOffset = new Vector2(
    183.                     GetStartOffset(0, requiredSpace.x),
    184.                     GetStartOffset(1, requiredSpace.y)
    185.                     );
    186.  
    187.             for (int i = 0; i < rectChildren.Count; i++)
    188.             {
    189.                 int positionX;
    190.                 int positionY;
    191.                 if (startAxis == Axis.Horizontal)
    192.                 {
    193.                     positionX = i % cellsPerMainAxis;
    194.                     positionY = i / cellsPerMainAxis;
    195.                 }
    196.                 else
    197.                 {
    198.                     positionX = i / cellsPerMainAxis;
    199.                     positionY = i % cellsPerMainAxis;
    200.                 }
    201.  
    202.                 if (cornerX == 1)
    203.                     positionX = actualCellCountX - 1 - positionX;
    204.                 if (cornerY == 1)
    205.                     positionY = actualCellCountY - 1 - positionY;
    206.  
    207.                 float realsize = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX);
    208.                 SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (realsize + spacing[0]) * positionX, realsize);
    209.                 SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
    210.             }
    211.         }
    212.     }
    213. }
     
  3. u-i

    u-i

    Joined:
    Nov 11, 2014
    Posts:
    5
    GREAT works just as i wanted! thx you
     
  4. Basic50

    Basic50

    Joined:
    Dec 30, 2013
    Posts:
    5
    Thank you so much! cellSize in read only... :( Great job Unity...
     
    Menion-Leah likes this.
  5. tredpro

    tredpro

    Joined:
    Nov 18, 2013
    Posts:
    403
    thanks for the code.

    added feature to choose between scaling on x, y, both, or none


    add enum

    Code (csharp):
    1.   public enum ExpandSetting { x, y, both, none };

    Code (csharp):
    1.   public ExpandSetting expandSetting;
    then replace

    Code (csharp):
    1.     float realsize = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX);
    2.                 SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (realsize + spacing[0]) * positionX, realsize);
    3.                 SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
    4.             }
    5.         }
    6.  
    with

    Code (csharp):
    1.  
    2.  
    3.  
    4.  float realsizey;
    5.                 if (expandSetting == ExpandSetting.both)
    6.                 {
    7.                     if (rectChildren.Count != 1)
    8.                     {
    9.                         realsizey = ((height - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    10.                     }
    11.                     else
    12.                     {
    13.                         realsizey = ((height / constraintCount - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    14.  
    15.                     }
    16.  
    17.                     float realsize = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX);
    18.                     SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (realsize + spacing[0]) * positionX, realsize);
    19.                     SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (realsizey + spacing[1]) * positionY, realsizey);
    20.  
    21.      
    22.                 }
    23.                 if (expandSetting == ExpandSetting.x)
    24.                 {
    25.              
    26.  
    27.                     float realsize = ((width - (spacing[0] * (actualCellCountX - 1))) / actualCellCountX);
    28.                     SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (realsize + spacing[0]) * positionX, realsize);
    29.                  
    30.                     SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
    31.  
    32.                 }
    33.                 if (expandSetting == ExpandSetting.y)
    34.                 {
    35.                     if (rectChildren.Count != 1)
    36.                     {
    37.                         realsizey = ((height - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    38.                     }
    39.                     else
    40.                     {
    41.                         realsizey = ((height / constraintCount - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    42.  
    43.                     }
    44.  
    45.                  
    46.                     SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (realsizey + spacing[1]) * positionY, realsizey);
    47.  
    48.                     SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]);
    49.            
    50.  
    51.                 }
    52.                 if (expandSetting == ExpandSetting.none)
    53.                 {
    54.                     SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
    55.                     SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]);
    56.                 }
    57.  
    58.  
    59.  
    60.  
     
  6. zyonneo

    zyonneo

    Joined:
    Apr 13, 2018
    Posts:
    208
    Hi ...i have done GridLayout using this AutoExpandGridLayoutGroup...Any idea on how to know which cell is clicked?So that I can perform certain action for that button click
     
  7. Grumpy-Dot

    Grumpy-Dot

    Joined:
    Feb 29, 2016
    Posts:
    37
    You can either add a button script to the cell elements OR add a custom script that implements the IPointerClickHandler interface and get the click event.
    The parent will have a collection (array/list) of the cell elements and add click listeners for all of them with a specific parameter to identify each cell.
    I can write some sample code if you are still confused about this.
     
    zyonneo likes this.
  8. zyonneo

    zyonneo

    Joined:
    Apr 13, 2018
    Posts:
    208
    I want to populate the Gridview with dynamic image buttons...which gets loaded from the data in the API.Sample code will be helpful,thanks..
     
  9. nbrindic

    nbrindic

    Joined:
    Mar 17, 2014
    Posts:
    1
    This script has small bug for vertical expansion of items. So, replace each occurence of:
    Code (csharp):
    1.  
    2. if (rectChildren.Count != 1)
    3. {
    4.     realsizey = ((height - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    5. }
    6. else
    7. {
    8.     realsizey = ((height / constraintCount - (spacing[0] * (actualCellCountY - 1))) / actualCellCountY);
    9. }
    10.  
    with:

    Code (csharp):
    1.  
    2. if (rectChildren.Count != 1)
    3. {
    4.     realsizey = ((height - (spacing[1] * (actualCellCountY - 1))) / actualCellCountY);
    5. }
    6. else
    7. {
    8.     realsizey = ((height / constraintCount - (spacing[1] * (actualCellCountY - 1))) / actualCellCountY);
    9. }
    10.  
    Basically, old script involved spacing[0] (X-spacing) in the equation for vertical (Y-spacing), so this fix just reverts spacing[0] to spacing[1] in the cases where Y-expansion should occur
     
    spinaLcord likes this.
  10. Aeridis

    Aeridis

    Joined:
    Apr 26, 2014
    Posts:
    1
    Padding from the bottom d the right don't work with this script, and padding from the top and the left pushes the cells outside the boundaries. How can I fix this?

    Also, is there a way to maintain an aspect ratio with the cells? For example if you have a 2:1 window with 1:1 cells in it. Is there a way to expand the cells to fit the window while maintaining it's shape?
     
    Cumuka likes this.
  11. Mathyx92

    Mathyx92

    Joined:
    Apr 9, 2019
    Posts:
    2
    not to be a downer, but the script scales all my objects to 0 along the x axis. It seems to ignore its cellsize parameter, or does not apply its calculations correctly

    Edit: To simply adjust width or height I created a script to attach to the gridlayoutgroup object.
    It gets the needed vales, calculates either width or height for a cell and reapplies the result

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. [ExecuteInEditMode]
    7. [RequireComponent(typeof(UnityEngine.UI.GridLayoutGroup))]
    8. public class AdjustGridLayoutCellSize : MonoBehaviour
    9. {
    10.     public enum ExpandSetting { X, Y,};
    11.  
    12.     public ExpandSetting expandingSetting;
    13.     GridLayoutGroup gridlayout;
    14.     int maxConstraintCount = 0;
    15.     RectTransform layoutRect;
    16.     private void Awake()
    17.     {
    18.         gridlayout = GetComponent<GridLayoutGroup>();
    19.     }
    20.  
    21.     // Start is called before the first frame update
    22.     void Start()
    23.     {
    24.         UpdateCellSize();
    25.     }
    26.  
    27.     // Update is called once per frame
    28.     void Update()
    29.     {
    30.        
    31.     }
    32.  
    33.     private void OnValidate()
    34.     {
    35.         UpdateCellSize();
    36.     }
    37.  
    38.     private void UpdateCellSize()
    39.     {
    40.         maxConstraintCount = gridlayout.constraintCount;
    41.         layoutRect = gridlayout.gameObject.GetComponent<RectTransform>();
    42.  
    43.         if (expandingSetting == ExpandSetting.X)
    44.         {
    45.             float width = layoutRect.rect.width;
    46.             float sizePerCell = width / maxConstraintCount;
    47.             gridlayout.cellSize = new Vector2(sizePerCell, gridlayout.cellSize.y);
    48.         }
    49.         else if (expandingSetting == ExpandSetting.Y)
    50.         {
    51.             float height = layoutRect.rect.height;
    52.             float sizePerCell = height / maxConstraintCount;
    53.             gridlayout.cellSize = new Vector2(gridlayout.cellSize.x, sizePerCell);
    54.         }
    55.     }
    56. }
    57.  
     
    Last edited: May 12, 2019
  12. pyjamads

    pyjamads

    Joined:
    Jun 12, 2013
    Posts:
    6
    cdr9042 likes this.
  13. vaices1974

    vaices1974

    Joined:
    Feb 4, 2019
    Posts:
    2
    I've updated the code to include spacing between cells.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. [ExecuteInEditMode]
    5. [RequireComponent(typeof(UnityEngine.UI.GridLayoutGroup))]
    6. public class AdjustGridLayoutCellSize : MonoBehaviour
    7. {
    8.     public enum ExpandSetting { X, Y };
    9.  
    10.     public ExpandSetting expandingSetting;
    11.     GridLayoutGroup _gridlayout;
    12.  
    13.     private GridLayoutGroup gridlayout
    14.     {
    15.         get
    16.         {
    17.             if(!_gridlayout)
    18.                 _gridlayout = GetComponent<GridLayoutGroup>();
    19.             return _gridlayout;
    20.         }      
    21.     }
    22.  
    23.     int maxConstraintCount = 0;
    24.     RectTransform layoutRect;
    25.  
    26.     // Start is called before the first frame update
    27.     void Start()
    28.     {      
    29.         UpdateCellSize();
    30.     }
    31.  
    32.    
    33.     private void OnValidate()
    34.     {
    35.         UpdateCellSize();
    36.     }
    37.  
    38.     private void UpdateCellSize()
    39.     {
    40.         maxConstraintCount = gridlayout.constraintCount;
    41.         layoutRect = gridlayout.gameObject.GetComponent<RectTransform>();
    42.  
    43.         if (expandingSetting == ExpandSetting.X)
    44.         {
    45.             float spaceForSpacing = (maxConstraintCount - 1) * gridlayout.spacing.x;
    46.             float width = layoutRect.rect.width - spaceForSpacing;
    47.          
    48.             float sizePerCell = width / maxConstraintCount ;
    49.             gridlayout.cellSize = new Vector2(sizePerCell, gridlayout.cellSize.y);
    50.         }
    51.         else if (expandingSetting == ExpandSetting.Y)
    52.         {
    53.             float spaceForSpacing = (maxConstraintCount - 1) * gridlayout.spacing.y;
    54.             float height = layoutRect.rect.height - spaceForSpacing;
    55.             float sizePerCell = height / maxConstraintCount;
    56.             gridlayout.cellSize = new Vector2(gridlayout.cellSize.x, sizePerCell);
    57.         }
    58.     }
    59. }
    60.