summaryrefslogtreecommitdiff
path: root/core/java/android/widget/GridLayout.java
diff options
context:
space:
mode:
authorPhilip Milne <pmilne@google.com>2012-01-26 16:55:30 -0800
committerPhilip Milne <pmilne@google.com>2012-02-13 16:55:57 -0800
commitd7dd89095ff2041f0793317c4ee8e8be49388148 (patch)
tree9ee7b1cf9fa0261b0c6196c3d1c8e0c42880f3a5 /core/java/android/widget/GridLayout.java
parent07f941f026ed2aef8945d0d44134162b3db50eb5 (diff)
New hooks to allow layouts to improve their performance by doing more caching
This change allows layouts to be notified of changes to LayoutParameters that have occurred between layout operations. If an assignment is made to the fields of LayoutParams instances that are already in use, cachced data may become inconsistent with the new values. For complex layouts, like GridLayout, in which the layout parameters define the structure of the layout, caching could have caused ArrayOutOfBoundsException to be raised without this change. This case is rare in normal code as initialisation is typically performed once. Its nevertheless possible and much more likely in environments like design tools where layout parametrs may be being edited on the fly. Prevent errors as follows (belt and braces): 1. Change javadoc to request that changes to the fields of LayoutParams be accompanied with a call to View.setLayoutParams(). (This calls requestLayout() which was what the previous javadoc advised.) Provide a (for now, private) hook for layouts with caches to receive notification of such calls so they can invalidate any relevant internal state. 2. For GridLayout, we cannot clone layout parameters as traditional Java grids do without retaining two complete copies because of the public getLayoutParameters() method on View. Retaining two copies is wasteful on constrainted devices. Instead, we keep just one copy and compute a hashCode for the critical fields of a GridLayout's layoutParams. The hashChode is checked it prior to all layout operations; clearing the cache and logging a warning when changes are detected, so that developers can fix their code to provide the call to setLayoutParams() as above. Change-Id: I819ea65ec0ab82202e2f94fd5cd3ae2723c1a9a0
Diffstat (limited to 'core/java/android/widget/GridLayout.java')
-rw-r--r--core/java/android/widget/GridLayout.java85
1 files changed, 74 insertions, 11 deletions
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 328ccaf25e8f..ea40dc24f1b7 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -212,6 +212,7 @@ public class GridLayout extends ViewGroup {
static final int PRF = 1;
static final int MAX_SIZE = 100000;
static final int DEFAULT_CONTAINER_MARGIN = 0;
+ static final int UNINITIALIZED_HASH = 0;
// Defaults
@@ -240,6 +241,7 @@ public class GridLayout extends ViewGroup {
boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
int alignmentMode = DEFAULT_ALIGNMENT_MODE;
int defaultGap;
+ int lastLayoutParamsHashCode = UNINITIALIZED_HASH;
// Constructors
@@ -668,7 +670,7 @@ public class GridLayout extends ViewGroup {
int[] maxSizes = new int[count];
for (int i = 0, N = getChildCount(); i < N; i++) {
- LayoutParams lp = getLayoutParams1(getChildAt(i));
+ LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
final Spec majorSpec = horizontal ? lp.rowSpec : lp.columnSpec;
final Interval majorRange = majorSpec.span;
@@ -713,6 +715,7 @@ public class GridLayout extends ViewGroup {
minor = minor + minorSpan;
}
+ lastLayoutParamsHashCode = computeLayoutParamsHashCode();
invalidateStructure();
}
@@ -733,8 +736,11 @@ public class GridLayout extends ViewGroup {
}
}
- private LayoutParams getLayoutParams1(View c) {
- return (LayoutParams) c.getLayoutParams();
+ /** @hide */
+ @Override
+ protected void onSetLayoutParams(View child, ViewGroup.LayoutParams layoutParams) {
+ super.onSetLayoutParams(child, layoutParams);
+ invalidateStructure();
}
final LayoutParams getLayoutParams(View c) {
@@ -742,7 +748,7 @@ public class GridLayout extends ViewGroup {
validateLayoutParams();
layoutParamsValid = true;
}
- return getLayoutParams1(c);
+ return (LayoutParams) c.getLayoutParams();
}
@Override
@@ -859,12 +865,29 @@ public class GridLayout extends ViewGroup {
}
}
- // Measurement
+ private int computeLayoutParamsHashCode() {
+ int result = 1;
+ for (int i = 0, N = getChildCount(); i < N; i++) {
+ View c = getChildAt(i);
+ if (c.getVisibility() == View.GONE) continue;
+ LayoutParams lp = (LayoutParams) c.getLayoutParams();
+ result = 31 * result + lp.hashCode();
+ }
+ return result;
+ }
- final boolean isGone(View c) {
- return c.getVisibility() == View.GONE;
+ private void checkForLayoutParamsModification() {
+ int layoutParamsHashCode = computeLayoutParamsHashCode();
+ if (lastLayoutParamsHashCode != UNINITIALIZED_HASH &&
+ lastLayoutParamsHashCode != layoutParamsHashCode) {
+ invalidateStructure();
+ Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
+ "operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
+ }
}
+ // Measurement
+
private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
int childWidth, int childHeight) {
int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
@@ -877,7 +900,7 @@ public class GridLayout extends ViewGroup {
private void measureChildrenWithMargins(int widthSpec, int heightSpec, boolean firstPass) {
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
- if (isGone(c)) continue;
+ if (c.getVisibility() == View.GONE) continue;
LayoutParams lp = getLayoutParams(c);
if (firstPass) {
measureChildWithMargins2(c, widthSpec, heightSpec, lp.width, lp.height);
@@ -902,6 +925,8 @@ public class GridLayout extends ViewGroup {
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
+ checkForLayoutParamsModification();
+
/** If we have been called by {@link View#measure(int, int)}, one of width or height
* is likely to have changed. We must invalidate if so. */
invalidateValues();
@@ -941,7 +966,7 @@ public class GridLayout extends ViewGroup {
}
final int getMeasurementIncludingMargin(View c, boolean horizontal) {
- if (isGone(c)) {
+ if (c.getVisibility() == View.GONE) {
return 0;
}
return getMeasurement(c, horizontal) + getTotalMargin(c, horizontal);
@@ -974,6 +999,8 @@ public class GridLayout extends ViewGroup {
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ checkForLayoutParamsModification();
+
int targetWidth = right - left;
int targetHeight = bottom - top;
@@ -990,7 +1017,7 @@ public class GridLayout extends ViewGroup {
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
- if (isGone(c)) continue;
+ if (c.getVisibility() == View.GONE) continue;
LayoutParams lp = getLayoutParams(c);
Spec columnSpec = lp.columnSpec;
Spec rowSpec = lp.rowSpec;
@@ -1527,7 +1554,7 @@ public class GridLayout extends ViewGroup {
int[] margins = leading ? leadingMargins : trailingMargins;
for (int i = 0, N = getChildCount(); i < N; i++) {
View c = getChildAt(i);
- if (isGone(c)) continue;
+ if (c.getVisibility() == View.GONE) continue;
LayoutParams lp = getLayoutParams(c);
Spec spec = horizontal ? lp.columnSpec : lp.rowSpec;
Interval span = spec.span;
@@ -1767,12 +1794,28 @@ public class GridLayout extends ViewGroup {
/**
* The spec that defines the vertical characteristics of the cell group
* described by these layout parameters.
+ * If an assignment is made to this field after a measurement or layout operation
+ * has already taken place, a call to
+ * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
+ * must be made to notify GridLayout of the change. GridLayout is normally able
+ * to detect when code fails to observe this rule, issue a warning and take steps to
+ * compensate for the omission. This facility is implemented on a best effort basis
+ * and should not be relied upon in production code - so it is best to include the above
+ * calls to remove the warnings as soon as it is practical.
*/
public Spec rowSpec = Spec.UNDEFINED;
/**
* The spec that defines the horizontal characteristics of the cell group
* described by these layout parameters.
+ * If an assignment is made to this field after a measurement or layout operation
+ * has already taken place, a call to
+ * {@link ViewGroup#setLayoutParams(ViewGroup.LayoutParams)}
+ * must be made to notify GridLayout of the change. GridLayout is normally able
+ * to detect when code fails to observe this rule, issue a warning and take steps to
+ * compensate for the omission. This facility is implemented on a best effort basis
+ * and should not be relied upon in production code - so it is best to include the above
+ * calls to remove the warnings as soon as it is practical.
*/
public Spec columnSpec = Spec.UNDEFINED;
@@ -1917,6 +1960,26 @@ public class GridLayout extends ViewGroup {
final void setColumnSpecSpan(Interval span) {
columnSpec = columnSpec.copyWriteSpan(span);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LayoutParams that = (LayoutParams) o;
+
+ if (!columnSpec.equals(that.columnSpec)) return false;
+ if (!rowSpec.equals(that.rowSpec)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = rowSpec.hashCode();
+ result = 31 * result + columnSpec.hashCode();
+ return result;
+ }
}
/*