MPAndroidChart之LineChart源码分析

LineChart比PieChart逻辑简单,主要技术点就是双指缩放逻辑。

   MPAndroidChart之LineChart源码分析_第1张图片MPAndroidChart之LineChart源码分析_第2张图片

1 数据准备

1.1 折线数据

1.1.1 坐标点对象Entry

参照构造函数,有两个参数:x,y对应横坐标和纵坐标的值。

1.1.2 数据1对象/数据2对象 LineDataSet

参照构造函数,有两个参数:构成折线坐标点集合  ListyVals,这条折线的描述 label。还保存坐标点表现形式,坐标点颜色,半径等。

1.1.3 线性图表对象LineData

参照构造函数,有一个参数:折线数据对象集合List sets。

1.2 绘制数据计算(按照代码执行顺序)

1.2.1 构造LineDataSet时找到折线对象中坐标点集合中横坐标和纵坐标的极限值。DataSet.mXMin、DataSet.mXMax、DataSet.mYMin、DataSet.mYMax

MPAndroidChart之LineChart源码分析_第3张图片


1.2.2 构造LineData时找到折线数组中折线经过横坐标和纵坐标的极限值ChartData.mMax、CartData.mMin、ChartData.mLeftAxisMax、ChartData.mLeftAxisMin       

MPAndroidChart之LineChart源码分析_第4张图片

1.2.3 view绘制开始准备

方法1~2   view执行onMeasure后会触发onSizeChanged获得当前View长宽。
方法3~10。是根据需要绘制数据的横(XAxis)纵(YAxis)轴的极限值,组装横坐标,纵坐标的刻度值。
方法11~13 计算图例,图表的上下左右的偏移量。
MPAndroidChart之LineChart源码分析_第5张图片
建议去看看方法7,还是觉得作者数学很好。计算整数间隔。向上取整,向下取整,判断一个数是几位数。

2 实现功能点

2.1 基本坐标系建立

(1) 图表底图                                  
(2)X轴坐标线/Y轴坐标线 
(3)X轴网格线/Y轴网格线                  
(4)X轴/Y轴极限辅助线
(5)X轴坐标值/Y轴坐标值 

MPAndroidChart之LineChart源码分析_第6张图片

2.2 数据绘制

(2~4)数据1折线/数据2折线
(5~6)数据1点/数据2点

MPAndroidChart之LineChart源码分析_第7张图片

2.2.1 重新计算数据起止点

问题:我到现在也没有搞清楚作者为什么要写这个逻辑?有知道的大神可以给我留言告诉我
        public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) {
            float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX()));

            float low = chart.getLowestVisibleX();   //mXAxis.mAxisMinimum
            float high = chart.getHighestVisibleX(); //mXAxis.mAxisMaximum
            Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN);

            Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP);
            min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom);
            max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo);
            range = (int) ((max - min) * phaseX);
        }

2.2.2 绘制折线(方法4)

(1)数学概念:两点确定一条直线。LineBuffer集合必须要有4个数据。0,1代表开始点坐标值;2,3代表结束点的坐标值。
(2)将坐标数值经过矩阵变化变成最终显示像素集合
(3)这里我以为作者还会延续path使用。将路线设定好在绘制。但是作者又来教做人了。作者采用了drawLines方法。API请见 绘制多条直线的 drawLines方法
for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) {

                Entry e = dataSet.getEntryForIndex(j);
                if (e == null) continue;

                mLineBuffer[0] = e.getX();
                mLineBuffer[1] = e.getY() * phaseY;

                if (j < mXBounds.max) {

                    e = dataSet.getEntryForIndex(j + 1);

                    if (e == null) break;

                     mLineBuffer[2] = e.getX();
                     mLineBuffer[3] = e.getY() * phaseY;
                   

                } else {
                    mLineBuffer[2] = mLineBuffer[0];
                    mLineBuffer[3] = mLineBuffer[1];
                }

                trans.pointValuesToPixel(mLineBuffer);

                canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint);
}

2.2.3 绘制数据坐标点(方法7,8)

(1) 创建数据点位图集合
protected boolean init(ILineDataSet set) {

            int size = set.getCircleColorCount();
            boolean changeRequired = false;

            if (circleBitmaps == null) {
                circleBitmaps = new Bitmap[size];
                changeRequired = true;
            } else if (circleBitmaps.length != size) {
                circleBitmaps = new Bitmap[size];
                changeRequired = true;
            }

            return changeRequired;
        }
(2)以数据点位图为画布,绘制圆形位图
protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) {

            int colorCount = set.getCircleColorCount();
            float circleRadius = set.getCircleRadius();
            float circleHoleRadius = set.getCircleHoleRadius();

            for (int i = 0; i < colorCount; i++) {

                Bitmap.Config conf = Bitmap.Config.ARGB_4444;
                Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf);

                Canvas canvas = new Canvas(circleBitmap);
                circleBitmaps[i] = circleBitmap;

                mRenderPaint.setColor(set.getCircleColor(i));

              
                    Paint paint = new Paint();
                    paint.setStyle(Paint.Style.FILL);
                    paint.setColor(set.getCircleColor(i));
                    paint.setAntiAlias(true);

                    canvas.drawCircle(
                            circleRadius,
                            circleRadius,
                            circleRadius,
                            paint);

            }
  }
