Android 自定义View实现芝麻分曲线图效果

时间:2021-05-21

1.简介

其实这个效果几天之前就写了,但是一直没有更新博客,本来想着把芝麻分雷达图也做好再发博客的,然后今天看到鸿洋的微信公众号有朋友发了芝麻分的雷达图,所以就算了,算是一个互补吧。平时文章也写的比较少,所以可能有点杂乱,有什么需要改进的地方欢迎给出建议,不胜感激。

效果图:

2.步骤:

初始化View的属性
初始化画笔
绘制代表最高分和最低分的两根虚线
绘制文字
绘制代表月份的属性
绘制芝麻分折线
绘制代表芝麻分的圆点
绘制选中分数的悬浮文字以及背景
处理点击事件

3.编码:

初始化View属性

/*** 初始化布局配置** @param context* @param attrs*/private void initConfig(Context context, AttributeSet attrs){TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.ScoreTrend);maxScore=a.getInt(R.styleable.ScoreTrend_max_score,700);minScore=a.getInt(R.styleable.ScoreTrend_min_score,650);brokenLineColor=a.getColor(R.styleable.ScoreTrend_broken_line_color,brokenLineColor);a.recycle();}

初始化画笔:

private void init(){brokenPath = new Path();brokenPaint = new Paint();brokenPaint.setAntiAlias(true);brokenPaint.setStyle(Paint.Style.STROKE);brokenPaint.setStrokeWidth(dipToPx(brokenLineWith));brokenPaint.setStrokeCap(Paint.Cap.ROUND);straightPaint = new Paint();straightPaint.setAntiAlias(true);straightPaint.setStyle(Paint.Style.STROKE);straightPaint.setStrokeWidth(brokenLineWith);straightPaint.setColor((straightLineColor));straightPaint.setStrokeCap(Paint.Cap.ROUND);dottedPaint = new Paint();dottedPaint.setAntiAlias(true);dottedPaint.setStyle(Paint.Style.STROKE);dottedPaint.setStrokeWidth(brokenLineWith);dottedPaint.setColor((straightLineColor));dottedPaint.setStrokeCap(Paint.Cap.ROUND);textPaint = new Paint();textPaint.setAntiAlias(true);textPaint.setTextAlign(Paint.Align.CENTER);textPaint.setStyle(Paint.Style.FILL);textPaint.setColor((textNormalColor));textPaint.setTextSize(dipToPx(15));}

绘制代表最高分和最低分虚线

//绘制虚线private void drawDottedLine(Canvas canvas, float startX, float startY, float stopX, float stopY){dottedPaint.setPathEffect(new DashPathEffect(new float[]{20, 10}, 4));dottedPaint.setStrokeWidth(1);// 实例化路径Path mPath = new Path();mPath.reset();// 定义路径的起点mPath.moveTo(startX, startY);mPath.lineTo(stopX, stopY);canvas.drawPath(mPath, dottedPaint);}

绘制文本

//绘制文本private void drawText(Canvas canvas){textPaint.setTextSize(dipToPx(12));textPaint.setColor(textNormalColor);canvas.drawText(String.valueOf(maxScore), viewWith * 0.1f - dipToPx(10), viewHeight * 0.15f + textSize * 0.25f, textPaint);canvas.drawText(String.valueOf(minScore), viewWith * 0.1f - dipToPx(10), viewHeight * 0.4f + textSize * 0.25f, textPaint);textPaint.setColor(0xff7c7c7c);float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔线距离最左边和最右边的距离是0.15倍的viewWithfloat coordinateX;//分隔线X坐标textPaint.setTextSize(dipToPx(12));textPaint.setStyle(Paint.Style.FILL);textPaint.setColor(textNormalColor);textSize = (int) textPaint.getTextSize();for(int i = 0; i < monthText.length; i++){coordinateX = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f);if(i == selectMonth - 1){textPaint.setStyle(Paint.Style.STROKE);textPaint.setColor(brokenLineColor);RectF r2 = new RectF();r2.left = coordinateX - textSize - dipToPx(4);r2.top = viewHeight * 0.7f + dipToPx(4) + textSize / 2;r2.right = coordinateX + textSize + dipToPx(4);r2.bottom = viewHeight * 0.7f + dipToPx(4) + textSize + dipToPx(8);canvas.drawRoundRect(r2, 10, 10, textPaint);}//绘制月份canvas.drawText(monthText[i], coordinateX, viewHeight * 0.7f + dipToPx(4) + textSize + dipToPx(5), textPaint);textPaint.setColor(textNormalColor);}}

绘制代表月份的属性

//绘制月份的直线(包括刻度)private void drawMonthLine(Canvas canvas){straightPaint.setStrokeWidth(dipToPx(1));canvas.drawLine(0, viewHeight * 0.7f, viewWith, viewHeight * 0.7f, straightPaint);float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔线距离最左边和最右边的距离是0.15倍的viewWithfloat coordinateX;//分隔线X坐标for(int i = 0; i < monthCount; i++){coordinateX = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f);canvas.drawLine(coordinateX, viewHeight * 0.7f, coordinateX, viewHeight * 0.7f + dipToPx(4), straightPaint);}}

绘制芝麻分折线

