Android RecyclerBarChart绘制使用教程

目录
  • 正文
    • 1.drawBarChart
    • 2.drawHighLight

正文

上篇介绍了几种图表的公共组件X、Y轴、背景Board的绘制。这章节介绍柱状图表的绘制,相对其它图表而言简单一些,这里主要介绍图表主体的绘制,以及高亮选中的其中一个的选中框的绘制的相关逻辑。对每个ItemView中的ItemDecoration上进行onDraw的操作,需要将View跟Model进行绑定在一起,单个柱子的一些属性可以通过Model来获取,整体的一些绘制的辅助信息color、size等可以通过Attribute类设置。View 跟Model的绑定不止是BarChart图表,所以的都是一样的。

以下是在BarAdapter中的onBindViewHolder方法中进行关联二者:

根据之前的介绍绘制的逻辑都在ItemDecoration里,我们看下BarChartItemDecoration的onDrawOver, 对于X、Y轴、Board的绘制其实可以沉淀到BaseItemDecoration中的,这里直接写了。

这里我们着重看下drawBarChart、drawHighLight、drawBarChartValues的绘制。

1.drawBarChart

绘制柱状图的主体,通过ItemView拿到对应的Entry对象,根据Entry中的Y值进行Y坐标值的转化,然后绘制单个Item RectF的绘制。

//绘制柱状图, mYAxis这个坐标会实时变动,所以通过 BarChartItemDecoration 传过来的精确值。
    final public void drawBarChart(final Canvas canvas, @NonNull final RecyclerView parent, final YAxis mYAxis) {
        final float parentRight = parent.getWidth() - parent.getPaddingRight();
        final float parentLeft = parent.getPaddingLeft();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            BarEntry barChart = (BarEntry) child.getTag();
            RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent, mYAxis, mBarChartAttrs, barChart);
            drawChart(canvas, rectF, parentLeft, parentRight);
        }
    }

绘制的逻辑具体在 drawChart(canvas, rectF, parentLeft, parentRight) 的方法里,这里我们先看看 RectF 的计算,工具类ChartComputeUtil.getBarChartRectF() 的方法。

public static <T extends BarEntry, V extends BaseYAxis, E extends BaseChartAttrs> RectF getBarChartRectF(View child, final RecyclerView parent, V mYAxis, E chartAttrs, T barEntry) {
        final RectF rectF = new RectF();
        float contentBottom = parent.getHeight() - parent.getPaddingBottom() - chartAttrs.contentPaddingBottom;
        float realYAxisLabelHeight = contentBottom - parent.getPaddingTop() - chartAttrs.contentPaddingTop;
        float width = child.getWidth();
        float barSpaceWidth = width * chartAttrs.barSpace;
        float barChartWidth = width - barSpaceWidth;//柱子的宽度
        final float left = child.getLeft() + barSpaceWidth / 2;
        final float right = left + barChartWidth;
        float height = barEntry.getY() / mYAxis.getAxisMaximum() * realYAxisLabelHeight;
        if (chartAttrs.yAxisReverse && barEntry.getY() > 0) {
            float valueTemp = mYAxis.getAxisMaximum() - barEntry.getY();
            height = valueTemp / mYAxis.getAxisMaximum() * realYAxisLabelHeight;
        }
        final float top = Math.max(contentBottom - height, parent.getPaddingTop());
        rectF.set(left, top, right, contentBottom);
        return rectF;
    }

柱子RectF 的计算,Width根据 itemView的width 以及每个ItemView的空余所占比例的一个ChartAttrs中的参数

barSpace计算得来,算出RectF的 left、right; height 的计算,涉及到Entry 的Y值以及YAxis 当前显示情况下的getAxisMaximum(),这里默认了Minmum为0,业务逻辑的Y值比例转化成 屏幕Pixel对应的高度,然后根据ItemView的top、bottom计算得到 RectF的 top, bottom. (这里的计算,到时候其它图表高度也可以用)。

获取到 单个ItemView 中BarChart 所占的RectF确定后,画RectF就比较简单了,稍微有点难点的是处理一下边界的问题,边界问题,柱状图相比线形图等简单一些,处于边界的柱子绘制的颜色不一样,交给用户mBarChartAttrs.chartEdgeColor传color值,这里默认设置的是Gray。

