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

以下是在BarAdapter中的onBindViewHolder办法中进行相关二者:

RecyclerBarChart绘制

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

RecyclerBarChart绘制

这儿咱们侧重看下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);
     }
  }

下面是个步数的周视图:

RecyclerBarChart绘制

2. drawHighLight

首要看下这儿高亮详细是如何显示的,直观的看些图:

RecyclerBarChart绘制

当时的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这儿相当于各个项目自己的需求。

本章节到此结束,介绍了BarChart的制作,一些鸿沟的处理,drawHighLight其实归于Common的部分,后续就不再重复。