Android 自定义View实现芝麻分曲线图效果
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倍的viewWith float 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倍的viewWith float 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); //P1 Point point = new Point(x, y); brokenPath.moveTo(point.x, point.y); //P2 point.x = point.x + dipToPx(5); point.y = point.y - dipToPx(5); brokenPath.lineTo(point.x, point.y); //P3 point.x = point.x + dipToPx(12); brokenPath.lineTo(point.x, point.y); //P4 point.y = point.y - dipToPx(17); brokenPath.lineTo(point.x, point.y); //P5 point.x = point.x - dipToPx(34); brokenPath.lineTo(point.x, point.y); //P6 point.y = point.y + dipToPx(17); brokenPath.lineTo(point.x, point.y); //P7 point.x = point.x + dipToPx(12); brokenPath.lineTo(point.x, point.y); //最后一个点连接到第一个点 brokenPath.lineTo(x, y); canvas.drawPath(brokenPath, brokenPaint); }
处理点击事件
@Override public boolean onTouchEvent(MotionEvent event) { this.getParent().requestDisallowInterceptTouchEvent(true);//一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action switch(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倍的viewWith float 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; }
获取控件的宽高
@Override protected 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实现芝麻分曲线图效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!
相关文章
Android 从底部弹出Dialog(横向满屏)的实例代码
在android开发中经常会遇到底部弹出框的功能,今天小编抽时间给大家整理一个底部弹出横向满屏的dialog,需要的朋友参考下2016-11-11Android Socket 线程连接openwrt与arduino单片机串口双向通信的实例解析
这篇文章主要介绍了Android Socket 线程连接openwrt与arduino单片机串口双向通信的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下2016-11-11Android中使用itemdecoration实现时间线效果
这篇文章主要介绍了Android中使用itemdecoration实现时间线效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2021-02-02
最新评论