由于折线统计图在绘制过程中,会经常拖动,而每次拖动都需要重新绘制,所以考虑使用surfaceview。
1、继承Surfaceview,实现SurfaceHolder.Callback, Runnable接口
/**
* 画布创建时候执行的方法
*
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
// 开始绘画
mIsDrawing = true;
// 启动绘画线程
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
/**
* 画布销毁时候执行的方法
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 停止绘画
mIsDrawing = false;
}
2、绘制线程执行的方法
/** 子线程,循环绘制折线 */
@Override
public void run() {
// 如果处于绘画状态,那么就开始绘制
while (mIsDrawing) {
// 设置滚动减速
setSpeedCut();
// 绘制方法
draw();
}
}
/**
* 设置快速滚动时,末尾的减速
*/
private void setSpeedCut() {
if (!mIsTouch && isScroll) {
// 通过当前速度计算所对应的偏移量
mOffset = mOffset + mSpeed;
//当处于边缘时候使值不在变化
setOffsetRange();
}
// 每次偏移量的计算
if (mSpeed != 0) {
time++;
//这里设置的是快速滚动时间不操作两秒
//可以看成是一个Y轴交点为(0,xVelocity)
// x轴交点为(40,xVelocity)
// 对称轴为40<每个单位时间为50毫秒,40对应两秒>
mSpeed = (int) (xVelocity + time * time *
(xVelocity / 1600.0) - (xVelocity / 20.0) * time);
} else {
time = 0;
mSpeed = 0;
}
}
/** 具体的绘制方法 */
private void draw() {
try {
long start = System.currentTimeMillis();
// 获取并锁定画布
mCanvas = mHolder.lockCanvas();
// 设置画布背景为白色
mCanvas.drawColor(0xffffffff);
// 绘制坐标轴
drawAxis();
// 绘制曲线
drawLine();
long end = System.currentTimeMillis();
if (end - start < 50) {
Thread.sleep(50 - (end - start));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
// 保证每次都将绘制的内容提交到服务器
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
3、绘制坐标轴
/**
* 绘制x、y轴
*/
private void drawXYLine() {
mAxisWidth = mWidth - 2 * mPaddint;
mAxisHeight = mHeight - 2 * mPaddint;
mCoordinatePaint.setStrokeWidth(mLineWidth);
mCoordinatePaint.setAntiAlias(true);
// 绘制x轴
mCoordinatePaint.setColor(mXLineColor);
mCanvas.drawLine(0, 0, mAxisWidth, 0, mCoordinatePaint);
// 绘制y轴
mCoordinatePaint.setColor(mYLineColor);
mCanvas.drawLine(0, 0, 0, -mAxisHeight, mCoordinatePaint);
}
/** 绘制刻度线和箭头 */
private void drawXYScale() {
// 画x轴的刻度
// x轴的分割线数量
mXScaleNum = mXRange / mXUnit;
// x轴尽头的空隙
mXBlank = dp2px(getContext(), DEFAULT_X_BLANK);
// 每个刻度的宽度
mXScaleWidth = (int) ((mAxisWidth - mXBlank) * 1.0 / mXScaleNum);
for (int i = 0; i < mXScaleNum; i++) {
mCanvas.drawLine(mXScaleWidth * (i + 1), 0, mXScaleWidth * (i + 1), -mScaleHeight, mCoordinatePaint);
}
// 画y轴的刻度
mYScaleNum = mYRange / mYUnit;
// y轴尽头的空间
mYBlank = dp2px(getContext(), DEFAULT_Y_BLANK);
mYScaleWidth = (int) ((mAxisHeight - mYBlank) * 1.0 / mYScaleNum);
for (int i = 0; i < mYScaleNum; i++) {
mCanvas.drawLine(0, -mYScaleWidth * (i + 1), mScaleHeight, -mYScaleWidth * (i + 1), mCoordinatePaint);
}
// 画X轴的箭头
mCanvas.drawLine(mAxisWidth, 0, mAxisWidth - mScaleHeight * 2, -mScaleHeight, mCoordinatePaint);
mCanvas.drawLine(mAxisWidth, 0, mAxisWidth - mScaleHeight * 2, mScaleHeight, mCoordinatePaint);
// 画Y轴的箭头
mCanvas.drawLine(0, -mAxisHeight, mScaleHeight, -mAxisHeight + 2 * mScaleHeight, mCoordinatePaint);
mCanvas.drawLine(0, -mAxisHeight, -mScaleHeight, -mAxisHeight + 2 * mScaleHeight, mCoordinatePaint);
}
4、绘制折线
/** 绘制折线 */
private void drawLine() {
mCanvas.save();
mCanvas.translate(mPaddint, mHeight - mPaddint);
// 设置画笔属性
mLinePaint.setAntiAlias(true);
mLinePaint.setStrokeWidth(3);
mLinePaint.setColor(mLineColor);
mLinePaint.setStyle(Style.STROKE);
// 如果折线集合不为空
if (mLines != null && mLines.size() > 0) {
// 循环绘制所有线条
for (int i = 1; i < mLines.size(); i++) {
// 上一个点的xy坐标
int previousY = (int) (mLines.get(i - 1) * 1.0 / mYRange * (mAxisHeight - mYBlank));
int previousX = (i - 1) * mXScaleWidth + mOffset;
// 当前点的xy坐标
int thisY = (int) (mLines.get(i) * 1.0 / mYRange * (mAxisHeight - mYBlank));
int thisX = i * mXScaleWidth + mOffset;
// 保证只绘制坐标轴范围内的部分
if (previousX > 0 && previousX < mAxisWidth - mXBlank && thisX > 0 && thisX < mAxisWidth - mXBlank)
// 两个坐标连线
mCanvas.drawLine(previousX, -previousY, thisX, -thisY, mLinePaint);
}
}
mCanvas.restore();
}
5、触摸事件处理
@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) (event.getRawX());
// 计算当前速度
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
// 计算速度的单位时间
velocityTracker.computeCurrentVelocity(50);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点坐标
lastX = rawX;
mIsTouch = true;
break;
case MotionEvent.ACTION_MOVE:
// 计算便宜量
int offsetX = rawX - lastX;
// 在当前偏移量的基础上增加偏移量
mOffset = mOffset + offsetX;
setOffsetRange();
// 偏移量修改后下次重绘会有变化
lastX = rawX;
// 获取X方向上的速度
xVelocity = velocityTracker.getXVelocity();
mSpeed = (int) xVelocity;
break;
case MotionEvent.ACTION_UP:
mIsTouch = false;
break;
}
// 计算完成后回收内存
velocityTracker.clear();
velocityTracker.recycle();
return true;
}