1、Chart的基本结构
在使用MPAndroidChart
的时候,一会绘制是直接使用PieChart
或者LineChart
这些类去实现,他们都有一个公共的父类Chart
,今天主要说一下这个类,从源头看一下MPAndroidChart
的封装。
Chart
自身是一个自定义的Viewgroup,所以其实核心操作也就是理解成自定义VIewGroup
。
2、流程分析
绘图有三宝onMeasure
、onLayout
、onDraw
,接下来就按照绘图流程来挨个击破
先看一下构造方法,常规这里面会初始化内容
2-1、先来瞅瞅构造方法初始化了啥?
protected void init() {
setWillNotDraw(false);
mAnimator = new ChartAnimator(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
postInvalidate();
}
});
// initialize the utils
Utils.init(getContext());
mMaxHighlightDistance = Utils.convertDpToPixel(500f);
mDescription = new Description();
mLegend = new Legend();
mLegendRenderer = new LegendRenderer(mViewPortHandler, mLegend);
mXAxis = new XAxis();
mDescPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
...
}
第一行代码看到设置了setWillNotDraw(false);
如果是有过自定义VIewGroup的朋友应该不会陌生,我们知道Android对于ViewGroup
的绘制是有过优化处理的,默认情况下不会执行onDraw
方法,而是执行的dispatchDraw
,而这句话的意思就是让onDraw
方法正常执行(当然还有一种方式也可以让onDraw
执行,就是给布局设置背景)
接下里初始化了一个ChartAnimator
对象,这个对象的声明是protected的,所以子类是可以自定覆盖实现的。
还初始化了Description
(图表描述信息)和Legend
(图例)以及Xasis
(x坐标),因为这些属性都是每个绘图类型控件都会有的,所以会做公共初始化。
2-2、onMeasure & onLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = (int) Utils.convertDpToPixel(50f);
setMeasuredDimension(
Math.max(getSuggestedMinimumWidth(),
resolveSize(size,
widthMeasureSpec)),
Math.max(getSuggestedMinimumHeight(),
resolveSize(size,
heightMeasureSpec)));
}
这里的布局测量基本没什么可讲的,其实就是为布局设置了一个最小的宽高度为50DP。
@Override
protected void onLayout(boolean changed, int left, int top,
int right, int bottom) {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).layout(left, top, right, bottom);
}
}
这里的布局排列也是一样,单纯的对子布局进行了排列,没啥特殊的。当然也是正常的,主要目的本来就是绘制。
2-3、onSizeChanged
在介绍onDraw
之前先说下这个方法,相信精通绘制的大家对这个方法很熟悉了,他是在onLayout
中调用的,所以这个方法中可以获取到布局的大小信息
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w > 0 && h > 0 && w < 10000 && h < 10000) {
mViewPortHandler.setChartDimens(w, h);
}
notifyDataSetChanged();
for (Runnable r : mJobs) {
post(r);
}
mJobs.clear();
super.onSizeChanged(w, h, oldw, oldh);
}
其实这块核心关注的就是mViewPortHandler.setChartDimens(w, h);
,可以看到把布局的宽高度设置到了```ViewPortHandler``中。这个类后面会专门讲解,先不着急。`
2-4、onDraw
@Override
protected void onDraw(Canvas canvas) {
if (mData == null) {
boolean hasText = !TextUtils.isEmpty(mNoDataText);
if (hasText) {
//获取布局中心点
MPPointF pt = getCenter();
switch (mInfoPaint.getTextAlign()) {
case LEFT:
pt.x = 0;
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
case RIGHT:
pt.x *= 2.0;
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
default:
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
}
}
return;
}
if (!mOffsetsCalculated) {
//计算图表到边框的距离
calculateOffsets();
mOffsetsCalculated = true;
}
}
有没有发现,这里只绘制了一个没有数据时候的展示文字,并没有绘制其他任何信息,连x轴、y轴都没有绘制,具体绘制的,下一篇文章会揭晓,包括ViewPortHandler
等类