MPAndroidChart 是Android开源库中最为著名的一个图标库,功能齐全,拓展性强,在github上start量超过25K,fork量超过6K。
由于MPAndroidChart开源Demo中的原型过于不太吸引广大用户的眼球,往往在开发过程中需要自定义一些效果,例如现在要讲的高亮效果,以Demo中的BarChart高亮效果为例,只覆盖在Bar所绘制的区域,如果想要改变这个区域,那么我们需要自定义高亮覆盖的区域。
先来看看高亮效果是怎么绘制出来的:
MPAndroidChart Api中暴露了一个接口用来设置高亮效果的开关:
chart.getData().setHighlightEnabled(boolean isEnable)
从getData开始跟踪代码到ChartData类,这个类里面有方法:
public void setHighlightEnabled(boolean enabled) {
for (IDataSet set : mDataSets) {
set.setHighlightEnabled(enabled);
}
}
从这段代码可以看到,绘制高亮的控制权掌控在IDataSet里面,也就是我们设置进图表中的数据里面。那么我们看看从数据对象中看看,找到BarChart这个类适配的对象BarData,进到这个类中,貌似并没有发现什么有价值的东西,那么我们再从BarChart中看看,在BarChart中有几个貌似跟Highlight有关的东西:
protected boolean mHighlightFullBarEnabled = false;
/**
* Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant
* only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry
* was tapped.
* Default: false
*
* @param enabled
*/
public void setHighlightFullBarEnabled(boolean enabled) {
mHighlightFullBarEnabled = enabled;
}
/**
* Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant
* only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry
* was tapped.
* Default: false
*
* @param enabled
*/
public void setHighlightFullBarEnabled(boolean enabled) {
mHighlightFullBarEnabled = enabled;
}
/**
* @return true the highlight operation is be full-bar oriented, false if single-value
*/
@Override
public boolean isHighlightFullBarEnabled() {
return mHighlightFullBarEnabled;
}
然而这些对我们来说并没什么用,进入父类看看也是如此,那么进入顶级类中看看,我们会在Chart中发现一个东西:
protected IHighlighter mHighlighter;
这个是将高亮对象封装到图表适配的数据对象中的:
/**
* An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex.
*
* @param set
* @param dataSetIndex
* @param xVal
* @param rounding
* @return
*/
protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
ArrayList<Highlight> highlights = new ArrayList<>();
//noinspection unchecked
List<Entry> entries = set.getEntriesForXValue(xVal);
if (entries.size() == 0) {
// Try to find closest x-value and take all entries for that x-value
final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
if (closest != null)
{
//noinspection unchecked
entries = set.getEntriesForXValue(closest.getX());
}
}
if (entries.size() == 0)
return highlights;
for (Entry e : entries) {
MPPointD pixels = mChart.getTransformer(
set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
highlights.add(new Highlight(
e.getX(), e.getY(),
(float) pixels.x, (float) pixels.y,
dataSetIndex, set.getAxisDependency()));
}
return highlights;
}
好像除了这些也没什么了。
难道没办法自定义自己喜欢的样式了吗?
当然不是,图表绘制时,还有一个渲染器,将数据渲染到图表中,找到相应的渲染器看看。
果然没让我们失望,在BarChartRenderer这个类中找到一个方法:
@Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
//获取数据对象
BarData barData = mChart.getBarData();
for (Highlight high : indices) {
//遍历高亮对象,设置高亮属性
IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
if (set == null || !set.isHighlightEnabled())
continue;
BarEntry e = set.getEntryForXValue(high.getX(), high.getY());
if (!isInBoundsX(e, set))
continue;
Transformer trans = mChart.getTransformer(set.getAxisDependency());
mHighlightPaint.setColor(set.getHighLightColor());
mHighlightPaint.setAlpha(set.getHighLightAlpha());
boolean isStack = (high.getStackIndex() >= 0 && e.isStacked()) ? true : false;
final float y1;
final float y2;
if (isStack) {
if(mChart.isHighlightFullBarEnabled()) {
y1 = e.getPositiveSum();
y2 = -e.getNegativeSum();
} else {
Range range = e.getRanges()[high.getStackIndex()];
y1 = range.from;
y2 = range.to;
}
} else {
y1 = e.getY();
y2 = 0.f;
}
//设置高亮区域
prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans);
//绘制高亮区域
setHighlightDrawPos(high, mBarRect);
c.drawRect(mBarRect, mHighlightPaint);
}
}
protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) {
float left = x - barWidthHalf;
float right = x + barWidthHalf;
float top = y1;
float bottom = y2;
mBarRect.set(left, top, right, bottom);
trans.rectToPixelPhase(mBarRect, mAnimator.getPhaseY());
}
/**
* Sets the drawing position of the highlight object based on the riven bar-rect.
* @param high
*/
protected void setHighlightDrawPos(Highlight high, RectF bar) {
high.setDraw(bar.centerX(), bar.top);
}
这个方法专门用户绘制高亮效果,改变mBarRect的区域就可以改变高亮区域了。