使用自定义View来实现K线图、分时图

时间:2024-05-19 14:54:48


废话不多说 先上效果图
使用自定义View来实现K线图、分时图

实现原理

1.分时图

分时图一般有两条线,一条均价线,一条时价线。其实每条线都是由很多点组合而来,通过canvas.drawLine方法将每两个点进行连接,最终绘制出分时图。

2.K线图

K线图是由一个一个的矩形组合而来,每个矩形代表了一个时间段的相关价格,通过这些价格来确定当前时间段的矩形的位置,这样就绘制出K线图。关于K线图的一些基础知识,如阴线、阳线、高开低收,去百度学习下,这样开发起来会方便很多。

3.十字标

首先对View进行手势监听GestureDetector,当触发长按onLongPress方法时,记录当前手指的X坐标,通过当前X坐标获取当前时间的Y轴坐标,最终绘制出十字标。

4.缩放、滑动

分时图是没有缩放和滑动功能的,所以主要说说K线图。
K线图的数据点有很多,但是一般在屏幕上不会展示完,所以我们要确定屏幕可以展示的矩形个数,并且展示的数据源的start、end。 滑动和缩放其实只是不停重新绘制的过程,滑动是改变数据源的start、end,缩放则是改变矩形个数。

实现步骤

基类BaseChart

BaseChart中除了包含了一些公用的常量和方法外,主要是定义了绘制步骤。

//1.初始化数据
initWidthAndHeight();
//2.画网格
drawGrid(canvas);
//3.画线
drawLine(canvas);
//4.画XY轴上的值
drawXYText(canvas);

画图

  1. initWidthAndHeight中我们需要来获取当前数据源在Y轴的最大值和最小值mMaxmMin,来确定Y轴上面的刻度值,在分时图中还需要确定当日的开盘时间和收盘时间;
  2. 有了上面的那些数据,我们就可以愉快的在drawGrid中绘制表格;
 	   mLinePaint.reset();
       mLinePaint.setAntiAlias(true);
       mLinePaint.setColor(color_grid);
       //X方向
       drawXGrid(canvas);
       //Y方向
       drawYGrid(canvas);
  1. drawLine则是画图的核心方法,循环遍历当前数据源的每条数据,将数据换算成图上的每一个点;
 	   mLinePaint.reset();
       mLinePaint.setStyle(Paint.Style.STROKE);    // 填充模式 - 描边
       mLinePaint.setStrokeWidth(pointWidth);
       mLinePaint.setAntiAlias(true);

       for (LineBean lineBean : mData) {
           if (!lineBean.isDisplay())
               return;

           float x = 0f;
           PointF pricePoint = null;
           Path shadowPaht = new Path();
           shadowPaht.moveTo(x, chartHeight);
           for (int i = 0; i < lineBean.getLineData().size(); i++) {
               LineBean.Line bean = lineBean.getLineData().get(i);
               //绘制时价
               mLinePaint.setColor(lineBean.getLineColor());
               float y = doubleToY(bean.getPrice());
               if (pricePoint == null) {
                   pricePoint = new PointF(x, y);
               }
               canvas.drawLine(pricePoint.x, pricePoint.y, x, y, mLinePaint);

               //绘制阴影
               shadowPaht.lineTo(x, y);
               if (i == lineBean.getLineData().size() - 1 && TSChartFragment.TIME_TAG.equals(lineBean.getTag())) {
                   shadowPaht.lineTo(x, chartHeight);
                   shadowPaht.close();
                   mLinePaint.setColor(color_shadow);
                   mLinePaint.setStyle(Paint.Style.FILL);
                   mLinePaint.setAlpha(100);
                   canvas.drawPath(shadowPaht, mLinePaint);
               }

               pricePoint.x = x;
               pricePoint.y = y;

               x = (float) (x + pointWidth + lineLength);
           }
       }
       mLinePaint.reset();
       mLinePaint.setAntiAlias(true);
       mLinePaint.setStrokeWidth(crossLineWidth);

       mTextPaint.reset();
       mTextPaint.setAntiAlias(true);
       mTextPaint.setTextSize(f10);
       mTextPaint.setColor(getResources().getColor(R.color.color_white));

       XList.clear();
       float x = (float) (stickWidth / 2);
       for (int i = start; i < end; i++) {
           if (kBean.getLineData().size() <= i) {
               continue;
           }
           KBean.KLine bean = kBean.getLineData().get(i);

           if (bean.getClose() > bean.getOpen()) { //阳线
               mLinePaint.setColor(colorRed);
               mLinePaint.setStyle(Paint.Style.STROKE);
           } else {
               mLinePaint.setColor(colorGreen);
               mLinePaint.setStyle(Paint.Style.FILL);
           }
           //绘制蜡烛矩形
           canvas.drawRect((float) (x - stickWidth / 2), doubleToY(bean.getClose()),
                   (float) (x + stickWidth / 2), doubleToY(bean.getOpen()), mLinePaint);

           canvas.drawLine(x, doubleToY(bean.getHigh()), x, doubleToY(Math.max(bean.getClose(), bean.getOpen())), mLinePaint);
           canvas.drawLine(x, doubleToY(bean.getLow()), x, doubleToY(Math.min(bean.getClose(), bean.getOpen())), mLinePaint);

           //绘制最高价和最低价
           if (bean.getHigh() == priceHigh) {
               drawHighLow(canvas, x, priceHigh);
           } else if (bean.getLow() == priceLow) {
               drawHighLow(canvas, x, priceLow);
           }

           XList.add(x);
           x += stickWidth + spaceWidth;
       }
  1. 最后通过drawXYText绘制XY轴上的刻度值,显示在K线、分时线上面,避免被覆盖造成无法看清的问题;
  2. 最后的最后还需要加上十字标;
      if (isShowCross) {
           int index = (int) (crossX / chartWidth * getSize());
           KBean.KLine bean = kBean.getLineData().get(start + index);

           crossX = XList.get(index);
           crossY = doubleToY(bean.getClose());

           mLinePaint.reset();
           mLinePaint.setAntiAlias(true);
           mLinePaint.setColor(color_cross);
           mLinePaint.setStyle(Paint.Style.FILL);    // 填充模式 - 描边
           mLinePaint.setStrokeWidth(crossLineWidth);
           //画线
           canvas.drawLine(0, crossY, chartWidth, crossY, mLinePaint);
           canvas.drawLine(crossX, 0, crossX, chartHeight, mLinePaint);
       }

总结

关于数据基类bean,注释已经写得很清楚了。

/**
    * 线标题
    */
   private String tag;

   /**
    * 线表示颜色
    */
   private int lineColor;

   /**
    * 是否显示线
    */
   private boolean display = true;

还有K线和分时线一起绘制的,在demo中没有使用。不过大家可以自己摸索实现下,毕竟整明白了还是挺简单的。

下面附上Demo的下载地址

https://download.****.net/download/baidu_33853807/10738895