androidandroid-layoutandroid-constraintlayout

Create grid (n × n) in Android ConstraintLayout with variable number of n


I want to create a square grid inside ConstraintLayout. My first thought was to create a horizontal chain, give some margin value and set to all single view size attributes width = match_constraint, height = match_constraint and set the ratio to 1:1. It works and it looks like:

enter image description here

And it's easy when a size of the grid is 2×2 - there are 4 elements so it's easy. But what I should do when I had to create a grid 7×7? We have 49 views so setting all of these views could be tricky. I want to do this in constraint layout because I want to have a flexible layout. :)


Solution

  • Since you say that you have a variable number of squares, I assume that you are willing to create the n*n grid in code. Here is an approach to creating the grid. This is just one way and there are probably others.

    First, create a layout with ConstraintLayout as the root view. In that layout, define a widget that has width and height of match_constraints and is constrained by the parent. This will give you a square widget regardless of the device orientation. (I use a View here so it can be seen, but it is better to use a Space widget although it probably doesn't really matter.)

    enter image description here

    activity_main.xml

    <androidx.constraintlayout.widget.ConstraintLayout 
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <View
            android:id="@+id/gridFrame"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_margin="16dp"
            android:background="@android:color/holo_blue_light"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="1:1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    Here is the code for the activity that creates a 7*7 grid. We will use the on-screen view from the layout as the "parent" view to contain the squares.

    enter image description here

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        int mRows = 7;
        int mCols = 7;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            ConstraintLayout layout = findViewById(R.id.layout);
    
            int color1 = getResources().getColor(android.R.color.holo_red_light);
            int color2 = getResources().getColor(android.R.color.holo_blue_light);
            TextView textView;
            ConstraintLayout.LayoutParams lp;
            int id;
            int idArray[][] = new int[mRows][mCols];
            ConstraintSet cs = new ConstraintSet();
    
            // Add our views to the ConstraintLayout.
            for (int iRow = 0; iRow < mRows; iRow++) {
                for (int iCol = 0; iCol < mCols; iCol++) {
                    textView = new TextView(this);
                    lp = new ConstraintLayout.LayoutParams(ConstraintSet.MATCH_CONSTRAINT,
                                                           ConstraintSet.MATCH_CONSTRAINT);
                    id = View.generateViewId();
                    idArray[iRow][iCol] = id;
                    textView.setId(id);
                    textView.setText(String.valueOf(id));
                    textView.setGravity(Gravity.CENTER);
                    textView.setBackgroundColor(((iRow + iCol) % 2 == 0) ? color1 : color2);
                    layout.addView(textView, lp);
                }
            }
    
            // Create horizontal chain for each row and set the 1:1 dimensions.
            // but first make sure the layout frame has the right ratio set.
            cs.clone(layout);
            cs.setDimensionRatio(R.id.gridFrame, mCols + ":" + mRows);
            for (int iRow = 0; iRow < mRows; iRow++) {
                for (int iCol = 0; iCol < mCols; iCol++) {
                    id = idArray[iRow][iCol];
                    cs.setDimensionRatio(id, "1:1");
                    if (iRow == 0) {
                        // Connect the top row to the top of the frame.
                        cs.connect(id, ConstraintSet.TOP, R.id.gridFrame, ConstraintSet.TOP);
                    } else {
                        // Connect top to bottom of row above.
                        cs.connect(id, ConstraintSet.TOP, idArray[iRow - 1][0], ConstraintSet.BOTTOM);
                    }
                }
                // Create a horiontal chain that will determine the dimensions of our squares.
                // Could also be createHorizontalChainRtl() with START/END.
                cs.createHorizontalChain(R.id.gridFrame, ConstraintSet.LEFT,
                                         R.id.gridFrame, ConstraintSet.RIGHT,
                                         idArray[iRow], null, ConstraintSet.CHAIN_PACKED);
            }
    
            cs.applyTo(layout);
        }
    }
    

    Just change mRows and mCols and the grid will adjust itself. If your grid will always be square, you will not need to set the ratio of the grid container in the code. You can also place your grid within a more complicated layout. Just make sure that the grid container has the right dimensions and you are good to go.