diff --git a/widgets/src/main/java/org/nativescript/widgets/GridLayout.java b/widgets/src/main/java/org/nativescript/widgets/GridLayout.java index f45beb34a..8945130b2 100644 --- a/widgets/src/main/java/org/nativescript/widgets/GridLayout.java +++ b/widgets/src/main/java/org/nativescript/widgets/GridLayout.java @@ -1,5 +1,5 @@ /** - * + * */ package org.nativescript.widgets; @@ -13,1137 +13,1093 @@ import android.view.View.MeasureSpec; /** * @author hhristov - * */ public class GridLayout extends LayoutBase { - private MeasureHelper helper = new MeasureHelper(this); - - private ArrayList _rows = new ArrayList(); - private ArrayList _cols = new ArrayList(); - private ArrayList columnOffsets = new ArrayList(); - private ArrayList rowOffsets = new ArrayList(); - private HashMap map = new HashMap(); - - public GridLayout(Context context) { - super(context); - } + private MeasureHelper helper = new MeasureHelper(this); - private static void validateItemSpec(ItemSpec itemSpec) { - if (itemSpec == null) { - throw new Error("itemSpec is null."); - } + private ArrayList _rows = new ArrayList(); + private ArrayList _cols = new ArrayList(); + private ArrayList columnOffsets = new ArrayList(); + private ArrayList rowOffsets = new ArrayList(); + private HashMap map = new HashMap(); - if (itemSpec.owner != null) { - throw new Error("itemSpec is already added to GridLayout."); - } - } + public GridLayout(Context context) { + super(context); + } - public void addRow(ItemSpec itemSpec) { - validateItemSpec(itemSpec); - itemSpec.owner = this; - this._rows.add(itemSpec); - - ItemGroup rowGroup = new ItemGroup(itemSpec); - this.helper.rows.add(rowGroup); - - this.requestLayout(); - } + private static void validateItemSpec(ItemSpec itemSpec) { + if (itemSpec == null) { + throw new Error("itemSpec is null."); + } - public void addColumn(ItemSpec itemSpec) { - validateItemSpec(itemSpec); - itemSpec.owner = this; - this._cols.add(itemSpec); - - ItemGroup columnGroup = new ItemGroup(itemSpec); - this.helper.columns.add(columnGroup); - - this.requestLayout(); - } + if (itemSpec.owner != null) { + throw new Error("itemSpec is already added to GridLayout."); + } + } - public void removeColumn(ItemSpec itemSpec) { - if (itemSpec == null) { - throw new Error("itemSpec is null."); - } + public void addRow(ItemSpec itemSpec) { + validateItemSpec(itemSpec); + itemSpec.owner = this; + this._rows.add(itemSpec); - int index = this._cols.indexOf(itemSpec); - if (itemSpec.owner != this || index < 0) { - throw new Error("itemSpec is not child of this GridLayout"); - } + ItemGroup rowGroup = new ItemGroup(itemSpec); + this.helper.rows.add(rowGroup); - this.removeColumnAt(index); - } - - public void removeColumnAt(int index) { - this._cols.remove(index); - this.helper.columns.get(index).children.clear(); - this.helper.columns.remove(index); - this.requestLayout(); - } + this.requestLayout(); + } - public void removeRow(ItemSpec itemSpec) { - if (itemSpec == null) { - throw new Error("itemSpec is null."); - } + public void addColumn(ItemSpec itemSpec) { + validateItemSpec(itemSpec); + itemSpec.owner = this; + this._cols.add(itemSpec); - int index = this._rows.indexOf(itemSpec); - if (itemSpec.owner != this || index < 0) { - throw new Error("itemSpec is not child of this GridLayout"); - } + ItemGroup columnGroup = new ItemGroup(itemSpec); + this.helper.columns.add(columnGroup); - this.removeRowAt(index); - } - - public void removeRowAt(int index) { - this._rows.remove(index); - this.helper.rows.get(index).children.clear(); - this.helper.rows.remove(index); - this.requestLayout(); - } - - public ItemSpec[] getColumns() { - ItemSpec copy[] = new ItemSpec[this._cols.size()]; - copy = this._cols.toArray(copy); - return copy; - } - - public ItemSpec[] getRows() { - ItemSpec copy[] = new ItemSpec[this._rows.size()]; - copy = this._rows.toArray(copy); - return copy; - } - - @Override - public void addView(View child) { - super.addView(child); - this.addToMap(child); - } - - @Override - public void addView(View child, int index) { - super.addView(child, index); - this.addToMap(child); - } - - @Override - public void addView(View child, LayoutParams params) { - super.addView(child, params); - this.addToMap(child); - } - - @Override - public void removeView(View view) { - this.removeFromMap(view); - super.removeView(view); - } - - @Override - public void removeViewAt(int index) { - View view = this.getChildAt(index); - this.removeFromMap(view); - super.removeViewAt(index); - } - - @Override - public void removeViews(int start, int count) { - int end = start + count; - for (int i = start; i < end; i++) { - View view = this.getChildAt(i); - this.removeFromMap(view); - } - - super.removeViews(start, count); - } - - private int getColumnIndex(CommonLayoutParams lp) { - return Math.max(0, Math.min(lp.column, this._cols.size() - 1)); - } - - private int getRowIndex(CommonLayoutParams lp) { - return Math.max(0, Math.min(lp.row, this._rows.size() - 1)); - } + this.requestLayout(); + } - private ItemSpec getColumnSpec(CommonLayoutParams lp) { - if (this._cols.size() == 0) { - return this.helper.singleColumn; - } + public void removeColumn(ItemSpec itemSpec) { + if (itemSpec == null) { + throw new Error("itemSpec is null."); + } - int columnIndex = Math.min(lp.column, this._cols.size() - 1); - return this._cols.get(columnIndex); - } + int index = this._cols.indexOf(itemSpec); + if (itemSpec.owner != this || index < 0) { + throw new Error("itemSpec is not child of this GridLayout"); + } - private ItemSpec getRowSpec(CommonLayoutParams lp) { - if (this._rows.size() == 0) { - return this.helper.singleRow; - } + this.removeColumnAt(index); + } - int rowIndex = Math.min(lp.row, this._rows.size() - 1); - return this._rows.get(rowIndex); - } + public void removeColumnAt(int index) { + this._cols.remove(index); + this.helper.columns.get(index).children.clear(); + this.helper.columns.remove(index); + this.requestLayout(); + } - private int getColumnSpan(CommonLayoutParams lp, int columnIndex) { - if (this._cols.size() == 0) { - return 1; - } + public void removeRow(ItemSpec itemSpec) { + if (itemSpec == null) { + throw new Error("itemSpec is null."); + } - return Math.min(lp.columnSpan, this._cols.size() - columnIndex); - } + int index = this._rows.indexOf(itemSpec); + if (itemSpec.owner != this || index < 0) { + throw new Error("itemSpec is not child of this GridLayout"); + } - private int getRowSpan(CommonLayoutParams lp, int rowIndex) { - if (this._rows.size() == 0) { - return 1; - } + this.removeRowAt(index); + } - return Math.min(lp.rowSpan, this._rows.size() - rowIndex); - } - - private void updateMeasureSpecs(View child, MeasureSpecs measureSpec) { - CommonLayoutParams lp = (CommonLayoutParams)child.getLayoutParams(); - int columnIndex = this.getColumnIndex(lp); - ItemSpec column = this.getColumnSpec(lp); - int rowIndex = this.getRowIndex(lp); - ItemSpec row = this.getRowSpec(lp); - int columnSpan = this.getColumnSpan(lp, columnIndex); - int rowSpan = this.getRowSpan(lp, rowIndex); + public void removeRowAt(int index) { + this._rows.remove(index); + this.helper.rows.get(index).children.clear(); + this.helper.rows.remove(index); + this.requestLayout(); + } - measureSpec.setColumn(column); - measureSpec.setRow(row); - measureSpec.setColumnIndex(columnIndex); - measureSpec.setRowIndex(rowIndex); - measureSpec.setColumnSpan(columnSpan); - measureSpec.setRowSpan(rowSpan); - measureSpec.autoColumnsCount = 0; - measureSpec.autoRowsCount = 0; - measureSpec.measured = false; - measureSpec.pixelHeight = 0; - measureSpec.pixelWidth = 0; - measureSpec.starColumnsCount = 0; - measureSpec.starRowsCount = 0; - } - - private void addToMap(View child) { - MeasureSpecs measureSpec = new MeasureSpecs(child); - this.map.put(child, measureSpec); - } - - private void removeFromMap(View child) { - this.map.remove(child); - } + public ItemSpec[] getColumns() { + ItemSpec copy[] = new ItemSpec[this._cols.size()]; + copy = this._cols.toArray(copy); + return copy; + } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + public ItemSpec[] getRows() { + ItemSpec copy[] = new ItemSpec[this._rows.size()]; + copy = this._rows.toArray(copy); + return copy; + } + + @Override + public void addView(View child) { + super.addView(child); + this.addToMap(child); + } + + @Override + public void addView(View child, int index) { + super.addView(child, index); + this.addToMap(child); + } + + @Override + public void addView(View child, LayoutParams params) { + super.addView(child, params); + this.addToMap(child); + } + + @Override + public void removeView(View view) { + this.removeFromMap(view); + super.removeView(view); + } + + @Override + public void removeViewAt(int index) { + View view = this.getChildAt(index); + this.removeFromMap(view); + super.removeViewAt(index); + } + + @Override + public void removeViews(int start, int count) { + int end = start + count; + for (int i = start; i < end; i++) { + View view = this.getChildAt(i); + this.removeFromMap(view); + } + + super.removeViews(start, count); + } + + private int getColumnIndex(CommonLayoutParams lp) { + return Math.max(0, Math.min(lp.column, this._cols.size() - 1)); + } + + private int getRowIndex(CommonLayoutParams lp) { + return Math.max(0, Math.min(lp.row, this._rows.size() - 1)); + } + + private ItemSpec getColumnSpec(CommonLayoutParams lp) { + if (this._cols.size() == 0) { + return this.helper.singleColumn; + } + + int columnIndex = Math.min(lp.column, this._cols.size() - 1); + return this._cols.get(columnIndex); + } + + private ItemSpec getRowSpec(CommonLayoutParams lp) { + if (this._rows.size() == 0) { + return this.helper.singleRow; + } + + int rowIndex = Math.min(lp.row, this._rows.size() - 1); + return this._rows.get(rowIndex); + } + + private int getColumnSpan(CommonLayoutParams lp, int columnIndex) { + if (this._cols.size() == 0) { + return 1; + } + + return Math.min(lp.columnSpan, this._cols.size() - columnIndex); + } + + private int getRowSpan(CommonLayoutParams lp, int rowIndex) { + if (this._rows.size() == 0) { + return 1; + } + + return Math.min(lp.rowSpan, this._rows.size() - rowIndex); + } + + private void updateMeasureSpecs(View child, MeasureSpecs measureSpec) { + CommonLayoutParams lp = (CommonLayoutParams) child.getLayoutParams(); + int columnIndex = this.getColumnIndex(lp); + ItemSpec column = this.getColumnSpec(lp); + int rowIndex = this.getRowIndex(lp); + ItemSpec row = this.getRowSpec(lp); + int columnSpan = this.getColumnSpan(lp, columnIndex); + int rowSpan = this.getRowSpan(lp, rowIndex); + + measureSpec.setColumn(column); + measureSpec.setRow(row); + measureSpec.setColumnIndex(columnIndex); + measureSpec.setRowIndex(rowIndex); + measureSpec.setColumnSpan(columnSpan); + measureSpec.setRowSpan(rowSpan); + measureSpec.autoColumnsCount = 0; + measureSpec.autoRowsCount = 0; + measureSpec.measured = false; + measureSpec.pixelHeight = 0; + measureSpec.pixelWidth = 0; + measureSpec.starColumnsCount = 0; + measureSpec.starRowsCount = 0; + } + + private void addToMap(View child) { + MeasureSpecs measureSpec = new MeasureSpecs(child); + this.map.put(child, measureSpec); + } + + private void removeFromMap(View child) { + this.map.remove(child); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { CommonLayoutParams.adjustChildrenLayoutParams(this, widthMeasureSpec, heightMeasureSpec); int measureWidth = 0; - int measureHeight = 0; + int measureHeight = 0; - int width = MeasureSpec.getSize(widthMeasureSpec); - int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int height = MeasureSpec.getSize(heightMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int verticalPadding = this.getPaddingTop() + this.getPaddingBottom(); - int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight(); + int verticalPadding = this.getPaddingTop() + this.getPaddingBottom(); + int horizontalPadding = this.getPaddingLeft() + this.getPaddingRight(); - boolean infinityWidth = widthMode == MeasureSpec.UNSPECIFIED; - boolean infinityHeight = heightMode == MeasureSpec.UNSPECIFIED; + boolean infinityWidth = widthMode == MeasureSpec.UNSPECIFIED; + boolean infinityHeight = heightMode == MeasureSpec.UNSPECIFIED; - this.helper.width = Math.max(0, width - horizontalPadding); - this.helper.height = Math.max(0, height - verticalPadding); + this.helper.width = Math.max(0, width - horizontalPadding); + this.helper.height = Math.max(0, height - verticalPadding); - int gravity = LayoutBase.getGravity(this); + int gravity = LayoutBase.getGravity(this); int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; final int layoutDirection = this.getLayoutDirection(); final int horizontalGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection) & Gravity.HORIZONTAL_GRAVITY_MASK; - - this.helper.stretchedHorizontally = widthMode == MeasureSpec.EXACTLY || (horizontalGravity == Gravity.FILL_HORIZONTAL && !infinityWidth); - this.helper.stretchedVertically = heightMode == MeasureSpec.EXACTLY || (verticalGravity == Gravity.FILL_VERTICAL && !infinityHeight); - - this.helper.setInfinityWidth(infinityWidth); - this.helper.setInfinityHeight(infinityHeight); - - this.helper.clearMeasureSpecs(); - this.helper.init(); - - for (int i = 0, count = this.getChildCount(); i < count; i++) { - View child = this.getChildAt(i); - if (child.getVisibility() == View.GONE) { - continue; - } - MeasureSpecs measureSpecs = this.map.get(child); - this.updateMeasureSpecs(child, measureSpecs); - this.helper.addMeasureSpec(measureSpecs); - } + this.helper.stretchedHorizontally = widthMode == MeasureSpec.EXACTLY || (horizontalGravity == Gravity.FILL_HORIZONTAL && !infinityWidth); + this.helper.stretchedVertically = heightMode == MeasureSpec.EXACTLY || (verticalGravity == Gravity.FILL_VERTICAL && !infinityHeight); - this.helper.measure(); + this.helper.setInfinityWidth(infinityWidth); + this.helper.setInfinityHeight(infinityHeight); - // Add in our padding - measureWidth = this.helper.measuredWidth + horizontalPadding; - measureHeight = this.helper.measuredHeight + verticalPadding; + this.helper.clearMeasureSpecs(); + this.helper.init(); - // Check against our minimum sizes - measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth()); - measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight()); + for (int i = 0, count = this.getChildCount(); i < count; i++) { + View child = this.getChildAt(i); + if (child.getVisibility() == View.GONE) { + continue; + } - int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0); - int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0); + MeasureSpecs measureSpecs = this.map.get(child); + this.updateMeasureSpecs(child, measureSpecs); + this.helper.addMeasureSpec(measureSpecs); + } - this.setMeasuredDimension(widthSizeAndState, heightSizeAndState); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int paddingLeft = this.getPaddingLeft(); - int paddingTop = this.getPaddingTop(); - - this.columnOffsets.clear(); - this.rowOffsets.clear(); - - this.columnOffsets.add(paddingLeft); - this.rowOffsets.add(paddingTop); - - float offset = this.columnOffsets.get(0); - int roundedOffset = paddingLeft; - int roundedLength = 0; - float actualLength = 0; - int size = this.helper.columns.size(); - for (int i = 0; i < size; i++) { - ItemGroup columnGroup = this.helper.columns.get(i); - offset += columnGroup.length; - - actualLength = offset - roundedOffset; - roundedLength = Math.round(actualLength); - columnGroup.rowOrColumn._actualLength = roundedLength; - roundedOffset += roundedLength; - - this.columnOffsets.add(roundedOffset); - } - - offset = this.rowOffsets.get(0); - roundedOffset = this.getPaddingTop(); - roundedLength = 0; - actualLength = 0; - size = this.helper.rows.size(); - for (int i = 0; i < size; i++) { - ItemGroup rowGroup = this.helper.rows.get(i); - offset += rowGroup.length; - - actualLength = offset - roundedOffset; - roundedLength = Math.round(actualLength); - rowGroup.rowOrColumn._actualLength = roundedLength; - roundedOffset += roundedLength; - - this.rowOffsets.add(roundedOffset); - } - - int columns = this.helper.columns.size(); - for (int i = 0; i < columns; i++) { - ItemGroup columnGroup = this.helper.columns.get(i); - int children = columnGroup.children.size(); - for (int j = 0; j < children; j++) { - - MeasureSpecs measureSpec = columnGroup.children.get(j); - int childLeft = this.columnOffsets.get(measureSpec.getColumnIndex()); - int childRight = this.columnOffsets.get(measureSpec.getColumnIndex() + measureSpec.getColumnSpan()); - int childTop = this.rowOffsets.get(measureSpec.getRowIndex()); - int childBottom = this.rowOffsets.get(measureSpec.getRowIndex() + measureSpec.getRowSpan()); - - // No need to include margins in the width, height - CommonLayoutParams.layoutChild(measureSpec.child, childLeft, childTop, childRight, childBottom); - } - } + this.helper.measure(); - CommonLayoutParams.restoreOriginalParams(this); - } + // Add in our padding + measureWidth = this.helper.measuredWidth + horizontalPadding; + measureHeight = this.helper.measuredHeight + verticalPadding; + + // Check against our minimum sizes + measureWidth = Math.max(measureWidth, this.getSuggestedMinimumWidth()); + measureHeight = Math.max(measureHeight, this.getSuggestedMinimumHeight()); + + int widthSizeAndState = resolveSizeAndState(measureWidth, widthMeasureSpec, 0); + int heightSizeAndState = resolveSizeAndState(measureHeight, heightMeasureSpec, 0); + + this.setMeasuredDimension(widthSizeAndState, heightSizeAndState); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int paddingLeft = this.getPaddingLeft(); + int paddingTop = this.getPaddingTop(); + + this.columnOffsets.clear(); + this.rowOffsets.clear(); + + this.columnOffsets.add(paddingLeft); + this.rowOffsets.add(paddingTop); + + float offset = this.columnOffsets.get(0); + int roundedOffset = paddingLeft; + int roundedLength = 0; + float actualLength = 0; + int size = this.helper.columns.size(); + for (int i = 0; i < size; i++) { + ItemGroup columnGroup = this.helper.columns.get(i); + offset += columnGroup.length; + + actualLength = offset - roundedOffset; + roundedLength = Math.round(actualLength); + columnGroup.rowOrColumn._actualLength = roundedLength; + roundedOffset += roundedLength; + + this.columnOffsets.add(roundedOffset); + } + + offset = this.rowOffsets.get(0); + roundedOffset = this.getPaddingTop(); + roundedLength = 0; + actualLength = 0; + size = this.helper.rows.size(); + for (int i = 0; i < size; i++) { + ItemGroup rowGroup = this.helper.rows.get(i); + offset += rowGroup.length; + + actualLength = offset - roundedOffset; + roundedLength = Math.round(actualLength); + rowGroup.rowOrColumn._actualLength = roundedLength; + roundedOffset += roundedLength; + + this.rowOffsets.add(roundedOffset); + } + + int columns = this.helper.columns.size(); + for (int i = 0; i < columns; i++) { + ItemGroup columnGroup = this.helper.columns.get(i); + int children = columnGroup.children.size(); + for (int j = 0; j < children; j++) { + + MeasureSpecs measureSpec = columnGroup.children.get(j); + int childLeft = this.columnOffsets.get(measureSpec.getColumnIndex()); + int childRight = this.columnOffsets.get(measureSpec.getColumnIndex() + measureSpec.getColumnSpan()); + int childTop = this.rowOffsets.get(measureSpec.getRowIndex()); + int childBottom = this.rowOffsets.get(measureSpec.getRowIndex() + measureSpec.getRowSpan()); + + // No need to include margins in the width, height + CommonLayoutParams.layoutChild(measureSpec.child, childLeft, childTop, childRight, childBottom); + } + } + + CommonLayoutParams.restoreOriginalParams(this); + } } class MeasureSpecs { - private int _columnSpan = 1; - private int _rowSpan = 1; - - public int pixelWidth = 0; - public int pixelHeight = 0; + private int _columnSpan = 1; + private int _rowSpan = 1; - public int starColumnsCount = 0; - public int starRowsCount = 0; + public int pixelWidth = 0; + public int pixelHeight = 0; - public int autoColumnsCount = 0; - public int autoRowsCount = 0; - - public boolean measured = false; + public int starColumnsCount = 0; + public int starRowsCount = 0; - public final View child; - private ItemSpec column; - private ItemSpec row; - private int columnIndex; - private int rowIndex; - - MeasureSpecs(View child) { - this.child = child; - } + public int autoColumnsCount = 0; + public int autoRowsCount = 0; - public boolean getSpanned() { - return this._columnSpan > 1 || this._rowSpan > 1; - } + public boolean measured = false; - public boolean getIsStar() { - return this.starRowsCount > 0 || this.starColumnsCount > 0; - } - - public int getColumnSpan() { - return this._columnSpan; - } + public final View child; + private ItemSpec column; + private ItemSpec row; + private int columnIndex; + private int rowIndex; - public int getRowSpan() { - return this._rowSpan; - } - - public void setRowSpan(int value) { - // cannot have zero rowSpan. - this._rowSpan = Math.max(1, value); - } - - public void setColumnSpan(int value) { - // cannot have zero colSpan. - this._columnSpan = Math.max(1, value); - } + MeasureSpecs(View child) { + this.child = child; + } - public int getRowIndex() { - return this.rowIndex; - } - - public int getColumnIndex() { - return this.columnIndex; - } - - public void setRowIndex(int value) { - this.rowIndex = value; - } - - public void setColumnIndex(int value) { - this.columnIndex = value; - } - - public ItemSpec getRow() { - return this.row; - } - - public ItemSpec getColumn() { - return this.column; - } - - public void setRow(ItemSpec value) { - this.row = value; - } - - public void setColumn(ItemSpec value) { - this.column = value; - } + public boolean getSpanned() { + return this._columnSpan > 1 || this._rowSpan > 1; + } + + public boolean getIsStar() { + return this.starRowsCount > 0 || this.starColumnsCount > 0; + } + + public int getColumnSpan() { + return this._columnSpan; + } + + public int getRowSpan() { + return this._rowSpan; + } + + public void setRowSpan(int value) { + // cannot have zero rowSpan. + this._rowSpan = Math.max(1, value); + } + + public void setColumnSpan(int value) { + // cannot have zero colSpan. + this._columnSpan = Math.max(1, value); + } + + public int getRowIndex() { + return this.rowIndex; + } + + public int getColumnIndex() { + return this.columnIndex; + } + + public void setRowIndex(int value) { + this.rowIndex = value; + } + + public void setColumnIndex(int value) { + this.columnIndex = value; + } + + public ItemSpec getRow() { + return this.row; + } + + public ItemSpec getColumn() { + return this.column; + } + + public void setRow(ItemSpec value) { + this.row = value; + } + + public void setColumn(ItemSpec value) { + this.column = value; + } } class ItemGroup { - float length = 0; - int measuredCount = 0; - ItemSpec rowOrColumn; - ArrayList children = new ArrayList(); + int length = 0; + int measuredCount = 0; + ItemSpec rowOrColumn; + ArrayList children = new ArrayList(); - public int measureToFix = 0; - public int currentMeasureToFixCount = 0; - private boolean infinityLength = false; - - ItemGroup(ItemSpec spec) { - this.rowOrColumn = spec; - } - - public void setIsLengthInfinity(boolean infinityLength) { - this.infinityLength = infinityLength; - } + public int measureToFix = 0; + public int currentMeasureToFixCount = 0; + private boolean infinityLength = false; - public void init() { - this.measuredCount = 0; - this.currentMeasureToFixCount = 0; - this.length = this.rowOrColumn.getIsAbsolute() ? this.rowOrColumn.getValue() : 0; - } + ItemGroup(ItemSpec spec) { + this.rowOrColumn = spec; + } - public boolean getAllMeasured() { - return this.measuredCount == this.children.size(); - } + public void setIsLengthInfinity(boolean infinityLength) { + this.infinityLength = infinityLength; + } - public boolean getCanBeFixed() { - return this.currentMeasureToFixCount == this.measureToFix; - } - - public boolean getIsAuto() { - return this.rowOrColumn.getIsAuto() || (this.rowOrColumn.getIsStar() && this.infinityLength); - } - - public boolean getIsStar() { - return this.rowOrColumn.getIsStar() && !this.infinityLength; - } - - public boolean getIsAbsolute() { - return this.rowOrColumn.getIsAbsolute(); - } + public void init() { + this.measuredCount = 0; + this.currentMeasureToFixCount = 0; + this.length = this.rowOrColumn.getIsAbsolute() ? this.rowOrColumn.getValue() : 0; + } + + public boolean getAllMeasured() { + return this.measuredCount == this.children.size(); + } + + public boolean getCanBeFixed() { + return this.currentMeasureToFixCount == this.measureToFix; + } + + public boolean getIsAuto() { + return this.rowOrColumn.getIsAuto() || (this.rowOrColumn.getIsStar() && this.infinityLength); + } + + public boolean getIsStar() { + return this.rowOrColumn.getIsStar() && !this.infinityLength; + } + + public boolean getIsAbsolute() { + return this.rowOrColumn.getIsAbsolute(); + } } class MeasureHelper { - public final ItemSpec singleRow = new ItemSpec(); - public final ItemSpec singleColumn = new ItemSpec(); - public final GridLayout grid; - - static int infinity = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - ArrayList rows = new ArrayList(); - ArrayList columns = new ArrayList(); + public final ItemSpec singleRow = new ItemSpec(); + public final ItemSpec singleColumn = new ItemSpec(); + public final GridLayout grid; - public int width; - public int height; - public boolean stretchedHorizontally = false; - public boolean stretchedVertically = false; - - private boolean infinityWidth = false; - private boolean infinityHeight = false; + static int infinity = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + ArrayList rows = new ArrayList(); + ArrayList columns = new ArrayList(); - int measuredWidth; - int measuredHeight; + public int width; + public int height; + public boolean stretchedHorizontally = false; + public boolean stretchedVertically = false; - private float columnStarValue; - private float rowStarValue; - - private boolean fakeRowAdded = false; - private boolean fakeColumnAdded = false; + private boolean infinityWidth = false; + private boolean infinityHeight = false; - MeasureHelper(GridLayout grid) { - this.grid = grid; - } + private float minColumnStarValue; + private float maxColumnStarValue; - public boolean getColumnsFixed() { - return this.columnStarValue >= 0; - } - - public void setInfinityWidth(boolean value) { - if (this.infinityWidth != value) { - this.infinityWidth = value; - - for(int i = 0, size = this.columns.size(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - columnGroup.setIsLengthInfinity(value); - } - } - } - - public void setInfinityHeight(boolean value) { - if (this.infinityHeight != value) { - this.infinityHeight = value; + private float minRowStarValue; + private float maxRowStarValue; - for(int i = 0, size = this.rows.size(); i < size; i++) { - ItemGroup rowGroup = this.rows.get(i); - rowGroup.setIsLengthInfinity(value); - } - } - } + int measuredWidth; + int measuredHeight; - public void addMeasureSpec(MeasureSpecs measureSpec) { - // Get column stats - int size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); - for (int i = measureSpec.getColumnIndex(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getIsAuto()) { - measureSpec.autoColumnsCount++; - } - else if (columnGroup.getIsStar()) { - measureSpec.starColumnsCount += columnGroup.rowOrColumn.getValue(); - } - else if (columnGroup.getIsAbsolute()) { - measureSpec.pixelWidth += columnGroup.rowOrColumn.getValue(); - } - } + private boolean fakeRowAdded = false; + private boolean fakeColumnAdded = false; - if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { - // Determine which auto columns are affected by this element - for (int i = measureSpec.getColumnIndex(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getIsAuto()) { - columnGroup.measureToFix++; - } - } - } + MeasureHelper(GridLayout grid) { + this.grid = grid; + } - // Get row stats - size = measureSpec.getRowIndex() + measureSpec.getRowSpan(); - for (int i = measureSpec.getRowIndex(); i < size; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsAuto()) { - measureSpec.autoRowsCount++; - } - else if (rowGroup.getIsStar()) { - measureSpec.starRowsCount += rowGroup.rowOrColumn.getValue(); - } - else if (rowGroup.getIsAbsolute()) { - measureSpec.pixelHeight += rowGroup.rowOrColumn.getValue(); - } - } + public void setInfinityWidth(boolean value) { + if (this.infinityWidth != value) { + this.infinityWidth = value; - if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount == 0) { - // Determine which auto rows are affected by this element - for (int i = measureSpec.getRowIndex(); i < size; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsAuto()) { - rowGroup.measureToFix++; - } - } - } + for (int i = 0, size = this.columns.size(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + columnGroup.setIsLengthInfinity(value); + } + } + } - this.columns.get(measureSpec.getColumnIndex()).children.add(measureSpec); - this.rows.get(measureSpec.getRowIndex()).children.add(measureSpec); - } - - public void clearMeasureSpecs() { - for (int i = 0, size = this.columns.size(); i < size; i++) { - this.columns.get(i).children.clear(); - } + public void setInfinityHeight(boolean value) { + if (this.infinityHeight != value) { + this.infinityHeight = value; - for (int i = 0, size = this.rows.size(); i < size; i++) { - this.rows.get(i).children.clear(); - } - } + for (int i = 0, size = this.rows.size(); i < size; i++) { + ItemGroup rowGroup = this.rows.get(i); + rowGroup.setIsLengthInfinity(value); + } + } + } - private static void initList(ArrayList list) { - for(int i = 0, size = list.size(); i < size; i++) { - ItemGroup item = list.get(i); - item.init(); - } - } + public void addMeasureSpec(MeasureSpecs measureSpec) { + // Get column stats + int size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); + for (int i = measureSpec.getColumnIndex(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getIsAuto()) { + measureSpec.autoColumnsCount++; + } else if (columnGroup.getIsStar()) { + measureSpec.starColumnsCount += columnGroup.rowOrColumn.getValue(); + } else if (columnGroup.getIsAbsolute()) { + measureSpec.pixelWidth += columnGroup.rowOrColumn.getValue(); + } + } - private ItemGroup singleRowGroup = new ItemGroup(singleRow); - private ItemGroup singleColumnGroup = new ItemGroup(singleColumn); - - void init() { - - int rows = this.rows.size(); - if (rows == 0) { - singleRowGroup.setIsLengthInfinity(this.infinityHeight); - this.rows.add(singleRowGroup); - this.fakeRowAdded = true; - } else if (rows > 1 && this.fakeRowAdded) { - this.rows.remove(0); - this.fakeRowAdded = false; - } - - int cols = this.columns.size(); - if (cols == 0) { - this.fakeColumnAdded = true; - singleColumnGroup.setIsLengthInfinity(this.infinityWidth); - this.columns.add(singleColumnGroup); - } - else if (cols > 1 && this.fakeColumnAdded) { - this.columns.remove(0); - this.fakeColumnAdded = false; - } - - initList(this.rows); - initList(this.columns); - - this.columnStarValue = -1; - this.rowStarValue = -1; - } - - private void itemMeasured(MeasureSpecs measureSpec, boolean isFakeMeasure) { - if (!isFakeMeasure) { - this.columns.get(measureSpec.getColumnIndex()).measuredCount++; - this.rows.get(measureSpec.getRowIndex()).measuredCount++; - measureSpec.measured = true; - } - - if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { - int size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); - for (int i = measureSpec.getColumnIndex(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getIsAuto()) { - columnGroup.currentMeasureToFixCount++; - } - } - } - - if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount == 0) { - int size = measureSpec.getRowIndex() + measureSpec.getRowSpan(); - for (int i = measureSpec.getRowIndex(); i < size; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsAuto()) { - rowGroup.currentMeasureToFixCount++; - } - } - } - } - - private void fixColumns() { - float currentColumnWidth = 0; - int columnStarCount = 0; - - int columnCount = this.columns.size(); - for(int i = 0; i < columnCount; i++) { - ItemGroup item = this.columns.get(i); - - // Star columns are still zeros (not calculated). - currentColumnWidth += item.length; - if (item.rowOrColumn.getIsStar()) { - columnStarCount += item.rowOrColumn.getValue(); - } - } - - this.columnStarValue = columnStarCount > 0 ? (this.width - currentColumnWidth) / columnStarCount : 0; - - if (this.stretchedHorizontally) { - for (int i = 0; i < columnCount; i++) { - ItemGroup item = this.columns.get(i); - if (item.getIsStar()) { - item.length = item.rowOrColumn.getValue() * this.columnStarValue; - } - } - } - } - - private void fixRows() { - float currentRowHeight = 0; - int rowStarCount = 0; - - int rowCount = this.rows.size(); - for(int i = 0; i < rowCount; i++) { - ItemGroup item = this.rows.get(i); - - // Star rows are still zeros (not calculated). - currentRowHeight += item.length; - if (item.rowOrColumn.getIsStar()) { - rowStarCount += item.rowOrColumn.getValue(); - } - } - - this.rowStarValue = rowStarCount > 0 ? (this.height - currentRowHeight) / rowStarCount : 0; - - if(this.stretchedVertically) { - for (int i = 0; i < rowCount; i++) { - ItemGroup item = this.rows.get(i); - if (item.getIsStar()) { - item.length = item.rowOrColumn.getValue() * this.rowStarValue; + if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { + // Determine which auto columns are affected by this element + for (int i = measureSpec.getColumnIndex(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getIsAuto()) { + columnGroup.measureToFix++; } } } - } - - private void fakeMeasure() { - // Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height - for (int i = 0, size = this.columns.size(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getAllMeasured()) { - continue; - } - - for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (measureSpec.starRowsCount > 0 && measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { - this.measureChild(measureSpec, true); - } - } - } - } - - private void measureFixedColumnsNoStarRows() { - for (int i = 0, size = this.columns.size(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (measureSpec.starColumnsCount > 0 && measureSpec.starRowsCount == 0) { - this.measureChildFixedColumns(measureSpec); - } - } - } - } - - private void measureNoStarColumnsFixedRows() { - for (int i = 0, size = this.columns.size(); i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount ; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (measureSpec.starRowsCount > 0 && measureSpec.starColumnsCount == 0) { - this.measureChildFixedRows(measureSpec); - } - } - } - } - - private static boolean canFix(ArrayList list) { - for (int i = 0, size = list.size(); i < size; i++) { - ItemGroup item = list.get(i); - if(!item.getCanBeFixed()) { - return false; - } - } - - return true; - } - - private static int getMeasureLength(ArrayList list) { - int result = 0; - for (int i = 0, size = list.size(); i < size; i++) { - ItemGroup item = list.get(i); - result += item.length; - } - - return result; - } - - public void measure() { - - // Measure auto & pixel columns and rows (no spans). - int size = this.columns.size(); - for (int i = 0; i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (measureSpec.getIsStar() || measureSpec.getSpanned()) { - continue; - } - - this.measureChild(measureSpec, false); - } - } - - // Measure auto & pixel columns and rows (with spans). - for (int i = 0; i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (measureSpec.getIsStar() || !measureSpec.getSpanned()) { - continue; - } - - this.measureChild(measureSpec, false); - } - } - - // try fix stars! - boolean fixColumns = canFix(this.columns); - boolean fixRows = canFix(this.rows); - - if (fixColumns) { - this.fixColumns(); - } - - if (fixRows) { - this.fixRows(); - } - - if (!fixColumns && !fixRows) { - // Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height - // should be able to fix rows after that - this.fakeMeasure(); - - this.fixColumns(); - - // Measure all elements that have star columns(already fixed), but no stars at the rows - this.measureFixedColumnsNoStarRows(); - - this.fixRows(); - } - else if (fixColumns && !fixRows) { - // Measure all elements that have star columns(already fixed) but no stars at the rows - this.measureFixedColumnsNoStarRows(); - - // Then - this.fixRows(); - } - else if (!fixColumns && fixRows) { - // Measure all elements that have star rows(already fixed) but no star at the columns - this.measureNoStarColumnsFixedRows(); - - // Then - this.fixColumns(); - } - - // Rows and columns are fixed here - measure remaining elements - size = this.columns.size(); - for (int i = 0; i < size; i++) { - ItemGroup columnGroup = this.columns.get(i); - for (int j = 0, childCount = columnGroup.children.size(); j < childCount; j++) { - MeasureSpecs measureSpec = columnGroup.children.get(j); - if (!measureSpec.measured) { - this.measureChildFixedColumnsAndRows(measureSpec); - } - } - } - - this.measuredWidth = getMeasureLength(this.columns); - this.measuredHeight = getMeasureLength(this.rows); - } - - private void measureChild(MeasureSpecs measureSpec, boolean isFakeMeasure) { - int widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelWidth, MeasureSpec.EXACTLY); - int heightMeasureSpec = (isFakeMeasure || measureSpec.autoRowsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelHeight, MeasureSpec.EXACTLY); - - CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); - final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); - final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); - - int columnSpanEnd = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); - int rowSpanEnd = measureSpec.getRowIndex() + measureSpec.getRowSpan(); - - if (measureSpec.autoColumnsCount > 0) { - int remainingSpace = childMeasuredWidth; - - for (int i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - remainingSpace -= columnGroup.length; - } - - if (remainingSpace > 0) { - int growSize = remainingSpace / measureSpec.autoColumnsCount; - for (int i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getIsAuto()) { - columnGroup.length += growSize; - } - } - } - } - - if (!isFakeMeasure && measureSpec.autoRowsCount > 0) { - int remainingSpace = childMeasuredHeight; - - for (int i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - remainingSpace -= rowGroup.length; - } - - if (remainingSpace > 0) { - int growSize = remainingSpace / measureSpec.autoRowsCount; - for (int i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsAuto()) { - rowGroup.length += growSize; - } - } - } - } - - this.itemMeasured(measureSpec, isFakeMeasure); - } - - private void measureChildFixedColumns(MeasureSpecs measureSpec) { - int columnIndex = measureSpec.getColumnIndex(); - int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); - int rowIndex = measureSpec.getRowIndex(); - int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); - - int columnsWidth = 0; - int growSize = 0; - - for (int i = columnIndex; i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (!columnGroup.getIsStar()) { - columnsWidth += columnGroup.length; - } - } - - int measureWidth = (int)(columnsWidth + measureSpec.starColumnsCount * this.columnStarValue); - - int widthMeasureSpec = MeasureSpec.makeMeasureSpec(measureWidth, this.stretchedHorizontally ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST); - int heightMeasureSpec = (measureSpec.autoRowsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelHeight, MeasureSpec.EXACTLY); - - CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); - final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); - final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); - - // Distribute width over star columns - if (!this.stretchedHorizontally) { - int remainingSpace = childMeasuredWidth; - for (int i = columnIndex; i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - remainingSpace -= columnGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.starColumnsCount; - for (int i = columnIndex; i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - if (columnGroup.getIsStar()) { - columnGroup.length += growSize; - } - } - } - } - - // Distribute height over auto rows - if (measureSpec.autoRowsCount > 0) { - int remainingSpace = childMeasuredHeight; - - for (int i = rowIndex; i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - remainingSpace -= rowGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.autoRowsCount; - for (int i = rowIndex; i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsAuto()) { - rowGroup.length += growSize; - } - } - } - } - - this.itemMeasured(measureSpec, false); - } - - private void measureChildFixedRows(MeasureSpecs measureSpec) { - int columnIndex = measureSpec.getColumnIndex(); - int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); - int rowIndex = measureSpec.getRowIndex(); - int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); - int rowsHeight = 0; - - for (int i = rowIndex; i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (!rowGroup.getIsStar()) { - rowsHeight += rowGroup.length; - } - } - - int measureHeight = (int)(rowsHeight + measureSpec.starRowsCount * this.rowStarValue); - - int widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelWidth, MeasureSpec.EXACTLY); - int heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, this.stretchedVertically ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST); - - CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); - final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); - final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); - - int remainingSpace = 0; - int growSize = 0; - - // Distribute width over auto columns - if (measureSpec.autoColumnsCount > 0) { - remainingSpace = childMeasuredWidth; - - for (int i = columnIndex; i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - remainingSpace -= columnGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.autoColumnsCount; - for (int i = columnIndex; i < columnSpanEnd; i++) { - ItemGroup columnGroup = this.columns.get(i); - - if (columnGroup.getIsAuto()) { - columnGroup.length += growSize; - } - } - } - } - - // Distribute height over star rows - if (!this.stretchedVertically) { - remainingSpace = childMeasuredHeight; - for (int i = rowIndex; i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - remainingSpace -= rowGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.starRowsCount; - for (int i = rowIndex; i < rowSpanEnd; i++) { - ItemGroup rowGroup = this.rows.get(i); - if (rowGroup.getIsStar()) { - rowGroup.length += growSize; - } - } - } - } - - this.itemMeasured(measureSpec, false); - } - - private void measureChildFixedColumnsAndRows(MeasureSpecs measureSpec) { - int columnIndex = measureSpec.getColumnIndex(); - int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); - int rowIndex = measureSpec.getRowIndex(); - int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); - - ItemGroup columnGroup; - ItemGroup rowGroup; - - float columnsWidth = 0; - for (int i = columnIndex; i < columnSpanEnd; i++) { - columnGroup = this.columns.get(i); - if (!columnGroup.getIsStar()) { - columnsWidth += columnGroup.length; - } - } - - float rowsHeight = 0; - for (int i = rowIndex; i < rowSpanEnd; i++) { - rowGroup = this.rows.get(i); - if (!rowGroup.getIsStar()) { - rowsHeight += rowGroup.length; - } - } - - int measureWidth = (int)(columnsWidth + measureSpec.starColumnsCount * this.columnStarValue); - int measureHeight = (int)(rowsHeight + measureSpec.starRowsCount * this.rowStarValue); - - // if (have stars) & (not stretch) - at most - int widthMeasureSpec = MeasureSpec.makeMeasureSpec(measureWidth, - (measureSpec.starColumnsCount > 0 && !this.stretchedHorizontally) ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY); - - int heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, - (measureSpec.starRowsCount > 0 && !this.stretchedVertically) ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY); - - CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); - final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); - final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); - - int remainingSpace = childMeasuredWidth; - int growSize = 0; - - if (!this.stretchedHorizontally) { - for (int i = columnIndex; i < columnSpanEnd; i++) { - columnGroup = this.columns.get(i); - remainingSpace -= columnGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.starColumnsCount; - for (int i = columnIndex; i < columnSpanEnd; i++) { - columnGroup = this.columns.get(i); - if (columnGroup.getIsStar()) { - columnGroup.length += growSize; - } - } - } - } - - remainingSpace = childMeasuredHeight; - - if (!this.stretchedVertically) { - for (int i = rowIndex; i < rowSpanEnd; i++) { - rowGroup = this.rows.get(i); - remainingSpace -= rowGroup.length; - } - - if (remainingSpace > 0) { - growSize = remainingSpace / measureSpec.starRowsCount; - for (int i = rowIndex; i < rowSpanEnd; i++) { - rowGroup = this.rows.get(i); - if (rowGroup.getIsStar()) { - rowGroup.length += growSize; - } - } - } - } - - this.itemMeasured(measureSpec, false); - } + + // Get row stats + size = measureSpec.getRowIndex() + measureSpec.getRowSpan(); + for (int i = measureSpec.getRowIndex(); i < size; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (rowGroup.getIsAuto()) { + measureSpec.autoRowsCount++; + } else if (rowGroup.getIsStar()) { + measureSpec.starRowsCount += rowGroup.rowOrColumn.getValue(); + } else if (rowGroup.getIsAbsolute()) { + measureSpec.pixelHeight += rowGroup.rowOrColumn.getValue(); + } + } + + if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount == 0) { + // Determine which auto rows are affected by this element + for (int i = measureSpec.getRowIndex(); i < size; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (rowGroup.getIsAuto()) { + rowGroup.measureToFix++; + } + } + } + + this.columns.get(measureSpec.getColumnIndex()).children.add(measureSpec); + this.rows.get(measureSpec.getRowIndex()).children.add(measureSpec); + } + + public void clearMeasureSpecs() { + for (int i = 0, size = this.columns.size(); i < size; i++) { + this.columns.get(i).children.clear(); + } + + for (int i = 0, size = this.rows.size(); i < size; i++) { + this.rows.get(i).children.clear(); + } + } + + private static void initList(ArrayList list) { + for (int i = 0, size = list.size(); i < size; i++) { + ItemGroup item = list.get(i); + item.init(); + } + } + + private ItemGroup singleRowGroup = new ItemGroup(singleRow); + private ItemGroup singleColumnGroup = new ItemGroup(singleColumn); + + void init() { + + int rows = this.rows.size(); + if (rows == 0) { + singleRowGroup.setIsLengthInfinity(this.infinityHeight); + this.rows.add(singleRowGroup); + this.fakeRowAdded = true; + } else if (rows > 1 && this.fakeRowAdded) { + this.rows.remove(0); + this.fakeRowAdded = false; + } + + int cols = this.columns.size(); + if (cols == 0) { + this.fakeColumnAdded = true; + singleColumnGroup.setIsLengthInfinity(this.infinityWidth); + this.columns.add(singleColumnGroup); + } else if (cols > 1 && this.fakeColumnAdded) { + this.columns.remove(0); + this.fakeColumnAdded = false; + } + + initList(this.rows); + initList(this.columns); + + this.minColumnStarValue = -1; + this.minRowStarValue = -1; + this.maxColumnStarValue = -1; + this.maxRowStarValue = -1; + } + + private void itemMeasured(MeasureSpecs measureSpec, boolean isFakeMeasure) { + if (!isFakeMeasure) { + this.columns.get(measureSpec.getColumnIndex()).measuredCount++; + this.rows.get(measureSpec.getRowIndex()).measuredCount++; + measureSpec.measured = true; + } + + if (measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { + int size = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); + for (int i = measureSpec.getColumnIndex(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getIsAuto()) { + columnGroup.currentMeasureToFixCount++; + } + } + } + + if (measureSpec.autoRowsCount > 0 && measureSpec.starRowsCount == 0) { + int size = measureSpec.getRowIndex() + measureSpec.getRowSpan(); + for (int i = measureSpec.getRowIndex(); i < size; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (rowGroup.getIsAuto()) { + rowGroup.currentMeasureToFixCount++; + } + } + } + } + + private void fixColumns() { + int currentColumnWidth = 0; + int columnStarCount = 0; + + int columnCount = this.columns.size(); + for (int i = 0; i < columnCount; i++) { + ItemGroup item = this.columns.get(i); + if (item.rowOrColumn.getIsStar()) { + columnStarCount += item.rowOrColumn.getValue(); + } else { + // Star columns are still zeros (not calculated). + currentColumnWidth += item.length; + } + } + + float widthForStarColumns = Math.max(0, this.width - currentColumnWidth); + this.maxColumnStarValue = columnStarCount > 0 ? widthForStarColumns / columnStarCount : 0; + + updateStarLength(this.columns, this.maxColumnStarValue); + } + + private void fixRows() { + int currentRowHeight = 0; + int rowStarCount = 0; + + int rowCount = this.rows.size(); + for (int i = 0; i < rowCount; i++) { + ItemGroup item = this.rows.get(i); + if (item.rowOrColumn.getIsStar()) { + rowStarCount += item.rowOrColumn.getValue(); + } else { + // Star rows are still zeros (not calculated). + currentRowHeight += item.length; + } + } + + float heightForStarRows = Math.max(0, this.height - currentRowHeight); + this.maxRowStarValue = rowStarCount > 0 ? heightForStarRows / rowStarCount : 0; + + updateStarLength(this.rows, this.maxRowStarValue); + } + + private static void updateStarLength(ArrayList list, float starValue) { + float offset = 0; + int roundedOffset = 0; + for (int i = 0, rowCount = list.size(); i < rowCount; i++) { + ItemGroup item = list.get(i); + if (item.getIsStar()) { + offset += item.rowOrColumn.getValue() * starValue; + + float actualLength = offset - roundedOffset; + int roundedLength = Math.round(actualLength); + item.length = roundedLength; + roundedOffset += roundedLength; + } + } + } + + private void fakeMeasure() { + // Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height + for (int i = 0, size = this.columns.size(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getAllMeasured()) { + continue; + } + + for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (measureSpec.starRowsCount > 0 && measureSpec.autoColumnsCount > 0 && measureSpec.starColumnsCount == 0) { + this.measureChild(measureSpec, true); + } + } + } + } + + private void measureFixedColumnsNoStarRows() { + for (int i = 0, size = this.columns.size(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (measureSpec.starColumnsCount > 0 && measureSpec.starRowsCount == 0) { + this.measureChildFixedColumns(measureSpec); + } + } + } + } + + private void measureNoStarColumnsFixedRows() { + for (int i = 0, size = this.columns.size(); i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (measureSpec.starRowsCount > 0 && measureSpec.starColumnsCount == 0) { + this.measureChildFixedRows(measureSpec); + } + } + } + } + + private static boolean canFix(ArrayList list) { + for (int i = 0, size = list.size(); i < size; i++) { + ItemGroup item = list.get(i); + if (!item.getCanBeFixed()) { + return false; + } + } + + return true; + } + + private static int getMeasureLength(ArrayList list) { + int result = 0; + for (int i = 0, size = list.size(); i < size; i++) { + ItemGroup item = list.get(i); + result += item.length; + } + + return result; + } + + public void measure() { + + // Measure auto & pixel columns and rows (no spans). + int size = this.columns.size(); + for (int i = 0; i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (measureSpec.getIsStar() || measureSpec.getSpanned()) { + continue; + } + + this.measureChild(measureSpec, false); + } + } + + // Measure auto & pixel columns and rows (with spans). + for (int i = 0; i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + for (int j = 0, childrenCount = columnGroup.children.size(); j < childrenCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (measureSpec.getIsStar() || !measureSpec.getSpanned()) { + continue; + } + + this.measureChild(measureSpec, false); + } + } + + // try fix stars! + boolean fixColumns = canFix(this.columns); + boolean fixRows = canFix(this.rows); + + if (fixColumns) { + this.fixColumns(); + } + + if (fixRows) { + this.fixRows(); + } + + if (!fixColumns && !fixRows) { + // Fake measure - measure all elements that have star rows and auto columns - with infinity Width and infinity Height + // should be able to fix rows after that + this.fakeMeasure(); + + this.fixColumns(); + + // Measure all elements that have star columns(already fixed), but no stars at the rows + this.measureFixedColumnsNoStarRows(); + + this.fixRows(); + } else if (fixColumns && !fixRows) { + // Measure all elements that have star columns(already fixed) but no stars at the rows + this.measureFixedColumnsNoStarRows(); + + // Then + this.fixRows(); + } else if (!fixColumns && fixRows) { + // Measure all elements that have star rows(already fixed) but no star at the columns + this.measureNoStarColumnsFixedRows(); + + // Then + this.fixColumns(); + } + + // Rows and columns are fixed here - measure remaining elements + size = this.columns.size(); + for (int i = 0; i < size; i++) { + ItemGroup columnGroup = this.columns.get(i); + for (int j = 0, childCount = columnGroup.children.size(); j < childCount; j++) { + MeasureSpecs measureSpec = columnGroup.children.get(j); + if (!measureSpec.measured) { + this.measureChildFixedColumnsAndRows(measureSpec); + } + } + } + + // If we are not stretched and minColumnStarValue is less than maxColumnStarValue + // we need to reduce the length of star columns. + if (!this.stretchedHorizontally && this.minColumnStarValue != -1 && this.minColumnStarValue < this.maxColumnStarValue) { + updateStarLength(this.columns, this.minColumnStarValue); + } + + // If we are not stretched and minRowStarValue is less than maxRowStarValue + // we need to reduce the height of star maxRowStarValue. + if (!this.stretchedVertically && this.minRowStarValue != -1 && this.minRowStarValue < this.maxRowStarValue) { + updateStarLength(this.rows, this.minRowStarValue); + } + + this.measuredWidth = getMeasureLength(this.columns); + this.measuredHeight = getMeasureLength(this.rows); + } + + private void measureChild(MeasureSpecs measureSpec, boolean isFakeMeasure) { + int widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelWidth, MeasureSpec.EXACTLY); + int heightMeasureSpec = (isFakeMeasure || measureSpec.autoRowsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelHeight, MeasureSpec.EXACTLY); + + CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); + final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); + final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); + + int columnSpanEnd = measureSpec.getColumnIndex() + measureSpec.getColumnSpan(); + int rowSpanEnd = measureSpec.getRowIndex() + measureSpec.getRowSpan(); + + if (measureSpec.autoColumnsCount > 0) { + int remainingSpace = childMeasuredWidth; + + for (int i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + remainingSpace -= columnGroup.length; + } + + if (remainingSpace > 0) { + int growSize = remainingSpace / measureSpec.autoColumnsCount; + for (int i = measureSpec.getColumnIndex(); i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getIsAuto()) { + columnGroup.length += growSize; + } + } + } + } + + if (!isFakeMeasure && measureSpec.autoRowsCount > 0) { + int remainingSpace = childMeasuredHeight; + + for (int i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + remainingSpace -= rowGroup.length; + } + + if (remainingSpace > 0) { + int growSize = remainingSpace / measureSpec.autoRowsCount; + for (int i = measureSpec.getRowIndex(); i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (rowGroup.getIsAuto()) { + rowGroup.length += growSize; + } + } + } + } + + this.itemMeasured(measureSpec, isFakeMeasure); + } + + private void measureChildFixedColumns(MeasureSpecs measureSpec) { + int columnIndex = measureSpec.getColumnIndex(); + int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); + int rowIndex = measureSpec.getRowIndex(); + int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); + + int measureWidth = 0; + int growSize = 0; + + for (int i = columnIndex; i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + measureWidth += columnGroup.length; + } + + int widthMeasureSpec = MeasureSpec.makeMeasureSpec(measureWidth, this.stretchedHorizontally ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST); + int heightMeasureSpec = (measureSpec.autoRowsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelHeight, MeasureSpec.EXACTLY); + + CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); + final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); + final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); + + this.updateMinColumnStarValueIfNeeded(measureSpec, childMeasuredWidth); + + // Distribute height over auto rows + if (measureSpec.autoRowsCount > 0) { + int remainingSpace = childMeasuredHeight; + + for (int i = rowIndex; i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + remainingSpace -= rowGroup.length; + } + + if (remainingSpace > 0) { + growSize = remainingSpace / measureSpec.autoRowsCount; + for (int i = rowIndex; i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (rowGroup.getIsAuto()) { + rowGroup.length += growSize; + } + } + } + } + + this.itemMeasured(measureSpec, false); + } + + private void measureChildFixedRows(MeasureSpecs measureSpec) { + int columnIndex = measureSpec.getColumnIndex(); + int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); + int rowIndex = measureSpec.getRowIndex(); + int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); + int measureHeight = 0; + + for (int i = rowIndex; i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + measureHeight += rowGroup.length; + } + + int widthMeasureSpec = (measureSpec.autoColumnsCount > 0) ? infinity : MeasureSpec.makeMeasureSpec(measureSpec.pixelWidth, MeasureSpec.EXACTLY); + int heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, this.stretchedVertically ? MeasureSpec.EXACTLY : MeasureSpec.AT_MOST); + + CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); + final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); + final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); + + int remainingSpace = 0; + int growSize = 0; + + // Distribute width over auto columns + if (measureSpec.autoColumnsCount > 0) { + remainingSpace = childMeasuredWidth; + + for (int i = columnIndex; i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + remainingSpace -= columnGroup.length; + } + + if (remainingSpace > 0) { + growSize = remainingSpace / measureSpec.autoColumnsCount; + for (int i = columnIndex; i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (columnGroup.getIsAuto()) { + columnGroup.length += growSize; + } + } + } + } + + this.updateMinRowStarValueIfNeeded(measureSpec, childMeasuredHeight); + this.itemMeasured(measureSpec, false); + } + + private void measureChildFixedColumnsAndRows(MeasureSpecs measureSpec) { + int columnIndex = measureSpec.getColumnIndex(); + int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); + int rowIndex = measureSpec.getRowIndex(); + int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); + + int measureWidth = 0; + for (int i = columnIndex; i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + measureWidth += columnGroup.length; + } + + int measureHeight = 0; + for (int i = rowIndex; i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + measureHeight += rowGroup.length; + } + + // if (have stars) & (not stretch) - at most + int widthMeasureSpec = MeasureSpec.makeMeasureSpec(measureWidth, + (measureSpec.starColumnsCount > 0 && !this.stretchedHorizontally) ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY); + + int heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, + (measureSpec.starRowsCount > 0 && !this.stretchedVertically) ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY); + + CommonLayoutParams.measureChild(measureSpec.child, widthMeasureSpec, heightMeasureSpec); + final int childMeasuredWidth = CommonLayoutParams.getDesiredWidth(measureSpec.child); + final int childMeasuredHeight = CommonLayoutParams.getDesiredHeight(measureSpec.child); + + this.updateMinColumnStarValueIfNeeded(measureSpec, childMeasuredWidth); + this.updateMinRowStarValueIfNeeded(measureSpec, childMeasuredHeight); + this.itemMeasured(measureSpec, false); + } + + private void updateMinRowStarValueIfNeeded(MeasureSpecs measureSpec, int childMeasuredHeight) { + if (!this.stretchedVertically && measureSpec.starRowsCount > 0) { + int remainingSpace = childMeasuredHeight; + int rowIndex = measureSpec.getRowIndex(); + int rowSpanEnd = rowIndex + measureSpec.getRowSpan(); + for (int i = rowIndex; i < rowSpanEnd; i++) { + ItemGroup rowGroup = this.rows.get(i); + if (!rowGroup.getIsStar()) { + remainingSpace -= rowGroup.length; + } + } + + if (remainingSpace > 0) { + this.minRowStarValue = Math.max(remainingSpace / measureSpec.starRowsCount, this.minRowStarValue); + } + } + } + + private void updateMinColumnStarValueIfNeeded(MeasureSpecs measureSpec, int childMeasuredWidth) { + // When not stretched star columns are not fixed so we may grow them here + // if there is an element that spans on multiple columns + if (!this.stretchedHorizontally && measureSpec.starColumnsCount > 0) { + int remainingSpace = childMeasuredWidth; + int columnIndex = measureSpec.getColumnIndex(); + int columnSpanEnd = columnIndex + measureSpec.getColumnSpan(); + for (int i = columnIndex; i < columnSpanEnd; i++) { + ItemGroup columnGroup = this.columns.get(i); + if (!columnGroup.getIsStar()) { + remainingSpace -= columnGroup.length; + } + } + + if (remainingSpace > 0) { + this.minColumnStarValue = Math.max(remainingSpace / measureSpec.starColumnsCount, this.minColumnStarValue); + } + } + } } \ No newline at end of file