Gradle dependencies
compile group: 'androidx.constraintlayout', name: 'constraintlayout', version: '2.2.0-beta01'
- groupId: androidx.constraintlayout
- artifactId: constraintlayout
- version: 2.2.0-beta01
Artifact androidx.constraintlayout:constraintlayout:2.2.0-beta01 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.constraintlayout:constraintlayout com.android.support.constraint:constraint-layout
Overview
A helper class that helps arrange widgets in a grid form
Grid
Attributes | Description |
grid_rows |
Indicates the number of rows will be created for the grid form. |
grid_columns |
Indicates the number of columns will be created for the grid form. |
grid_rowWeights |
Specifies the weight of each row in the grid form (default value is 1). |
grid_columnWeights |
Specifies the weight of each column in the grid form (default value is 1). |
grid_spans |
Offers the capability to span a widget across multiple rows and columns |
grid_skips |
Enables skip certain positions in the grid and leave them empty |
grid_orientation |
Defines how the associated widgets will be arranged - vertically or horizontally |
grid_horizontalGaps |
Adds margin horizontally between widgets |
grid_verticalGaps |
Adds margin vertically between widgets |
Summary
Constructors |
---|
public | Grid(Context context)
|
public | Grid(Context context, AttributeSet attrs)
|
public | Grid(Context context, AttributeSet attrs, int defStyleAttr)
|
Methods |
---|
public int | getColumns()
get the value of columns |
public java.lang.String | getColumnWeights()
get the string value of columnWeights |
public float | getHorizontalGaps()
get the value of horizontalGaps |
public int | getOrientation()
get the value of orientation |
public int | getRows()
get the value of rows |
public java.lang.String | getRowWeights()
get the string value of rowWeights |
public java.lang.String | getSkips()
get the string value of skips |
public java.lang.String | getSpans()
get the string value of spans |
public float | getVerticalGaps()
get the value of verticalGaps |
protected void | init(AttributeSet attrs)
|
public void | onAttachedToWindow()
|
public void | onDraw(Canvas canvas)
Visualize the boxViews that are used to constraint widgets. |
public void | setColumns(int columns)
set new columns value and also invoke initVariables and invalidate |
public void | setColumnWeights(java.lang.String columnWeights)
set new columnWeights value and also invoke invalidate |
public void | setHorizontalGaps(float horizontalGaps)
set new horizontalGaps value and also invoke invalidate |
public void | setOrientation(int orientation)
set new orientation value and also invoke invalidate |
public void | setRows(int rows)
set new rows value and also invoke initVariables and invalidate |
public void | setRowWeights(java.lang.String rowWeights)
set new rowWeights value and also invoke invalidate |
public void | setSkips(java.lang.String skips)
set new skips value and also invoke invalidate |
public void | setSpans(java.lang.CharSequence spans)
set new spans value and also invoke invalidate |
public void | setVerticalGaps(float verticalGaps)
set new verticalGaps value and also invoke invalidate |
from VirtualLayout | applyLayoutFeaturesInConstraintSet, onMeasure, setElevation, setVisibility |
from ConstraintHelper | addView, applyHelperParams, applyLayoutFeatures, applyLayoutFeatures, containsId, getReferencedIds, getViews, indexFromId, isChildOfHelper, loadParameters, onMeasure, removeView, resolveRtl, setIds, setReferencedIds, setReferenceTags, setTag, updatePostConstraints, updatePostLayout, updatePostMeasure, updatePreDraw, updatePreLayout, updatePreLayout, validateParams |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
VERTICALpublic static final int
HORIZONTALConstructors
public
Grid(Context context)
public
Grid(Context context, AttributeSet attrs)
public
Grid(Context context, AttributeSet attrs, int defStyleAttr)
Methods
protected void
init(AttributeSet attrs)
public void
onAttachedToWindow()
public void
onDraw(Canvas canvas)
Visualize the boxViews that are used to constraint widgets.
Parameters:
canvas: canvas to visualize the boxViews
get the value of rows
Returns:
the value of rows
public void
setRows(int rows)
set new rows value and also invoke initVariables and invalidate
Parameters:
rows: new rows value
get the value of columns
Returns:
the value of columns
public void
setColumns(int columns)
set new columns value and also invoke initVariables and invalidate
Parameters:
columns: new rows value
public int
getOrientation()
get the value of orientation
Returns:
the value of orientation
public void
setOrientation(int orientation)
set new orientation value and also invoke invalidate
Parameters:
orientation: new orientation value
public java.lang.String
getSpans()
get the string value of spans
Returns:
the string value of spans
public void
setSpans(java.lang.CharSequence spans)
set new spans value and also invoke invalidate
Parameters:
spans: new spans value
public java.lang.String
getSkips()
get the string value of skips
Returns:
the string value of skips
public void
setSkips(java.lang.String skips)
set new skips value and also invoke invalidate
Parameters:
skips: new spans value
public java.lang.String
getRowWeights()
get the string value of rowWeights
Returns:
the string value of rowWeights
public void
setRowWeights(java.lang.String rowWeights)
set new rowWeights value and also invoke invalidate
Parameters:
rowWeights: new rowWeights value
public java.lang.String
getColumnWeights()
get the string value of columnWeights
Returns:
the string value of columnWeights
public void
setColumnWeights(java.lang.String columnWeights)
set new columnWeights value and also invoke invalidate
Parameters:
columnWeights: new columnWeights value
public float
getHorizontalGaps()
get the value of horizontalGaps
Returns:
the value of horizontalGaps
public void
setHorizontalGaps(float horizontalGaps)
set new horizontalGaps value and also invoke invalidate
Parameters:
horizontalGaps: new horizontalGaps value
public float
getVerticalGaps()
get the value of verticalGaps
Returns:
the value of verticalGaps
public void
setVerticalGaps(float verticalGaps)
set new verticalGaps value and also invoke invalidate
Parameters:
verticalGaps: new verticalGaps value
Source
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.constraintlayout.helper.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.constraintlayout.widget.R;
import androidx.constraintlayout.widget.VirtualLayout;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* A helper class that helps arrange widgets in a grid form
*
* <h2>Grid</h2>
* <table summary="Grid attributes">
* <tr>
* <th>Attributes</th><th>Description</th>
* </tr>
* <tr>
* <td>grid_rows</td>
* <td>Indicates the number of rows will be created for the grid form.</td>
* </tr>
* <tr>
* <td>grid_columns</td>
* <td>Indicates the number of columns will be created for the grid form.</td>
* </tr>
* <tr>
* <td>grid_rowWeights</td>
* <td>Specifies the weight of each row in the grid form (default value is 1).</td>
* </tr>
* <tr>
* <td>grid_columnWeights</td>
* <td>Specifies the weight of each column in the grid form (default value is 1).</td>
* </tr>
* <tr>
* <td>grid_spans</td>
* <td>Offers the capability to span a widget across multiple rows and columns</td>
* </tr>
* <tr>
* <td>grid_skips</td>
* <td>Enables skip certain positions in the grid and leave them empty</td>
* </tr>
* <tr>
* <td>grid_orientation</td>
* <td>Defines how the associated widgets will be arranged - vertically or horizontally</td>
* </tr>
* <tr>
* <td>grid_horizontalGaps</td>
* <td>Adds margin horizontally between widgets</td>
* </tr>
* <tr>
* <td>grid_verticalGaps</td>
* <td>Adds margin vertically between widgets</td>
* </tr>
* </table>
*/
public class Grid extends VirtualLayout {
private static final String TAG = "Grid";
public static final int VERTICAL = 1;
public static final int HORIZONTAL = 0;
private static final boolean DEBUG_BOXES = false;
private final int mMaxRows = 50; // maximum number of rows can be specified.
private final int mMaxColumns = 50; // maximum number of columns can be specified.
// private final ConstraintSet mConstraintSet = new ConstraintSet();
private View[] mBoxViews;
ConstraintLayout mContainer;
/**
* number of rows of the grid
*/
private int mRows;
/**
* number of rows set by the XML or API
*/
private int mRowsSet;
/**
* number of columns of the grid
*/
private int mColumns;
/**
* number of columns set by the XML or API
*/
private int mColumnsSet;
/**
* string format of the input Spans
*/
private String mStrSpans;
/**
* string format of the input Skips
*/
private String mStrSkips;
/**
* string format of the row weight
*/
private String mStrRowWeights;
/**
* string format of the column weight
*/
private String mStrColumnWeights;
/**
* Horizontal gaps in Dp
*/
private float mHorizontalGaps;
/**
* Vertical gaps in Dp
*/
private float mVerticalGaps;
/**
* orientation of the view arrangement - vertical or horizontal
*/
private int mOrientation;
/**
* Indicates what is the next available position to place an widget
*/
private int mNextAvailableIndex = 0;
/**
* Indicates whether the input attributes need to be validated
*/
private boolean mValidateInputs;
/**
* Indicates whether to use RTL layout direction
*/
@SuppressWarnings("unused")
private boolean mUseRtl;
/**
* A integer matrix that tracks the positions that are occupied by skips and spans
* true: available position
* false: non-available position
*/
private boolean[][] mPositionMatrix;
/**
* Store the view ids of handled spans
*/
Set<Integer> mSpanIds = new HashSet<>();
/**
* Ids of the boxViews
*/
private int[] mBoxViewIds;
public Grid(Context context) {
super(context);
}
public Grid(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Grid(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void init(AttributeSet attrs) {
super.init(attrs);
mUseViewMeasure = true;
// Parse the relevant attributes from layout xml
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.Grid);
final int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
if (attr == R.styleable.Grid_grid_rows) {
mRowsSet = a.getInteger(attr, 0);
} else if (attr == R.styleable.Grid_grid_columns) {
mColumnsSet = a.getInteger(attr, 0);
} else if (attr == R.styleable.Grid_grid_spans) {
mStrSpans = a.getString(attr);
} else if (attr == R.styleable.Grid_grid_skips) {
mStrSkips = a.getString(attr);
} else if (attr == R.styleable.Grid_grid_rowWeights) {
mStrRowWeights = a.getString(attr);
} else if (attr == R.styleable.Grid_grid_columnWeights) {
mStrColumnWeights = a.getString(attr);
} else if (attr == R.styleable.Grid_grid_orientation) {
mOrientation = a.getInt(attr, 0);
} else if (attr == R.styleable.Grid_grid_horizontalGaps) {
mHorizontalGaps = a.getDimension(attr, 0);
} else if (attr == R.styleable.Grid_grid_verticalGaps) {
mVerticalGaps = a.getDimension(attr, 0);
} else if (attr == R.styleable.Grid_grid_validateInputs) {
// @TODO handle validation
mValidateInputs = a.getBoolean(attr, false);
} else if (attr == R.styleable.Grid_grid_useRtl) {
// @TODO handle RTL
mUseRtl = a.getBoolean(attr, false);
}
}
updateActualRowsAndColumns();
initVariables();
a.recycle();
}
}
/**
* Compute the actual rows and columns given what was set
* if 0,0 find the most square rows and columns that fits
* if 0,n or n,0 scale to fit
*/
private void updateActualRowsAndColumns() {
if (mRowsSet == 0 || mColumnsSet == 0) {
if (mColumnsSet > 0) {
mColumns = mColumnsSet;
mRows = (mCount + mColumns - 1) / mColumnsSet; // round up
} else if (mRowsSet > 0) {
mRows = mRowsSet;
mColumns = (mCount + mRowsSet - 1) / mRowsSet; // round up
} else { // as close to square as possible favoring more rows
mRows = (int) (1.5 + Math.sqrt(mCount));
mColumns = (mCount + mRows - 1) / mRows;
}
} else {
mRows = mRowsSet;
mColumns = mColumnsSet;
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mContainer = (ConstraintLayout) getParent();
generateGrid(false);
}
/**
* generate the Grid form based on the input attributes
*
* @param isUpdate whether to update the existing grid (true) or create a new one (false)
* @return true if all the inputs are valid else false
*/
private boolean generateGrid(boolean isUpdate) {
if (mContainer == null || mRows < 1 || mColumns < 1) {
return false;
}
if (isUpdate) {
for (int i = 0; i < mPositionMatrix.length; i++) {
for (int j = 0; j < mPositionMatrix[0].length; j++) {
mPositionMatrix[i][j] = true;
}
}
mSpanIds.clear();
}
mNextAvailableIndex = 0;
boolean isSuccess = true;
buildBoxes();
if (mStrSkips != null && !mStrSkips.trim().isEmpty()) {
int[][] mSkips = parseSpans(mStrSkips);
if (mSkips != null) {
isSuccess &= handleSkips(mSkips);
}
}
if (mStrSpans != null && !mStrSpans.trim().isEmpty()) {
int[][] mSpans = parseSpans(mStrSpans);
if (mSpans != null) {
isSuccess &= handleSpans(mIds, mSpans);
}
}
isSuccess &= arrangeWidgets();
return isSuccess || !mValidateInputs;
}
/**
* Initialize the relevant variables
*/
private void initVariables() {
mPositionMatrix = new boolean[mRows][mColumns];
for (boolean[] row : mPositionMatrix) {
Arrays.fill(row, true);
}
}
/**
* parse the weights/pads in the string format into a float array
*
* @param size size of the return array
* @param str weights/pads in a string format
* @return a float array with weights/pads values
*/
private float[] parseWeights(int size, String str) {
if (str == null || str.trim().isEmpty()) {
return null;
}
String[] values = str.split(",");
if (values.length != size) {
return null;
}
float[] arr = new float[size];
for (int i = 0; i < arr.length; i++) {
arr[i] = Float.parseFloat(values[i].trim());
}
return arr;
}
private ConstraintLayout.LayoutParams params(View v) {
return (ConstraintLayout.LayoutParams) v.getLayoutParams();
}
/**
* Connect the view to the corresponding viewBoxes based on the input params
*
* @param view the Id of the view
* @param row row position to place the view
* @param column column position to place the view
*/
private void connectView(View view, int row, int column, int rowSpan, int columnSpan) {
ConstraintLayout.LayoutParams params = params(view);
// @TODO handle RTL
// Connect the 4 sides
params.leftToLeft = mBoxViewIds[column];
params.topToTop = mBoxViewIds[row];
params.rightToRight = mBoxViewIds[column + columnSpan - 1];
params.bottomToBottom = mBoxViewIds[row + rowSpan - 1];
view.setLayoutParams(params);
}
/**
* Arrange the views in the constraint_referenced_ids
*
* @return true if all the widgets can be arranged properly else false
*/
private boolean arrangeWidgets() {
int position;
View[] views = getViews(mContainer);
// @TODO handle RTL
for (int i = 0; i < mCount; i++) {
if (mSpanIds.contains(mIds[i])) {
// skip the viewId that's already handled by handleSpans
continue;
}
position = getNextPosition();
int row = getRowByIndex(position);
int col = getColByIndex(position);
if (position == -1) {
// no more available position.
return false;
}
connectView(views[i], row, col, 1, 1);
}
return true;
}
/**
* Convert a 1D index to a 2D index that has index for row and index for column
*
* @param index index in 1D
* @return row as its values.
*/
private int getRowByIndex(int index) {
if (mOrientation == 1) {
return index % mRows;
} else {
return index / mColumns;
}
}
/**
* Convert a 1D index to a 2D index that has index for row and index for column
*
* @param index index in 1D
* @return column as its values.
*/
private int getColByIndex(int index) {
if (mOrientation == 1) {
return index / mRows;
} else {
return index % mColumns;
}
}
/**
* Get the next available position for widget arrangement.
*
* @return int[] -> [row, column]
*/
private int getNextPosition() {
// int[] position = new int[] {0, 0};
int position = 0;
boolean positionFound = false;
while (!positionFound) {
if (mNextAvailableIndex >= mRows * mColumns) {
return -1;
}
// position = getPositionByIndex(mNextAvailableIndex);
position = mNextAvailableIndex;
int row = getRowByIndex(mNextAvailableIndex);
int col = getColByIndex(mNextAvailableIndex);
if (mPositionMatrix[row][col]) {
mPositionMatrix[row][col] = false;
positionFound = true;
}
mNextAvailableIndex++;
}
return position;
}
/**
* Check if the value of the spans/skips is valid
*
* @param str spans/skips in string format
* @return true if it is valid else false
*/
private boolean isSpansValid(@SuppressWarnings("unused") CharSequence str) {
// TODO: check string has a valid format.
return true;
}
/**
* Check if the value of the rowWeights or columnsWeights is valid
*
* @param str rowWeights/columnsWeights in string format
* @return true if it is valid else false
*/
private boolean isWeightsValid(@SuppressWarnings("unused") String str) {
// TODO: check string has a valid format.
return true;
}
/**
* parse the skips/spans in the string format into a int matrix
* that each row has the information - [index, row_span, col_span]
* the format of the input string is index:row_spanxcol_span.
* index - the index of the starting position
* row_span - the number of rows to span
* col_span- the number of columns to span
*
* @param str string format of skips or spans
* @return a int matrix that contains skip information.
*/
private int[][] parseSpans(String str) {
if (!isSpansValid(str)) {
return null;
}
String[] spans = str.split(",");
int[][] spanMatrix = new int[spans.length][3];
String[] indexAndSpan;
String[] rowAndCol;
for (int i = 0; i < spans.length; i++) {
indexAndSpan = spans[i].trim().split(":");
rowAndCol = indexAndSpan[1].split("x");
spanMatrix[i][0] = Integer.parseInt(indexAndSpan[0]);
spanMatrix[i][1] = Integer.parseInt(rowAndCol[0]);
spanMatrix[i][2] = Integer.parseInt(rowAndCol[1]);
}
return spanMatrix;
}
/**
* Handle the span use cases
*
* @param spansMatrix a int matrix that contains span information
* @return true if the input spans is valid else false
*/
private boolean handleSpans(int[] mId, int[][] spansMatrix) {
View[] views = getViews(mContainer);
for (int i = 0; i < spansMatrix.length; i++) {
int row = getRowByIndex(spansMatrix[i][0]);
int col = getColByIndex(spansMatrix[i][0]);
if (!invalidatePositions(row, col,
spansMatrix[i][1], spansMatrix[i][2])) {
return false;
}
connectView(views[i], row, col,
spansMatrix[i][1], spansMatrix[i][2]);
mSpanIds.add(mId[i]);
}
return true;
}
/**
* Make positions in the grid unavailable based on the skips attr
*
* @param skipsMatrix a int matrix that contains skip information
* @return true if all the skips are valid else false
*/
private boolean handleSkips(int[][] skipsMatrix) {
for (int i = 0; i < skipsMatrix.length; i++) {
int row = getRowByIndex(skipsMatrix[i][0]);
int col = getColByIndex(skipsMatrix[i][0]);
if (!invalidatePositions(row, col,
skipsMatrix[i][1], skipsMatrix[i][2])) {
return false;
}
}
return true;
}
/**
* Make the specified positions in the grid unavailable.
*
* @param startRow the row of the staring position
* @param startColumn the column of the staring position
* @param rowSpan how many rows to span
* @param columnSpan how many columns to span
* @return true if we could properly invalidate the positions else false
*/
private boolean invalidatePositions(int startRow, int startColumn,
int rowSpan, int columnSpan) {
for (int i = startRow; i < startRow + rowSpan; i++) {
for (int j = startColumn; j < startColumn + columnSpan; j++) {
if (i >= mPositionMatrix.length || j >= mPositionMatrix[0].length
|| !mPositionMatrix[i][j]) {
// the position is already occupied.
return false;
}
mPositionMatrix[i][j] = false;
}
}
return true;
}
/**
* Visualize the boxViews that are used to constraint widgets.
*
* @param canvas canvas to visualize the boxViews
*/
@Override
public void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
// Visualize the viewBoxes if isInEditMode() is true
if (!isInEditMode()) {
return;
}
@SuppressLint("DrawAllocation")
Paint paint = new Paint(); // used only during design time
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.STROKE);
int myTop = getTop();
int myLeft = getLeft();
int myBottom = getBottom();
int myRight = getRight();
for (View box : mBoxViews) {
int l = box.getLeft() - myLeft;
int t = box.getTop() - myTop;
int r = box.getRight() - myLeft;
int b = box.getBottom() - myTop;
canvas.drawRect(l, 0, r, myBottom - myTop, paint);
canvas.drawRect(0, t, myRight - myLeft, b, paint);
}
}
/**
* Set chain between boxView horizontally
*/
private void setBoxViewHorizontalChains() {
int gridId = getId();
int maxVal = Math.max(mRows, mColumns);
float[] columnWeights = parseWeights(mColumns, mStrColumnWeights);
ConstraintLayout.LayoutParams params = params(mBoxViews[0]);
// chain all the views on the longer side (either horizontal or vertical)
if (mColumns == 1) {
clearHParams(mBoxViews[0]);
params.leftToLeft = gridId;
params.rightToRight = gridId;
mBoxViews[0].setLayoutParams(params);
return;
}
// chains are grid <- box <-> box <-> box -> grid
for (int i = 0; i < mColumns; i++) {
params = params(mBoxViews[i]);
clearHParams(mBoxViews[i]);
if (columnWeights != null) {
params.horizontalWeight = columnWeights[i];
}
if (i > 0) {
params.leftToRight = mBoxViewIds[i - 1];
} else {
params.leftToLeft = gridId;
}
if (i < mColumns - 1) {
params.rightToLeft = mBoxViewIds[i + 1];
} else {
params.rightToRight = gridId;
}
if (i > 0) {
params.leftMargin = (int) mHorizontalGaps;
}
mBoxViews[i].setLayoutParams(params);
}
// excess boxes are connected to grid those sides are not use
// for efficiency they should be connected to parent
for (int i = mColumns; i < maxVal; i++) {
params = params(mBoxViews[i]);
clearHParams(mBoxViews[i]);
params.leftToLeft = gridId;
params.rightToRight = gridId;
mBoxViews[i].setLayoutParams(params);
}
}
/**
* Set chain between boxView vertically
*/
private void setBoxViewVerticalChains() {
int gridId = getId();
int maxVal = Math.max(mRows, mColumns);
float[] rowWeights = parseWeights(mRows, mStrRowWeights);
ConstraintLayout.LayoutParams params;
// chain all the views on the longer side (either horizontal or vertical)
if (mRows == 1) {
params = params(mBoxViews[0]);
clearVParams(mBoxViews[0]);
params.topToTop = gridId;
params.bottomToBottom = gridId;
mBoxViews[0].setLayoutParams(params);
return;
}
// chains are constrained like this: grid <- box <-> box <-> box -> grid
for (int i = 0; i < mRows; i++) {
params = params(mBoxViews[i]);
clearVParams(mBoxViews[i]);
if (rowWeights != null) {
params.verticalWeight = rowWeights[i];
}
if (i > 0) {
params.topToBottom = mBoxViewIds[i - 1];
} else {
params.topToTop = gridId;
}
if (i < mRows - 1) {
params.bottomToTop = mBoxViewIds[i + 1];
} else {
params.bottomToBottom = gridId;
}
if (i > 0) {
params.topMargin = (int) mHorizontalGaps;
}
mBoxViews[i].setLayoutParams(params);
}
// excess boxes are connected to grid those sides are not use
// for efficiency they should be connected to parent
for (int i = mRows; i < maxVal; i++) {
params = params(mBoxViews[i]);
clearVParams(mBoxViews[i]);
params.topToTop = gridId;
params.bottomToBottom = gridId;
mBoxViews[i].setLayoutParams(params);
}
}
/**
* Create a new boxView
* @return boxView
*/
private View makeNewView() {
View v = new View(getContext());
v.setId(View.generateViewId());
v.setVisibility(INVISIBLE);
if (DEBUG_BOXES) {
v.setVisibility(VISIBLE);
v.setBackgroundColor(0xFF880088);
}
ConstraintLayout.LayoutParams params =
new ConstraintLayout.LayoutParams(0, 0);
mContainer.addView(v, params);
return v;
}
/**
* Clear vertical related layout params
* @param view view that has the layout params to be cleared
*/
private void clearVParams(View view) {
ConstraintLayout.LayoutParams params = params(view);
params.verticalWeight = ConstraintSet.UNSET;
params.topToBottom = ConstraintSet.UNSET;
params.topToTop = ConstraintSet.UNSET;
params.bottomToTop = ConstraintSet.UNSET;
params.bottomToBottom = ConstraintSet.UNSET;
params.topMargin = ConstraintSet.UNSET;
view.setLayoutParams(params);
}
/**
* Clear horizontal related layout params
* @param view view that has the layout params to be cleared
*/
private void clearHParams(View view) {
ConstraintLayout.LayoutParams params = params(view);
params.horizontalWeight = ConstraintSet.UNSET;
params.leftToRight = ConstraintSet.UNSET;
params.leftToLeft = ConstraintSet.UNSET;
params.rightToLeft = ConstraintSet.UNSET;
params.rightToRight = ConstraintSet.UNSET;
params.leftMargin = ConstraintSet.UNSET;
view.setLayoutParams(params);
}
/**
* create boxViews for constraining widgets
*/
private void buildBoxes() {
int boxCount = Math.max(mRows, mColumns);
if (mBoxViews == null) { // no box views build all
mBoxViews = new View[boxCount];
for (int i = 0; i < mBoxViews.length; i++) {
mBoxViews[i] = makeNewView(); // need to remove old Views
}
} else {
if (boxCount != mBoxViews.length) {
View[] temp = new View[boxCount];
for (int i = 0; i < boxCount; i++) {
if (i < mBoxViews.length) { // use old one
temp[i] = mBoxViews[i];
} else { // make new one
temp[i] = makeNewView();
}
}
// remove excess
for (int j = boxCount; j < mBoxViews.length; j++) {
View view = mBoxViews[j];
mContainer.removeView(view);
}
mBoxViews = temp;
}
}
mBoxViewIds = new int[boxCount];
for (int i = 0; i < mBoxViews.length; i++) {
mBoxViewIds[i] = mBoxViews[i].getId();
}
setBoxViewVerticalChains();
setBoxViewHorizontalChains();
}
/**
* get the value of rows
*
* @return the value of rows
*/
public int getRows() {
return mRowsSet;
}
/**
* set new rows value and also invoke initVariables and invalidate
*
* @param rows new rows value
*/
public void setRows(int rows) {
if (rows > mMaxRows) {
return;
}
if (mRowsSet == rows) {
return;
}
mRowsSet = rows;
updateActualRowsAndColumns();
initVariables();
generateGrid(false);
invalidate();
}
/**
* get the value of columns
*
* @return the value of columns
*/
public int getColumns() {
return mColumnsSet;
}
/**
* set new columns value and also invoke initVariables and invalidate
*
* @param columns new rows value
*/
public void setColumns(int columns) {
if (columns > mMaxColumns) {
return;
}
if (mColumnsSet == columns) {
return;
}
mColumnsSet = columns;
updateActualRowsAndColumns();
initVariables();
generateGrid(false);
invalidate();
}
/**
* get the value of orientation
*
* @return the value of orientation
*/
public int getOrientation() {
return mOrientation;
}
/**
* set new orientation value and also invoke invalidate
*
* @param orientation new orientation value
*/
public void setOrientation(int orientation) {
if (!(orientation == HORIZONTAL || orientation == VERTICAL)) {
return;
}
if (mOrientation == orientation) {
return;
}
mOrientation = orientation;
generateGrid(true);
invalidate();
}
/**
* get the string value of spans
*
* @return the string value of spans
*/
public String getSpans() {
return mStrSpans;
}
/**
* set new spans value and also invoke invalidate
*
* @param spans new spans value
*/
public void setSpans(CharSequence spans) {
if (!isSpansValid(spans)) {
return;
}
if (mStrSpans != null && mStrSpans.contentEquals(spans)) {
return;
}
mStrSpans = spans.toString();
generateGrid(true);
invalidate();
}
/**
* get the string value of skips
*
* @return the string value of skips
*/
public String getSkips() {
return mStrSkips;
}
/**
* set new skips value and also invoke invalidate
*
* @param skips new spans value
*/
public void setSkips(String skips) {
if (!isSpansValid(skips)) {
return;
}
if (mStrSkips != null && mStrSkips.equals(skips)) {
return;
}
mStrSkips = skips;
generateGrid(true);
invalidate();
}
/**
* get the string value of rowWeights
*
* @return the string value of rowWeights
*/
public String getRowWeights() {
return mStrRowWeights;
}
/**
* set new rowWeights value and also invoke invalidate
*
* @param rowWeights new rowWeights value
*/
public void setRowWeights(String rowWeights) {
if (!isWeightsValid(rowWeights)) {
return;
}
if (mStrRowWeights != null && mStrRowWeights.equals(rowWeights)) {
return;
}
mStrRowWeights = rowWeights;
generateGrid(true);
invalidate();
}
/**
* get the string value of columnWeights
*
* @return the string value of columnWeights
*/
public String getColumnWeights() {
return mStrColumnWeights;
}
/**
* set new columnWeights value and also invoke invalidate
*
* @param columnWeights new columnWeights value
*/
public void setColumnWeights(String columnWeights) {
if (!isWeightsValid(columnWeights)) {
return;
}
if (mStrColumnWeights != null && mStrColumnWeights.equals(columnWeights)) {
return;
}
mStrColumnWeights = columnWeights;
generateGrid(true);
invalidate();
}
/**
* get the value of horizontalGaps
*
* @return the value of horizontalGaps
*/
public float getHorizontalGaps() {
return mHorizontalGaps;
}
/**
* set new horizontalGaps value and also invoke invalidate
*
* @param horizontalGaps new horizontalGaps value
*/
public void setHorizontalGaps(float horizontalGaps) {
if (horizontalGaps < 0) {
return;
}
if (mHorizontalGaps == horizontalGaps) {
return;
}
mHorizontalGaps = horizontalGaps;
generateGrid(true);
invalidate();
}
/**
* get the value of verticalGaps
*
* @return the value of verticalGaps
*/
public float getVerticalGaps() {
return mVerticalGaps;
}
/**
* set new verticalGaps value and also invoke invalidate
*
* @param verticalGaps new verticalGaps value
*/
public void setVerticalGaps(float verticalGaps) {
if (verticalGaps < 0) {
return;
}
if (mVerticalGaps == verticalGaps) {
return;
}
mVerticalGaps = verticalGaps;
generateGrid(true);
invalidate();
}
}