本篇介绍线性图标RecyclerLineChart 的制作,关于图表的公共部分X、Y轴,布景Board等的制作先前章节已经有过介绍,这儿不再重复;以及高亮选中顶部的poupWindow基本的制作逻辑跟BarChart相似,可参照之前章节。所以针对LineChart,这儿只介绍主体图表的制作逻辑,以及线性表底部的drawFillColor填充。

首要介绍主体图表的逻辑,与BarChart不同之处在于,BartChart的每个Item的制作比较独立,而LineChart关于当时的Item,需求找到PreItem或许NextItem中的Y的点进行drawLine, 比较而言稍显杂乱一些。图表的中间位置的Line还比较容易制作,图表左右鸿沟是LineChart制作最难的当地。

整个的制作逻辑第一章节有介绍在Render类中,这儿的话是LineChartRender的drawLineChartWithoutPoint 办法里,这个办法代码比较长,分段介绍:

private <T extends BarEntry> void drawLineChartWithoutPoint(Canvas canvas, RecyclerView parent, YAxis mYAxis) {
 final float parentRightBoard = parent.getWidth() - parent.getPaddingRight();
 final float parentLeft = parent.getPaddingLeft();
 BaseBarChartAdapter adapter = (BaseBarChartAdapter) parent.getAdapter();
 List<T> entryList = adapter.getEntries();
 final int childCount = parent.getChildCount();
​
 int adapterPosition;
 for (int i = 0; i < childCount; i++) {
  View child = parent.getChildAt(i);
  T barEntry = (T) child.getTag();
  if (barEntry.getY() == 0) {
   continue;
   }
  adapterPosition = parent.getChildAdapterPosition(child);
  RectF rectF = ChartComputeUtil.getBarChartRectF(child, parent,
                          mYAxis, mLineChartAttrs, barEntry);
  PointF pointF2 = new PointF((rectF.left + rectF.right) / 2, rectF.top);
      
     // 这儿还有好多制作逻辑代码
  
       }//end for    
}// end  drawLineChartWithoutPoint

整个制作制作顺次遍历 Adapter中当时展现的点,一共childcount 个,遍历的当时点位pointF2, 以pointF2 为基准接下来会涉及找 pointF0,pointF1, 这两在pointF2左面(假定存在的状况下);pointF3, pointF4, 这两个点在PointFd2的右边,之所以要找左右各两个点是处理鸿沟状况。

正常状况下的制作逻辑,衔接pointF1、pointF2。