(3) 把数据点位图绘制到坐标系画布上。
protected void drawCircles(Canvas c)  {

Bitmap circleBitmap = imageCache.getBitmap(j);

if (circleBitmap != null) {
	c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, mRenderPaint);
}
}

2.3 触摸mark绘制

(12~13)坐标点辅助线绘制
(14~15)markView绘制
MPAndroidChart之LineChart源码分析_第8张图片

3 双指缩放

首先又涉及到基本知识。
Android多点触控。 android触控,先了解MotionEvent。
Matrix矩阵变换 Android Matrix详解

(1~13)初始化Matrix
(14~15)手势触发.变换Matrix

MPAndroidChart之LineChart源码分析_第9张图片

3.1(1~13)初始化Matrix

初始化就不详细介绍了.给大家展示一下日志
mMatrixValueToPx 坐标系原始矩阵(不改变)
mMatrixOffests 坐标系偏移量矩阵(不改变)
mMatrixTouch  手势缩放变换矩阵
E/mmnn    ( 2441): init mMatrix = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): onSizChanged

E/mmnn    ( 2441): refresh mMatrixTouch = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): notifyDataSetChanged

E/mmnn    ( 2441): calculateOffsets

E/mmnn    ( 2441): calculateOffsets mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): calculateOffsets mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): calculateOffsets mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]}

3.2 绘制坐标系(2.2方法方法4为例)

//构造绘制集合
//进行坐标变化然后绘制
protected void drawLinear(Canvas c, ILineDataSet dataSet) {
	for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) {
		trans.pointValuesToPixel(mLineBuffer);
	}
	
	canvas.drawLines(mLineBuffer, 0, size, mRenderPaint);
}
以第0个点为例
E/mmnn    ( 2441): pts0.0

E/mmnn    ( 2441): mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixTouch = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]} pts39.0
3.3手势控制变换刷新矩阵(方法14,15)
这边就是设置mMatrixTouch 手势缩放变换矩阵. 然后调用方法4绘制
public boolean onTouch(View v, MotionEvent event) {
	 switch (event.getAction() & MotionEvent.ACTION_MASK) {
		 case MotionEvent.ACTION_DOWN:
			saveTouchStart(event);
		 break;
		 
		 case MotionEvent.ACTION_POINTER_DOWN:
			 if (event.getPointerCount() >= 2) {
				saveTouchStart(event);
			 }
				mTouchMode =  X_ZOOM;
		  break;
		  
		  case MotionEvent.ACTION_MOVE:
			performZoom(event);
		  break;
		  
		  case MotionEvent.ACTION_UP:
			mChart.calculateOffsets();
            mChart.postInvalidate();
			 mTouchMode = NONE;
		  break;
		  
		  case MotionEvent.ACTION_POINTER_UP:
			mTouchMode = POST_ZOOM;
		  break;
		  
		  case MotionEvent.ACTION_CANCEL:
			mTouchMode = NONE;
		  break;
	 }
	 mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true);
     return true;
}

//当move时,满足条件.会将mMatrixTouch进行矩阵缩放.要主意set.post.pre的区别
private void performZoom(MotionEvent event) {
	mMatrix.set(mSavedMatrix);
    mMatrix.postScale(scaleX, 1f, t.x, t.y);
}

矩阵设置完成,会调用refresh里面的invalidate()方法通知ondraw重新绘制view.这样缩放逻辑就完成了.
Log:
E/mmnn    ( 2441): ACTION_DOWN

E/mmnn    ( 2441): refresh mMatrixTouch = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): pts0.0

E/mmnn    ( 2441): mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixTouch = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]} pts39.0

E/mmnn    ( 2441): ACTION_MOVE

E/mmnn    ( 2441): performZoom X_ZOOM mMatrix = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): refresh mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): pts0.0

E/mmnn    ( 2441): mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} pts-87.966156

E/mmnn    ( 2441): mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]} pts-48.966156

E/mmnn    ( 2441): ACTION_POINTER_UP

E/mmnn    ( 2441): refresh mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): pts0.0

E/mmnn    ( 2441): mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} pts-87.966156

E/mmnn    ( 2441): mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]} pts-48.966156

E/mmnn    ( 2441): ACTION_UP

E/mmnn    ( 2441): calculateOffsets

E/mmnn    ( 2441): calculateOffsets mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): calculateOffsets mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): refresh mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

E/mmnn    ( 2441): pts0.0

E/mmnn    ( 2441): mMatrixValueToPx = Matrix{[37.944443, 0.0, 0.0][0.0, -1.4998698, -74.99349][0.0, 0.0, 1.0]} pts0.0

E/mmnn    ( 2441): mMatrixTouch = Matrix{[1.223548, 0.0, -87.966156][0.0, 1.0, 0.0][0.0, 0.0, 1.0]} pts-87.966156

E/mmnn    ( 2441): mMatrixOffset = Matrix{[1.0, 0.0, 39.0][0.0, 1.0, 922.4219][0.0, 0.0, 1.0]} pts-48.966156

4 拖拽和绘制动画

与MPAndroidChart类似,可以参考 MPAndroidChart之PieChart源码分析





你可能感兴趣的:(Android应用,Android)