近期需要有一个曲线图表的需求,最前的图表功能的自定义View写,感觉性能上不过关,所以这次使用那个*炸天的Android图表宇宙无敌开源库MPAndroidChart,凭着中学生英语的水平大致看了下文档。
还是自己好好撸源码吧,MPAndroidChart其实已经封装的很好了,API也很易懂,但是有一些细节需要改动源码,希望可以帮到大家,先看看MPAndroidChart的包。
只选标了几个,其实包名已经超清楚了,其实Utils里就有很多干货,计算Text的高度等方法很好用,当然常做自定义控件的朋友应该都很熟悉了。
由于直接用的公司代码,所以没有整段的贴。。。。好了,开始撸代码。
很简单一个demo,这里可能有两个问题,我在看相关文档的时候看见过有一些同学问过一个问题,Line模式的图怎么让它一出来就呈现缩放的状态,而且要支持手势。这里其实是用的ViewPortHandler类做的处理,这个类作者声明必须要仔细阅读文档之后再使用,否则容易出现问题,真是吓屎了好吧。ViewPortHandler内部包含一个Matrix对象,拿这个Matrix就可以对Line做一些操作了。此处参考了gogooing的处理方式。
private void setHandler(CombinedChart combinedChart) {
final ViewPortHandler viewPortHandlerBar = combinedChart.getViewPortHandler();
viewPortHandlerBar.setMaximumScaleX(3);//最大缩放因子。
Matrix touchmatrix = viewPortHandlerBar.getMatrixTouch();
final float xscale = 3;
touchmatrix.postScale(xscale, 1f);
}
这个方法在数据加载设置完之后调用,然后还需要一步操作,因为这个时候你会发现,你的线是从头开始的,一般初始显示都希望是显示最后一根。
setHandler(mChartPrice);
mChartPrice.moveViewToX(Data.size() - 1);
哈哈,最后移动一下就好啦。
另外一个问题是,最左边labels如果过长的话就会被挡住一半。
其实源码是做过Label超出屏幕的处理,结果最后一个Label的判断一直没有走进去过,把i == mXAxis.mEntryCount - 1改为i == mXAxis.mEntryCount就可以了,这里不知道是不是有什么问题,总之哪个判断就是进不去。
/**
* draws the x-labels on the specified y-position
*
* @param pos
*/
protected void drawLabels(Canvas c, float pos, MPPointF anchor) {
........//省略N行代码
for (int i = 0; i < positions.length; i += 2) {
float x = positions[i];
if (mViewPortHandler.isInBoundsX(x)) {
String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis);
//这个判断里是处理第一个和最后一个labels,当然也可以自己写。
if (mXAxis.isAvoidFirstLastClippingEnabled()) {
// avoid clipping of the last
if (i == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) {
float width = Utils.calcTextWidth(mAxisLabelPaint, label);
if (width > mViewPortHandler.offsetRight() * 2
&& x + width > mViewPortHandler.getChartWidth())
x -= width / 2;
// avoid clipping of the first
} else if (i == 0) {
//第一个点不做操作
// float width = Utils.calcTextWidth(mAxisLabelPaint, label);
// x += width / 2;
}
}
drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees);
}
}
}
显示的问题解决了,还有是事件问题,MPAndroidChart提供的手势很多,但是Highlight的手势很单一,直接在Chart上滑动就可以触发Highlight显示,还有Chart缩放的时候好像是不能出现Highlight的,个人观察是这样,那就只能改源码了,我们希望可以长按显示Highlight,并且松开手Highlight消失。
这个库的包名都可以很好的帮助理解功能,关于绘制的类都在renderer包下面,关于手势很容易联想带listener,没错就是listener包下的BarLineChartTouchListener(折线图是这个)。onTouch方法简直不要太熟悉,手势操作就在这了,200行左右很少,逻辑也很清楚。我们在MotionEvent.ACTION_MOVE里面做改动。可以看到这个类里面还有一个onLongPress方法,这个是系统类GestureDetector内部类SimpleOnGestureListener的一个长按事件的回调方法。现在我们找一个标志位,来标记长按事件,事实上可以直接拿SimpleOnGestureListener里的mLastGesture来用。直接添加5-8行处的代码,这就解决了缩放状态下不能是使用Highlight的问题。然后抬手的时候就是在ACTION_CANCEL、ACTION_POINTER_UP、ACTION_POINTER_UP中加入mChart.highlightValue(null);mLastGesture=ChartGesture.NONE;两句代码,把标志位和Highlight复原即可。
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
case MotionEvent.ACTION_MOVE:
if (mLastGesture==ChartGesture.LONG_PRESS){
performHighlightDrag(event);
break;
}
if (mTouchMode == DRAG) {
mChart.disableScroll();
float x = mChart.isDragXEnabled() ? event.getX() - mTouchStartPoint.x : 0.f;
float y = mChart.isDragYEnabled() ? event.getY() - mTouchStartPoint.y : 0.f;
performDrag(event, x, y);
} else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) {
mChart.disableScroll();
if (mChart.isScaleXEnabled() || mChart.isScaleYEnabled())
performZoom(event);
} else if (mTouchMode == NONE
&& Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(),
mTouchStartPoint.y)) > mDragTriggerDist) {
if (mChart.isDragEnabled()) {
boolean shouldPan = !mChart.isFullyZoomedOut() ||
!mChart.hasNoDragOffset();
if (shouldPan) {
float distanceX = Math.abs(event.getX() - mTouchStartPoint.x);
float distanceY = Math.abs(event.getY() - mTouchStartPoint.y);
// Disable dragging in a direction that's disallowed
if ((mChart.isDragXEnabled() || distanceY >= distanceX) &&
(mChart.isDragYEnabled() || distanceY <= distanceX)) {
mLastGesture = ChartGesture.DRAG;
mTouchMode = DRAG;
}
} else {
if (mChart.isHighlightPerDragEnabled()) {
mLastGesture = ChartGesture.DRAG;
if (mChart.isHighlightPerDragEnabled())
performHighlightDrag(event);
}
}
}
}
break;
case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = event.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, Utils.getMaximumFlingVelocity());
final float velocityY = velocityTracker.getYVelocity(pointerId);
final float velocityX = velocityTracker.getXVelocity(pointerId);
if (Math.abs(velocityX) > Utils.getMinimumFlingVelocity() ||
Math.abs(velocityY) > Utils.getMinimumFlingVelocity()) {
if (mTouchMode == DRAG && mChart.isDragDecelerationEnabled()) {
stopDeceleration();
mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis();
mDecelerationCurrentPoint.x = event.getX();
mDecelerationCurrentPoint.y = event.getY();
mDecelerationVelocity.x = velocityX;
mDecelerationVelocity.y = velocityY;
Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by
// Google
}
}
if (mTouchMode == X_ZOOM ||
mTouchMode == Y_ZOOM ||
mTouchMode == PINCH_ZOOM ||
mTouchMode == POST_ZOOM) {
// Range might have changed, which means that Y-axis labels
// could have changed in size, affecting Y-axis size.
// So we need to recalculate offsets.
mChart.calculateOffsets();
mChart.postInvalidate();
}
mTouchMode = NONE;
mChart.enableScroll();
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
endAction(event);
break;
case MotionEvent.ACTION_POINTER_UP:
Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker);
mTouchMode = POST_ZOOM;
break;
case MotionEvent.ACTION_CANCEL:
mTouchMode = NONE;
endAction(event);
break;
}
// perform the transformation, update the chart
mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true);
return true; // indicate event was handled
}
@Override
public void onLongPress(MotionEvent e) {
mLastGesture = ChartGesture.LONG_PRESS;
OnChartGestureListener l = mChart.getOnChartGestureListener();
if (l != null) {
l.onChartLongPressed(e);
}
}
先记录这几个问题- -。