private void drawChart(Canvas canvas, RectF rectF, float parentLeft, float parentRight) {
    float radius = (rectF.right - rectF.left) *mBarChartAttrs.barChartRoundRectRadiusRatio;
    // 浮点数的 == 比较需要注意
    if (DecimalUtil.smallOrEquals(rectF.right, parentLeft)) {
      //continue 会闪,原因是end == parentLeft 没有过滤掉,显示出来柱状图了。
      return;
    } else if (rectF.left < parentLeft && rectF.right > parentLeft) {
      //左边部分滑入的时候,处理柱状图的显示
      rectF.left = parentLeft;
      Path path = CanvasUtil.createRectRoundPath(rectF, radius, RoundRectType.TYPE_RIGHT);
      mBarChartPaint.setColor(mBarChartAttrs.chartEdgeColor);
      canvas.drawPath(path, mBarChartPaint);
    } else if (DecimalUtil.bigOrEquals(rectF.left, parentLeft) && DecimalUtil.smallOrEquals(rectF.right, parentRight)) {
      //中间的; 浮点数的 == 比较需要注意
      mBarChartPaint.setColor(mBarChartAttrs.chartColor);
      Path path = CanvasUtil.createRectRoundPath(rectF, radius);
      canvas.drawPath(path, mBarChartPaint);
    } else if (DecimalUtil.smallOrEquals(rectF.left, parentRight) &&
               rectF.right > parentRight) {
      //右边部分滑出的时候,处理柱状图,文字的显示
      float distance = (parentRight - rectF.left);
      rectF.right = rectF.left + distance;
      Path path = CanvasUtil.createRectRoundPath(rectF, radius, RoundRectType.TYPE_LEFT);
            mBarChartPaint.setColor(mBarChartAttrs.chartEdgeColor);
            canvas.drawPath(path, mBarChartPaint);
        }
   }

下面是个步数的周视图:

2.drawHighLight

首先看下这里高亮具体是如何显示的,直观的看些图:

当前的RecyclerView的getChildCount内每个ItemView对应的Entry,设定了一个 selected 的字段来确定显示高亮,至于该字段的值的设定及变化,后续章节会介绍,这里假定已经确定了当前的某一个ItemView的Entry的selected是选中状态的,它有可能在中间,或者在边界需要处理边界绘制的问题,这里分画顶部的矩形框及drawTextValue值,底部绘制drawLine(这个不存在绘制的边界问题)

//绘制选中时 highLight 标线及浮框。
public <E extends BaseYAxis> void drawHighLight(Canvas canvas, @NonNull RecyclerView parent, E yAxis) {
  if (mBarChartAttrs.enableValueMark) {
    int childCount = parent.getChildCount();
    View child;
    for (int i = 0; i < childCount; i++) {
      child = parent.getChildAt(i);
      T entry = (T) child.getTag();
      RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent, yAxis, mBarChartAttrs, entry);
      float width = child.getWidth();
      float childCenter = child.getLeft() + width / 2;
      String valueStr = mHighLightValueFormatter.getBarLabel(entry);
      if (entry.isSelected() && !TextUtils.isEmpty(valueStr)) {
        int chartColor = getChartColor(entry);
        float rectHeight = drawHighLightValue(canvas, valueStr, childCenter, parent, chartColor);//绘制顶部的poupWindow,高亮矩形框及drawText
        float[] points = new float[]{childCenter, rectF.top, childCenter, rectHeight};
        drawHighLightLine(canvas, points, chartColor);//绘制底部的Line
      }
    }
  }
}

以上中 drawHighLightValue 中, 包含了绘制矩形、drawText两项具体的内容:

