From 1557fd7809078e421f751efc7d2539b3efdc54b2 Mon Sep 17 00:00:00 2001 From: Philip Milne Date: Wed, 4 Apr 2012 23:41:34 -0700 Subject: [PATCH] Fix for bug 6110465. Add layout bound metadata to 9-patch files and make layouts take them into account. This CL contains a proposed API for dealing with layout bounds. This solution exposes: 1. Class: Insets - for storing layout Insets (and later possibly padding). 2. Methods: View:(get/set)LayoutInsets() - for storing layoutBounds. 3. Methods: ViewGroup:(get/set)LayoutMode() - for controlling layoutMode. It also iuncudes the changes to GridLayout to support layout bounds. Change-Id: I60c836b6530b61c5abf37f93ee9c44aad73573f1 --- api/current.txt | 4 + core/java/android/view/View.java | 30 +++++ core/java/android/view/ViewGroup.java | 70 +++++++++++ core/java/android/widget/GridLayout.java | 50 +++++--- graphics/java/android/graphics/Insets.java | 114 ++++++++++++++++++ .../android/graphics/drawable/Drawable.java | 18 ++- .../graphics/drawable/DrawableContainer.java | 12 ++ .../graphics/drawable/NinePatchDrawable.java | 79 ++++++++---- tests/GridLayoutTest/AndroidManifest.xml | 7 ++ .../res/drawable/btn_default.xml | 33 +++++ .../res/drawable/my_btn_default_normal.9.png | Bin 0 -> 1269 bytes .../my_btn_default_normal_disable.9.png | Bin 0 -> 3601 bytes ...y_btn_default_normal_disable_focused.9.png | Bin 0 -> 1781 bytes .../res/drawable/my_btn_default_pressed.9.png | Bin 0 -> 1913 bytes .../drawable/my_btn_default_selected.9.png | Bin 0 -> 1507 bytes tests/GridLayoutTest/res/layout/grid7.xml | 2 +- .../android/test/layout/LayoutInsetsTest.java | 59 +++++++++ 17 files changed, 436 insertions(+), 42 deletions(-) create mode 100644 graphics/java/android/graphics/Insets.java create mode 100644 tests/GridLayoutTest/res/drawable/btn_default.xml create mode 100644 tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png create mode 100755 tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png create mode 100755 tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable_focused.9.png create mode 100755 tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png create mode 100755 tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png create mode 100644 tests/GridLayoutTest/src/com/android/test/layout/LayoutInsetsTest.java diff --git a/api/current.txt b/api/current.txt index 5304e001f6e7a..bcda821efd743 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24079,6 +24079,7 @@ package android.view { method public android.view.View getFocusedChild(); method public android.view.animation.LayoutAnimationController getLayoutAnimation(); method public android.view.animation.Animation.AnimationListener getLayoutAnimationListener(); + method public int getLayoutMode(); method public android.animation.LayoutTransition getLayoutTransition(); method public int getPersistentDrawingCache(); method public int indexOfChild(android.view.View); @@ -24126,6 +24127,7 @@ package android.view { method public void setDescendantFocusability(int); method public void setLayoutAnimation(android.view.animation.LayoutAnimationController); method public void setLayoutAnimationListener(android.view.animation.Animation.AnimationListener); + method public void setLayoutMode(int); method public void setLayoutTransition(android.animation.LayoutTransition); method public void setMotionEventSplittingEnabled(boolean); method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener); @@ -24138,9 +24140,11 @@ package android.view { method public void startViewTransition(android.view.View); method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams); field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22 + field public static final int COMPONENT_BOUNDS = 0; // 0x0 field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000 field public static final int FOCUS_BEFORE_DESCENDANTS = 131072; // 0x20000 field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000 + field public static final int LAYOUT_BOUNDS = 1; // 0x1 field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3 field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1 field public static final int PERSISTENT_NO_CACHE = 0; // 0x0 diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1fa19d134a1a2..d1e90b465d624 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -24,6 +24,7 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Camera; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; import android.graphics.Matrix; @@ -2597,6 +2598,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal @ViewDebug.ExportedProperty(category = "padding") protected int mPaddingBottom; + /** + * The layout insets in pixels, that is the distance in pixels between the + * visible edges of this view its bounds. + */ + private Insets mLayoutInsets; + /** * Briefly describes the view and is primarily used for accessibility support. */ @@ -13272,6 +13279,29 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return mUserPaddingRelative; } + /** + * @hide + */ + public Insets getLayoutInsets() { + if (mLayoutInsets == null) { + if (mBackground == null) { + mLayoutInsets = Insets.NONE; + } else { + Rect insetRect = new Rect(); + boolean hasInsets = mBackground.getLayoutInsets(insetRect); + mLayoutInsets = hasInsets ? Insets.of(insetRect) : Insets.NONE; + } + } + return mLayoutInsets; + } + + /** + * @hide + */ + public void setLayoutInsets(Insets layoutInsets) { + mLayoutInsets = layoutInsets; + } + /** * Changes the selection state of this view. A view can be selected or not. * Note that selection is not the same as focus. Views are typically diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 121b544bf62c3..e5c5e459dad6a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -169,6 +169,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ protected int mGroupFlags; + /* + * THe layout mode: either {@link #UNDEFINED_LAYOUT_MODE}, {@link #COMPONENT_BOUNDS} or + * {@link #LAYOUT_BOUNDS} + */ + private int mLayoutMode = UNDEFINED_LAYOUT_MODE; + /** * NOTE: If you change the flags below make sure to reflect the changes * the DisplayList class @@ -334,6 +340,24 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public static final int PERSISTENT_ALL_CACHES = 0x3; + // Layout Modes + + private static final int UNDEFINED_LAYOUT_MODE = -1; + + /** + * This constant is a {@link #setLayoutMode(int) layoutMode}. + * Component bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top}, + * {@link #getRight() right} and {@link #getBottom() bottom}. + */ + public static final int COMPONENT_BOUNDS = 0; + + /** + * This constant is a {@link #setLayoutMode(int) layoutMode}. + * Layout bounds are derived by offsetting the component bounds using + * {@link View#getLayoutInsets()}. + */ + public static final int LAYOUT_BOUNDS = 1; + /** * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL * are set at the same time. @@ -4367,6 +4391,52 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES; } + /** + * Returns the basis of alignment during the layout of this view group: + * either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}. + * + * @return whether or not this view group should use the component or layout bounds during + * layout operations + * + * @see #setLayoutMode(int) + */ + public int getLayoutMode() { + if (mLayoutMode == UNDEFINED_LAYOUT_MODE) { + ViewParent parent = getParent(); + if (parent instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) parent; + return viewGroup.getLayoutMode(); + } else { + int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + boolean preJellyBean = targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN; + return preJellyBean ? COMPONENT_BOUNDS : LAYOUT_BOUNDS; + } + + } + return mLayoutMode; + } + + /** + * Sets the basis of alignment during alignment of this view group. + * Valid values are either {@link #COMPONENT_BOUNDS} or {@link #LAYOUT_BOUNDS}. + *

+ * The default is to query the property of the parent if this view group has a parent. + * If this ViewGroup is the root of the view hierarchy the default + * value is {@link #LAYOUT_BOUNDS} for target SDK's greater than JellyBean, + * {@link #LAYOUT_BOUNDS} otherwise. + * + * @return whether or not this view group should use the component or layout bounds during + * layout operations + * + * @see #getLayoutMode() + */ + public void setLayoutMode(int layoutMode) { + if (mLayoutMode != layoutMode) { + mLayoutMode = layoutMode; + requestLayout(); + } + } + /** * Returns a new set of layout parameters based on the supplied attributes set. * diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index 60dd55cec36ed..1cb676f5c405a 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Insets; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; @@ -559,9 +560,9 @@ public class GridLayout extends ViewGroup { int flags = (gravity & mask) >> shift; switch (flags) { case (AXIS_SPECIFIED | AXIS_PULL_BEFORE): - return LEADING; + return horizontal ? LEFT : TOP; case (AXIS_SPECIFIED | AXIS_PULL_AFTER): - return TRAILING; + return horizontal ? RIGHT : BOTTOM; case (AXIS_SPECIFIED | AXIS_PULL_BEFORE | AXIS_PULL_AFTER): return FILL; case AXIS_SPECIFIED: @@ -1042,12 +1043,15 @@ public class GridLayout extends ViewGroup { int rightMargin = getMargin(c, true, false); int bottomMargin = getMargin(c, false, false); - // Alignment offsets: the location of the view relative to its alignment group. - int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin); - int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin); + int sumMarginsX = leftMargin + rightMargin; + int sumMarginsY = topMargin + bottomMargin; - int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin); - int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin); + // Alignment offsets: the location of the view relative to its alignment group. + int alignmentOffsetX = boundsX.getOffset(this, c, hAlign, pWidth + sumMarginsX, true); + int alignmentOffsetY = boundsY.getOffset(this, c, vAlign, pHeight + sumMarginsY, false); + + int width = hAlign.getSizeInCell(c, pWidth, cellWidth - sumMarginsX); + int height = vAlign.getSizeInCell(c, pHeight, cellHeight - sumMarginsY); int dx = x1 + gravityOffsetX + alignmentOffsetX; @@ -1181,7 +1185,7 @@ public class GridLayout extends ViewGroup { View c = getChildAt(i); LayoutParams lp = getLayoutParams(c); Spec spec = horizontal ? lp.columnSpec : lp.rowSpec; - groupBounds.getValue(i).include(c, spec, GridLayout.this, this); + groupBounds.getValue(i).include(GridLayout.this, c, spec, this); } } @@ -2138,16 +2142,30 @@ public class GridLayout extends ViewGroup { return before + after; } - protected int getOffset(View c, Alignment alignment, int size) { - return before - alignment.getAlignmentValue(c, size); + private int getAlignmentValue(GridLayout gl, View c, int size, Alignment a, boolean horiz) { + boolean useLayoutBounds = gl.getLayoutMode() == LAYOUT_BOUNDS; + if (!useLayoutBounds) { + return a.getAlignmentValue(c, size); + } else { + Insets insets = c.getLayoutInsets(); + int leadingInset = horiz ? insets.left : insets.top; // RTL? + int trailingInset = horiz ? insets.right : insets.bottom; // RTL? + int totalInset = leadingInset + trailingInset; + return leadingInset + a.getAlignmentValue(c, size - totalInset); + } } - protected final void include(View c, Spec spec, GridLayout gridLayout, Axis axis) { + protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean horizontal) { + return before - getAlignmentValue(gl, c, size, a, horizontal); + } + + protected final void include(GridLayout gl, View c, Spec spec, Axis axis) { this.flexibility &= spec.getFlexibility(); - int size = gridLayout.getMeasurementIncludingMargin(c, axis.horizontal); - Alignment alignment = gridLayout.getAlignment(spec.alignment, axis.horizontal); + boolean horizontal = axis.horizontal; + int size = gl.getMeasurementIncludingMargin(c, horizontal); + Alignment alignment = gl.getAlignment(spec.alignment, horizontal); // todo test this works correctly when the returned value is UNDEFINED - int before = alignment.getAlignmentValue(c, size); + int before = getAlignmentValue(gl, c, size, alignment, horizontal); include(before, size - before); } @@ -2614,8 +2632,8 @@ public class GridLayout extends ViewGroup { } @Override - protected int getOffset(View c, Alignment alignment, int size) { - return max(0, super.getOffset(c, alignment, size)); + protected int getOffset(GridLayout gl, View c, Alignment a, int size, boolean hrz) { + return max(0, super.getOffset(gl, c, a, size, hrz)); } }; } diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java new file mode 100644 index 0000000000000..c570cd4a4f693 --- /dev/null +++ b/graphics/java/android/graphics/Insets.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006 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 android.graphics; + +/** + * An Insets instance holds four integer offsets which describe changes to the four + * edges of a Rectangle. By convention, positive values move edges towards the + * centre of the rectangle. + *

+ * Insets are immutable so may be treated as values. + * + * @hide + */ +public class Insets { + public static final Insets NONE = new Insets(0, 0, 0, 0); + + public final int left; + public final int top; + public final int right; + public final int bottom; + + private Insets(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + // Factory methods + + /** + * Return an Insets instance with the appropriate values. + * + * @param left the left inset + * @param top the top inset + * @param right the right inset + * @param bottom the bottom inset + * + * @return Insets instance with the appropriate values + */ + public static Insets of(int left, int top, int right, int bottom) { + if (left == 0 && top == 0 && right == 0 && bottom == 0) { + return NONE; + } + return new Insets(left, top, right, bottom); + } + + /** + * Return an Insets instance with the appropriate values. + * + * @param r the rectangle from which to take the values + * + * @return an Insets instance with the appropriate values + */ + public static Insets of(Rect r) { + return of(r.left, r.top, r.right, r.bottom); + } + + /** + * Two Insets instances are equal iff they belong to the same class and their fields are + * pairwise equal. + * + * @param o the object to compare this instance with. + * + * @return true iff this object is equal {@code o} + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Insets insets = (Insets) o; + + if (bottom != insets.bottom) return false; + if (left != insets.left) return false; + if (right != insets.right) return false; + if (top != insets.top) return false; + + return true; + } + + @Override + public int hashCode() { + int result = left; + result = 31 * result + top; + result = 31 * result + right; + result = 31 * result + bottom; + return result; + } + + @Override + public String toString() { + return "Insets{" + + "left=" + left + + ", top=" + top + + ", right=" + right + + ", bottom=" + bottom + + '}'; + } +} diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 86e824b9e8ea2..7d1942acaf43f 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -704,6 +704,20 @@ public abstract class Drawable { return false; } + /** + * Return in insets the layout insets suggested by this Drawable for use with alignment + * operations during layout. Positive values move toward the + * center of the Drawable. Returns true if this drawable + * actually has a layout insets, else false. When false is returned, the padding + * is always set to 0. + * + * @hide + */ + public boolean getLayoutInsets(Rect insets) { + insets.set(0, 0, 0, 0); + return false; + } + /** * Make this drawable mutable. This operation cannot be reversed. A mutable * drawable is guaranteed to not share its state with any other drawable. @@ -965,9 +979,7 @@ public abstract class Drawable { Rect pad, Rect layoutBounds, String srcName) { if (np != null) { - NinePatchDrawable npd = new NinePatchDrawable(res, bm, np, pad, srcName); - npd.setLayoutBounds(layoutBounds); - return npd; + return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName); } return new BitmapDrawable(res, bm); diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index b0f7fd3e1e80d..e10f9e8d8de19 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -90,6 +90,18 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } + /** + * @hide + */ + @Override + public boolean getLayoutInsets(Rect insets) { + if (mCurrDrawable != null) { + return mCurrDrawable.getLayoutInsets(insets); + } else { + return super.getLayoutInsets(insets); + } + } + @Override public void setAlpha(int alpha) { if (mAlpha != alpha) { diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 1272071b2cda2..e502b7a0c16e8 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -16,9 +16,17 @@ package android.graphics.drawable; -import android.graphics.*; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.NinePatch; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -29,7 +37,7 @@ import java.io.IOException; import java.io.InputStream; /** - * + * * A resizeable bitmap, with stretchable areas that you define. This type of image * is defined in a .png file with a special format. * @@ -47,7 +55,6 @@ public class NinePatchDrawable extends Drawable { private NinePatchState mNinePatchState; private NinePatch mNinePatch; private Rect mPadding; - private Rect mLayoutBounds; private Paint mPaint; private boolean mMutated; @@ -56,7 +63,7 @@ public class NinePatchDrawable extends Drawable { // These are scaled to match the target density. private int mBitmapWidth; private int mBitmapHeight; - + NinePatchDrawable() { } @@ -69,7 +76,7 @@ public class NinePatchDrawable extends Drawable { public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) { this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), null); } - + /** * Create drawable from raw nine-patch data, setting initial target density * based on the display metrics of the resources. @@ -79,7 +86,19 @@ public class NinePatchDrawable extends Drawable { this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding), res); mNinePatchState.mTargetDensity = mTargetDensity; } - + + /** + * Create drawable from raw nine-patch data, setting initial target density + * based on the display metrics of the resources. + * + * @hide + */ + public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk, + Rect padding, Rect layoutInsets, String srcName) { + this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding, layoutInsets), res); + mNinePatchState.mTargetDensity = mTargetDensity; + } + /** * Create drawable from existing nine-patch, not dealing with density. * @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)} @@ -99,13 +118,6 @@ public class NinePatchDrawable extends Drawable { mNinePatchState.mTargetDensity = mTargetDensity; } - /** - * @hide - */ - void setLayoutBounds(Rect layoutBounds) { - mLayoutBounds = layoutBounds; - } - private void setNinePatchState(NinePatchState state, Resources res) { mNinePatchState = state; mNinePatch = state.mNinePatch; @@ -201,13 +213,26 @@ public class NinePatchDrawable extends Drawable { public int getChangingConfigurations() { return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations; } - + @Override public boolean getPadding(Rect padding) { padding.set(mPadding); return true; } + /** + * @hide + */ + @Override + public boolean getLayoutInsets(Rect insets) { + Rect layoutInsets = mNinePatchState.mLayoutInsets; + if (layoutInsets == null) { + return super.getLayoutInsets(insets); + } + insets.set(layoutInsets); + return true; + } + @Override public void setAlpha(int alpha) { if (mPaint == null && alpha == 0xFF) { @@ -217,7 +242,7 @@ public class NinePatchDrawable extends Drawable { getPaint().setAlpha(alpha); invalidateSelf(); } - + @Override public void setColorFilter(ColorFilter cf) { if (mPaint == null && cf == null) { @@ -267,6 +292,7 @@ public class NinePatchDrawable extends Drawable { options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; final Rect padding = new Rect(); + final Rect layoutInsets = new Rect(); Bitmap bitmap = null; try { @@ -290,7 +316,7 @@ public class NinePatchDrawable extends Drawable { setNinePatchState(new NinePatchState( new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"), - padding, dither), r); + padding, layoutInsets, dither), r); mNinePatchState.mTargetDensity = mTargetDensity; a.recycle(); @@ -344,7 +370,7 @@ public class NinePatchDrawable extends Drawable { public Region getTransparentRegion() { return mNinePatch.getTransparentRegion(getBounds()); } - + @Override public ConstantState getConstantState() { mNinePatchState.mChangingConfigurations = getChangingConfigurations(); @@ -361,27 +387,36 @@ public class NinePatchDrawable extends Drawable { return this; } - final static class NinePatchState extends ConstantState { + private final static class NinePatchState extends ConstantState { final NinePatch mNinePatch; final Rect mPadding; + final Rect mLayoutInsets; final boolean mDither; int mChangingConfigurations; int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; NinePatchState(NinePatch ninePatch, Rect padding) { - this(ninePatch, padding, DEFAULT_DITHER); + this(ninePatch, padding, new Rect(), DEFAULT_DITHER); } - NinePatchState(NinePatch ninePatch, Rect rect, boolean dither) { + NinePatchState(NinePatch ninePatch, Rect padding, Rect layoutInsets) { + this(ninePatch, padding, layoutInsets, DEFAULT_DITHER); + } + + NinePatchState(NinePatch ninePatch, Rect rect, Rect layoutInsets, boolean dither) { mNinePatch = ninePatch; mPadding = rect; + mLayoutInsets = layoutInsets; mDither = dither; } + // Copy constructor + NinePatchState(NinePatchState state) { mNinePatch = new NinePatch(state.mNinePatch); // Note we don't copy the padding because it is immutable. mPadding = state.mPadding; + mLayoutInsets = state.mLayoutInsets; mDither = state.mDither; mChangingConfigurations = state.mChangingConfigurations; mTargetDensity = state.mTargetDensity; @@ -391,12 +426,12 @@ public class NinePatchDrawable extends Drawable { public Drawable newDrawable() { return new NinePatchDrawable(this, null); } - + @Override public Drawable newDrawable(Resources res) { return new NinePatchDrawable(this, res); } - + @Override public int getChangingConfigurations() { return mChangingConfigurations; diff --git a/tests/GridLayoutTest/AndroidManifest.xml b/tests/GridLayoutTest/AndroidManifest.xml index 141e8fafda76a..677220db8a539 100644 --- a/tests/GridLayoutTest/AndroidManifest.xml +++ b/tests/GridLayoutTest/AndroidManifest.xml @@ -83,6 +83,13 @@ + + + + + + + diff --git a/tests/GridLayoutTest/res/drawable/btn_default.xml b/tests/GridLayoutTest/res/drawable/btn_default.xml new file mode 100644 index 0000000000000..c6cfda04287a6 --- /dev/null +++ b/tests/GridLayoutTest/res/drawable/btn_default.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_normal.9.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0b7d59a3a93968201eb5d19324d12f732ecfe4 GIT binary patch literal 1269 zcmVh=JVdb6Wd7kHaT-@rl zbN;KU*6HI@)z#Hyi2zsnWvlP4|9rP@_rh|wwA4S2PALDs<0t?>MLA9Ro^qaYF>}t_ z?$dTw^zk+2hwkp~e|L6vD#-5cZe?$8ud=_tU(Lb6LFMr9Pz}!UYq1s`t`FUqVP{1j z-%x(q*x0D7udf^WX*M@E&DPeI+1}nZR9q7pXlQU5{EqPRScA3bK$q>TV1}I)eSAy# zd2MaYtgfyW^s}I{g31aiFK9?X!>Xa-HR0!QE!JQyI?!c14QAL`-cOx8&Qg9^Sy?fZ z>@akq8#C;zhG4%gFE1NP;oCJA4sjS4ZMV>gZp`?kV3%WI94B9|1G}6ATUuH&l=3|x zaXugiX()7|6Wyt>E6u{(bphBF59}xj#viGM_#&% zcb=mZ*o_p}AG5QwhT`56Dv%>NM%yU_J?KO?W+cOAW@g+SdSMbLQ4Z#AuvyqmLIq(G zC!@j8g-#2*nF6~7*!1*td4~??1Ny=rAv(~7PIO~N78u{TSR8+ZxnQ@Og~{DgV7F6X zcd&SBYRVNI19GGm2|@>gP7Aw}0=wG`j6XgOLl=YHO@ZB`l4CFpCxIHu1G|?3`;!XH z4G$z%-mh1z`76U=OIkOiWC;qMCA!4255h z4s_X0z#gQ)9%AwM_;?j2agCrnu!koC3*j`BgW1kx*k5#KRhaK_YC!G0Lpd0_F(U~^ z1!in)%oWuDLr86x+#^J6XEN-Ogz*nT3{Hcgi@_eHz#e1q=;-J%SUns+CwEK09;d*b zaF~3Y5~qb<%VD+?uqR0{Dlj7>Bjp{sN2=|fFC61`3bCEZu%`}&JM`f+AV24Wp&K(& zV9#PIT*SzBL(&%1d9P`yM-eV6Qb;3{IZs zYX$6e)3AEDP&g_F>`e}s+${lnlLC7yVG>slsG$VxZOVV&qF@?MyCx^>od)yaLXqnk z>|GL!N>o@QIBjjSu=gQY49>UCb^`W31ZxSw`uqEfzP`R-a1Bq)^-aNgdwWe!PtS=2 z-6^m({4v_z-ag&c)nz(6J55JNN0y)qo#?iI@U}GqJ43l-Kh7U0|76Yw+kMH-iauH? z-%-v{exzK;_!X@Sw)32w(b~xO$G)-)&e*L!n>lT^yVcH$hJCc(xcdV80{c8*_(i7i fuCy=S`ey$J^=avCy2u?600000NkvXXu0mjfh_zb9 literal 0 HcmV?d00001 diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_normal_disable.9.png new file mode 100755 index 0000000000000000000000000000000000000000..f4f01c75462881b4dd20ad8463a43ff1ac59db36 GIT binary patch literal 3601 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 i0y+EaYb_9K#t!$ehr1oOY6?!K1* literal 0 HcmV?d00001 diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_pressed.9.png new file mode 100755 index 0000000000000000000000000000000000000000..4312c27f4d6d134c9cc95ca95c3ab95b547b4c67 GIT binary patch literal 1913 zcmbVNX;2eq7*2(v1yQQ0B|vo>k1Ck#5!f6<#2iEt4TcaE5eCC%fk?6&lMMu`R#9rH zCyo>kMq6tY5UEtLo(LXsJg}Xj7#Ts-ilYr8cvK8%Hwv~t9Dj6YcE9iazV~_Fd$v#$ zKP}Kd#Gk=n1S;d?T6zp5dA^!Hl8g!S~oqGFS%L^6ZQ)mw3f#6YIwfYPkb#I?BI zkhkzW9?f9*WEgcxRFZmz2qVp0y;q0pFk5IegApC$u;{UDoC4DEOd}y?cOE;*28;$V zdrp)ZQd?wrmN9OT6;E6gufrB)V?qNvW(pAP5YYr?oYDghvx%^Y9AfrsUJYdzvCZq%{+Sg+ig113@^n2FI31Pb70yA`ABkui1vOy}zVBV81|Nls#RGfq%SxtLAga1BO-NWteT zV3;2X@ev3Wz_5T9DUrg`NQfsCNZ?5M0LQzqC?bf3At5SJph6gyD~1h0JR=75?JV?kFu=}xw|C2M2 zb_Vn|$A6k-;E3)(@Ahr$)56=~!3la|tn_G5{uzz*#FZ=M5}l*jojjc!2?sqhD9_Bh;4oPcBSbAk~5=Z`+=*K0ymMqoU>Lu zQMcECbdH+&H6opRW{~XA8Hoqel-50B-3AL_qwgzMaP~cWZXOeYA|rNuq$k5%=9 zE0f0#su#|lT}7nKJe+{$Jdz-`;b^%WyKpK&f@~b$lA2dsy=+WkK5xy|BcXq7-D$4O z!oF`pO4imlr!wqwMmdKqYjlQBmmUlc#eCgIC$BuWLFP;w=PC}lA}ofB8Nr$rj@HJ& z(u%O>qua2`cmtMPJdNpet6Z$Hd$otk*QYp9IEWHS0^G>yrFuwd;OM+cKtluhz#S z!!I2UnE$l*Leu_FnS&!nmi=;or;Q7_VaO?3wff^6=eVevhx2@o-wl19j*ngw()*J@ z)3^EhcokBieVL|L49fQW)I5|Ky5GKX(!{!9N8R~(a~_13UKx`;qsLjL>PR`aO?5c> z_t6_YS-m)W)<&Moxl8P>*rbcu{Q_lrs!U~dfF!4S#F=(WMSgA^UTLbYS&rU02nRmc zaGP7PF1@x}opSMc1KNJGc)k)S*fqIm#b?9?kBnvdw%a^)*4@0D$L6;bw~jVVOfZco z(!A zq)kP=a-uiQr5|#ZEC~QH4}?_jxiiYefgToh?((z6>&}MNu4TRQ4Q5ak%cC+(u*~~2 NRK~{3tEBVu{{aLk_WJ+; literal 0 HcmV?d00001 diff --git a/tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png b/tests/GridLayoutTest/res/drawable/my_btn_default_selected.9.png new file mode 100755 index 0000000000000000000000000000000000000000..06b7790ffcabae1b04cd28d302ce98ae7be118f7 GIT binary patch literal 1507 zcmbVMZA=qq96w$J1~Z6m0$Ye@LE|dDw?eONsL=M>QAV+r;>i5qj_W}W+Pm(0*iyxy zs1t!{+?cQ>!^No}OffF%-mrvih=5_};&e_n%m5c15p`;!W>=tYAB-P%$=&n(pXdMk z{ok+7Qtr)W4C+8Wms>;wKvuTTO;Q!K0G7~Y46Bh$eSJm( zGIovR%}g_5cI#;elk4Z{LVtmc@>ftQyCi!RnB`N81TI=2L7&UXdelCRWQJEQ?t{m$ z1e{S3Dm0Q=r;5!MP|xu+mB2?CpE-C&>o6s z+ycX~V33h4;VK1|4p&Kc?ei-Qw5kAriA`oc`mOi~ORl!N7&jh3K! zW&>?E@|+8t$*-DuhJB+EGvEdU&&-^QTc=Yh6)K%Up_3yjTq&8u+W$|^V9^;k*c|_9 zme7&dfx+$5))$4R!$Y&;#PH&1>`i$R4FKVbOoZ0vyK?ib;|0CQf_n$sZAlB9xx@2% zEOqgdfxzGdG9A{iDZI63Fc63a!*nI4%a;126>VwbQ020mcj|Z;b zy0DphyfZcR(kVFc-Ll$6kNmYsBcl`4P^GYGUrzPa?3k8A_eL(3*KOwFqqCHKtzKzu zb~*;=rSz@+{OyVRgDrnf?maM&wC3_+)lmNTXp?2#;nAPnOj=uHO~=iKh8wHfNK_Gd z=UeJ}^Mm$n1}tjp=oe^-|LWwDgbi(J`OA{2=&1U-(NF%~ej>SkVEYSezngL{kr(#w zj{D|9u_LjON$j@v$K*p7p!D-lR(0ARH=+upODzvaJ4P-)Y_{7zI@a+SFYW1RxLtpS zDXb`awbLMN9NTwy?7habQAbzmixduYti0%}m7Bfo!kvS8ZRh5F$Tf^{E1JkF*W;VI zy9-X09Vg=>%hM@mVvB;>)<1CM#cJoZBaif(hM2C!zib~@{xt2uJ~;5#xV<*jjlGn* z>(i2#J5Sc*DWCt=w`E#}{<_9`*gV`ieC%vO?`bJ=E^W&?{raN7{vTFp1J{Pv|Jb+l z_Y=~t+#B4<`-fQB!s_<$=A}D27b1!2KwKca>mWPrir|`tP8HpJqb3YUkv=%Tv9I%D P@V9F+6c9&sZ*Tnv0u?Jn literal 0 HcmV?d00001 diff --git a/tests/GridLayoutTest/res/layout/grid7.xml b/tests/GridLayoutTest/res/layout/grid7.xml index b9e58d718efec..0e5be0c0b4cdf 100644 --- a/tests/GridLayoutTest/res/layout/grid7.xml +++ b/tests/GridLayoutTest/res/layout/grid7.xml @@ -17,7 +17,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> - android:columnCount="2" +