Java tutorial
/* * Copyright (C) 2015 RECRUIT LIFESTYLE CO., LTD. * * 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. */ package feifei.project.view.widget; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.support.v4.view.ViewCompat; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.BounceInterpolator; /** * @author amyu * <p/> * ??????View */ public class WaveView extends View implements ViewTreeObserver.OnPreDrawListener { /** * {@link WaveView#mDropCircleAnimator} ?Duration */ private static final long DROP_CIRCLE_ANIMATOR_DURATION = 500; /** * {@link WaveView#mDropBounceVerticalAnimator} ?Duration */ private static final long DROP_VERTEX_ANIMATION_DURATION = 500; /** * {@link WaveView#mDropBounceVerticalAnimator} ? {@link WaveView#mDropBounceHorizontalAnimator} * ?Duration */ private static final long DROP_BOUNCE_ANIMATOR_DURATION = 500; /** * {@link WaveView#mDisappearCircleAnimator} ?Duration */ private static final int DROP_REMOVE_ANIMATOR_DURATION = 200; /** * ??????????Duration */ private static final int WAVE_ANIMATOR_DURATION = 1000; /** * ??? */ private static final float MAX_WAVE_HEIGHT = 0.2f; /** * ? */ private static final int SHADOW_COLOR = 0xf707252b; /** * ?Radius */ private float mDropCircleRadius = 90; /** * ??????Paint */ private Paint mPaint; /** * ????????Path */ private Path mWavePath; /** * ?????????Path */ private Path mDropTangentPath; /** * ????????Path */ private Path mDropCirclePath; /** * ?Paint */ private Paint mShadowPaint; /** * ?Path */ private Path mShadowPath; /** * ?????RectF */ private RectF mDropRect; /** * View? */ private int mWidth; /** * {@link WaveView#mDropCircleAnimator} ??????Y */ private float mCurrentCircleCenterY; /** * ????? */ private int mMaxDropHeight; private boolean mIsManualRefreshing = false; /** * ??????????? */ private boolean mDropHeightUpdated = false; /** * {@link WaveView#mMaxDropHeight} ???????? */ private int mUpdateMaxDropHeight; /** * ??????????????Animator */ private ValueAnimator mDropVertexAnimator; /** * ??????????Animator */ private ValueAnimator mDropBounceVerticalAnimator; /** * ???????????Animator */ private ValueAnimator mDropBounceHorizontalAnimator; /** * ????Animator */ private ValueAnimator mDropCircleAnimator; /** * ???????Animator */ private ValueAnimator mDisappearCircleAnimator; /** * ??????Animator */ private ValueAnimator mWaveReverseAnimator; /** * ??? * ??2??????????? */ private static final float[][] BEGIN_PHASE_POINTS = { //1 { 0.1655f, 0 }, //? { 0.4188f, -0.0109f }, //? { 0.4606f, -0.0049f }, //? //2 { 0.4893f, 0.f }, //? { 0.4893f, 0.f }, //? { 0.5f, 0.f } //? }; private static final float[][] APPEAR_PHASE_POINTS = { //1 { 0.1655f, 0.f }, //? { 0.5237f, 0.0553f }, //? { 0.4557f, 0.0936f }, //? //2 { 0.3908f, 0.1302f }, //? { 0.4303f, 0.2173f }, //? { 0.5f, 0.2173f } //? }; private static final float[][] EXPAND_PHASE_POINTS = { //1 { 0.1655f, 0.f }, //? { 0.5909f, 0.0000f }, //? { 0.4557f, 0.1642f }, //? //2 { 0.3941f, 0.2061f }, //? { 0.4303f, 0.2889f }, //? { 0.5f, 0.2889f } //? }; /** * ?Animator?AnimatorUpdateListener */ private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { postInvalidate(); } }; /** * Constructor * {@inheritDoc} */ public WaveView(Context context) { super(context); getViewTreeObserver().addOnPreDrawListener(this); initView(); } /** * View????? {@link WaveView#mWidth} ? * {@inheritDoc} */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mWidth = w; mDropCircleRadius = w / 14.4f; updateMaxDropHeight((int) Math.min(Math.min(w, h), getHeight() - mDropCircleRadius)); super.onSizeChanged(w, h, oldw, oldh); } /** * ????? {@link WaveView#mMaxDropHeight} ? * {@inheritDoc} */ @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); if (mDropHeightUpdated) { updateMaxDropHeight(mUpdateMaxDropHeight); } return false; } @Override protected void onDraw(Canvas canvas) { //??????????? canvas.drawPath(mWavePath, mShadowPaint); canvas.drawPath(mWavePath, mPaint); mWavePath.rewind(); //?????? mDropTangentPath.rewind(); mDropCirclePath.rewind(); float circleCenterY = (Float) mDropCircleAnimator.getAnimatedValue(); float circleCenterX = mWidth / 2.f; mDropRect.setEmpty(); //?RectF?? float scale = (Float) mDisappearCircleAnimator.getAnimatedValue(); float vertical = (Float) mDropBounceVerticalAnimator.getAnimatedValue(); float horizontal = (Float) mDropBounceHorizontalAnimator.getAnimatedValue(); mDropRect.set( circleCenterX - mDropCircleRadius * (1 + vertical) * scale + mDropCircleRadius * horizontal / 2, circleCenterY + mDropCircleRadius * (1 + horizontal) * scale - mDropCircleRadius * vertical / 2, circleCenterX + mDropCircleRadius * (1 + vertical) * scale - mDropCircleRadius * horizontal / 2, circleCenterY - mDropCircleRadius * (1 + horizontal) * scale + mDropCircleRadius * vertical / 2); float vertex = (Float) mDropVertexAnimator.getAnimatedValue(); mDropTangentPath.moveTo(circleCenterX, vertex); //?(p1,q),(p2,q) double q = (Math.pow(mDropCircleRadius, 2) + circleCenterY * vertex - Math.pow(circleCenterY, 2)) / (vertex - circleCenterY); //2???????? double b = -2.0 * mWidth / 2; double c = Math.pow(q - circleCenterY, 2) + Math.pow(circleCenterX, 2) - Math.pow(mDropCircleRadius, 2); double p1 = (-b + Math.sqrt(b * b - 4 * c)) / 2; double p2 = (-b - Math.sqrt(b * b - 4 * c)) / 2; mDropTangentPath.lineTo((float) p1, (float) q); mDropTangentPath.lineTo((float) p2, (float) q); mDropTangentPath.close(); mShadowPath.set(mDropTangentPath); mShadowPath.addOval(mDropRect, Path.Direction.CCW); mDropCirclePath.addOval(mDropRect, Path.Direction.CCW); if (mDropVertexAnimator.isRunning()) { canvas.drawPath(mShadowPath, mShadowPaint); } else { canvas.drawPath(mDropCirclePath, mShadowPaint); } canvas.drawPath(mDropTangentPath, mPaint); canvas.drawPath(mDropCirclePath, mPaint); } @Override protected void onDetachedFromWindow() { if (mDisappearCircleAnimator != null) { mDisappearCircleAnimator.end(); mDisappearCircleAnimator.removeAllUpdateListeners(); } if (mDropCircleAnimator != null) { mDropCircleAnimator.end(); mDropCircleAnimator.removeAllUpdateListeners(); } if (mDropVertexAnimator != null) { mDropVertexAnimator.end(); mDropVertexAnimator.removeAllUpdateListeners(); } if (mWaveReverseAnimator != null) { mWaveReverseAnimator.end(); mWaveReverseAnimator.removeAllUpdateListeners(); } if (mDropBounceHorizontalAnimator != null) { mDropBounceHorizontalAnimator.end(); mDropBounceHorizontalAnimator.removeAllUpdateListeners(); } if (mDropBounceVerticalAnimator != null) { mDropBounceVerticalAnimator.end(); mDropBounceVerticalAnimator.removeAllUpdateListeners(); } super.onDetachedFromWindow(); } private void initView() { setUpPaint(); setUpPath(); resetAnimator(); mDropRect = new RectF(); setLayerType(View.LAYER_TYPE_SOFTWARE, null); } private void setUpPaint() { mPaint = new Paint(); mPaint.setColor(0xff2196F3); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mShadowPaint = new Paint(); mShadowPaint.setShadowLayer(10.0f, 0.0f, 2.0f, SHADOW_COLOR); } private void setUpPath() { mWavePath = new Path(); mDropTangentPath = new Path(); mDropCirclePath = new Path(); mShadowPath = new Path(); } private void resetAnimator() { mDropVertexAnimator = ValueAnimator.ofFloat(0.f, 0.f); mDropBounceVerticalAnimator = ValueAnimator.ofFloat(0.f, 0.f); mDropBounceHorizontalAnimator = ValueAnimator.ofFloat(0.f, 0.f); mDropCircleAnimator = ValueAnimator.ofFloat(-1000.f, -1000.f); mDropCircleAnimator.start(); mDisappearCircleAnimator = ValueAnimator.ofFloat(1.f, 1.f); mDisappearCircleAnimator.setDuration(1); // immediately finish animation cycle mDisappearCircleAnimator.start(); } private void onPreDragWave() { if (mWaveReverseAnimator != null) { if (mWaveReverseAnimator.isRunning()) { mWaveReverseAnimator.cancel(); } } } public void manualRefresh() { if (mIsManualRefreshing) { return; } mIsManualRefreshing = true; mDropCircleAnimator = ValueAnimator.ofFloat(mMaxDropHeight, mMaxDropHeight); mDropCircleAnimator.start(); mDropVertexAnimator = ValueAnimator.ofFloat(mMaxDropHeight - mDropCircleRadius, mMaxDropHeight - mDropCircleRadius); mDropVertexAnimator.start(); mCurrentCircleCenterY = mMaxDropHeight; postInvalidate(); } public void beginPhase(float move1) { onPreDragWave(); //????????????? mWavePath.moveTo(0, 0); //???? mWavePath.cubicTo(mWidth * BEGIN_PHASE_POINTS[0][0], BEGIN_PHASE_POINTS[0][1], mWidth * BEGIN_PHASE_POINTS[1][0], mWidth * (BEGIN_PHASE_POINTS[1][1] + move1), mWidth * BEGIN_PHASE_POINTS[2][0], mWidth * (BEGIN_PHASE_POINTS[2][1] + move1)); mWavePath.cubicTo(mWidth * BEGIN_PHASE_POINTS[3][0], mWidth * (BEGIN_PHASE_POINTS[3][1] + move1), mWidth * BEGIN_PHASE_POINTS[4][0], mWidth * (BEGIN_PHASE_POINTS[4][1] + move1), mWidth * BEGIN_PHASE_POINTS[5][0], mWidth * (BEGIN_PHASE_POINTS[5][1] + move1)); //????? mWavePath.cubicTo(mWidth - mWidth * BEGIN_PHASE_POINTS[4][0], mWidth * (BEGIN_PHASE_POINTS[4][1] + move1), mWidth - mWidth * BEGIN_PHASE_POINTS[3][0], mWidth * (BEGIN_PHASE_POINTS[3][1] + move1), mWidth - mWidth * BEGIN_PHASE_POINTS[2][0], mWidth * (BEGIN_PHASE_POINTS[2][1] + move1)); mWavePath.cubicTo(mWidth - mWidth * BEGIN_PHASE_POINTS[1][0], mWidth * (BEGIN_PHASE_POINTS[1][1] + move1), mWidth - mWidth * BEGIN_PHASE_POINTS[0][0], BEGIN_PHASE_POINTS[0][1], mWidth, 0); ViewCompat.postInvalidateOnAnimation(this); } public void appearPhase(float move1, float move2) { onPreDragWave(); mWavePath.moveTo(0, 0); //???? mWavePath.cubicTo(mWidth * APPEAR_PHASE_POINTS[0][0], mWidth * APPEAR_PHASE_POINTS[0][1], mWidth * Math.min(BEGIN_PHASE_POINTS[1][0] + move2, APPEAR_PHASE_POINTS[1][0]), mWidth * Math.max(BEGIN_PHASE_POINTS[1][1] + move1 - move2, APPEAR_PHASE_POINTS[1][1]), mWidth * Math.max(BEGIN_PHASE_POINTS[2][0] - move2, APPEAR_PHASE_POINTS[2][0]), mWidth * Math.max(BEGIN_PHASE_POINTS[2][1] + move1 - move2, APPEAR_PHASE_POINTS[2][1])); mWavePath.cubicTo(mWidth * Math.max(BEGIN_PHASE_POINTS[3][0] - move2, APPEAR_PHASE_POINTS[3][0]), mWidth * Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]), mWidth * Math.max(BEGIN_PHASE_POINTS[4][0] - move2, APPEAR_PHASE_POINTS[4][0]), mWidth * Math.min(BEGIN_PHASE_POINTS[4][1] + move1 + move2, APPEAR_PHASE_POINTS[4][1]), mWidth * APPEAR_PHASE_POINTS[5][0], mWidth * Math.min(BEGIN_PHASE_POINTS[0][1] + move1 + move2, APPEAR_PHASE_POINTS[5][1])); //????? mWavePath.cubicTo(mWidth - mWidth * Math.max(BEGIN_PHASE_POINTS[4][0] - move2, APPEAR_PHASE_POINTS[4][0]), mWidth * Math.min(BEGIN_PHASE_POINTS[4][1] + move1 + move2, APPEAR_PHASE_POINTS[4][1]), mWidth - mWidth * Math.max(BEGIN_PHASE_POINTS[3][0] - move2, APPEAR_PHASE_POINTS[3][0]), mWidth * Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]), mWidth - mWidth * Math.max(BEGIN_PHASE_POINTS[2][0] - move2, APPEAR_PHASE_POINTS[2][0]), mWidth * Math.max(BEGIN_PHASE_POINTS[2][1] + move1 - move2, APPEAR_PHASE_POINTS[2][1])); mWavePath.cubicTo(mWidth - mWidth * Math.min(BEGIN_PHASE_POINTS[1][0] + move2, APPEAR_PHASE_POINTS[1][0]), mWidth * Math.max(BEGIN_PHASE_POINTS[1][1] + move1 - move2, APPEAR_PHASE_POINTS[1][1]), mWidth - mWidth * APPEAR_PHASE_POINTS[0][0], mWidth * APPEAR_PHASE_POINTS[0][1], mWidth, 0); mCurrentCircleCenterY = mWidth * Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]) + mDropCircleRadius; ViewCompat.postInvalidateOnAnimation(this); } public void expandPhase(float move1, float move2, float move3) { onPreDragWave(); mWavePath.moveTo(0, 0); //???? mWavePath.cubicTo(mWidth * EXPAND_PHASE_POINTS[0][0], mWidth * EXPAND_PHASE_POINTS[0][1], mWidth * Math.min(Math.min(BEGIN_PHASE_POINTS[1][0] + move2, APPEAR_PHASE_POINTS[1][0]) + move3, EXPAND_PHASE_POINTS[1][0]), mWidth * Math.max( Math.max(BEGIN_PHASE_POINTS[1][1] + move1 - move2, APPEAR_PHASE_POINTS[1][1]) - move3, EXPAND_PHASE_POINTS[1][1]), mWidth * Math.max(BEGIN_PHASE_POINTS[2][0] - move2, EXPAND_PHASE_POINTS[2][0]), mWidth * Math.min( Math.max(BEGIN_PHASE_POINTS[2][1] + move1 - move2, APPEAR_PHASE_POINTS[2][1]) + move3, EXPAND_PHASE_POINTS[2][1])); mWavePath .cubicTo( mWidth * Math.min( Math.max(BEGIN_PHASE_POINTS[3][0] - move2, APPEAR_PHASE_POINTS[3][0]) + move3, EXPAND_PHASE_POINTS[3][0]), mWidth * Math .min(Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]) + move3, EXPAND_PHASE_POINTS[3][1]), mWidth * Math.max(BEGIN_PHASE_POINTS[4][0] - move2, EXPAND_PHASE_POINTS[4][0]), mWidth * Math .min(Math.min(BEGIN_PHASE_POINTS[4][1] + move1 + move2, APPEAR_PHASE_POINTS[4][1]) + move3, EXPAND_PHASE_POINTS[4][1]), mWidth * EXPAND_PHASE_POINTS[5][0], mWidth * Math .min(Math.min(BEGIN_PHASE_POINTS[0][1] + move1 + move2, APPEAR_PHASE_POINTS[5][1]) + move3, EXPAND_PHASE_POINTS[5][1])); //????? mWavePath.cubicTo(mWidth - mWidth * Math.max(BEGIN_PHASE_POINTS[4][0] - move2, EXPAND_PHASE_POINTS[4][0]), mWidth * Math.min( Math.min(BEGIN_PHASE_POINTS[4][1] + move1 + move2, APPEAR_PHASE_POINTS[4][1]) + move3, EXPAND_PHASE_POINTS[4][1]), mWidth - mWidth * Math.min(Math.max(BEGIN_PHASE_POINTS[3][0] - move2, APPEAR_PHASE_POINTS[3][0]) + move3, EXPAND_PHASE_POINTS[3][0]), mWidth * Math.min( Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]) + move3, EXPAND_PHASE_POINTS[3][1]), mWidth - mWidth * Math.max(BEGIN_PHASE_POINTS[2][0] - move2, EXPAND_PHASE_POINTS[2][0]), mWidth * Math.min( Math.max(BEGIN_PHASE_POINTS[2][1] + move1 - move2, APPEAR_PHASE_POINTS[2][1]) + move3, EXPAND_PHASE_POINTS[2][1])); mWavePath .cubicTo( mWidth - mWidth * Math.min( Math.min(BEGIN_PHASE_POINTS[1][0] + move2, APPEAR_PHASE_POINTS[1][0]) + move3, EXPAND_PHASE_POINTS[1][0]), mWidth * Math .max(Math.max(BEGIN_PHASE_POINTS[1][1] + move1 - move2, APPEAR_PHASE_POINTS[1][1]) - move3, EXPAND_PHASE_POINTS[1][1]), mWidth - mWidth * EXPAND_PHASE_POINTS[0][0], mWidth * EXPAND_PHASE_POINTS[0][1], mWidth, 0); mCurrentCircleCenterY = mWidth * Math.min(Math.min(BEGIN_PHASE_POINTS[3][1] + move1 + move2, APPEAR_PHASE_POINTS[3][1]) + move3, EXPAND_PHASE_POINTS[3][1]) + mDropCircleRadius; ViewCompat.postInvalidateOnAnimation(this); } /** * @param height ? */ private void updateMaxDropHeight(int height) { if (500 * (mWidth / 1440.f) > height) { Log.w("WaveView", "DropHeight is more than " + 500 * (mWidth / 1440.f)); return; } mMaxDropHeight = (int) Math.min(height, getHeight() - mDropCircleRadius); if (mIsManualRefreshing) { mIsManualRefreshing = false; manualRefresh(); } } public void startDropAnimation() { // show dropBubble again mDisappearCircleAnimator = ValueAnimator.ofFloat(1.f, 1.f); mDisappearCircleAnimator.setDuration(1); mDisappearCircleAnimator.start(); //todo ? mDropCircleAnimator = ValueAnimator.ofFloat(500 * (mWidth / 1440.f), mMaxDropHeight - 20); mDropCircleAnimator.setDuration(DROP_CIRCLE_ANIMATOR_DURATION); mDropCircleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurrentCircleCenterY = (float) animation.getAnimatedValue(); ViewCompat.postInvalidateOnAnimation(WaveView.this); } }); mDropCircleAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); mDropCircleAnimator.start(); mDropVertexAnimator = ValueAnimator.ofFloat(0.f, mMaxDropHeight - mDropCircleRadius); mDropVertexAnimator.setDuration(DROP_VERTEX_ANIMATION_DURATION); mDropVertexAnimator.addUpdateListener(mAnimatorUpdateListener); mDropVertexAnimator.start(); mDropBounceVerticalAnimator = ValueAnimator.ofFloat(0.f, 1.f); mDropBounceVerticalAnimator.setDuration(DROP_BOUNCE_ANIMATOR_DURATION); mDropBounceVerticalAnimator.addUpdateListener(mAnimatorUpdateListener); mDropBounceVerticalAnimator.setInterpolator(new DropBounceInterpolator()); mDropBounceVerticalAnimator.setStartDelay(DROP_VERTEX_ANIMATION_DURATION); mDropBounceVerticalAnimator.start(); mDropBounceHorizontalAnimator = ValueAnimator.ofFloat(0.f, 1.f); mDropBounceHorizontalAnimator.setDuration(DROP_BOUNCE_ANIMATOR_DURATION); mDropBounceHorizontalAnimator.addUpdateListener(mAnimatorUpdateListener); mDropBounceHorizontalAnimator.setInterpolator(new DropBounceInterpolator()); mDropBounceHorizontalAnimator .setStartDelay((long) (DROP_VERTEX_ANIMATION_DURATION + DROP_BOUNCE_ANIMATOR_DURATION * 0.25)); mDropBounceHorizontalAnimator.start(); } public void startDisappearCircleAnimation() { mDisappearCircleAnimator = ValueAnimator.ofFloat(1.f, 0.f); mDisappearCircleAnimator.addUpdateListener(mAnimatorUpdateListener); mDisappearCircleAnimator.setDuration(DROP_REMOVE_ANIMATOR_DURATION); mDisappearCircleAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { //?Animator????????????-100.f?? resetAnimator(); mIsManualRefreshing = false; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); mDisappearCircleAnimator.start(); } /** * @param h ??? */ public void startWaveAnimation(float h) { h = Math.min(h, MAX_WAVE_HEIGHT) * mWidth; mWaveReverseAnimator = ValueAnimator.ofFloat(h, 0.f); mWaveReverseAnimator.setDuration(WAVE_ANIMATOR_DURATION); mWaveReverseAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float h = (Float) valueAnimator.getAnimatedValue(); mWavePath.moveTo(0, 0); mWavePath.quadTo(0.25f * mWidth, 0, 0.333f * mWidth, h * 0.5f); mWavePath.quadTo(mWidth * 0.5f, h * 1.4f, 0.666f * mWidth, h * 0.5f); mWavePath.quadTo(0.75f * mWidth, 0, mWidth, 0); postInvalidate(); } }); mWaveReverseAnimator.setInterpolator(new BounceInterpolator()); mWaveReverseAnimator.start(); } public void animationDropCircle() { if (mDisappearCircleAnimator.isRunning()) { return; } startDropAnimation(); startWaveAnimation(0.1f); } public float getCurrentCircleCenterY() { return mCurrentCircleCenterY; } /** * @param maxDropHeight ??? */ public void setMaxDropHeight(int maxDropHeight) { if (mDropHeightUpdated) { updateMaxDropHeight(maxDropHeight); } else { mUpdateMaxDropHeight = maxDropHeight; mDropHeightUpdated = true; } } public boolean isDisappearCircleAnimatorRunning() { return mDisappearCircleAnimator.isRunning(); } /** * @param radius ?? */ public void setShadowRadius(int radius) { mShadowPaint.setShadowLayer(radius, 0.0f, 2.0f, SHADOW_COLOR); } public void setWaveColor(int color) { mPaint.setColor(color); invalidate(); } }