//绘制折线private void drawBrokenLine(Canvas canvas){brokenPath.reset();brokenPaint.setColor(brokenLineColor);brokenPaint.setStyle(Paint.Style.STROKE);if(score.length == 0){return;}Log.v("ScoreTrend", "drawBrokenLine: " + scorePoints.get(0));brokenPath.moveTo(scorePoints.get(0).x, scorePoints.get(0).y);for(int i = 0; i < scorePoints.size(); i++){brokenPath.lineTo(scorePoints.get(i).x, scorePoints.get(i).y);}canvas.drawPath(brokenPath, brokenPaint);}

绘制代表芝麻分的圆点

//绘制折线穿过的点private void drawPoint(Canvas canvas){if(scorePoints == null){return;}brokenPaint.setStrokeWidth(dipToPx(1));for(int i = 0; i < scorePoints.size(); i++){brokenPaint.setColor(brokenLineColor);brokenPaint.setStyle(Paint.Style.STROKE);canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(3), brokenPaint);brokenPaint.setColor(Color.WHITE);brokenPaint.setStyle(Paint.Style.FILL);if(i == selectMonth - 1){brokenPaint.setColor(0xffd0f3f2);canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(8f), brokenPaint);brokenPaint.setColor(0xff81dddb);canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(5f), brokenPaint);//绘制浮动文本背景框drawFloatTextBackground(canvas, scorePoints.get(i).x, scorePoints.get(i).y - dipToPx(8f));textPaint.setColor(0xffffffff);//绘制浮动文字canvas.drawText(String.valueOf(score[i]), scorePoints.get(i).x, scorePoints.get(i).y - dipToPx(5f) - textSize, textPaint);}brokenPaint.setColor(0xffffffff);canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(1.5f), brokenPaint);brokenPaint.setStyle(Paint.Style.STROKE);brokenPaint.setColor(brokenLineColor);canvas.drawCircle(scorePoints.get(i).x, scorePoints.get(i).y, dipToPx(2.5f), brokenPaint);}}

绘制选中分数的悬浮文字以及背景

//绘制显示浮动文字的背景private void drawFloatTextBackground(Canvas canvas, int x, int y){brokenPath.reset();brokenPaint.setColor(brokenLineColor);brokenPaint.setStyle(Paint.Style.FILL);//P1Point point = new Point(x, y);brokenPath.moveTo(point.x, point.y);//P2point.x = point.x + dipToPx(5);point.y = point.y - dipToPx(5);brokenPath.lineTo(point.x, point.y);//P3point.x = point.x + dipToPx(12);brokenPath.lineTo(point.x, point.y);//P4point.y = point.y - dipToPx(17);brokenPath.lineTo(point.x, point.y);//P5point.x = point.x - dipToPx(34);brokenPath.lineTo(point.x, point.y);//P6point.y = point.y + dipToPx(17);brokenPath.lineTo(point.x, point.y);//P7point.x = point.x + dipToPx(12);brokenPath.lineTo(point.x, point.y);//最后一个点连接到第一个点brokenPath.lineTo(x, y);canvas.drawPath(brokenPath, brokenPaint);}

处理点击事件

@Overridepublic boolean onTouchEvent(MotionEvent event){this.getParent().requestDisallowInterceptTouchEvent(true);//一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的actionswitch(event.getAction()){case MotionEvent.ACTION_DOWN:break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:onActionUpEvent(event);this.getParent().requestDisallowInterceptTouchEvent(false);break;case MotionEvent.ACTION_CANCEL:this.getParent().requestDisallowInterceptTouchEvent(false);break;}return true;}private void onActionUpEvent(MotionEvent event){boolean isValidTouch = validateTouch(event.getX(), event.getY());if(isValidTouch){invalidate();}}//是否是有效的触摸范围private boolean validateTouch(float x, float y){//曲线触摸区域for(int i = 0; i < scorePoints.size(); i++){// dipToPx(8)乘以2为了适当增大触摸面积if(x > (scorePoints.get(i).x - dipToPx(8) * 2) && x < (scorePoints.get(i).x + dipToPx(8) * 2)){if(y > (scorePoints.get(i).y - dipToPx(8) * 2) && y < (scorePoints.get(i).y + dipToPx(8) * 2)){selectMonth = i + 1;return true;}}}//月份触摸区域//计算每个月份X坐标的中心点float monthTouchY = viewHeight * 0.7f - dipToPx(3);//减去dipToPx(3)增大触摸面积float newWith = viewWith - (viewWith * 0.15f) * 2;//分隔线距离最左边和最右边的距离是0.15倍的viewWithfloat validTouchX[] = new float[monthText.length];for(int i = 0; i < monthText.length; i++){validTouchX[i] = newWith * ((float) (i) / (monthCount - 1)) + (viewWith * 0.15f);}if(y > monthTouchY){for(int i = 0; i < validTouchX.length; i++){Log.v("ScoreTrend", "validateTouch: validTouchX:" + validTouchX[i]);if(x < validTouchX[i] + dipToPx(8) && x > validTouchX[i] - dipToPx(8)){Log.v("ScoreTrend", "validateTouch: " + (i + 1));selectMonth = i + 1;return true;}}}return false;}

获取控件的宽高

@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh){super.onSizeChanged(w, h, oldw, oldh);viewWith = w;viewHeight = h;initData();}

4.总结

还有一些比较不够完善的地方需要处理,比如说可以通过XML调节的属性太少了。平时写的东西还是太少了,希望以后多总结完善写作功底吧。需要的属性后面需要再完善吧

GitHub地址:https://github.com/FelixLee0527/ZhiMaScoreCurve

以上所述是小编给大家介绍的Android 自定义View实现芝麻分曲线图效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章