if (i < childCount - 1) {//这儿的LayoutManager设置的reverse倒叙,所以i+1在i的左面i对应的是pointF2
 View pointF1Child = parent.getChildAt(i + 1);
 T barEntryLeft = (T) pointF1Child.getTag();
 //这儿的RectF跟之前的Barchart相似,为ItemView中除去space后所占的RectF区域,其间pointF1的X为RectF的X轴方向的中心。
 RectF rectFLeft = ChartComputeUtil.getBarChartRectF(pointF1Child, parent, mYAxis,  mLineChartAttrs, barEntryLeft);
 //找到PointF1
 PointF pointF1 = new PointF((rectFLeft.left + rectFLeft.right) / 2, rectFLeft.top);
 //parentLeft为左鸿沟, parentRightBoard 为Chart的右鸿沟
 if (pointF1.x >= parentLeft && pointF2.x <= parentRightBoard) {
  
  float[] pointsOut = new float[]{pointF1.x, pointF1.y, pointF2.x, pointF2.y};
  drawChartLine(canvas, pointsOut);//制作正常状况下的Line。
  drawFill(parent, mLineChartAttrs, canvas, pointF1, pointF2, rectF.bottom);
  //其它鸿沟制作逻辑。 
}//end if

左鸿沟制作

以上的状况是pointF1.x 在Chart内,见图, 黄色为当时的PointF1, 紫色为PointF2, 上面代码里drawLine制作的是PointF1跟PointF2之前的连线。

RecyclerLineChart图表绘制

持续看上面的那个图,要制作PointF1到Chart左面鸿沟的线段,需求持续找到PointF0,然后用PointF0、PointF1与Chart相交得到上图黑色圈里的点,记为pointFIntercept, drawLine(pointFIntercept, PointF1)

if (pointF1Child.getLeft() < parentLeft) {//左鸿沟,处理pointF1值显示出来了的状况。
 if (adapterPosition + 2 < entryList.size()) {
 float x = pointF1.x - pointF1Child.getWidth();
 T barEntry0 = entryList.get(adapterPosition + 2);
 float y = ChartComputeUtil.getYPosition(barEntry0, parent, mYAxis, mLineChartAttrs);
 PointF pointF0 = new PointF(x, y);
  //PointF0、PointF1 跟Chart的交点pointFIntercept
 PointF pointFIntercept = ChartComputeUtil.getInterceptPointF(pointF0, pointF1, parentLeft);
 float[] points = new float[]{pointFIntercept.x, pointFIntercept.y, pointF1.x, pointF1.y};
 drawChartLine(canvas, points);
 drawFill(parent, mLineChartAttrs, canvas, pointFIntercept, pointF1, rectF.bottom);
  }
}

上面是 pointF1.x >= parentLeft,在左鸿沟内的状况,当pointF1.x < parentLeft时,rectLeft 出来一小部分的状况,如下图所示:紫色为当时的PointF2点

RecyclerLineChart图表绘制

这时需求求PointF1、PointF2跟Chart相交的点pointF, 然后drawLine(pointF, PointF2)即可, 见代码:

if (pointF1.x < parentLeft && pointF1Child.getRight() >= parentLeft) {//左鸿沟,处理pointF1值没有显示出来
 PointF pointF = ChartComputeUtil.getInterceptPointF(pointF1, pointF2, parentLeft);
 float[] points = new float[]{pointF.x, pointF.y, pointF2.x, pointF2.y};
 drawChartLine(canvas, points);
 drawFill(parent, mLineChartAttrs, canvas, pointF, pointF2, rectF.bottom);
}

右鸿沟制作

处理完左鸿沟的制作,右鸿沟的制作跟左鸿沟大致一样,PointF2 往右两个点PointF3, PointF4; 注意这儿RecyclerView的LayoutManager为reverse, 所以当 PointF2对应的下标为i时, PointF3对应的为i-1, PointF4为i-2.

然后便是分状况评论PointF3.x 是否在Chart范围内,跟parentRightBorad比较即可。

看PointF3.x 在 Chart范围内的状况,如图:紫色为PointF2点,黄色为PonitF3点,黑色为PointF3,PointF4跟Chart的交点,这儿只需求制作PointF3跟交点之间的Line;PointF2、PointF3之间的Line 在当黄色点遍历到i时,紫色点位PointF1,所以这儿不需求重复制作了。

RecyclerLineChart图表绘制

代码逻辑如下:

if (pointF3.x < parentRightBoard) {//pointF3 在界内。
 if (adapterPosition - 2 > 0) {
 float xInner = pointF3.x + child.getWidth();
 T barEntry4 = entryList.get(adapterPosition - 2);
 float yInner = ChartComputeUtil.getYPosition(barEntry4, parent, mYAxis, mLineChartAttrs);
 PointF pointF4 = new PointF(xInner, yInner);//找到PointF4.
 PointF pointFInterceptInner = ChartComputeUtil.getInterceptPointF(pointF3, pointF4, parentRightBoard);
 float[] pointsInner = new float[]{pointF3.x, pointF3.y, pointFInterceptInner.x, pointFInterceptInner.y};
 drawChartLine(canvas, pointsInner);
 drawFill(parent, mLineChartAttrs, canvas, pointF3, pointFInterceptInner, rectF.bottom);
  }
}

最终便是 pointF3.x >parentRightBoard的状况,见图:紫色为PointF2, 黄色为 PointF2、PointF3跟Chart的交点:

RecyclerLineChart图表绘制

代码逻辑如下:

if (pointF3.x > parentRightBoard) {//在Chart之外。
 PointF pointFIntercept = ChartComputeUtil.getInterceptPointF(pointF2, pointF3, parentRightBoard);
 float[] points = new float[]{pointF2.x, pointF2.y, pointFIntercept.x, pointFIntercept.y};
 drawChartLine(canvas, points);
 drawFill(parent, mLineChartAttrs, canvas, pointFIntercept, pointF2, rectF.bottom);
} 

以上的鸿沟处理中涉及到的工具类办法求相交点,简单的数学公司带入:

public static PointF getInterceptPointF(PointF pointF1, PointF pointF2, float x) {
  float width = Math.abs(pointF1.x - pointF2.x);
  float height = Math.abs(pointF1.y - pointF2.y);
  float interceptWidth = Math.abs(pointF1.x - x);
  float interceptHeight = interceptWidth * 1.0f / width * height;
  float y;
  if (pointF2.y < pointF1.y) {
   y = pointF1.y - interceptHeight;
   } else {
   y = pointF1.y + interceptHeight;
   }
  return new PointF(x, y);
}

见以上图表中的红色半透明的FillColor的制作,每次drawLine()紧跟着便是drawFill(), 以下是drawFill的逻辑,跟X轴构建一个path,然后drawPath 即可:

private void drawFill(RecyclerView parent, LineChartAttrs mBarChartAttrs, Canvas canvas, PointF pointF, PointF pointF1, float bottom) {
    if (mBarChartAttrs.enableLineFill) {
      float yBottom = parent.getBottom() - parent.getPaddingBottom();
      float yTop = parent.getTop() + parent.getPaddingTop();
      LinearGradient mLinearGradient = new LinearGradient(
          0,
          yBottom,
          0,
          yTop,
          new int[]{
              mBarChartAttrs.lineShaderBeginColor, mBarChartAttrs.lineShaderEndColor},
          null,
          Shader.TileMode.CLAMP
       );
      mLineFillPaint.setShader(mLinearGradient);
      Path path = ChartComputeUtil.createColorRectPath(pointF, pointF1, bottom);
      LineChartDrawable drawable = new LineChartDrawable(mLineFillPaint, path);
      drawable.draw(canvas);
     }
   }

设置了一个Color的Linear渐变从bottom到top.

至此,RecyclerLineChart的主体图表制作逻辑介绍完毕。还有部分的细节,当时Point带圆圈,以及鸿沟圆圈的制作等,选中圆圈的处理等多处细节,读者想了解的,可以GitHub上下载看源码demo, 衔接在本专栏的第一篇里有链接。