为了账号安全,请及时绑定邮箱和手机立即绑定

Android自定义控件 仿芝麻信用曲线图

标签:
Android

1.简介

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

2.步骤:

  1. 初始化View的属性

  2. 初始化画笔

  3. 绘制代表最高分和最低分的两根虚线

  4. 绘制文字

  5. 绘制代表月份的属性

  6. 绘制芝麻分折线

  7. 绘制代表芝麻分的圆点

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

  9. 处理点击事件


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调节的属性太少了。平时写的东西还是太少了,希望以后多总结完善写作功底吧。需要的属性后面需要再完善吧

原文链接:http://www.apkbus.com/blog-873057-77085.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消