MPAndroidChart自定义图表
声明:本文MPChart 代表的就是 MPAndroidChart。
1. 自定义Chart的Attribute
咱们回想一下自定义View的进程里,通常咱们会将一些特点控制Attribute经过自定义View的构造办法传入,然后制作或许layout的情况下使用这些特点。Attribute类中的特点,经过读取attr.xml 中自定义的 View的 declare-styleable,在xml layoutInflate 时加载赋值给Attribute类。而MPChart的图表相同也是View,经过传入Attribute特点进行自定义相关的制作,工欲善其事必先利其器。
例如,咱们自定义一个CustomChart 承继自BarChart,传入CustomBarChartAttr 包含自定义的一些特点控制变量的调集类, 类似图1.0将一些公共的特点放入BaseChartAttrs里。
图1.0 自定义特点Attribute
经过特点控制,能够将咱们自定义的一些差异性的数据,需求用户设定的比方色彩,大小、圆角的半径等露出出来;对于共性的特点比方坐标相关的,四个坐标,只显示Bottom的XAxis,跟右边的YAxis, 就能够直接在自定义的BarChart中写死,工程需求不雷同于MPChart这种库的供给者,他们需求考虑的是各色用户的各种需求,所以需求考虑到API的全面性,而咱们需求做的是尽量抽取共性到一个公共的类里,然后需求里的可变因素,削减犯错的几率,将差异性的东西经过这儿比方Attribute去设定,或许后续还会提到在Entry里包含特点,Entry能够详细到Chart里某一个item的特点,而Attribute 一般只能控制到Chart, 或许这些Item的共有特点上。
2. Render 自定义制作逻辑
上篇文章里咱们介绍了Chart的制作,分别由担任各个小部件的Render去制作,当自定义的需求仅仅需改局部比方图表的主体的时候,而XAxis、YAxis、边框等的制作不用改动,则只需替换担任主体制作的Render目标,例如MPChart原本库里的Chart里有一个担任制作图表的主体的DataRenderer mRenderer; 这儿只需替换该Render目标,将自定义的制作逻辑加在自定义的Render里。
@Override
protected void init() {
super.init();
mRenderer = new CustomBarChartRenderer(this, mAnimator, mViewPortHandler);
}
这儿自定义的是BarChart类型,因而CustomBarChartRenderer 承继自BarChartRenderer, 这样能够免去许多BarChart中原本的制作,而只需重视需求所要的改动。
当拿到Renderer之后,咱们能够为所欲为地去制作各种需求,比方如下制作带小红花的7天的柱状图:
图1.1 flowerBarChart
从上篇文章里咱们了解到BarChart的制作会在drawDataSet()办法里,涉及到制作矩形方位的信息存储在BarBuffer里,然后经过Transformer转为RectF的坐标点,这儿有7个矩形,所以Buffer里存储有 4 * 7 = 28个值的数组,每次取四个值组成RectF, 然后就在RectF里制作背景,制作Bitmap、制作各色各样的需求都Okay。
图1.2 FlowerBarChart 的制作。
3. DataBuffer
接下来咱们看一个实际的生产环境里的一种图表的制作,涉及到需求处理 Buffer 数据的存储转化。咱们先看IPhone的 苹果健康的一个数据图表:
图1.3 苹果健康心率图表
MPChart内目测是没有这种类型图表的,那么拿到这个咱们该如何做呢?首先剖析图形中每个Item或许会有若干个小圆角矩形组成,如示例图中绿色框里有三个小矩形组成,本来的BarBuffer数组4个值对应一个RectF在这儿不在适用了,笔者这儿供给的一种解决方案如下:每个Entry还是对应一个Item,这儿自定义了一种SegmentEntry承继自BarEntry,SegmentEntry 里包含了若干矩形制作对应的数据model, SegmentRectModel, 由于同属于一个Item,这些同属于Item的小矩形都有相同的XAxis坐标特点,所以SegmentRectModel 里只需保存YAxis对应所需的信息。
public class SegmentRectModel {
public float topValue;
public float bottomValue;
public int rectColor = -1;
public int boardColor = -1;
public int boardWidth = 0;//dp
}
open class SegmentBarEntry : RecyclerBarEntry {
@JvmField
var rectValueModelList: MutableList<SegmentRectModel>? = null
private var segmentRange = 0 // 每种事务数据的segmentRange不一样,心率、血氧、血压等。
var maxValue = 0
var minValue = 0
}
SegmentBarEntry内部存有 List 小矩形对应的事务数据。
这儿简略介绍一下如何生成这一些列的小区间SegmentRectModel[bottom, top] , 以心率值为例。比方这一天下来穿戴设备产生了一下的心率值:
【45, 46, 48, 49, 52, 67, 69, 70, 75, 78, 90, 93, 94, 97, 99, 103, 158, 164, 169, 170】
这儿引入一个参数用来将这些散点数据归并成 数区间,Segment_range。或许不同种类的数据,这个值不同,比方血氧 segment_range_spo 跟心率的 segment_range_hrm 取值不一样,假如这儿的segment_range_hrm取值5,那么上面的心率值核算后得到的区间列表为:
【[45, 52], [67,78], [90, 103], [158], [164,170]】这儿有个单点值,能够写成[158, 158], 由此对应成SegmentRectModel的 bottom, top 值, 单点在小矩形中退化成为一个小圆点。
这些SegmentEntry 最总已调集的形式存入到DataSet中,然后核算生成Buffer数据,这儿的Buffer核算方式改动,所以特地定义一个类用来包含这种SegmentEntry的数据转化逻辑。
//将DataSet中的SegmentEntry 的List<SegmentRectModel>的size累加给到Buffer
private int computeBufferSize(IBarDataSet dataSet){
int rectListSizeSum = 0;
int entryCount = dataSet.getEntryCount();
for (int i = 0; i < entryCount; i++) {
BarEntry entry = dataSet.getEntryForIndex(i);
if (entry instanceof SegmentBarEntry) {
SegmentBarEntry segmentBarEntry = (SegmentBarEntry) entry;
List<SegmentRectModel> rectList = segmentBarEntry.rectValueModelList;
rectListSizeSum += rectList.size();
}
}
return rectListSizeSum;
}
int bufferSize = computeBufferSize(set) * 4;//乘以4 表明矩形。
mBarBuffers[i] = new SegmentBarChartBuffer(bufferSize, barData.getDataSetCount(), set.isStacked());
在自定义的SegmentBarChartBuffer的feed() 内核算并保存SegmentEntry 中的系列小矩形经过Transformer转化前的数据。
图1.4 SegmentBarChartBuffer
转化成Buffer 内的数据后,至此交给Transformer,最终会生成一系列的小矩形,同一Item Entry下面的RectF具有相同的X轴坐标值,制作的Render也不再考虑这些差异性,从Buffer数组的0号index开始取,每次取4个制作RectF,需求留意的是当bottom – top 值小于 width时,或许退化成一条线。需求将它处理成一个圆点。
图1.5 SegmentChart的制作
至此,整个SegmentChart的制作解说完了,比较完好的一个流程,涉及到图表的全体特点Attribute定义,Item Entry根据需求自定义,然后自定义Buffer数据的feed方式,最终交给Render去制作的逻辑。最终看一下制作的效果图。
图1.6 MPChart自定义心率图表
下一章节,咱们介绍一种打破Item Entry 均分概念的图表制作,敬请期待!!
\