package codetail.widget; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.view.View; import codetail.components.R; import codetail.graphics.drawables.RippleDrawable; import codetail.graphics.drawables.TouchTracker; import codetail.utils.ViewUtils; import static codetail.utils.ViewUtils.isVisible; public class FloatingActionButton extends View{ RippleDrawable mRippleDrawable; Drawable mActionIcon; final int mActionSize; final int mInnerCircleOffset; final Circle mCircle; final Path mCirclePath; final Paint mCirclePaint; public FloatingActionButton(Context context) { this(context, null); } public FloatingActionButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setFocusable(true); setClickable(true); if(Build.VERSION.SDK_INT > 14 && Build.VERSION.SDK_INT < 16){ setLayerType(LAYER_TYPE_SOFTWARE, null); } Resources r = context.getResources(); mInnerCircleOffset = r.getDimensionPixelSize(R.dimen.fab_inner_circle_offset); mActionSize = r.getDimensionPixelSize(R.dimen.fab_action_icon_size); mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCirclePaint.setStyle(Paint.Style.FILL); // Resolving attribute styles TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FloatingActionButton); mCirclePaint.setColor(array.getColor(R.styleable.FloatingActionButton_actionColor, Color.WHITE)); mActionIcon = array.getDrawable(R.styleable.FloatingActionButton_actionIcon); if(mActionIcon != null){ mActionIcon.setBounds(0, 0, mActionSize, mActionSize); } array.recycle(); mCircle = new Circle(); mCirclePath = new Path(); ViewUtils.setBackground(this, r.getDrawable(R.drawable.floatingactionbutton_shadow_layer)); mRippleDrawable = new RippleDrawable(r.getColorStateList(R.color.overlay_color), null); mRippleDrawable.setVisible(true, false); mRippleDrawable.setCallback(this); mRippleDrawable.setState(getDrawableState()); setOnTouchListener(new TouchTracker()); } public void setActionIcon(Drawable drawable){ mActionIcon = drawable; mActionIcon.setBounds(0, 0, mActionSize, mActionSize); invalidate(); } @Override protected boolean verifyDrawable(Drawable who) { return who == mRippleDrawable || who == mActionIcon || super.verifyDrawable(who); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); mRippleDrawable.setState(getDrawableState()); if(mActionIcon != null && mActionIcon.isStateful()){ mActionIcon.setState(getDrawableState()); } } @Override @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); mRippleDrawable.jumpToCurrentState(); if(mActionIcon != null){ mActionIcon.jumpToCurrentState(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); final int size = Math.min(right - left, bottom - top) - (mInnerCircleOffset * 2); mCircle.radius = size / 2; mCircle.x = mCircle.y = mInnerCircleOffset + mCircle.radius; mRippleDrawable.setBounds(mInnerCircleOffset, mInnerCircleOffset, mInnerCircleOffset + size, mInnerCircleOffset + size); mCirclePath.reset(); mCirclePath.addCircle(mCircle.x, mCircle.y, mCircle.radius, Path.Direction.CW); } public void setColor(int color){ mCirclePaint.setColor(color); invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final int state = canvas.save(); canvas.clipPath(mCirclePath); canvas.drawPath(mCirclePath, mCirclePaint); mRippleDrawable.draw(canvas); if(mActionIcon != null){ final int beforeActionState = canvas.save(); canvas.translate( (mCircle.x - (mActionSize / 2)), (mCircle.y - (mActionSize / 2))); mActionIcon.draw(canvas); canvas.restoreToCount(beforeActionState); } canvas.restoreToCount(state); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mRippleDrawable.setVisible(isVisible(this), false); if(mActionIcon != null){ mActionIcon.setVisible(isVisible(this), false); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mRippleDrawable.setVisible(false, false); if(mActionIcon != null){ mActionIcon.setVisible(false, false); } } private static class Circle{ float x, y, radius; } }