//绘制柱状图选中浮框
protected float drawHighLightValue(Canvas canvas, String valueStr, float childCenter,
                                   RecyclerView parent, int barChartColor) {
  float parentTop = parent.getPaddingTop();
  float contentRight = parent.getWidth() - parent.getPaddingRight();
  float contentLeft = parent.getPaddingLeft();
  String[] strings = valueStr.split(DefaultHighLightMarkValueFormatter.CONNECT_STR);
  float leftEdgeDistance = Math.abs(childCenter - contentLeft);
  float rightEdgeDistance = Math.abs(contentRight - childCenter);
  float leftPadding = DisplayUtil.dip2px(8);
  float rightPadding = DisplayUtil.dip2px(8);
  float centerPadding = DisplayUtil.dip2px(16);
  float rectBottom = parentTop;
  float txtTopPadding = DisplayUtil.dip2px(8);
  String leftStr = strings[0];
  String rightStr = strings[1];
  float txtLeftWidth = mHighLightValuePaint.measureText(leftStr);
  float txtRightWidth = mHighLightValuePaint.measureText(rightStr);
  float rectFHeight = TextUtil.getTxtHeight1(mHighLightValuePaint) + txtTopPadding * 2;
  float txtWidth = txtLeftWidth + txtRightWidth + leftPadding +
    rightPadding + centerPadding;
  float edgeDistance = txtWidth / 2.0f;
  float rectTop = parentTop - rectFHeight;
  //绘制RectF
  RectF rectF = new RectF();
  mBarChartPaint.setColor(barChartColor);
  if (leftEdgeDistance <= edgeDistance) {//矩形框靠左对齐
    rectF.set(contentLeft, rectTop, contentLeft + txtWidth, rectBottom);
    float radius = DisplayUtil.dip2px(8);
    canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
  } else if (rightEdgeDistance <= edgeDistance) {//矩形框靠右对齐
    rectF.set(contentRight - txtWidth, rectTop, contentRight, rectBottom);
    float radius = DisplayUtil.dip2px(8);
    canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
  } else {//居中对齐。
    rectF.set(childCenter - edgeDistance, rectTop, childCenter + edgeDistance, rectBottom);
    float radius = DisplayUtil.dip2px(8);
    canvas.drawRoundRect(rectF, radius, radius, mBarChartPaint);
  }
  //绘文字
  RectF leftRectF = new RectF(rectF.left + leftPadding, rectTop + txtTopPadding,
                              rectF.left + leftPadding +
                              txtLeftWidth, rectTop + txtTopPadding + rectFHeight);
  mHighLightValuePaint.setTextAlign(Paint.Align.LEFT);
  Paint.FontMetrics fontMetrics = mHighLightValuePaint.getFontMetrics();
  float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
  float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom
  int baseLineY = (int) (leftRectF.centerY() + (top + bottom) / 2);//基线中间点的y轴计算公式
  canvas.drawText(leftStr, rectF.left + leftPadding, baseLineY, mHighLightValuePaint);
  float dividerLineStartX = rectF.left + leftPadding + txtLeftWidth + centerPadding / 2.0f;
  float dividerLineStartY = rectTop + DisplayUtil.dip2px(10);
  float dividerLineEndX = dividerLineStartX;
  float dividerLineEndY = rectBottom - DisplayUtil.dip2px(10);
  float[] lines = new float[]{dividerLineStartX, dividerLineStartY,
                              dividerLineEndX, dividerLineEndY};
  canvas.drawLines(lines, mHighLightValuePaint);
  float rightRectFStart = rectF.left + leftPadding + txtLeftWidth + centerPadding;
  RectF rightRectF = new RectF(rightRectFStart, rectTop + txtTopPadding,
                               rectF.right - rightPadding, rectBottom - txtTopPadding);
  canvas.drawText(rightStr, rightRectF.left, baseLineY, mHighLightValuePaint);
  return rectFHeight;
}

具体的文案绘制内容 valueStr 从 ValueFormatter里获取,我这里需要拆分一下ValueStr,然后绘制leftStr, rightStr这里相当于各个项目自己的需求。

