FloatingActionButton.java revision 14d064edb3e4a16a3b90a4a850560177bea1e60d
19840efe3dbdc7026521da8576574c55120782f6cChris Banes/* 29840efe3dbdc7026521da8576574c55120782f6cChris Banes * Copyright (C) 2015 The Android Open Source Project 39840efe3dbdc7026521da8576574c55120782f6cChris Banes * 49840efe3dbdc7026521da8576574c55120782f6cChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 59840efe3dbdc7026521da8576574c55120782f6cChris Banes * you may not use this file except in compliance with the License. 69840efe3dbdc7026521da8576574c55120782f6cChris Banes * You may obtain a copy of the License at 79840efe3dbdc7026521da8576574c55120782f6cChris Banes * 89840efe3dbdc7026521da8576574c55120782f6cChris Banes * http://www.apache.org/licenses/LICENSE-2.0 99840efe3dbdc7026521da8576574c55120782f6cChris Banes * 109840efe3dbdc7026521da8576574c55120782f6cChris Banes * Unless required by applicable law or agreed to in writing, software 119840efe3dbdc7026521da8576574c55120782f6cChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 129840efe3dbdc7026521da8576574c55120782f6cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139840efe3dbdc7026521da8576574c55120782f6cChris Banes * See the License for the specific language governing permissions and 149840efe3dbdc7026521da8576574c55120782f6cChris Banes * limitations under the License. 159840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 169840efe3dbdc7026521da8576574c55120782f6cChris Banes 179840efe3dbdc7026521da8576574c55120782f6cChris Banespackage android.support.design.widget; 189840efe3dbdc7026521da8576574c55120782f6cChris Banes 199840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.annotation.TargetApi; 209840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.Context; 219840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.ColorStateList; 229840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.TypedArray; 239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff; 249840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.Rect; 259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable; 269840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.os.Build; 279840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.annotation.Nullable; 289840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.design.R; 299840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.util.AttributeSet; 3014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.View; 3114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.ViewGroup; 3214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.ViewParent; 339840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.widget.ImageView; 349840efe3dbdc7026521da8576574c55120782f6cChris Banes 359840efe3dbdc7026521da8576574c55120782f6cChris Banes/** 369840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons are used for a special type of promoted action. They are distinguished 3714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * by a circled icon floating above the UI and have special motion behaviors related to morphing, 389840efe3dbdc7026521da8576574c55120782f6cChris Banes * launching, and the transferring anchor point. 399840efe3dbdc7026521da8576574c55120782f6cChris Banes * 409840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons come in two sizes: the default, which should be used in most cases, and 419840efe3dbdc7026521da8576574c55120782f6cChris Banes * the mini, which should only be used to create visual continuity with other elements on the 429840efe3dbdc7026521da8576574c55120782f6cChris Banes * screen. 439840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 449840efe3dbdc7026521da8576574c55120782f6cChris Banespublic class FloatingActionButton extends ImageView { 459840efe3dbdc7026521da8576574c55120782f6cChris Banes 469840efe3dbdc7026521da8576574c55120782f6cChris Banes // These values must match those in the attrs declaration 479840efe3dbdc7026521da8576574c55120782f6cChris Banes private static final int SIZE_MINI = 1; 489840efe3dbdc7026521da8576574c55120782f6cChris Banes private static final int SIZE_NORMAL = 0; 499840efe3dbdc7026521da8576574c55120782f6cChris Banes 509840efe3dbdc7026521da8576574c55120782f6cChris Banes private ColorStateList mBackgroundTint; 519840efe3dbdc7026521da8576574c55120782f6cChris Banes private PorterDuff.Mode mBackgroundTintMode; 529840efe3dbdc7026521da8576574c55120782f6cChris Banes 539840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mRippleColor; 549840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mSize; 559840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mContentPadding; 569840efe3dbdc7026521da8576574c55120782f6cChris Banes 579840efe3dbdc7026521da8576574c55120782f6cChris Banes private final Rect mShadowPadding; 589840efe3dbdc7026521da8576574c55120782f6cChris Banes 599840efe3dbdc7026521da8576574c55120782f6cChris Banes private final FloatingActionButtonImpl mImpl; 609840efe3dbdc7026521da8576574c55120782f6cChris Banes 619840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context) { 629840efe3dbdc7026521da8576574c55120782f6cChris Banes this(context, null); 639840efe3dbdc7026521da8576574c55120782f6cChris Banes } 649840efe3dbdc7026521da8576574c55120782f6cChris Banes 659840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs) { 669840efe3dbdc7026521da8576574c55120782f6cChris Banes this(context, attrs, 0); 679840efe3dbdc7026521da8576574c55120782f6cChris Banes } 689840efe3dbdc7026521da8576574c55120782f6cChris Banes 699840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 709840efe3dbdc7026521da8576574c55120782f6cChris Banes super(context, attrs, defStyleAttr); 719840efe3dbdc7026521da8576574c55120782f6cChris Banes 729840efe3dbdc7026521da8576574c55120782f6cChris Banes mShadowPadding = new Rect(); 739840efe3dbdc7026521da8576574c55120782f6cChris Banes 749840efe3dbdc7026521da8576574c55120782f6cChris Banes TypedArray a = context.obtainStyledAttributes(attrs, 759840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton, defStyleAttr, 769840efe3dbdc7026521da8576574c55120782f6cChris Banes R.style.Widget_Design_FloatingActionButton); 779840efe3dbdc7026521da8576574c55120782f6cChris Banes Drawable background = a.getDrawable(R.styleable.FloatingActionButton_android_background); 789840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint); 799840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTintMode = parseTintMode(a.getInt( 809840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_backgroundTintMode, -1), null); 819840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0); 829840efe3dbdc7026521da8576574c55120782f6cChris Banes mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL); 839840efe3dbdc7026521da8576574c55120782f6cChris Banes final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f); 849840efe3dbdc7026521da8576574c55120782f6cChris Banes final float pressedTranslationZ = a.getDimension( 859840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_pressedTranslationZ, 0f); 869840efe3dbdc7026521da8576574c55120782f6cChris Banes a.recycle(); 879840efe3dbdc7026521da8576574c55120782f6cChris Banes 889840efe3dbdc7026521da8576574c55120782f6cChris Banes final ShadowViewDelegate delegate = new ShadowViewDelegate() { 899840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 909840efe3dbdc7026521da8576574c55120782f6cChris Banes public float getRadius() { 919840efe3dbdc7026521da8576574c55120782f6cChris Banes return getSizeDimension() / 2f; 929840efe3dbdc7026521da8576574c55120782f6cChris Banes } 939840efe3dbdc7026521da8576574c55120782f6cChris Banes 949840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 959840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setShadowPadding(int left, int top, int right, int bottom) { 969840efe3dbdc7026521da8576574c55120782f6cChris Banes mShadowPadding.set(left, top, right, bottom); 979840efe3dbdc7026521da8576574c55120782f6cChris Banes 989840efe3dbdc7026521da8576574c55120782f6cChris Banes setPadding(left + mContentPadding, top + mContentPadding, 999840efe3dbdc7026521da8576574c55120782f6cChris Banes right + mContentPadding, bottom + mContentPadding); 1009840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1019840efe3dbdc7026521da8576574c55120782f6cChris Banes 1029840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1039840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundDrawable(Drawable background) { 1049840efe3dbdc7026521da8576574c55120782f6cChris Banes FloatingActionButton.super.setBackgroundDrawable(background); 1059840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1069840efe3dbdc7026521da8576574c55120782f6cChris Banes }; 1079840efe3dbdc7026521da8576574c55120782f6cChris Banes 1089840efe3dbdc7026521da8576574c55120782f6cChris Banes if (Build.VERSION.SDK_INT >= 21) { 1099840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl = new FloatingActionButtonLollipop(this, delegate); 1109840efe3dbdc7026521da8576574c55120782f6cChris Banes } else { 1119840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl = new FloatingActionButtonEclairMr1(this, delegate); 1129840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1139840efe3dbdc7026521da8576574c55120782f6cChris Banes 1149840efe3dbdc7026521da8576574c55120782f6cChris Banes final int maxContentSize = (int) getResources().getDimension(R.dimen.fab_content_size); 1159840efe3dbdc7026521da8576574c55120782f6cChris Banes mContentPadding = (getSizeDimension() - maxContentSize) / 2; 1169840efe3dbdc7026521da8576574c55120782f6cChris Banes 1179840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setBackgroundDrawable(background, mBackgroundTint, 1189840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTintMode, mRippleColor); 1199840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setElevation(elevation); 1209840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setPressedTranslationZ(pressedTranslationZ); 1219840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1229840efe3dbdc7026521da8576574c55120782f6cChris Banes 1239840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1249840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1259840efe3dbdc7026521da8576574c55120782f6cChris Banes final int preferredSize = getSizeDimension(); 1269840efe3dbdc7026521da8576574c55120782f6cChris Banes 1279840efe3dbdc7026521da8576574c55120782f6cChris Banes final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec); 1289840efe3dbdc7026521da8576574c55120782f6cChris Banes final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec); 1299840efe3dbdc7026521da8576574c55120782f6cChris Banes 1309840efe3dbdc7026521da8576574c55120782f6cChris Banes // As we want to stay circular, we set both dimensions to be the 1319840efe3dbdc7026521da8576574c55120782f6cChris Banes // smallest resolved dimension 1329840efe3dbdc7026521da8576574c55120782f6cChris Banes final int d = Math.min(w, h); 1339840efe3dbdc7026521da8576574c55120782f6cChris Banes 1349840efe3dbdc7026521da8576574c55120782f6cChris Banes // We add the shadow's padding to the measured dimension 1359840efe3dbdc7026521da8576574c55120782f6cChris Banes setMeasuredDimension( 1369840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.left + mShadowPadding.right, 1379840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.top + mShadowPadding.bottom); 1389840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1399840efe3dbdc7026521da8576574c55120782f6cChris Banes 14014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes @Override 14114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 14214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes super.onLayout(changed, left, top, right, bottom); 14314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes updateOffset(); 14414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 14514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 1469840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1479840efe3dbdc7026521da8576574c55120782f6cChris Banes * Set the ripple color for this {@link FloatingActionButton}. 1489840efe3dbdc7026521da8576574c55120782f6cChris Banes * <p> 1499840efe3dbdc7026521da8576574c55120782f6cChris Banes * When running on devices with KitKat or below, we draw a fill rather than a ripple. 1509840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1519840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param color ARGB color to use for the ripple. 1529840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1539840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setRippleColor(int color) { 1549840efe3dbdc7026521da8576574c55120782f6cChris Banes if (mRippleColor != color) { 1559840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = color; 1569840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setRippleColor(color); 1579840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1589840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1599840efe3dbdc7026521da8576574c55120782f6cChris Banes 1609840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1619840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the tint applied to the background drawable, if specified. 1629840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1639840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the tint applied to the background drawable 1649840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintList(ColorStateList) 1659840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1669840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 1679840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1689840efe3dbdc7026521da8576574c55120782f6cChris Banes public ColorStateList getBackgroundTintList() { 1699840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTint; 1709840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1719840efe3dbdc7026521da8576574c55120782f6cChris Banes 1729840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1739840efe3dbdc7026521da8576574c55120782f6cChris Banes * Applies a tint to the background drawable. Does not modify the current tint 1749840efe3dbdc7026521da8576574c55120782f6cChris Banes * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 1759840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1769840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tint the tint to apply, may be {@code null} to clear tint 1779840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1789840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintList(@Nullable ColorStateList tint) { 1799840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setBackgroundTintList(tint); 1809840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1819840efe3dbdc7026521da8576574c55120782f6cChris Banes 1829840efe3dbdc7026521da8576574c55120782f6cChris Banes 1839840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1849840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the blending mode used to apply the tint to the background 1859840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable, if specified. 1869840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1879840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the blending mode used to apply the tint to the background 1889840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable 1899840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintMode(PorterDuff.Mode) 1909840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1919840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 1929840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1939840efe3dbdc7026521da8576574c55120782f6cChris Banes public PorterDuff.Mode getBackgroundTintMode() { 1949840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTintMode; 1959840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1969840efe3dbdc7026521da8576574c55120782f6cChris Banes 1979840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1989840efe3dbdc7026521da8576574c55120782f6cChris Banes * Specifies the blending mode used to apply the tint specified by 1999840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@link #setBackgroundTintList(ColorStateList)}} to the background 2009840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 2019840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2029840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tintMode the blending mode used to apply the tint, may be 2039840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@code null} to clear tint 2049840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2059840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 2069840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setBackgroundTintMode(tintMode); 2079840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2089840efe3dbdc7026521da8576574c55120782f6cChris Banes 2099840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2109840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundDrawable(Drawable background) { 2119840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setBackgroundDrawable(background, mBackgroundTint, mBackgroundTintMode, mRippleColor); 2129840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2139840efe3dbdc7026521da8576574c55120782f6cChris Banes 2149840efe3dbdc7026521da8576574c55120782f6cChris Banes final int getSizeDimension() { 2159840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (mSize) { 2169840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_MINI: 2179840efe3dbdc7026521da8576574c55120782f6cChris Banes return getResources().getDimensionPixelSize(R.dimen.fab_size_mini); 2189840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_NORMAL: 2199840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 2209840efe3dbdc7026521da8576574c55120782f6cChris Banes return getResources().getDimensionPixelSize(R.dimen.fab_size_normal); 2219840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2229840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2239840efe3dbdc7026521da8576574c55120782f6cChris Banes 2249840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2259840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void drawableStateChanged() { 2269840efe3dbdc7026521da8576574c55120782f6cChris Banes super.drawableStateChanged(); 2279840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.onDrawableStateChanged(getDrawableState()); 2289840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2299840efe3dbdc7026521da8576574c55120782f6cChris Banes 2309840efe3dbdc7026521da8576574c55120782f6cChris Banes @TargetApi(Build.VERSION_CODES.HONEYCOMB) 2319840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2329840efe3dbdc7026521da8576574c55120782f6cChris Banes public void jumpDrawablesToCurrentState() { 2339840efe3dbdc7026521da8576574c55120782f6cChris Banes super.jumpDrawablesToCurrentState(); 2349840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.jumpDrawableToCurrentState(); 2359840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2369840efe3dbdc7026521da8576574c55120782f6cChris Banes 2379840efe3dbdc7026521da8576574c55120782f6cChris Banes private static int resolveAdjustedSize(int desiredSize, int measureSpec) { 2389840efe3dbdc7026521da8576574c55120782f6cChris Banes int result = desiredSize; 2399840efe3dbdc7026521da8576574c55120782f6cChris Banes int specMode = MeasureSpec.getMode(measureSpec); 2409840efe3dbdc7026521da8576574c55120782f6cChris Banes int specSize = MeasureSpec.getSize(measureSpec); 2419840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (specMode) { 2429840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.UNSPECIFIED: 2439840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want. Just don't be larger 2449840efe3dbdc7026521da8576574c55120782f6cChris Banes // than max size imposed on ourselves. 2459840efe3dbdc7026521da8576574c55120782f6cChris Banes result = desiredSize; 2469840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2479840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.AT_MOST: 2489840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want, up to specSize. 2499840efe3dbdc7026521da8576574c55120782f6cChris Banes // Don't be larger than specSize, and don't be larger than 2509840efe3dbdc7026521da8576574c55120782f6cChris Banes // the max size imposed on ourselves. 2519840efe3dbdc7026521da8576574c55120782f6cChris Banes result = Math.min(desiredSize, specSize); 2529840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2539840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.EXACTLY: 2549840efe3dbdc7026521da8576574c55120782f6cChris Banes // No choice. Do what we are told. 2559840efe3dbdc7026521da8576574c55120782f6cChris Banes result = specSize; 2569840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2579840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2589840efe3dbdc7026521da8576574c55120782f6cChris Banes return result; 2599840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2609840efe3dbdc7026521da8576574c55120782f6cChris Banes 2619840efe3dbdc7026521da8576574c55120782f6cChris Banes static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { 2629840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (value) { 2639840efe3dbdc7026521da8576574c55120782f6cChris Banes case 3: 2649840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_OVER; 2659840efe3dbdc7026521da8576574c55120782f6cChris Banes case 5: 2669840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_IN; 2679840efe3dbdc7026521da8576574c55120782f6cChris Banes case 9: 2689840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_ATOP; 2699840efe3dbdc7026521da8576574c55120782f6cChris Banes case 14: 2709840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.MULTIPLY; 2719840efe3dbdc7026521da8576574c55120782f6cChris Banes case 15: 2729840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SCREEN; 2739840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 2749840efe3dbdc7026521da8576574c55120782f6cChris Banes return defaultMode; 2759840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2769840efe3dbdc7026521da8576574c55120782f6cChris Banes } 27714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 27814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes /** 27914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method 28014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * offsets our layout position so that we're positioned correctly if we're on one of 28114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * our parent's edges. 28214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes */ 28314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes private void updateOffset() { 28414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes if (mShadowPadding.width() > 0 && mShadowPadding.height() > 0) { 28514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes int offsetTB = 0, offsetLR = 0; 28614d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 28714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes if (isOnRightParentEdge()) { 28814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetLR = mShadowPadding.right; 28914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } else if (isOnLeftParentEdge()) { 29014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetLR = -mShadowPadding.left; 29114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 29214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes if (isOnBottomParentEdge()) { 29314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetTB = mShadowPadding.bottom; 29414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } else if (isOnTopParentEdge()) { 29514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetTB = -mShadowPadding.top; 29614d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 29714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 29814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetTopAndBottom(offsetTB); 29914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes offsetLeftAndRight(offsetLR); 30014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 30114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 30214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 30314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes private boolean isOnLeftParentEdge() { 30414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ? 30514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ((ViewGroup.MarginLayoutParams) getLayoutParams()).leftMargin : 0; 30614d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return getLeft() <= margin; 30714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 30814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 30914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes private boolean isOnTopParentEdge() { 31014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ? 31114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin : 0; 31214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return getTop() <= margin; 31314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 31414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 31514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes private boolean isOnRightParentEdge() { 31614d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ? 31714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ((ViewGroup.MarginLayoutParams) getLayoutParams()).rightMargin : 0; 31814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 31914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ViewParent parent = getParent(); 32014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes if (parent instanceof View) { 32114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return getRight() >= (((View) getParent()).getWidth() - margin); 32214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 32314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return false; 32414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 32514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 32614d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes private boolean isOnBottomParentEdge() { 32714d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes final int margin = getLayoutParams() instanceof ViewGroup.MarginLayoutParams ? 32814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ((ViewGroup.MarginLayoutParams) getLayoutParams()).bottomMargin : 0; 32914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 33014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes ViewParent parent = getParent(); 33114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes if (parent instanceof View) { 33214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return getBottom() >= (((View) getParent()).getHeight() - margin); 33314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 33414d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes return false; 33514d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes } 3369840efe3dbdc7026521da8576574c55120782f6cChris Banes} 337