认识观和Canvas
首先,应该研究从Android官方文档Canvas and Drawables Guide。尤其值得注意的是,LineChart
,BarChart
等是View
的子类,它们通过覆盖View超类的onDraw(Canvas c)
回调来显示它们自己。还要注意“画布”的定义:
画布对你来说是作为你的图形绘制的实际表面的伪装或界面 - 它包含你所有的“绘制”调用。
当您使用渲染器时,您将处理在画布上绘制线条等的功能。
画布图表上
点被指定为x和y的值相对于所述图表上的单位对所述图表上的值和像素之间的翻译。例如,在下面的图表中,第一栏的中心位于x = 0
。第一栏的y值为52.28
。
这显然不对应于画布上的像素坐标。在画布上,画布上的x = 0
将是最左边的像素,它们显然是空白的。同样,由于像素枚举从顶部开始为y = 0
,因此柱形的顶端显然不是52.28
(图表上的y值)。如果我们使用开发人员选项/指针位置,我们可以看到第一个小节的提示大约为x = 165
和y = 1150
。
A Transformer
负责将图表值转换为像素(屏幕上)坐标,反之亦然。渲染器中的一种常见模式是使用图表值(更易于理解)执行计算,然后在最后使用变换器将变换应用于屏幕上。
查看端口和边界
视图端口是一个窗口,即,在图表上的有界区域。视图端口用于确定用户当前可以看到哪部分图表。每个图表都有一个ViewPortHandler
,它封装了与视图端口相关的功能。我们可以使用ViewPortHandler#isInBoundsLeft(float x)
isInBoundsRight(float x)
来确定用户当前可以看到哪些x值。
在上图所示的图表中,BarChart“知道”为6或更高,但由于它们超出界限而不在当前视口中,因此不会呈现6和向上。因此,x值0
到5
在当前视口内。
ChartAnimator
的ChartAnimator
提供要被施加到该图表的额外转化。通常这是一个简单的乘法。例如,假设我们需要一个动画,其中图表的点从底部开始并逐渐上升到正确的y值超过1秒。动画师将提供一个phaseY
,这是一个简单的标量,从0.000
开始,时间为0ms
,并且逐渐上升到1.000
,1000ms
。
现在
我们理解所涉及的基本概念的渲染代码的例子,让我们来看看一些代码LineChartRenderer
:
protected void drawHorizontalBezier(ILineDataSet dataSet) {
float phaseY = mAnimator.getPhaseY();
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mXBounds.set(mChart, dataSet);
cubicPath.reset();
if (mXBounds.range >= 1) {
Entry prev = dataSet.getEntryForIndex(mXBounds.min);
Entry cur = prev;
// let the spline start
cubicPath.moveTo(cur.getX(), cur.getY() * phaseY);
for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) {
prev = cur;
cur = dataSet.getEntryForIndex(j);
final float cpx = (prev.getX())
+ (cur.getX() - prev.getX())/2.0f;
cubicPath.cubicTo(
cpx, prev.getY() * phaseY,
cpx, cur.getY() * phaseY,
cur.getX(), cur.getY() * phaseY);
}
}
// if filled is enabled, close the path
if (dataSet.isDrawFilledEnabled()) {
cubicFillPath.reset();
cubicFillPath.addPath(cubicPath);
// create a new path, this is bad for performance
drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds);
}
mRenderPaint.setColor(dataSet.getColor());
mRenderPaint.setStyle(Paint.Style.STROKE);
trans.pathValueToPixel(cubicPath);
mBitmapCanvas.drawPath(cubicPath, mRenderPaint);
mRenderPaint.setPathEffect(null);
}
的for
循环之前的前几行是设置为渲染器循环。请注意,我们从ChartAnimator(变换器)获得phaseY
,并计算视图端口边界。
for
循环的基本含义是“对于位于视口左右边界内的每个点”。呈现无法看到的x值没有意义。
在循环中,我们使用dataSet.getEntryForIndex(j)
获取当前条目的x值和y值,并在该条和前一条目之间创建一条路径。请注意路径全部乘以phaseY
的动画。
最后,之后已计算出的路径的变换被施加trans.pathValueToPixel(cubicPath);
和路径被渲染到画布mBitmapCanvas.drawPath(cubicPath, mRenderPaint);
编写自定义渲染
的第一步是选择正确类子类。请注意包com.github.mikephil.charting.renderer
中的类 ,包括XAxisRenderer
和LineChartRenderer
等。一旦创建了子类,就可以简单地覆盖相应的方法。根据上面的示例代码,我们将覆盖void drawHorizontalBezier(ILineDataSet dataSet)
而不调用super
(以便不会调用渲染阶段两次)并将其替换为我们想要的功能。如果你这样做是正确的,重写的方法至少应该有点像你覆盖方法:
- 变压器,动画师获得一个手柄,和界限
- 通过可见性×循环 - 值(将x值是视口范围内的是)
- 准备个图表呈现值
- 转化点为像素的画布
- 上的使用
Canvas
类方法绘制在画布上
您应该研究Canvas class(drawBitmap
等)中的方法,以查看允许您在渲染器循环中执行哪些操作。
如果您需要覆盖的方法未公开,您可能必须继承像LineRadarRenderer
这样的基础渲染器才能实现所需的功能。
一旦你设计了你想要的渲染器子类,你可以用Chart#setRenderer(DataRenderer renderer)
或BarLineChartBase#setXAxisRenderer(XAxisRenderer renderer)
或其他方法轻松地使用它。