package com.yydcdut.note.widget.fab; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable.ShaderFactory; import android.graphics.drawable.StateListDrawable; import android.graphics.drawable.shapes.OvalShape; import android.os.Build; import android.os.Build.VERSION_CODES; import android.support.annotation.ColorRes; import android.support.annotation.DimenRes; import android.support.annotation.DrawableRes; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.widget.ImageButton; import android.widget.TextView; import com.yydcdut.note.R; import com.yydcdut.note.utils.AppCompat; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class FloatingActionButton extends ImageButton { public static final int SIZE_NORMAL = 0; public static final int SIZE_MINI = 1; @Retention(RetentionPolicy.SOURCE) @IntDef({SIZE_NORMAL, SIZE_MINI}) public @interface FAB_SIZE { } /** * 正常的颜色 */ int mColorNormal; /** * 按压的颜色 */ int mColorPressed; /** * 当enable为false的时候的颜色 */ int mColorDisabled; /** * 标题 */ String mTitle; /** * icon Res资源 */ @DrawableRes private int mIcon; /** * icon为drawable的 */ private Drawable mIconDrawable; private int mSize; /** * 圆的大小 * 大的56dip,小的40dip */ private float mCircleSize; /** * 阴影的半径 */ private float mShadowRadius; /** * 阴影的偏移 */ private float mShadowOffset; /** * 最终drawable的大小,算上阴影那些 */ private int mDrawableSize; /** * 是否描边 */ boolean mStrokeVisible; public FloatingActionButton(Context context) { this(context, null); } public FloatingActionButton(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public FloatingActionButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } /** * 初始化 * * @param context * @param attributeSet */ void init(Context context, AttributeSet attributeSet) { TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionButton, 0, 0); mColorNormal = attr.getColor(R.styleable.FloatingActionButton_fab_colorNormal, getColor(android.R.color.holo_blue_dark)); mColorPressed = attr.getColor(R.styleable.FloatingActionButton_fab_colorPressed, getColor(android.R.color.holo_blue_light)); mColorDisabled = attr.getColor(R.styleable.FloatingActionButton_fab_colorDisabled, getColor(android.R.color.darker_gray)); mSize = attr.getInt(R.styleable.FloatingActionButton_fab_size, SIZE_NORMAL); mIcon = attr.getResourceId(R.styleable.FloatingActionButton_fab_icon, 0); mTitle = attr.getString(R.styleable.FloatingActionButton_fab_title); mStrokeVisible = attr.getBoolean(R.styleable.FloatingActionButton_fab_stroke_visible, true); attr.recycle(); updateCircleSize();//通过xml的值判定出圆的大小 mShadowRadius = getDimension(R.dimen.fab_shadow_radius);//拿到阴影半径 mShadowOffset = getDimension(R.dimen.fab_shadow_offset);//拿到阴影偏移量 updateDrawableSize();//计算出最后的drawable的大小 updateBackground(); } /** * 计算出最后的drawable的大小 */ private void updateDrawableSize() { mDrawableSize = (int) (mCircleSize + 2 * mShadowRadius); } /** * 通过xml的值判定出圆的大小 */ private void updateCircleSize() { mCircleSize = getDimension(mSize == SIZE_NORMAL ? R.dimen.fab_size_normal : R.dimen.fab_size_mini); } /** * 设置大小,值只能是SIZE_MINI或SIZE_NORMAL * * @param size */ public void setSize(@FAB_SIZE int size) { if (size != SIZE_MINI && size != SIZE_NORMAL) { throw new IllegalArgumentException("Use @FAB_SIZE constants only!"); } if (mSize != size) { mSize = size; updateCircleSize(); updateDrawableSize(); updateBackground(); } } /** * 得到button的大小 * * @return */ @FAB_SIZE public int getSize() { return mSize; } /** * 设置icon * * @param icon */ public void setIcon(@DrawableRes int icon) { if (mIcon != icon) { mIcon = icon; mIconDrawable = null; updateBackground(); } } /** * 设置icon * * @param iconDrawable */ public void setIconDrawable(@NonNull Drawable iconDrawable) { if (mIconDrawable != iconDrawable) { mIcon = 0; mIconDrawable = iconDrawable; updateBackground(); } } /** * @return the current Color for normal state. */ public int getColorNormal() { return mColorNormal; } /** * 设置normal的时候的RES颜色 * * @param colorNormal */ public void setColorNormalResId(@ColorRes int colorNormal) { setColorNormal(getColor(colorNormal)); } /** * 设置normal的时候的颜色 * * @param color */ public void setColorNormal(int color) { if (mColorNormal != color) { mColorNormal = color; updateBackground(); } } /** * @return the current color for pressed state. */ public int getColorPressed() { return mColorPressed; } /** * 设置按压的Res颜色 * * @param colorPressed */ public void setColorPressedResId(@ColorRes int colorPressed) { setColorPressed(getColor(colorPressed)); } /** * 设置按压的颜色 * * @param color */ public void setColorPressed(int color) { if (mColorPressed != color) { mColorPressed = color; updateBackground(); } } /** * @return the current color for disabled state. */ public int getColorDisabled() { return mColorDisabled; } /** * 设置enable为false的时候的颜色,为RES的 * * @param colorDisabled */ public void setColorDisabledResId(@ColorRes int colorDisabled) { setColorDisabled(getColor(colorDisabled)); } /** * 设置enable为false的时候的颜色 * * @param color */ public void setColorDisabled(int color) { if (mColorDisabled != color) { mColorDisabled = color; updateBackground(); } } /** * 设置描边是否可见 * * @param visible */ public void setStrokeVisible(boolean visible) { if (mStrokeVisible != visible) { mStrokeVisible = visible; updateBackground(); } } /** * 描边是否可见 * * @return */ public boolean isStrokeVisible() { return mStrokeVisible; } /** * 得到颜色 * * @param id * @return */ int getColor(@ColorRes int id) { return getResources().getColor(id); } /** * 得到尺寸 * * @param id * @return */ float getDimension(@DimenRes int id) { return getResources().getDimension(id); } /** * 设置title * * @param title */ public void setTitle(String title) { mTitle = title; TextView label = getLabelView(); if (label != null) { label.setText(title); } } /** * 得到title的view * * @return */ TextView getLabelView() { return (TextView) getTag(R.id.fab_label); } /** * 得到title的文字 * * @return */ public String getTitle() { return mTitle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //设置圆大小 setMeasuredDimension(mDrawableSize, mDrawableSize); } /** * 更新背景 */ void updateBackground() { final float strokeWidth = getDimension(R.dimen.fab_stroke_width); final float halfStrokeWidth = strokeWidth / 2f; //LayerDrawable,叠层显示 LayerDrawable layerDrawable = new LayerDrawable( new Drawable[]{ AppCompat.getDrawable(getContext(), mSize == SIZE_NORMAL ? R.drawable.fab_bg_normal : R.drawable.fab_bg_mini),//最下面那层 createFillDrawable(strokeWidth), createOuterStrokeDrawable(strokeWidth), getIconDrawable() }); //icon的偏移 int iconOffset = (int) (mCircleSize - getDimension(R.dimen.fab_icon_size)) / 2; int circleInsetHorizontal = (int) (mShadowRadius);//相当于left的偏移,因为真正的Drawable是加了阴影 int circleInsetTop = (int) (mShadowRadius - mShadowOffset);// int circleInsetBottom = (int) (mShadowRadius + mShadowOffset);// //setLayerInset这里的right和bottom是减去的意思 layerDrawable.setLayerInset(1, circleInsetHorizontal, circleInsetTop, circleInsetHorizontal, circleInsetBottom); layerDrawable.setLayerInset(2, (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetTop - halfStrokeWidth), (int) (circleInsetHorizontal - halfStrokeWidth), (int) (circleInsetBottom - halfStrokeWidth)); layerDrawable.setLayerInset(3, circleInsetHorizontal + iconOffset, circleInsetTop + iconOffset, circleInsetHorizontal + iconOffset, circleInsetBottom + iconOffset); //设置到background中 setBackgroundCompat(layerDrawable); } /** * icon的drawable * 如果没有设置icon的话返回一个透明的drawable * * @return */ Drawable getIconDrawable() { if (mIconDrawable != null) { return mIconDrawable; } else if (mIcon != 0) { return AppCompat.getDrawable(getContext(), mIcon); } else { return new ColorDrawable(Color.TRANSPARENT); } } /** * 通过不同的按压情况那些来显示drawable * 主要是内层的drawable * * @param strokeWidth * @return */ private StateListDrawable createFillDrawable(float strokeWidth) { StateListDrawable drawable = new StateListDrawable(); drawable.addState(new int[]{-android.R.attr.state_enabled}, createCircleDrawable(mColorDisabled, strokeWidth)); drawable.addState(new int[]{android.R.attr.state_pressed}, createCircleDrawable(mColorPressed, strokeWidth)); drawable.addState(new int[]{}, createCircleDrawable(mColorNormal, strokeWidth)); return drawable; } /** * 绘制一个drawable出来 * * @param color * @param strokeWidth * @return */ private Drawable createCircleDrawable(int color, float strokeWidth) { int alpha = Color.alpha(color);//拿到alpha int opaqueColor = opaque(color);//拿到rgb //圆 ShapeDrawable fillDrawable = new ShapeDrawable(new OvalShape()); //笔 final Paint paint = fillDrawable.getPaint(); paint.setAntiAlias(true); paint.setColor(opaqueColor); Drawable[] layers = { fillDrawable, createInnerStrokesDrawable(opaqueColor, strokeWidth) }; //将Drawable[] layers 合成 LayerDrawable drawable LayerDrawable drawable = (alpha == 255 || !mStrokeVisible) ? new LayerDrawable(layers) : new TranslucentLayerDrawable(alpha, layers); //一半的描边宽度 int halfStrokeWidth = (int) (strokeWidth / 2f); //描边的drawable drawable.setLayerInset(1, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth, halfStrokeWidth); //返回这个drawable return drawable; } /** * 半透明LayerDrawable */ private static class TranslucentLayerDrawable extends LayerDrawable { private final int mAlpha; public TranslucentLayerDrawable(int alpha, Drawable... layers) { super(layers); mAlpha = alpha; } @Override public void draw(Canvas canvas) { Rect bounds = getBounds();//获得边界 canvas.saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom, mAlpha, Canvas.ALL_SAVE_FLAG); super.draw(canvas); canvas.restore(); } } /** * 绘制外层的drawable * 就是画边框 * * @param strokeWidth * @return */ private Drawable createOuterStrokeDrawable(float strokeWidth) { //圆 ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); //笔 final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true);//锯齿 paint.setStrokeWidth(strokeWidth);//描边宽度 paint.setStyle(Style.STROKE);//描边style,就是中空 paint.setColor(Color.BLACK);//颜色黑色,用来画边框 paint.setAlpha(opacityToAlpha(0.02f));//透明度 return shapeDrawable; } /** * 处理透明度 * * @param opacity * @return */ private int opacityToAlpha(float opacity) { return (int) (255f * opacity); } /** * 计算出这个rgb值的暗的值?? * * @param argb * @return */ private int darkenColor(int argb) { //factor给的是0.9 return adjustColorBrightness(argb, 0.9f); } /** * 计算出这个rgb值的亮的值?? * * @param argb * @return */ private int lightenColor(int argb) { return adjustColorBrightness(argb, 1.1f); } /** * 这尼玛是什么算法。。。。 * http://www.rapidtables.com/convert/color/rgb-to-hsv.htm * rgb转hsv * http://www.rapidtables.com/convert/color/hsv-to-rgb.htm * hsv转rgb * * @param argb * @param factor * @return */ private int adjustColorBrightness(int argb, float factor) { float[] hsv = new float[3]; Color.colorToHSV(argb, hsv);//Convert the argb color to its HSV components. hsv[2] = Math.min(hsv[2] * factor, 1f); return Color.HSVToColor(Color.alpha(argb), hsv); } /** * 通过ARGB算出一半透明的ARGB * * @param argb * @return */ private int halfTransparent(int argb) { return Color.argb( Color.alpha(argb) / 2, Color.red(argb), Color.green(argb), Color.blue(argb) ); } /** * 从ARGB中拿出RGB * * @param argb * @return */ private int opaque(int argb) { return Color.rgb( Color.red(argb), Color.green(argb), Color.blue(argb) ); } /** * 创建内层drawable * * @param color * @param strokeWidth * @return */ private Drawable createInnerStrokesDrawable(final int color, float strokeWidth) { //不描边的话直接返回一个颜色为透明的drawable if (!mStrokeVisible) { return new ColorDrawable(Color.TRANSPARENT); } //圆 drawable ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape()); final int bottomStrokeColor = darkenColor(color);//这个暗颜色 final int bottomStrokeColorHalfTransparent = halfTransparent(bottomStrokeColor);//比bottomStrokeColor透明一半 final int topStrokeColor = lightenColor(color);//这个亮颜色 final int topStrokeColorHalfTransparent = halfTransparent(topStrokeColor);//比topStrokeColor透明一半 final Paint paint = shapeDrawable.getPaint(); paint.setAntiAlias(true);//锯齿 paint.setStrokeWidth(strokeWidth);//描边宽度 paint.setStyle(Style.STROKE);//描边 //draws a linear gradient shapeDrawable.setShaderFactory(new ShaderFactory() { @Override public Shader resize(int width, int height) { return new LinearGradient(width / 2, 0, width / 2, height, new int[]{topStrokeColor, topStrokeColorHalfTransparent, color, bottomStrokeColorHalfTransparent, bottomStrokeColor}, new float[]{0f, 0.2f, 0.5f, 0.8f, 1f}, TileMode.CLAMP ); } }); return shapeDrawable; } /** * 设置background * * @param drawable */ @SuppressWarnings("deprecation") @SuppressLint("NewApi") private void setBackgroundCompat(Drawable drawable) { if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { setBackground(drawable); } else { setBackgroundDrawable(drawable); } } /** * 设置title的可见性 * * @param visibility */ @Override public void setVisibility(int visibility) { TextView label = getLabelView(); if (label != null) { label.setVisibility(visibility); } super.setVisibility(visibility); } }