FloatingActionButtonEclairMr1.java revision 7e82b99953680915596eaf0eb35927388e574ca8
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.design.widget; 18 19import android.content.res.ColorStateList; 20import android.graphics.Color; 21import android.graphics.PorterDuff; 22import android.graphics.Rect; 23import android.graphics.drawable.Drawable; 24import android.graphics.drawable.GradientDrawable; 25import android.graphics.drawable.LayerDrawable; 26import android.support.v4.graphics.drawable.DrawableCompat; 27import android.support.v4.view.animation.FastOutSlowInInterpolator; 28import android.view.View; 29import android.view.animation.Animation; 30import android.view.animation.Interpolator; 31import android.view.animation.Transformation; 32 33class FloatingActionButtonEclairMr1 extends FloatingActionButtonImpl { 34 35 private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator(); 36 37 private Drawable mShapeDrawable; 38 private Drawable mRippleDrawable; 39 40 private float mElevation; 41 private float mPressedTranslationZ; 42 private int mAnimationDuration; 43 44 private StateListAnimator mStateListAnimator; 45 46 ShadowDrawableWrapper mShadowDrawable; 47 48 FloatingActionButtonEclairMr1(View view, ShadowViewDelegate shadowViewDelegate) { 49 super(view, shadowViewDelegate); 50 51 mAnimationDuration = view.getResources().getInteger(android.R.integer.config_shortAnimTime); 52 53 mStateListAnimator = new StateListAnimator(); 54 mStateListAnimator.setTarget(view); 55 56 // Elevate with translationZ when pressed or focused 57 mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET, 58 setupAnimation(new ElevateToTranslationZAnimation())); 59 mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET, 60 setupAnimation(new ElevateToTranslationZAnimation())); 61 // Reset back to elevation by default 62 mStateListAnimator.addState(EMPTY_STATE_SET, 63 setupAnimation(new ResetElevationAnimation())); 64 } 65 66 @Override 67 void setBackgroundDrawable(Drawable originalBackground, ColorStateList backgroundTint, 68 PorterDuff.Mode backgroundTintMode, int rippleColor) { 69 // First we need to tint the original background with the tint 70 mShapeDrawable = DrawableCompat.wrap(originalBackground); 71 DrawableCompat.setTintList(mShapeDrawable, backgroundTint); 72 if (backgroundTintMode != null) { 73 DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode); 74 } 75 76 // Now we created a mask Drawable which will be used for touch feedback. 77 // As we don't know the actual outline of mShapeDrawable, we'll just guess that it's a 78 // circle 79 GradientDrawable touchFeedbackShape = new GradientDrawable(); 80 touchFeedbackShape.setShape(GradientDrawable.OVAL); 81 touchFeedbackShape.setColor(Color.WHITE); 82 touchFeedbackShape.setCornerRadius(mShadowViewDelegate.getRadius()); 83 84 // We'll now wrap that touch feedback mask drawable with a ColorStateList 85 mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape); 86 DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor)); 87 DrawableCompat.setTintMode(mRippleDrawable, PorterDuff.Mode.MULTIPLY); 88 89 mShadowDrawable = new ShadowDrawableWrapper( 90 mView.getResources(), 91 new LayerDrawable(new Drawable[] {mShapeDrawable, mRippleDrawable}), 92 mShadowViewDelegate.getRadius(), 93 mElevation, 94 mElevation + mPressedTranslationZ); 95 mShadowDrawable.setAddPaddingForCorners(false); 96 97 mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable); 98 99 updatePadding(); 100 } 101 102 @Override 103 void setBackgroundTintList(ColorStateList tint) { 104 DrawableCompat.setTintList(mShapeDrawable, tint); 105 } 106 107 @Override 108 void setBackgroundTintMode(PorterDuff.Mode tintMode) { 109 DrawableCompat.setTintMode(mShapeDrawable, tintMode); 110 } 111 112 @Override 113 void setRippleColor(int rippleColor) { 114 DrawableCompat.setTint(mRippleDrawable, rippleColor); 115 } 116 117 @Override 118 void setElevation(float elevation) { 119 if (mElevation != elevation && mShadowDrawable != null) { 120 mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ); 121 mElevation = elevation; 122 updatePadding(); 123 } 124 } 125 126 @Override 127 void setPressedTranslationZ(float translationZ) { 128 if (mPressedTranslationZ != translationZ && mShadowDrawable != null) { 129 mPressedTranslationZ = translationZ; 130 mShadowDrawable.setMaxShadowSize(mElevation + translationZ); 131 updatePadding(); 132 } 133 } 134 135 @Override 136 void onDrawableStateChanged(int[] state) { 137 mStateListAnimator.setState(state); 138 } 139 140 @Override 141 void jumpDrawableToCurrentState() { 142 mStateListAnimator.jumpToCurrentState(); 143 } 144 145 private void updatePadding() { 146 Rect rect = new Rect(); 147 mShadowDrawable.getPadding(rect); 148 mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom); 149 } 150 151 private Animation setupAnimation(Animation animation) { 152 animation.setInterpolator(INTERPOLATOR); 153 animation.setDuration(mAnimationDuration); 154 return animation; 155 } 156 157 private abstract class BaseShadowAnimation extends Animation { 158 private float mShadowSizeStart; 159 private float mShadowSizeDiff; 160 161 @Override 162 public void reset() { 163 super.reset(); 164 165 mShadowSizeStart = mShadowDrawable.getShadowSize(); 166 mShadowSizeDiff = getTargetShadowSize() - mShadowSizeStart; 167 } 168 169 @Override 170 protected void applyTransformation(float interpolatedTime, Transformation t) { 171 mShadowDrawable.setShadowSize(mShadowSizeStart + (mShadowSizeDiff * interpolatedTime)); 172 } 173 174 /** 175 * @return the shadow size we want to animate to. 176 */ 177 protected abstract float getTargetShadowSize(); 178 } 179 180 private class ResetElevationAnimation extends BaseShadowAnimation { 181 @Override 182 protected float getTargetShadowSize() { 183 return mElevation; 184 } 185 } 186 187 private class ElevateToTranslationZAnimation extends BaseShadowAnimation { 188 @Override 189 protected float getTargetShadowSize() { 190 return mElevation + mPressedTranslationZ; 191 } 192 } 193 194 private static ColorStateList createColorStateList(int selectedColor) { 195 final int[][] states = new int[3][]; 196 final int[] colors = new int[3]; 197 int i = 0; 198 199 states[i] = FOCUSED_ENABLED_STATE_SET; 200 colors[i] = selectedColor; 201 i++; 202 203 states[i] = PRESSED_ENABLED_STATE_SET; 204 colors[i] = selectedColor; 205 i++; 206 207 // Default enabled state 208 states[i] = new int[0]; 209 colors[i] = Color.TRANSPARENT; 210 i++; 211 212 return new ColorStateList(states, colors); 213 } 214}