How to set layout_columnWeight for a GridLayout child when added programmatically?
Asked Answered
I

2

9

I'd like to use a GridLayout (not GridView) as board for a game like chess or checkers. As I'm a little reluctant to use an xml file with 64 child Views, I've tried adding them programmatically.

To keep things simple, I started with using TextViews as child Views for the GridLayout.

My problem is that the Views are not distributed evenly, and that I don't know how to get an even distribution in my java code. There is no method like "setWeight()" for setting layout_columnWeight and layout_rowWeight.

At present, this is my activity_dynamic_grid_layout.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

<ImageView
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:id="@+id/ivLogo"
    android:background="#ff0000"
    android:layout_alignParentTop="true"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"/>

<android.support.v7.widget.GridLayout
    android:id="@+id/grid_layout"
    android:background="#004080"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/ivLogo"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_marginTop="36dp">

</android.support.v7.widget.GridLayout>

I've set the GridLayout width and height to match_parent here, but I'm changing them at runtime using a ViewTreeObserver.OnPreDrawListener in order to get a square board. This works, the colored background is showing a square space as intended.

My onCreate()

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dynamic_grid_layout);

    GridLayout gl = (GridLayout) findViewById(R.id.grid_layout);
    gl.setColumnCount(8);
    gl.setRowCount(8);

    for(int i=0; i<gl.getRowCount(); i++)
    {
        GridLayout.Spec rowSpec = GridLayout.spec(i, 1, GridLayout.FILL);

        for(int j=0;j<gl.getColumnCount();j++)
        {
            GridLayout.Spec colSpec = GridLayout.spec(j,1, GridLayout.FILL);

            TextView tvChild = new TextView(this);
            tvChild.setText("[ " + i + " | " + j + " ]");
            tvChild.setTextSize(18f);
            tvChild.setTextColor(Color.WHITE);
            tvChild.setGravity(Gravity.CENTER);

            GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();
            myGLP.rowSpec = rowSpec;
            myGLP.columnSpec = colSpec;

            gl.addView(tvChild, myGLP );
        }
    }

    final View rootView = findViewById(R.id.dynamic_root);

    rootView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
    {
        @Override
        public boolean onPreDraw()
        {
            int w = rootView.getMeasuredWidth();
            int h = rootView.getMeasuredHeight();
            int min = Math.min(w, h);

            ViewGroup.LayoutParams lp = gl.getLayoutParams();
            lp.width = min - min % 9;
            lp.height = lp.width;
            gl.setLayoutParams(lp);

            rootView.getViewTreeObserver().removeOnPreDrawListener(this);

            return true;
        }
    });
}

What I've tried already:

I put one TextView child in the layout file and tried to copy the layout_columnWeight and layout_rowWeight from its GridLayout.LayoutParams:

 <android.support.v7.widget.GridLayout
 ...>   
    <TextView
        android:id="@+id/clone_my_params"
        android:text="[ 0 | 0 ]"
        android:textColor="#ffffff"
        android:textSize="18sp"
        app:layout_column="0"
        app:layout_row="0"
        app:layout_columnWeight="1"
        app:layout_rowWeight="1"
    />
</android.support.v7.widget.GridLayout>

Additional code in onCreate(), before the double loop:

TextView v = (TextView)gl.findViewById(R.id.clone_my_params);
v.setGravity(Gravity.CENTER);
GridLayout.LayoutParams gridLayoutParamsToCopy = new GridLayout.LayoutParams(v.getLayoutParams());

Inside the loop, I skipped (i,j) = (0,0) and changed

 GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams();

to

 GridLayout.LayoutParams myGLP = new GridLayout.LayoutParams(gridLayoutParamsToCopy);

Before the change, all elements were in the upper left corner, the excess space was given to the last row/ column. After the change, the first row/ column had the excess space, no change for the other elements.

Calling gl.invalidate() and/or gl.requestLayout() after the double loop had no effect.

So it seems that I did not manage to set the desired weight by using the copy constructor.

Inconclusive answered 24/2, 2016 at 11:6 Comment(3)
First of all, if all you want is a square 8x8 grid it might be easier(and efficient) to make a custom ViewGroup(it's implementation would be quite simple). Secondly, shouldn't you be using a spec method which receives a float like developer.android.com/reference/android/widget/…, int, android.widget.GridLayout.Alignment, float) for the Spec?Andraandrade
@Luksprog - about the custom ViewGroup: I have a prototype of the game with a custom View as a gameboard, overriding "onDraw()", and now I'm experimenting with GridView as well as GridLayout. I'm rather new to Android development and I'm trying to figure out which version will be best from a performance point of view. About the method: OK, that's what happens if you have the API level set to 14 - I used spec(int start, int size) and only now noticed there is a spec(int start, float weight). I think my support GridLayout has it as well, maybe that's what I was missing. Thanks for the hint!Anthelion
@Luksprog - mission accomplished :) Adding " 1f" as last parameter in the spec() did the trick. Would you like to write it down as an answer so I can upvote?Anthelion
A
1

To set the weight on the children of your Gridlayout use one of the spec()(like this one) methods that takes a float value.

Another approach would be a either make a custom View(in which case you'll need to manually draw the pieces) or a custom ViewGroup(in which case the custom ViewGroup will just take care of the pieces positioning, this will be appropriate if you plan to have more complex view hierarchies than a simple TextView).

Andraandrade answered 24/2, 2016 at 14:10 Comment(2)
thanks once more, and just one question: if I'm extending ImageButton for the gaming pieces, and would like to take advantage of View and ViewGroup animations, then should I use a GridView or a GridLayout?Anthelion
@0X0nosugar It doesn't matter. Extending from ImageButton or using any ViewGroup doesn't change the options for animating views either directly or through layout transitions. You should use a GridLayout only because a GridView it's much better suited for other cases.Andraandrade
B
6

-Here you are ! :>

Button button = new Button(this);
GridLayout.LayoutParams param= new GridLayout.LayoutParams(GridLayout.spec(
            GridLayout.UNDEFINED,GridLayout.FILL,1f),
            GridLayout.spec(GridLayout.UNDEFINED,GridLayout.FILL,1f));
param.height = 0;                                                                                                           
param.width  = 0;
button.setLayoutParams(param);
Bacchanalia answered 16/11, 2017 at 0:23 Comment(2)
Please explain (edit your answer) how your code solves the problem. It might not be clear from the code itself.Pinder
Tested with three different emulators (Android 5 to 7, phone & tablet, hdpi and mdpi): it works, so thank you :)Anthelion
A
1

To set the weight on the children of your Gridlayout use one of the spec()(like this one) methods that takes a float value.

Another approach would be a either make a custom View(in which case you'll need to manually draw the pieces) or a custom ViewGroup(in which case the custom ViewGroup will just take care of the pieces positioning, this will be appropriate if you plan to have more complex view hierarchies than a simple TextView).

Andraandrade answered 24/2, 2016 at 14:10 Comment(2)
thanks once more, and just one question: if I'm extending ImageButton for the gaming pieces, and would like to take advantage of View and ViewGroup animations, then should I use a GridView or a GridLayout?Anthelion
@0X0nosugar It doesn't matter. Extending from ImageButton or using any ViewGroup doesn't change the options for animating views either directly or through layout transitions. You should use a GridLayout only because a GridView it's much better suited for other cases.Andraandrade

© 2022 - 2024 — McMap. All rights reserved.