Android仿墨迹天气折线图效果
一、效果展示
		
	
		
	
二、分析
自定义View的核心是什么? 那就是计算每一个要绘制的图案的位置。
在我们这个Demo里面,View首先可以看作是一个对象,
backColor; //背景色
barColor; //柱状图色
lineColor; //线色
dotColor; //点色
topHeight; //顶部偏移
lineHeight; //行高
lineWidth; //柱状图宽
lineSpace; //柱状图之间的缝隙
viewWidth; //view宽
viewHeight; //view高
MAX_SCROLL_X; //最大偏移
public class Bar {private int left;private int top;private int right;private int bottom;//中心点的位置private int mx;private int my;private String number;private String title;}
三、绘制
String data = "[{\"title\": \"今天\", \"number\": \"42\"}, {\"title\": \"06:00\", \"number\": \"24\"}, {\"title\": \"12:00\", \"number\": \"8\"}, {\"title\": \"18:00\", \"number\": \"42\"}, {\"title\": \"明天\", \"number\": \"12\"}, {\"title\": \"06:00\", \"number\": \"36\"}, {\"title\": \"12:00\", \"number\": \"4\"}, {\"title\": \"18:00\", \"number\": \"16\"}, {\"title\": \"后天\", \"number\": \"50\"}, {\"title\": \"06:00\", \"number\": \"24\"}, {\"title\": \"12:00\", \"number\": \"42\"}, {\"title\": \"18:00\", \"number\": \"25\"}]";bars = JSONArray.parseArray(data, Bar.class);
/*** 得到数据计算每一个柱状图的位置*/private void compute() {if (bars == null || bars.size() == 0) {return;}int size = bars.size();for (int i = 0; i < size; i++) {Bar bar = bars.get(i);bar.setLeft(i * (lineWidth + lineSpace));bar.setTop((int) (viewHeight - (lineHeight * Integer.parseInt(bar.getNumber())) * 1.0 / 25));bar.setRight((i + 1) * lineWidth + i * lineSpace);bar.setBottom(viewHeight);bar.computeMidDot();if (i == size - 1) {MAX_SCROLL_X = bar.getRight() - viewWidth;}}}
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {viewWidth = MeasureSpec.getSize(widthMeasureSpec);viewHeight = topHeight + lineHeight * 2;setMeasuredDimension(viewWidth, viewHeight);compute();computeY();}
	
确定完View的宽高以及每一个柱形图的位置,就是绘制了:
/*** 绘制柱状图** @param canvas*/private void drawBar(Canvas canvas) {for (Bar bar : bars) {canvas.drawRect(new Rect(bar.getLeft(), bar.getTop(), bar.getRight(), bar.getBottom()), barPaint);}}
@Overridepublic boolean onTouchEvent(MotionEvent event) {obtainVelocityTracker();float currentX = event.getX();float currentY = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:downX = currentX;downY = currentY;if (!mScroller.isFinished()) {mScroller.abortAnimation();}break;case MotionEvent.ACTION_MOVE:float dx = currentX - downX;float dy = currentY - downY;if(Math.abs(dx) > Math.abs(dy)){getParent().requestDisallowInterceptTouchEvent(true);scrollTo((int) (getScrollX() - dx), 0);computeY();invalidate();downX = currentX;downY = currentY;}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int initialVelocity = (int) mVelocityTracker.getXVelocity();if ((Math.abs(initialVelocity) > mMinimumVelocity)) {flingX(-initialVelocity);}releaseVelocityTracker();break;}if (mVelocityTracker != null) {mVelocityTracker.addMovement(event);}return true;}
//两种类型 折线 柱状public static final int TYPE_LINE = 1;public static final int TYPE_BAR = 2;private int type = TYPE_BAR;
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (getType() == TYPE_BAR) {drawBar(canvas);} else if (getType() == TYPE_LINE) {drawLine(canvas);drawDialog(canvas);}}
/*** 绘制折线图** @param canvas*/private void drawLine(Canvas canvas) {int size = bars.size();for (int i = 0; i < size; i++) {Bar bar = bars.get(i);canvas.drawCircle(bar.getMx(), bar.getMy(), dp2px(mContext, 4), dotPaint);if (i != size - 1) {mPath.reset();Bar nextBar = bars.get(i + 1);boolean isUp = Integer.parseInt(nextBar.getNumber()) > Integer.parseInt(bar.getNumber());mPath.moveTo(bar.getMx(), bar.getMy());int midX = (bar.getMx() + nextBar.getMx()) / 2;int midY = (bar.getMy() + nextBar.getMy()) / 2;int p1x = (bar.getMx() + midX) / 2;int p1y = (bar.getMy() + midY) / 2 + (isUp ? 50 : -50);int p2x = (midX + nextBar.getMx()) / 2;int p2y = (midY + nextBar.getMy()) / 2 + (isUp ? -50 : 50);mPath.cubicTo(p1x, p1y, p2x, p2y, nextBar.getMx(), nextBar.getMy());canvas.drawPath(mPath, linePaint);}}}
/*** 计算指示器的位置*/private void computeXY() {int scrollX = getScrollX();dialogX = (int) (scrollX * 1.0 / MAX_SCROLL_X * (viewWidth - lineWidth)) + scrollX + lineWidth / 2;int size = bars.size();int currentIndex = 0;for (int i = 0; i < size; i++) {if(i != size - 1){Bar currentBar = bars.get(i);Bar nextBar = bars.get(i + 1);if (currentBar.getMx() < dialogX && nextBar.getMx() > dialogX) {currentIndex = i;break;}}}if (currentIndex < size - 1) {Bar currentBar = bars.get(currentIndex);Bar nextBar = bars.get(currentIndex + 1);dialogY = (int) ((dialogX - currentBar.getMx()) * (nextBar.getMy() - currentBar.getMy()) * 1.0 / (nextBar.getMx() - currentBar.getMx()) + currentBar.getMy());}}
private void drawDialog(Canvas canvas) {canvas.drawCircle(dialogX, dialogY, 20, tipPaint);}
评论
