您的位置:首页 > 移动开发 > Android开发

MPAndroidChart之LineChart源码分析

2017-05-27 14:56 393 查看
LineChart比PieChart逻辑简单,主要技术点就是双指缩放逻辑。

   




1 数据准备

1.1 折线数据

1.1.1 坐标点对象Entry

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

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

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

1.1.3 线性图表对象LineData

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

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

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




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



1.2.3 view绘制开始准备

方法1~2   view执行onMeasure后会触发onSizeChanged获得当前View长宽。
方法3~10。是根据需要绘制数据的横(XAxis)纵(YAxis)轴的极限值,组装横坐标,纵坐标的刻度值。

方法11~13 计算图例,图表的上下左右的偏移量。



建议去看看方法7,还是觉得作者数学很好。计算整数间隔。向上取整,向下取整,判断一个数是几位数。

2 实现功能点

2.1 基本坐标系建立

(1) 图表底图                                  
(2)X轴坐标线/Y轴坐标线 

(3)X轴网格线/Y轴网格线                  

(4)X轴/Y轴极限辅助线
(5)X轴坐标值/Y轴坐标值 



2.2 数据绘制

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



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.Ro
4000
unding.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绘制



3 双指缩放

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

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



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源码分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息