diff options
| author | Philip Milne <pmilne@google.com> | 2012-01-26 16:55:30 -0800 |
|---|---|---|
| committer | Philip Milne <pmilne@google.com> | 2012-02-13 16:55:57 -0800 |
| commit | d7dd89095ff2041f0793317c4ee8e8be49388148 (patch) | |
| tree | 9ee7b1cf9fa0261b0c6196c3d1c8e0c42880f3a5 /core/java/android/widget/GridLayout.java | |
| parent | 07f941f026ed2aef8945d0d44134162b3db50eb5 (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.java | 85 |
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; + } } /* |
