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