// A mildly-modified version of the MaterialBadgeTextView library from: // https://github.com/matrixxun/MaterialBadgeTextView package com.jasonette.seed.Lib; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RadialGradient; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.os.Build; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; import com.jasonette.seed.Helper.JasonHelper; /** * Created by matrixxun on 2016/8/30. */ public class MaterialBadgeTextView extends TextView { private static final int DEFAULT_FILL_TYPE = 0; private int backgroundColor; private int borderColor; private float borderWidth; private float borderAlpha; private int ctType; private static final float SHADOW_RADIUS = 3.5f; private static final int FILL_SHADOW_COLOR = 0x55000000; private static final int KEY_SHADOW_COLOR = 0x55000000; private static final float X_OFFSET = 0f; private static final float Y_OFFSET = 1.75f; private float density; private int mShadowRadius; private int shadowYOffset; private int shadowXOffset; private int basePadding; private int diffWH; private boolean isHighLightMode; public MaterialBadgeTextView(final Context context) { this(context, null); } public MaterialBadgeTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MaterialBadgeTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context, AttributeSet attrs) { setGravity(Gravity.CENTER); density = getContext().getResources().getDisplayMetrics().density; mShadowRadius = (int) (density * SHADOW_RADIUS); shadowYOffset = (int) (density * Y_OFFSET); shadowXOffset = (int) (density * X_OFFSET); basePadding = (mShadowRadius * 2); float textHeight = getTextSize(); float textWidth = textHeight / 4; diffWH = (int) (Math.abs(textHeight - textWidth) / 2); int horizontalPadding = basePadding + diffWH; setPadding(horizontalPadding, basePadding, horizontalPadding, basePadding); backgroundColor = JasonHelper.parse_color("#ff0000"); borderColor = Color.TRANSPARENT; borderWidth = 0; borderAlpha = 1; ctType = DEFAULT_FILL_TYPE; } @Override protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); /** 纯色小红点模式下若有文本需要从无变为有, 要归位view的大小.*/ String strText = text==null?"":text.toString().trim(); if(isHighLightMode && !"".equals(strText)){ ViewGroup.LayoutParams lp = getLayoutParams(); lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; setLayoutParams(lp); isHighLightMode = false; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); refreshBackgroundDrawable(w, h); } private void refreshBackgroundDrawable(int targetWidth, int targetHeight) { if (targetWidth <= 0 || targetHeight <= 0) { return; } CharSequence text = getText(); if (text == null) { return; } if (text.length() == 1) {/**第一种背景是一个正圆形, 当文本为个位数字时 */ int max = Math.max(targetWidth, targetHeight); ShapeDrawable circle; final int diameter = max - (2 * mShadowRadius); OvalShape oval = new OvalShadow(mShadowRadius, diameter); circle = new ShapeDrawable(oval); ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint()); circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR); circle.getPaint().setColor(backgroundColor); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { setBackgroundDrawable(circle); } else { setBackground(circle); } } else if (text.length() > 1) {/**第二种背景是上下两边为直线的椭圆, 当文本长度大于1时 */ SemiCircleRectDrawable sr = new SemiCircleRectDrawable(); ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, sr.getPaint()); sr.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR); sr.getPaint().setColor(backgroundColor); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { setBackground(sr); } else { setBackgroundDrawable(sr); } } else { /** 第三种情况就是text="", 即文本长度为0, 因为无任何文本, 则对当前的TextView背景不做任何更新, * 但是有时候我们需要一个无字的纯色小圆形,用来表达强调.这种情况因为要重新设置View的大小, 所以不在这里表现, 请使用另外一个方法setHighLightMode()来完成. */ } } public void setBadgeCount(String count){ setBadgeCount(count, false); } public void setBadgeCount(String count, boolean goneWhenZero) { int temp = -1; try { temp = Integer.parseInt(count); } catch (Exception e) { } if (temp != -1) { setBadgeCount(temp, goneWhenZero); } } public void setBadgeCount(int count){ setBadgeCount(count, true); } public void setBadgeCount(int count, boolean goneWhenZero){ if(count >0 && count <= 99){ setText(String.valueOf(count)); setVisibility(View.VISIBLE); }else if(count >99){ setText("99+"); setVisibility(View.VISIBLE); }else if(count <= 0){ setText("0"); if(goneWhenZero){ setVisibility(View.GONE); }else{ setVisibility(View.VISIBLE); } } } /** * 明确的展现一个无任何文本的红色圆点, * 主要是通过设置文本setText("")触发onTextChanged(), 再连锁触发onSizeChanged()最后更新了背景. */ public void setHighLightMode(){ setHighLightMode(false); } public void clearHighLightMode(){ isHighLightMode = false; setBadgeCount(0); } /** * * @param isDisplayInToolbarMenu */ public void setHighLightMode(boolean isDisplayInToolbarMenu){ isHighLightMode = true; ViewGroup.LayoutParams params = getLayoutParams(); params.width = dp2px(getContext(), 8); params.height = params.width; if(isDisplayInToolbarMenu && params instanceof FrameLayout.LayoutParams){ ((FrameLayout.LayoutParams)params).topMargin=dp2px(getContext(), 8); ((FrameLayout.LayoutParams)params).rightMargin=dp2px(getContext(), 8); } setLayoutParams(params); ShapeDrawable drawable = new ShapeDrawable(new OvalShape()); ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, drawable.getPaint()); drawable.getPaint().setColor(backgroundColor); drawable.getPaint().setAntiAlias(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { setBackground(drawable); } else { setBackgroundDrawable(drawable); } setText(""); setVisibility(View.VISIBLE); } public void setBackgroundColor(int color){ backgroundColor = color; refreshBackgroundDrawable(getWidth(), getHeight()); } private class OvalShadow extends OvalShape { private RadialGradient mRadialGradient; private Paint mShadowPaint; private int mCircleDiameter; public OvalShadow(int shadowRadius, int circleDiameter) { super(); mShadowPaint = new Paint(); mShadowRadius = shadowRadius; mCircleDiameter = circleDiameter; mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[]{ FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP); mShadowPaint.setShader(mRadialGradient); } @Override public void draw(Canvas canvas, Paint paint) { final int viewWidth = MaterialBadgeTextView.this.getWidth(); final int viewHeight = MaterialBadgeTextView.this.getHeight(); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint); } } class SemiCircleRectDrawable extends Drawable { private final Paint mPaint; private RectF rectF; public Paint getPaint() { return mPaint; } public SemiCircleRectDrawable() { mPaint = new Paint(); mPaint.setAntiAlias(true); } @Override public void setBounds(int left, int top, int right, int bottom) { super.setBounds(left, top, right, bottom); if (rectF == null) { rectF = new RectF(left + diffWH, top + mShadowRadius+4, right - diffWH, bottom - mShadowRadius-4); } else { rectF.set(left + diffWH, top + mShadowRadius+4, right - diffWH, bottom - mShadowRadius-4); } } @Override public void draw(Canvas canvas) { float R = (float)(rectF.bottom * 0.4); if (rectF.right < rectF.bottom) { R = (float)(rectF.right * 0.4); } canvas.drawRoundRect(rectF, R, R, mPaint); } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSPARENT; } } public static int dp2px(Context context, float dpValue) { try { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } catch (Exception e) { return (int) (dpValue + 0.5f); } } }