RippleBackground.java revision 191ac0a72b0a7ba883d9cebee6eff71fbea4ef17
11b86fe063badb5f28c467ade39be0f4008688947Andreas Huber/* 21b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * Copyright (C) 2013 The Android Open Source Project 31b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * 41b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License"); 51b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * you may not use this file except in compliance with the License. 61b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * You may obtain a copy of the License at 71b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * 81b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * http://www.apache.org/licenses/LICENSE-2.0 91b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * 101b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * Unless required by applicable law or agreed to in writing, software 111b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 121b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * See the License for the specific language governing permissions and 141b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * limitations under the License. 151b86fe063badb5f28c467ade39be0f4008688947Andreas Huber */ 161b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 171b86fe063badb5f28c467ade39be0f4008688947Andreas Huberpackage android.graphics.drawable; 181b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 191b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.animation.Animator; 201b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.animation.AnimatorSet; 211b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.animation.ObjectAnimator; 221b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.animation.TimeInterpolator; 231b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.graphics.Canvas; 241b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.graphics.CanvasProperty; 251b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.graphics.Paint; 261b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.graphics.Rect; 271b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.util.FloatProperty; 281b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.view.DisplayListCanvas; 291b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.view.RenderNodeAnimator; 301b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.view.animation.LinearInterpolator; 311b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 321b86fe063badb5f28c467ade39be0f4008688947Andreas Huber/** 331b86fe063badb5f28c467ade39be0f4008688947Andreas Huber * Draws a ripple background. 341b86fe063badb5f28c467ade39be0f4008688947Andreas Huber */ 351b86fe063badb5f28c467ade39be0f4008688947Andreas Huberclass RippleBackground extends RippleComponent { 361b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); 371b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 381b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private static final int OPACITY_ENTER_DURATION = 600; 391b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private static final int OPACITY_ENTER_DURATION_FAST = 120; 401b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private static final int OPACITY_EXIT_DURATION = 480; 411b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 421b86fe063badb5f28c467ade39be0f4008688947Andreas Huber // Hardware rendering properties. 431b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private CanvasProperty<Paint> mPropPaint; 441b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private CanvasProperty<Float> mPropRadius; 451b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private CanvasProperty<Float> mPropX; 461b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private CanvasProperty<Float> mPropY; 471b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 481b86fe063badb5f28c467ade39be0f4008688947Andreas Huber // Software rendering properties. 491b86fe063badb5f28c467ade39be0f4008688947Andreas Huber private float mOpacity = 0; 501b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 511b86fe063badb5f28c467ade39be0f4008688947Andreas Huber public RippleBackground(RippleDrawable owner, Rect bounds) { 521b86fe063badb5f28c467ade39be0f4008688947Andreas Huber super(owner, bounds); 531b86fe063badb5f28c467ade39be0f4008688947Andreas Huber } 541b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 551b86fe063badb5f28c467ade39be0f4008688947Andreas Huber public boolean isVisible() { 561b86fe063badb5f28c467ade39be0f4008688947Andreas Huber return mOpacity > 0 || isHardwareAnimating(); 571b86fe063badb5f28c467ade39be0f4008688947Andreas Huber } 581b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 591b86fe063badb5f28c467ade39be0f4008688947Andreas Huber @Override 601b86fe063badb5f28c467ade39be0f4008688947Andreas Huber protected boolean drawSoftware(Canvas c, Paint p) { 611b86fe063badb5f28c467ade39be0f4008688947Andreas Huber boolean hasContent = false; 621b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 631b86fe063badb5f28c467ade39be0f4008688947Andreas Huber final int origAlpha = p.getAlpha(); 641b86fe063badb5f28c467ade39be0f4008688947Andreas Huber final int alpha = (int) (origAlpha * mOpacity + 0.5f); 651b86fe063badb5f28c467ade39be0f4008688947Andreas Huber if (alpha > 0) { 661b86fe063badb5f28c467ade39be0f4008688947Andreas Huber p.setAlpha(alpha); 671b86fe063badb5f28c467ade39be0f4008688947Andreas Huber c.drawCircle(0, 0, mTargetRadius, p); 681b86fe063badb5f28c467ade39be0f4008688947Andreas Huber p.setAlpha(origAlpha); 691b86fe063badb5f28c467ade39be0f4008688947Andreas Huber hasContent = true; 701b86fe063badb5f28c467ade39be0f4008688947Andreas Huber } 711b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 721b86fe063badb5f28c467ade39be0f4008688947Andreas Huber return hasContent; 731b86fe063badb5f28c467ade39be0f4008688947Andreas Huber } 741b86fe063badb5f28c467ade39be0f4008688947Andreas Huber 751b86fe063badb5f28c467ade39be0f4008688947Andreas Huber @Override 761b86fe063badb5f28c467ade39be0f4008688947Andreas Huber protected boolean drawHardware(DisplayListCanvas c) { 771b86fe063badb5f28c467ade39be0f4008688947Andreas Huber c.drawCircle(mPropX, mPropY, mPropRadius, mPropPaint); 78 return true; 79 } 80 81 @Override 82 protected Animator createSoftwareEnter(boolean fast) { 83 // Linear enter based on current opacity. 84 final int maxDuration = fast ? OPACITY_ENTER_DURATION_FAST : OPACITY_ENTER_DURATION; 85 final int duration = (int) ((1 - mOpacity) * maxDuration); 86 87 final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); 88 opacity.setAutoCancel(true); 89 opacity.setDuration(duration); 90 opacity.setInterpolator(LINEAR_INTERPOLATOR); 91 92 return opacity; 93 } 94 95 @Override 96 protected Animator createSoftwareExit() { 97 final AnimatorSet set = new AnimatorSet(); 98 99 // Linear exit after enter is completed. 100 final ObjectAnimator exit = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 0); 101 exit.setInterpolator(LINEAR_INTERPOLATOR); 102 exit.setDuration(OPACITY_EXIT_DURATION); 103 exit.setAutoCancel(true); 104 105 final AnimatorSet.Builder builder = set.play(exit); 106 107 // Linear "fast" enter based on current opacity. 108 final int fastEnterDuration = (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST); 109 if (fastEnterDuration > 0) { 110 final ObjectAnimator enter = ObjectAnimator.ofFloat(this, RippleBackground.OPACITY, 1); 111 enter.setInterpolator(LINEAR_INTERPOLATOR); 112 enter.setDuration(fastEnterDuration); 113 enter.setAutoCancel(true); 114 115 builder.after(enter); 116 } 117 118 return set; 119 } 120 121 @Override 122 protected RenderNodeAnimatorSet createHardwareExit(Paint p) { 123 final RenderNodeAnimatorSet set = new RenderNodeAnimatorSet(); 124 125 final int targetAlpha = p.getAlpha(); 126 final int currentAlpha = (int) (mOpacity * targetAlpha + 0.5f); 127 p.setAlpha(currentAlpha); 128 129 mPropPaint = CanvasProperty.createPaint(p); 130 mPropRadius = CanvasProperty.createFloat(mTargetRadius); 131 mPropX = CanvasProperty.createFloat(0); 132 mPropY = CanvasProperty.createFloat(0); 133 134 final int fastEnterDuration = (int) ((1 - mOpacity) * OPACITY_ENTER_DURATION_FAST); 135 136 // Linear exit after enter is completed. 137 final RenderNodeAnimator exit = new RenderNodeAnimator( 138 mPropPaint, RenderNodeAnimator.PAINT_ALPHA, 0); 139 exit.setInterpolator(LINEAR_INTERPOLATOR); 140 exit.setDuration(OPACITY_EXIT_DURATION); 141 exit.setStartDelay(fastEnterDuration); 142 set.add(exit); 143 144 // Linear "fast" enter based on current opacity. 145 if (fastEnterDuration > 0) { 146 final RenderNodeAnimator enter = new RenderNodeAnimator( 147 mPropPaint, RenderNodeAnimator.PAINT_ALPHA, targetAlpha); 148 enter.setInterpolator(LINEAR_INTERPOLATOR); 149 enter.setDuration(fastEnterDuration); 150 set.add(enter); 151 } 152 153 return set; 154 } 155 156 @Override 157 protected void jumpValuesToExit() { 158 mOpacity = 0; 159 } 160 161 private static abstract class BackgroundProperty extends FloatProperty<RippleBackground> { 162 public BackgroundProperty(String name) { 163 super(name); 164 } 165 } 166 167 private static final BackgroundProperty OPACITY = new BackgroundProperty("opacity") { 168 @Override 169 public void setValue(RippleBackground object, float value) { 170 object.mOpacity = value; 171 object.invalidateSelf(); 172 } 173 174 @Override 175 public Float get(RippleBackground object) { 176 return object.mOpacity; 177 } 178 }; 179} 180