Search code examples
c#androidxamarinxamarin.android

GridLayout autosize items to fit screen


I am trying to make a custom Calendar control that fills the screen. The calendar is 7 days by 6 weeks (static). I am using a GridLayout (android.support.V7.widget.GridLayout) for the layout of the days on the screen.

The contents of the grid are being placed on load using a separate layout file.

However the layout of the items in the grid seems to be all placed towards the top left corner. The LayoutGrid is the size of the parent, but the items are all as small as possible (even with coded sizing on the item).

I am looking to get the layout to fill the entire screen. I know I could do this with nested LinearLayout layouts, but that defeats the purpose of the GridLayout.

Any help would be greatly appreciated.

P.S. The listview in the Day also causes unexpected layout, each day being the full width of the screen.

GridLayout Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0FC"
    android:paddingTop="?attr/actionBarSize">
  <android.support.v7.widget.GridLayout
    android:id="@+id/grdlayCalendarMonth"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FC0"

    app:alignmentMode="alignBounds"
    app:columnCount="7"
    app:rowCount="6"
    app:useDefaultMargins="true"
  />
</LinearLayout>

Day Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="250dp"
    android:layout_height="250dp"
    android:background="#4444ff"
    app:layout_columnWeight="1"
    app:layout_rowWeight="1"
    app:layout_gravity="fill_vertical">
  <android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cardCornerRadius="3dp"
    app:cardElevation="3dp"
    app:cardUseCompatPadding="true"
    android:layout_margin="0dp"
    android:paddingVertical="3dp"
    app:cardBackgroundColor="@color/cardTextGreen">

    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:id="@+id/layoutCalendarDay"
      android:background="#fc03e8">
      <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lblCalendarDayDate"/>
      <!--<ListView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/lvwCalendarDay"/>-->
    </LinearLayout>

  </android.support.v7.widget.CardView>
</LinearLayout>

Add day to GridLayout

 Android.Support.V7.Widget.GridLayout monthGrid = FindViewById<Android.Support.V7.Widget.GridLayout>(Resource.Id.grdlayCalendarMonth);

            //Find the first day of the month.
            DateTime firstDayOfMonth = DateTime.Now.GetFirstDayOfMonth();
            DayOfWeek firstDayOfWeek = firstDayOfMonth.DayOfWeek;
            DateTime firstDayOfDisplayCalendar = firstDayOfMonth.AddDays(-(int)firstDayOfWeek);

            for (int r = 0; r < 6; r++) //ROW
            {
                for (int c = 0; c < 7; c++) //COLUMN
                {    
                    Android.Support.V7.Widget.GridLayout.LayoutParams param = new Android.Support.V7.Widget.GridLayout.LayoutParams(
                        Android.Support.V7.Widget.GridLayout.InvokeSpec(r, 1), Android.Support.V7.Widget.GridLayout.InvokeSpec(c, 1));

                    View dayView = View.Inflate(this, Resource.Layout.calendar_month_dayview, null);
                    dayView.LayoutParameters = param;
                    TextView dayListDate = dayView.FindViewById<TextView>(Resource.Id.lblCalendarDayDate);
                    dayView.Tag = firstDayOfDisplayCalendar.Ticks;
                    dayListDate.Text = firstDayOfDisplayCalendar.ToString("dd").Ordinalize();                      
                    firstDayOfDisplayCalendar = firstDayOfDisplayCalendar.AddDays(1);  
                    monthGrid.AddView(dayView, param);
                }
            }

Current Layout Result

UPDATE: While the answer below does what it shows. When any extra controls get added to the grids day layout, the scaling of the column and rows changes and it all goes out of sync again. I am going to go to a nested LinearLayout structure to save time.

Failed GridLayout view


Solution

  • According to your description, you want to split the screen row height and column width for GridLayout item, if yes, you can take a look the following code to modify your code.

    You can use:

    GridLayout.InvokeSpec(r, GridLayout.Fill, 1f)
    

    to average current available space:

     for (int r=0;r<5;r++)
            {
                for(int c=0;c<7;c++)
                {
                    var row = GridLayout.InvokeSpec(r, GridLayout.Fill, 1f);
                    var col = GridLayout.InvokeSpec(c, GridLayout.Fill,1f);
                    View layout1 = View.Inflate(this, Resource.Layout.layout1, null);
                    TextView textview = layout1.FindViewById<TextView>(Resource.Id.textView1);
                    textview.Text = "test!";
                    layout1.LayoutParameters = new GridLayout.LayoutParams(row, col);
    
                    monthGrid.AddView(layout1);
                }
            }
    

    Result:

    enter image description here