package com.steven.babyiyo.component; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.AssetManager; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.os.Build; import android.support.annotation.NonNull; import android.text.TextPaint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.steven.babyiyo.R; /** * Date: 14-10-9. * Time: 17:04. */ public class PaperButton extends View { private static final String TAG = PaperButton.class.getSimpleName(); private static final long ANIMATION_DURATION = 200; private static final int StateNormal = 1; private static final int StateTouchDown = 2; private static final int StateTouchUp = 3; private static final float SHADOW_RADIUS = 8.0f; private static final float SHADOW_OFFSET_X = 0.0f; private static final float SHADOW_OFFSET_Y = 4.0f; private static final float MIN_SHADOW_COLOR_ALPHA = 0.1f; private static final float MAX_SHADOW_COLOR_ALPHA = 0.4f; private int mState = StateNormal; private long mStartTime; private int mColor; private int mShadowColor; private int mCornerRadius; private int mPadding; private int mTextSize; private int mTextColor; private float mShadowRadius; private float mShadowOffsetX; private float mShadowOffsetY; private CharSequence mText; private RectF backgroundRectF; private Rect mFingerRect; private Path rippleClipPath; private boolean mMoveOutside; private Point mTouchPoint = new Point(); private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Paint ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG); private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); public PaperButton(Context context) { this(context, null); } public PaperButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } @SuppressLint("NewApi") public PaperButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPadding = getResources().getDimensionPixelSize(R.dimen.paper_padding); TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.PaperButton); mColor = attributes.getColor(R.styleable.PaperButton_paper_color, getResources().getColor(R.color.paper_button_color)); mShadowColor = attributes.getColor(R.styleable.PaperButton_paper_shadow_color, getResources().getColor(R.color.paper_button_shadow_color)); mCornerRadius = attributes.getDimensionPixelSize(R.styleable.PaperButton_paper_corner_radius, getResources().getDimensionPixelSize(R.dimen.paper_button_corner_radius)); mText = attributes.getText(R.styleable.PaperButton_paper_text); mTextSize = attributes.getDimensionPixelSize(R.styleable.PaperButton_paper_text_size, getResources().getDimensionPixelSize(R.dimen.paper_text_size)); mTextColor = attributes.getColor(R.styleable.PaperButton_paper_text_color, getResources().getColor(R.color.paper_text_color)); final String assetPath = attributes.getString(R.styleable.PaperButton_paper_font); if (assetPath != null) { AssetManager assets = context.getAssets(); Typeface typeface = Typeface.createFromAsset(assets, assetPath); textPaint.setTypeface(typeface); } mShadowRadius = attributes.getFloat(R.styleable.PaperButton_paper_shadow_radius, SHADOW_RADIUS); mShadowOffsetX = attributes.getFloat(R.styleable.PaperButton_paper_shadow_offset_x, SHADOW_OFFSET_X); mShadowOffsetY = attributes.getFloat(R.styleable.PaperButton_paper_shadow_offset_y, SHADOW_OFFSET_Y); attributes.recycle(); backgroundPaint.setColor(mColor); backgroundPaint.setStyle(Paint.Style.FILL); int shadowColor = changeColorAlpha(mShadowColor, MIN_SHADOW_COLOR_ALPHA); backgroundPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, shadowColor); textPaint.setColor(mTextColor); textPaint.setTextSize(mTextSize); textPaint.setTextAlign(TextPaint.Align.CENTER); ripplePaint.setColor(darkenColor(mColor)); setWillNotDraw(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { setLayerType(View.LAYER_TYPE_SOFTWARE, null); } } private int changeColorAlpha(int color, float value) { int alpha = Math.round(Color.alpha(color) * value); int red = Color.red(color); int green = Color.green(color); int blue = Color.blue(color); return Color.argb(alpha, red, green, blue); } private int darkenColor(int color) { float[] hsv = new float[3]; Color.colorToHSV(color, hsv); hsv[2] *= 0.9f; return Color.HSVToColor(hsv); } public void setColor(int color) { mColor = color; backgroundPaint.setColor(mColor); invalidate(); } public void setShadowColor(int color) { mShadowColor = color; backgroundPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, mShadowColor); invalidate(); } public void setTextSize(int pixel) { mTextSize = pixel; textPaint.setTextSize(mTextSize); invalidate(); } public void setTextColor(int color) { mTextColor = color; textPaint.setColor(mTextColor); invalidate(); } public void setText(String text){ mText = text; invalidate(); } private RectF getRectF() { if (backgroundRectF == null) { backgroundRectF = new RectF(); backgroundRectF.left = mPadding; backgroundRectF.top = mPadding; backgroundRectF.right = getWidth() - mPadding; backgroundRectF.bottom = getHeight() - mPadding; } return backgroundRectF; } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mMoveOutside = false; mFingerRect = new Rect(getLeft(), getTop(), getRight(), getBottom()); mTouchPoint.set(Math.round(event.getX()), Math.round(event.getY())); mState = StateTouchDown; mStartTime = System.currentTimeMillis(); invalidate(); break; case MotionEvent.ACTION_MOVE: if (!mFingerRect.contains(getLeft() + (int) event.getX(), getTop() + (int) event.getY())) { mMoveOutside = true; mState = StateNormal; invalidate(); } break; case MotionEvent.ACTION_UP: if (!mMoveOutside) { mState = StateTouchUp; mStartTime = System.currentTimeMillis(); invalidate(); performClick(); } break; case MotionEvent.ACTION_CANCEL: mState = StateNormal; invalidate(); break; } return true; } @Override protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); int radius = 0; int shadowColor = changeColorAlpha(mShadowColor, MIN_SHADOW_COLOR_ALPHA); long elapsed = System.currentTimeMillis() - mStartTime; switch (mState) { case StateNormal: shadowColor = changeColorAlpha(mShadowColor, MIN_SHADOW_COLOR_ALPHA); break; case StateTouchDown: ripplePaint.setAlpha(255); if (elapsed < ANIMATION_DURATION) { radius = Math.round(elapsed * getWidth() / 2 / ANIMATION_DURATION); float shadowAlpha = (MAX_SHADOW_COLOR_ALPHA - MIN_SHADOW_COLOR_ALPHA) * elapsed / ANIMATION_DURATION + MIN_SHADOW_COLOR_ALPHA; shadowColor = changeColorAlpha(mShadowColor, shadowAlpha); } else { radius = getWidth() / 2; shadowColor = changeColorAlpha(mShadowColor, MAX_SHADOW_COLOR_ALPHA); } postInvalidate(); break; case StateTouchUp: if (elapsed < ANIMATION_DURATION) { int alpha = Math.round((ANIMATION_DURATION - elapsed) * 255 / ANIMATION_DURATION); ripplePaint.setAlpha(alpha); radius = getWidth() / 2 + Math.round(elapsed * getWidth() / 2 / ANIMATION_DURATION); float shadowAlpha = (MAX_SHADOW_COLOR_ALPHA - MIN_SHADOW_COLOR_ALPHA) * (ANIMATION_DURATION - elapsed) / ANIMATION_DURATION + MIN_SHADOW_COLOR_ALPHA; shadowColor = changeColorAlpha(mShadowColor, shadowAlpha); } else { mState = StateNormal; radius = 0; ripplePaint.setAlpha(0); shadowColor = changeColorAlpha(mShadowColor, MIN_SHADOW_COLOR_ALPHA); } postInvalidate(); break; } backgroundPaint.setShadowLayer(mShadowRadius, mShadowOffsetX, mShadowOffsetY, shadowColor); canvas.drawRoundRect(getRectF(), mCornerRadius, mCornerRadius, backgroundPaint); canvas.save(); if (mState == StateTouchDown || mState == StateTouchUp) { if (rippleClipPath == null) { rippleClipPath = new Path(); rippleClipPath.addRoundRect(getRectF(), mCornerRadius, mCornerRadius, Path.Direction.CW); } canvas.clipPath(rippleClipPath); } canvas.drawCircle(mTouchPoint.x, mTouchPoint.y, radius, ripplePaint); canvas.restore(); if (mText != null && mText.length() > 0) { int y = (int) (getHeight() / 2 - ((textPaint.descent() + textPaint.ascent()) / 2)); canvas.drawText(mText.toString(), getWidth() / 2, y, textPaint); } } }