Android实现仿TIM“服务号”图标
本篇来用自定义View来绘制TIM“服务号”的图标
效果图:


原理
绘制分3步,分别为:
- 绘制背景的圆形 
- 绘制中间的八角形 
- 绘制中心的勾 
- 绘制圆形很简单,知道圆的半径和位置坐标,使用canvas.drawCircle()即可。 
- 细看这个八角形的角还是带圆角的,如果使用计算每个角的坐标的话,会很复杂!但还是有投机取巧的办法,我们可以将这个8角形看成2个圆角矩形,他们重叠在一起,只是上层的圆角矩形是经过画布旋转45度再进行绘制的,这样就得出了一个八角形,哈哈,难度瞬间减去了80%。 
- 画勾子,可以参考之前文章Android自定义实现对勾CheckBox功能 
我们也是采用画布旋转,在中间,画一个直角的方式,来得到一个对勾,这样就大功告成了!
完整代码
- 自定义属性 
<declare-styleable name="TIMServiceNoView"><!-- 背景圆的颜色 --><attr name="tim_snv_bg_circle_color" format="color" /><!-- 圆角矩形图标的颜色 --><attr name="tim_snv_round_rect_icon_color" format="color" /></declare-styleable>
- Java代码 
public class TIMServiceNoView extends View {/*** View默认最小宽度*/private static final int DEFAULT_MIN_WIDTH = 100;/*** 控件宽*/private int mViewWidth;/*** 控件高*/private int mViewHeight;/*** 背景圆的半径*/private float mBgCircleRadius;/*** 背景圆的画笔*/private Paint mBgCirclePaint;/*** 背景圆的颜色*/private int mBgCircleColor;/*** 圆角矩形的图标的画笔*/private Paint mRoundRectIconPaint;/*** 圆角矩形的图标的颜色*/private int mRoundRectIconColor;/*** 图标圆角矩形的半径*/private float mRoundRectIconRadius;/*** 图标的圆角矩形的边圆角半径*/private float mRoundRectIconEdgeRoundRadius;/*** 钩子的画笔*/private Paint mHookPaint;/*** 钩子的线长度*/private float mHookLineLength;public TIMServiceNoView(Context context) {this(context, null);}public TIMServiceNoView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public TIMServiceNoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context, attrs, defStyleAttr);}private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {initAttr(context, attrs, defStyleAttr);//取消硬件加速setLayerType(LAYER_TYPE_SOFTWARE, null);//背景圆的画笔mBgCirclePaint = new Paint();mBgCirclePaint.setAntiAlias(true);mBgCirclePaint.setStyle(Paint.Style.FILL);mBgCirclePaint.setColor(mBgCircleColor);mBgCirclePaint.setStrokeWidth(dip2px(context, 2f));//圆角矩形的图标的画笔mRoundRectIconPaint = new Paint();mRoundRectIconPaint.setAntiAlias(true);mRoundRectIconPaint.setStyle(Paint.Style.FILL);mRoundRectIconPaint.setColor(mRoundRectIconColor);mRoundRectIconPaint.setStrokeWidth(dip2px(context, 2f));//钩子的画笔mHookPaint = new Paint();mHookPaint.setAntiAlias(true);mHookPaint.setStyle(Paint.Style.STROKE);mHookPaint.setColor(mBgCircleColor);//设置连接处为直角mHookPaint.setStrokeJoin(Paint.Join.MITER);//设置笔触为圆形mHookPaint.setStrokeCap(Paint.Cap.ROUND);}private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {//默认背景圆的颜色int defaultBgCircleColor = Color.argb(255, 251, 146, 69);int defaultRoundRectIconColor = Color.argb(255, 255, 255, 255);if (attrs != null) {TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TIMServiceNoView, defStyleAttr, 0);mBgCircleColor = array.getColor(R.styleable.TIMServiceNoView_tim_snv_bg_circle_color, defaultBgCircleColor);mRoundRectIconColor = array.getColor(R.styleable.TIMServiceNoView_tim_snv_round_rect_icon_color, defaultRoundRectIconColor);array.recycle();} else {mBgCircleColor = defaultBgCircleColor;mRoundRectIconColor = defaultRoundRectIconColor;}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mViewWidth = w;mViewHeight = h;//计算背景圆的半径mBgCircleRadius = (Math.min(mViewWidth, mViewHeight) / 2f) * 0.95f;//计算图标圆角矩形的半径mRoundRectIconRadius = (Math.min(mViewWidth, mViewHeight) / 2f) * 0.45f;//计算图标的圆角矩形的边圆角半径mRoundRectIconEdgeRoundRadius = (Math.min(mViewWidth, mViewHeight) / 2f) * 0.1f;//计算对勾的长度mHookLineLength = mRoundRectIconRadius * 0.5f;//设置对勾的线宽mHookPaint.setStrokeWidth(mRoundRectIconRadius * 0.13f);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//将画布中心移动到中心点canvas.translate(mViewWidth / 2, mViewHeight / 2);//画背景圆drawBgCircle(canvas);//画圆角矩形的图标drawRoundRectIcon(canvas);//画钩子drawHook(canvas);}/*** 画背景圆*/private void drawBgCircle(Canvas canvas) {canvas.drawCircle(0, 0, mBgCircleRadius, mBgCirclePaint);}/*** 画圆角矩形的图标*/private void drawRoundRectIcon(Canvas canvas) {RectF rect = new RectF(-mRoundRectIconRadius, -mRoundRectIconRadius, mRoundRectIconRadius, mRoundRectIconRadius);canvas.drawRoundRect(rect, mRoundRectIconEdgeRoundRadius, mRoundRectIconEdgeRoundRadius, mRoundRectIconPaint);canvas.save();//旋转画布45度再画一次canvas.rotate(45);canvas.drawRoundRect(rect, mRoundRectIconEdgeRoundRadius, mRoundRectIconEdgeRoundRadius, mRoundRectIconPaint);canvas.restore();}/*** 画钩子*/private void drawHook(Canvas canvas) {//其中一个钩子长度会多一点点float edgeLength = mRoundRectIconRadius / 3f;canvas.save();//画布向下平移一半的半径长度canvas.translate(-(mRoundRectIconRadius / 10f), mRoundRectIconRadius / 3.5f);//旋转画布45度canvas.rotate(-45);Path path = new Path();path.reset();path.moveTo(0, 0);//向右画一条线path.lineTo(mHookLineLength + edgeLength, 0);//回到中心点path.moveTo(0, 0);//向上画一条线path.lineTo(0, -mHookLineLength);//画路径canvas.drawPath(path, mHookPaint);canvas.restore();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);setMeasuredDimension(handleMeasure(widthMeasureSpec), handleMeasure(heightMeasureSpec));}/*** 处理MeasureSpec*/private int handleMeasure(int measureSpec) {int result = DEFAULT_MIN_WIDTH;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {result = specSize;} else {//处理wrap_content的情况if (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}}return result;}public static int dip2px(Context context, float dipValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale + 0.5f);}}
简单使用
- 布局 
<com.zh.cavas.sample.widget.TIMServiceNoViewandroid:layout_width="50dp"android:layout_height="50dp" />
到这里就结束啦。
评论
