OSDN Git Service

Fix visual styling for Material Spinner background
authorAlan Viverette <alanv@google.com>
Tue, 21 Apr 2015 20:00:33 +0000 (13:00 -0700)
committerAlan Viverette <alanv@google.com>
Tue, 21 Apr 2015 20:00:33 +0000 (13:00 -0700)
Adds explicit padding attributes to LayerDrawable, constrains the ripple
to fit within the container to avoid projection.

Bug: 19359934
Bug: 20083467
Change-Id: I46da941fba37552adeb2c1fe617b799f576df0c6

api/current.txt
api/system-current.txt
core/res/res/drawable/ic_spinner_caret.xml [new file with mode: 0644]
core/res/res/drawable/spinner_background_material.xml
core/res/res/values/attrs.xml
graphics/java/android/graphics/drawable/LayerDrawable.java
graphics/java/android/graphics/drawable/RippleDrawable.java

index 9a4e905..23dc128 100644 (file)
@@ -12408,7 +12408,9 @@ package android.graphics.drawable {
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public int findIndexByLayerId(int);
+    method public int getBottomPadding();
     method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getEndPadding();
     method public int getId(int);
     method public int getLayerGravity(int);
     method public int getLayerHeight(int);
@@ -12419,9 +12421,13 @@ package android.graphics.drawable {
     method public int getLayerInsetStart(int);
     method public int getLayerInsetTop(int);
     method public int getLayerWidth(int);
+    method public int getLeftPadding();
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
+    method public int getRightPadding();
+    method public int getStartPadding();
+    method public int getTopPadding();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
     method public void setAlpha(int);
@@ -12442,7 +12448,9 @@ package android.graphics.drawable {
     method public void setLayerSize(int, int, int);
     method public void setLayerWidth(int, int);
     method public void setOpacity(int);
+    method public void setPadding(int, int, int, int);
     method public void setPaddingMode(int);
+    method public void setPaddingRelative(int, int, int, int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
index 8cdf938..92000c8 100644 (file)
@@ -12702,7 +12702,9 @@ package android.graphics.drawable {
     method public void draw(android.graphics.Canvas);
     method public android.graphics.drawable.Drawable findDrawableByLayerId(int);
     method public int findIndexByLayerId(int);
+    method public int getBottomPadding();
     method public android.graphics.drawable.Drawable getDrawable(int);
+    method public int getEndPadding();
     method public int getId(int);
     method public int getLayerGravity(int);
     method public int getLayerHeight(int);
@@ -12713,9 +12715,13 @@ package android.graphics.drawable {
     method public int getLayerInsetStart(int);
     method public int getLayerInsetTop(int);
     method public int getLayerWidth(int);
+    method public int getLeftPadding();
     method public int getNumberOfLayers();
     method public int getOpacity();
     method public int getPaddingMode();
+    method public int getRightPadding();
+    method public int getStartPadding();
+    method public int getTopPadding();
     method public void invalidateDrawable(android.graphics.drawable.Drawable);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
     method public void setAlpha(int);
@@ -12736,7 +12742,9 @@ package android.graphics.drawable {
     method public void setLayerSize(int, int, int);
     method public void setLayerWidth(int, int);
     method public void setOpacity(int);
+    method public void setPadding(int, int, int, int);
     method public void setPaddingMode(int);
+    method public void setPaddingRelative(int, int, int, int);
     method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
     field public static final int PADDING_MODE_NEST = 0; // 0x0
     field public static final int PADDING_MODE_STACK = 1; // 0x1
diff --git a/core/res/res/drawable/ic_spinner_caret.xml b/core/res/res/drawable/ic_spinner_caret.xml
new file mode 100644 (file)
index 0000000..6a18f89
--- /dev/null
@@ -0,0 +1,26 @@
+<!--
+    Copyright (C) 2015 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:pathData="M7,10l5,5,5-5z"
+        android:fillColor="@color/white"/>
+</vector>
index d5b509f..892dbc5 100644 (file)
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack">
-    <item android:drawable="@drawable/item_background_borderless_material"
-          android:gravity="end|center_vertical"
-          android:width="24dp"
-          android:height="24dp" />
-    <item android:gravity="end|center_vertical">
-        <vector android:width="24dp"
-                android:height="24dp"
-                android:viewportWidth="24.0"
-                android:viewportHeight="24.0"
-                android:tint="?attr/colorControlNormal">
-            <path android:pathData="M7,10l5,5,5-5z"
-                  android:fillColor="@color/white"/>
-        </vector>
+            android:paddingMode="stack"
+            android:paddingStart="0dp"
+            android:paddingEnd="48dp"
+            android:paddingLeft="0dp"
+            android:paddingRight="0dp">
+    <item
+        android:gravity="end|center_vertical"
+        android:width="48dp"
+        android:height="48dp">
+        <ripple
+            android:color="?attr/colorControlHighlight"
+            android:radius="24dp" />
     </item>
-    <item android:end="48dp"
-          android:drawable="@color/transparent" />
+
+    <item
+        android:drawable="@drawable/ic_spinner_caret"
+        android:gravity="end|center_vertical"
+        android:width="24dp"
+        android:height="24dp"
+        android:end="12dp" />
 </layer-list>
index 674c695..a7c4800 100644 (file)
             <!-- Stack each layer directly atop the previous layer. -->
             <enum name="stack" value="1" />
         </attr>
+        <!-- Explicit top padding. Overrides child padding. -->
+        <attr name="paddingTop" />
+        <!-- Explicit bottom padding. Overrides child padding. -->
+        <attr name="paddingBottom" />
+        <!-- Explicit left padding. Overrides child padding. -->
+        <attr name="paddingLeft" />
+        <!-- Explicit right padding. Overrides child padding. -->
+        <attr name="paddingRight" />
+        <!-- Explicit start padding. Overrides child padding. Takes precedence
+             over absolute padding (e.g. left when layout direction is LTR). -->
+        <attr name="paddingStart" />
+        <!-- Explicit end padding. Overrides child padding. Takes precedence
+             over absolute padding (e.g. right when layout direction is LTR). -->
+        <attr name="paddingEnd" />
     </declare-styleable>
 
     <!-- Describes an item (or child) of a LayerDrawable. -->
index 8468d9e..3bbbc71 100644 (file)
@@ -86,7 +86,6 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
 
     LayerState mLayerState;
 
-    private int mOpacityOverride = PixelFormat.UNKNOWN;
     private int[] mPaddingL;
     private int[] mPaddingT;
     private int[] mPaddingR;
@@ -177,12 +176,39 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
-        mOpacityOverride = a.getInt(R.styleable.LayerDrawable_opacity, mOpacityOverride);
-
-        state.mAutoMirrored = a.getBoolean(R.styleable.LayerDrawable_autoMirrored,
-                state.mAutoMirrored);
-        state.mPaddingMode = a.getInteger(R.styleable.LayerDrawable_paddingMode,
-                state.mPaddingMode);
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case R.styleable.LayerDrawable_opacity:
+                    state.mOpacityOverride = a.getInt(attr, state.mOpacityOverride);
+                    break;
+                case R.styleable.LayerDrawable_paddingTop:
+                    state.mPaddingTop = a.getDimensionPixelOffset(attr, state.mPaddingTop);
+                    break;
+                case R.styleable.LayerDrawable_paddingBottom:
+                    state.mPaddingBottom = a.getDimensionPixelOffset(attr, state.mPaddingBottom);
+                    break;
+                case R.styleable.LayerDrawable_paddingLeft:
+                    state.mPaddingLeft = a.getDimensionPixelOffset(attr, state.mPaddingLeft);
+                    break;
+                case R.styleable.LayerDrawable_paddingRight:
+                    state.mPaddingRight = a.getDimensionPixelOffset(attr, state.mPaddingRight);
+                    break;
+                case R.styleable.LayerDrawable_paddingStart:
+                    state.mPaddingStart = a.getDimensionPixelOffset(attr, state.mPaddingStart);
+                    break;
+                case R.styleable.LayerDrawable_paddingEnd:
+                    state.mPaddingEnd = a.getDimensionPixelOffset(attr, state.mPaddingEnd);
+                    break;
+                case R.styleable.LayerDrawable_autoMirrored:
+                    state.mAutoMirrored = a.getBoolean(attr, state.mAutoMirrored);
+                    break;
+                case R.styleable.LayerDrawable_paddingMode:
+                    state.mPaddingMode = a.getInteger(attr, state.mPaddingMode);
+                    break;
+            }
+        }
     }
 
     /**
@@ -895,15 +921,210 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
 
     @Override
     public boolean getPadding(Rect padding) {
-        if (mLayerState.mPaddingMode == PADDING_MODE_NEST) {
+        final LayerState layerState = mLayerState;
+        if (layerState.mPaddingMode == PADDING_MODE_NEST) {
             computeNestedPadding(padding);
         } else {
             computeStackedPadding(padding);
         }
 
+        // If padding was explicitly specified (e.g. not -1) then override the
+        // computed padding in that dimension.
+        if (layerState.mPaddingTop >= 0) {
+            padding.top = layerState.mPaddingTop;
+        }
+
+        if (layerState.mPaddingBottom >= 0) {
+            padding.bottom = layerState.mPaddingBottom;
+        }
+
+        final int paddingRtlLeft;
+        final int paddingRtlRight;
+        if (getLayoutDirection() == LayoutDirection.RTL) {
+            paddingRtlLeft = layerState.mPaddingEnd;
+            paddingRtlRight = layerState.mPaddingStart;
+        } else {
+            paddingRtlLeft = layerState.mPaddingStart;
+            paddingRtlRight = layerState.mPaddingEnd;
+        }
+
+        final int paddingLeft =  paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
+        if (paddingLeft >= 0) {
+            padding.left = paddingLeft;
+        }
+
+        final int paddingRight =  paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
+        if (paddingRight >= 0) {
+            padding.right = paddingRight;
+        }
+
         return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
     }
 
+    /**
+     * Sets the absolute padding.
+     * <p>
+     * If padding in a dimension is specified as {@code -1}, the resolved
+     * padding will use the value computed according to the padding mode (see
+     * {@link #setPaddingMode(int)}).
+     * <p>
+     * Calling this method clears any relative padding values previously set
+     * using {@link #setPaddingRelative(int, int, int, int)}.
+     *
+     * @param left the left padding in pixels, or -1 to use computed padding
+     * @param top the top padding in pixels, or -1 to use computed padding
+     * @param right the right padding in pixels, or -1 to use computed padding
+     * @param bottom the bottom padding in pixels, or -1 to use computed
+     *               padding
+     * @attr ref android.R.styleable#LayerDrawable_paddingLeft
+     * @attr ref android.R.styleable#LayerDrawable_paddingTop
+     * @attr ref android.R.styleable#LayerDrawable_paddingRight
+     * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+     * @see #setPaddingRelative(int, int, int, int)
+     */
+    public void setPadding(int left, int top, int right, int bottom) {
+        final LayerState layerState = mLayerState;
+        layerState.mPaddingLeft = left;
+        layerState.mPaddingTop = top;
+        layerState.mPaddingRight = right;
+        layerState.mPaddingBottom = bottom;
+
+        // Clear relative padding values.
+        layerState.mPaddingStart = -1;
+        layerState.mPaddingEnd = -1;
+    }
+
+    /**
+     * Sets the relative padding.
+     * <p>
+     * If padding in a dimension is specified as {@code -1}, the resolved
+     * padding will use the value computed according to the padding mode (see
+     * {@link #setPaddingMode(int)}).
+     * <p>
+     * Calling this method clears any absolute padding values previously set
+     * using {@link #setPadding(int, int, int, int)}.
+     *
+     * @param start the start padding in pixels, or -1 to use computed padding
+     * @param top the top padding in pixels, or -1 to use computed padding
+     * @param end the end padding in pixels, or -1 to use computed padding
+     * @param bottom the bottom padding in pixels, or -1 to use computed
+     *               padding
+     * @attr ref android.R.styleable#LayerDrawable_paddingStart
+     * @attr ref android.R.styleable#LayerDrawable_paddingTop
+     * @attr ref android.R.styleable#LayerDrawable_paddingEnd
+     * @attr ref android.R.styleable#LayerDrawable_paddingBottom
+     * @see #setPadding(int, int, int, int)
+     */
+    public void setPaddingRelative(int start, int top, int end, int bottom) {
+        final LayerState layerState = mLayerState;
+        layerState.mPaddingStart = start;
+        layerState.mPaddingTop = top;
+        layerState.mPaddingEnd = end;
+        layerState.mPaddingBottom = bottom;
+
+        // Clear absolute padding values.
+        layerState.mPaddingLeft = -1;
+        layerState.mPaddingRight = -1;
+    }
+
+    /**
+     * Returns the left padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the left padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getLeftPadding() {
+        return mLayerState.mPaddingLeft;
+    }
+
+    /**
+     * Returns the right padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the right padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getRightPadding() {
+        return mLayerState.mPaddingRight;
+    }
+
+    /**
+     * Returns the start padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the start padding in pixels, or -1 if not explicitly specified
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getStartPadding() {
+        return mLayerState.mPaddingStart;
+    }
+
+    /**
+     * Returns the end padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the end padding in pixels, or -1 if not explicitly specified
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getEndPadding() {
+        return mLayerState.mPaddingEnd;
+    }
+
+    /**
+     * Returns the top padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the top padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getTopPadding() {
+        return mLayerState.mPaddingTop;
+    }
+
+    /**
+     * Returns the bottom padding in pixels.
+     * <p>
+     * A return value of {@code -1} means there is no explicit padding set for
+     * this dimension. As a result, the value for this dimension returned by
+     * {@link #getPadding(Rect)} will be computed from the child layers
+     * according to the padding mode (see {@link #getPaddingMode()}.
+     *
+     * @return the bottom padding in pixels, or -1 if not explicitly specified
+     * @see #setPadding(int, int, int, int)
+     * @see #setPaddingRelative(int, int, int, int)
+     * @see #getPadding(Rect)
+     */
+    public int getBottomPadding() {
+        return mLayerState.mPaddingBottom;
+    }
+
     private void computeNestedPadding(Rect padding) {
         padding.left = 0;
         padding.top = 0;
@@ -1109,8 +1330,8 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
     }
 
     /**
-     * Sets the opacity of this drawable directly, instead of collecting the
-     * states from the layers
+     * Sets the opacity of this drawable directly instead of collecting the
+     * states from the layers.
      *
      * @param opacity The opacity to use, or {@link PixelFormat#UNKNOWN
      *            PixelFormat.UNKNOWN} for the default behavior
@@ -1120,13 +1341,13 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
      * @see PixelFormat#OPAQUE
      */
     public void setOpacity(int opacity) {
-        mOpacityOverride = opacity;
+        mLayerState.mOpacityOverride = opacity;
     }
 
     @Override
     public int getOpacity() {
-        if (mOpacityOverride != PixelFormat.UNKNOWN) {
-            return mOpacityOverride;
+        if (mLayerState.mOpacityOverride != PixelFormat.UNKNOWN) {
+            return mLayerState.mOpacityOverride;
         }
         return mLayerState.getOpacity();
     }
@@ -1265,12 +1486,12 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
      * dimension, defaults to START or TOP. Otherwise, defaults to FILL to
      * preserve legacy behavior.
      *
-     * @param gravity
-     * @param width
-     * @param height
-     * @return
+     * @param gravity layer gravity
+     * @param width width of the layer if set, -1 otherwise
+     * @param height height of the layer if set, -1 otherwise
+     * @return the default gravity for the layer
      */
-    private int resolveGravity(int gravity, int width, int height) {
+    private static int resolveGravity(int gravity, int width, int height) {
         if (!Gravity.isHorizontal(gravity)) {
             if (width < 0) {
                 gravity |= Gravity.FILL_HORIZONTAL;
@@ -1504,6 +1725,14 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
         ChildDrawable[] mChildren;
         int[] mThemeAttrs;
 
+        int mPaddingTop = -1;
+        int mPaddingBottom = -1;
+        int mPaddingLeft = -1;
+        int mPaddingRight = -1;
+        int mPaddingStart = -1;
+        int mPaddingEnd = -1;
+        int mOpacityOverride = PixelFormat.UNKNOWN;
+
         int mChangingConfigurations;
         int mChildrenChangingConfigurations;
 
@@ -1540,6 +1769,13 @@ public class LayerDrawable extends Drawable implements Drawable.Callback {
                 mAutoMirrored = orig.mAutoMirrored;
                 mPaddingMode = orig.mPaddingMode;
                 mThemeAttrs = orig.mThemeAttrs;
+                mPaddingTop = orig.mPaddingTop;
+                mPaddingBottom = orig.mPaddingBottom;
+                mPaddingLeft = orig.mPaddingLeft;
+                mPaddingRight = orig.mPaddingRight;
+                mPaddingStart = orig.mPaddingStart;
+                mPaddingEnd = orig.mPaddingEnd;
+                mOpacityOverride = orig.mOpacityOverride;
             } else {
                 mNum = 0;
                 mChildren = null;
index 6731a17..f67dcb3 100644 (file)
@@ -323,7 +323,21 @@ public class RippleDrawable extends LayerDrawable {
      */
     @Override
     public boolean isProjected() {
-        return getNumberOfLayers() == 0;
+        // If the maximum radius is contained entirely within the bounds, we
+        // don't need to project this ripple.
+        final int radius = mState.mMaxRadius;
+        final Rect bounds = getBounds();
+        if (radius != RADIUS_AUTO && radius <= bounds.width() / 2
+                && radius <= bounds.height() / 2) {
+            return false;
+        }
+
+        // Otherwise, if the layer is bounded then we don't need to project.
+        return !isBounded();
+    }
+
+    private boolean isBounded() {
+        return getNumberOfLayers() > 0;
     }
 
     @Override
@@ -545,7 +559,7 @@ public class RippleDrawable extends LayerDrawable {
                 y = mHotspotBounds.exactCenterY();
             }
 
-            final boolean isBounded = !isProjected();
+            final boolean isBounded = isBounded();
             mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
         }
 
@@ -866,7 +880,7 @@ public class RippleDrawable extends LayerDrawable {
 
     @Override
     public Rect getDirtyBounds() {
-        if (isProjected()) {
+        if (!isBounded()) {
             final Rect drawingBounds = mDrawingBounds;
             final Rect dirtyBounds = mDirtyBounds;
             dirtyBounds.set(drawingBounds);