以上就是Android RecyclerBarChart绘制使用教程的详细内容,更多关于Android RecyclerBarChart绘制的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android RecyclerLineChart实现图表绘制教程

    目录 正文 正常情况下绘制逻辑 左边界绘制 右边界绘制 代码逻辑 正文 本篇介绍线性图标RecyclerLineChart 的绘制,对于图表的公共部分X.Y轴,背景Board等的绘制先前章节已经有过介绍,这里不再重复:以及高亮选中顶部的poupWindow基本的绘制逻辑跟BarChart类似,可参照之前章节.所以针对LineChart,这里只介绍主体图表的绘制逻辑,以及线性表底部的drawFillColor填充. 首先介绍主体图表的逻辑,与BarChart不同之处在于,BartChart的每个I

  • Android RecyclerChart其它图表绘制示例详解

    目录 正文 1. 心电图 2. 睡眠图 正文 之前章节介绍了RecyclerChart 中一些通用的图表的相关绘制逻辑,本章节介绍两种Special的Chart的绘制,一种是心电图,一种是睡眠图.首先我们来看下心电图EcgChart的绘制. 1. 心电图 EcgChart 跟LineChart形态上是相似的,但是EcgChart的点相对于LineChart密集的多,之前的LineChart相当于每个RecyclerView的Itemview 中的Model对应的value值,而心电图的ItemD

  • Android RecyclerBarChart绘制使用教程

    目录 正文 1.drawBarChart 2.drawHighLight 正文 上篇介绍了几种图表的公共组件X.Y轴.背景Board的绘制.这章节介绍柱状图表的绘制,相对其它图表而言简单一些,这里主要介绍图表主体的绘制,以及高亮选中的其中一个的选中框的绘制的相关逻辑.对每个ItemView中的ItemDecoration上进行onDraw的操作,需要将View跟Model进行绑定在一起,单个柱子的一些属性可以通过Model来获取,整体的一些绘制的辅助信息color.size等可以通过Attrib

  • Android编程绘制圆形图片的方法

    本文实例讲述了Android编程绘制圆形图片的方法.分享给大家供大家参考,具体如下: 效果图如下: 第一步:新建RoundView自定义控件继承View package com.rong.activity; import com.rong.test.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.grap

  • Android编程绘制抛物线的方法示例

    本文实例讲述了Android编程绘制抛物线的方法.分享给大家供大家参考,具体如下: package com.yarin.android.Examples_05_04; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import android.content.Context; import

  • Android openGl 绘制简单图形的实现示例

    学习五部曲,弄清楚5个W一个H(when(什么时候使用).where(在哪个地方使用?).who(对谁使用).what(是个什么东西).why(为什么要这么用?).一个H即:how(到底该怎么用?)),基本的概念篇主要围绕这几个方面进行分析 1. What? openGl是什么?openGl ES又是什么? 相信很多人从事开发的都或多或少听到过有关OpenGl这个东西,但是平时用的少,只知道有这么个东西,而且学起来不简单,所以大多数人都不能讲出个个所以然来. 官方对OpenGl的描述为: Ope

  • Android 动态高斯模糊效果教程

    写在前面 最近一直在做毕设项目的准备工作,考虑到可能要用到一个模糊的效果,所以就学习了一些高斯模糊效果的实现.比较有名的就是 FastBlur 以及它衍生的一些优化方案,还有就是今天要说的RenderScript . 因为这东西是现在需要才去学习的,所以关于一些图像处理和渲染问题就不提了.不过在使用的过程中确实能感受到,虽然不同的方案都能实现相同的模糊效果,但是效率差别真的很大. 本篇文章实现的高斯模糊是根据下面这篇文章学习的,先推荐一下.本文内容与其内容差不多,只是稍微讲的详细一点,并修改了代

  • Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

    这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下: 最终效果图 接下来一步一步的实现整个过程. 基本原理 其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的.然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小.最后就是松手后返回或者爆裂的实现. Path介绍: 顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有 moveTo() 移动Path到一个指定的点 qua

  • Android listView 绘制表格实例详解

    Android  listView 绘制表格 效果图: 二,创建步骤: 1,创建布局: activity_main中的布局: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:

  • 浅谈Android View绘制三大流程探索及常见问题

    View绘制的三大流程,指的是measure(测量).layout(布局).draw(绘制) measure负责确定View的测量宽/高,也就是该View需要占用屏幕的大小,确定完View需要占用的屏幕大小后,就会通过layout确定View的最终宽/高和四个顶点在手机界面上的位置,等通过measure和layout过程确定了View的宽高和要显示的位置后,就会执行draw绘制View的内容到手机屏幕上. 在详细介绍这三大流程之前,需要简单了解一下ViewRootImpl,View绘制的三大步骤

  • Android shape 绘制图形的实例详解

    Android shape 绘制图形 Android 绘制图形可以使用shape也可以使用自定义控件的方式,这里我们说下shape的方式去实现. 在绘制图形之前,我们先来了解下shape的几个属性. shape /* * 线行 圆形 矩形 / android:shape="line" android:shape="oval" android:shape="rectangle" size 图形的大小 <size android:height=

  • 13问13答全面学习Android View绘制

    本文通过13问13答学习Android View绘制,供大家参考,具体内容如下 1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw流程结束以后就可以在屏幕上看到view了. 2.view的测量宽高和实际宽高有区别吗? 答:基本上百分之99的情况下都是可以认为没有区别的.有两种情况,有区别.第一种 就是有的时候会因为某些原因 view会多次测量,那第

随机推荐