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.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绘制
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 拖拽和绘制动画