实现效果如下:
自定义view代码如下:
public class MyWidget extends View {
public MyWidget(Context context) {
super(context);
}
public MyWidget(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyWidget(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyWidget(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取建议的最小高度
final int minimumHeight = getSuggestedMinimumHeight();
//获取建议的最小宽度
final int minimumWidth = getSuggestedMinimumWidth();
int width = measureWidth(minimumWidth, widthMeasureSpec);
int height = measureHeight(minimumHeight, heightMeasureSpec);
//设置测量的尺寸,用于父布局进行view排列
setMeasuredDimension(width, height);
}
private int measureWidth(int defaultWidth, int measureSpec) {
//获取测量的模式
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.AT_MOST://wrap_content
defaultWidth = /*内容视图大小+*/defaultWidth + getPaddingRight() + getPaddingLeft();
break;
case MeasureSpec.EXACTLY://固定值,match_parent
defaultWidth = specSize;
break;
case MeasureSpec.UNSPECIFIED://0dp,空值
defaultWidth = Math.max(defaultWidth, specSize);
}
return defaultWidth;
}
private int measureHeight(int defaultHeight, int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.AT_MOST://wrap_content
defaultHeight = /*内容视图大小+*/defaultHeight + getPaddingTop() + getPaddingBottom();
break;
case MeasureSpec.EXACTLY:
defaultHeight = specSize;
break;
case MeasureSpec.UNSPECIFIED://0dp,空值
defaultHeight = Math.max(defaultHeight, specSize);
break;
}
return defaultHeight;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
Paint mPaint = new Paint();
mPaint.setColor(Color.YELLOW);
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
int canvasWidth = getMeasuredWidth();
int canvasHeight = getMeasuredHeight();
//图画的中心位置
int centreY = canvasHeight / 2;
//绘制一个鱼头
//计算鱼头的高度
int fishHeight = canvasHeight - (int) (canvasHeight * 0.1);
int fishWidth = canvasWidth - (int) (canvasWidth * 0.1);
Path mHeadPath = new Path();
//点1
int x1 = (int) (canvasWidth * 0.8);
int y1 = centreY - fishHeight / 2;
mHeadPath.moveTo(x1, y1);
//点2
int x2 = canvasWidth - (canvasWidth - fishWidth) / 2;
int y2 = centreY;
mHeadPath.lineTo(x2, y2);
//点3
int x3 = (int) (canvasWidth * 0.8);
int y3 = centreY + fishHeight / 2;
mHeadPath.lineTo(x3, y3);
mHeadPath.close();
canvas.drawPath(mHeadPath, mPaint);
//鱼眼
int fisheyeWidth = (int) Math.min(fishHeight * 0.06, fishWidth * 0.06);
int x4 = (int) (x2 - ((x2 - x1) * 0.5));
x4 = (int) (x1 + ((x4 - x1) * 0.2));
int y4 = (int) (centreY - fishHeight * 0.25);
canvas.drawOval(x4, y4, x4 + fisheyeWidth, y4 + fisheyeWidth, mPaint);
//鱼尾巴
int x5 = (int) ((canvasWidth - fishWidth) * 0.5);
int y5 = (int) (centreY - fishHeight * 0.25);
int stopX = (int) (x5 + fishWidth * 0.1);
int stopY = centreY;
canvas.drawLine(x5, y5, stopX, stopY, mPaint);
int x6 = x5;
int y6 = (int) (centreY + fishHeight * 0.25);
canvas.drawLine(x6, y6, stopX, stopY, mPaint);
//脊梁骨
int x7 = x3;
canvas.drawLine(x7, centreY, stopX, centreY, mPaint);
//鱼骨
int count = 6;
//脊梁骨节点宽度
int mWidth = (x7 - stopX) / count;
//鱼刺垂直高度
int y8 = (int) (centreY - fishHeight * 0.5);
int y9 = (int) (centreY + fishHeight * 0.5);
for (int index = 0; index < count; index++) {
int x8 = (int) (mWidth * index) + stopX;
int stopX8 = x8 + mWidth;
if (index % 2 == 0) {
canvas.drawLine(x8, y8, stopX8, centreY, mPaint);
} else {
canvas.drawLine(x8, y9, stopX8, centreY, mPaint);
}
}
}
}
通过获取view自身的宽高进行相应的计算,通过直线,椭圆的绘制构成鱼骨图。