package com.anjuke.library.uicomponent.chart.bessel; import java.util.List; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; import android.support.v4.view.ViewCompat; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.Scroller; import com.anjuke.library.uicomponent.chart.bessel.BesselChart.ChartListener; import com.anjuke.library.uicomponent.chart.bessel.ChartData.Label; /** * 贝塞尔曲线图 * * @author tomkeyzhang([email protected]) * @date :2014年5月4日 */ class BesselChartView extends View { /** 通用画笔 */ private Paint paint; /** 曲线的路径,用于绘制曲线 */ private Path curvePath; /** 曲线图绘制的计算信息 */ private BesselCalculator calculator; /** 曲线图的样式 */ private ChartStyle style; /** 曲线图的数据 */ private ChartData data; /** 手势解析 */ private GestureDetector detector; /** 是否绘制全部贝塞尔结点 */ private boolean drawBesselPoint; /** 滚动计算器 */ private Scroller scroller; /** 曲线图事件监听器 */ private ChartListener chartListener; public BesselChartView(Context context, ChartData data, ChartStyle style, BesselCalculator calculator) { super(context); this.calculator = calculator; this.data = data; this.style = style; this.paint = new Paint(Paint.ANTI_ALIAS_FLAG); this.curvePath = new Path(); this.drawBesselPoint = false; this.scroller = new Scroller(context); this.detector = new GestureDetector(getContext(), new SimpleOnGestureListener() { float lastScrollX = 0f; @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (Math.abs(distanceX / distanceY) > 1) { getParent().requestDisallowInterceptTouchEvent(true); BesselChartView.this.calculator.move(distanceX); ViewCompat.postInvalidateOnAnimation(BesselChartView.this); if (e1.getX() != lastScrollX) { lastScrollX = e1.getX(); if (chartListener != null) chartListener.onMove(); } return true; } return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling((int) BesselChartView.this.calculator.getTranslateX(), 0, (int) velocityX, 0, -getWidth(), 0, 0, 0); ViewCompat.postInvalidateOnAnimation(BesselChartView.this); return true; } @Override public boolean onDown(MotionEvent e) { scroller.forceFinished(true); ViewCompat.postInvalidateOnAnimation(BesselChartView.this); return true; } }); } public void animateScrollToEnd(int duration) { scroller.startScroll(0, 0, -calculator.xAxisWidth / 2, 0, duration); } public void setChartListener(ChartListener chartListener) { this.chartListener = chartListener; } @Override public boolean onTouchEvent(MotionEvent event) { return detector.onTouchEvent(event); } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { calculator.moveTo(scroller.getCurrX()); ViewCompat.postInvalidateOnAnimation(this); } } @Override protected void onDraw(Canvas canvas) { if (data.getSeriesList().size() == 0) return; calculator.ensureTranslation(); canvas.translate(calculator.getTranslateX(), 0); drawGrid(canvas); drawCurveAndPoints(canvas); drawMarker(canvas); drawHorLabels(canvas); } /** 绘制曲线图中的房源 */ private void drawMarker(Canvas canvas) { Marker marker = data.getMarker(); if (marker != null) { paint.setAlpha(255); canvas.drawBitmap(marker.getBitmap(), null, marker.updateRect(marker.getPoint().x, marker.getPoint().y, marker.getWidth(), marker.getHeight()), paint); } } /** 绘制网格线 */ private void drawGrid(Canvas canvas) { paint.setStrokeWidth(1); paint.setStyle(Paint.Style.FILL); paint.setColor(style.getGridColor()); paint.setAlpha(80); List<Label> yLabels = data.getYLabels(); // 先绘制两条水平线 float coordinateY = yLabels.get(0).y; canvas.drawLine(0, coordinateY, calculator.xAxisWidth, coordinateY, paint); coordinateY = yLabels.get(yLabels.size() - 1).y; canvas.drawLine(0, coordinateY, calculator.xAxisWidth, coordinateY, paint); // 再绘制竖直线 for (Point point : calculator.gridPoints) { if (point != null && point.willDrawing && point.valueY > 0) canvas.drawLine(point.x, point.y, point.x, calculator.yAxisHeight, paint); } } /** 绘制曲线和结点 */ private void drawCurveAndPoints(Canvas canvas) { paint.setStrokeWidth(5); for (Series series : data.getSeriesList()) { paint.setColor(series.getColor()); curvePath.reset(); List<Point> list = series.getBesselPoints(); for (int i = 0; i < list.size(); i = i + 3) { if (i == 0) { curvePath.moveTo(list.get(i).x, list.get(i).y); } else { curvePath.cubicTo(list.get(i - 2).x, list.get(i - 2).y, list.get(i - 1).x, list.get(i - 1).y, list.get(i).x, list.get(i).y); } } paint.setStyle(Paint.Style.STROKE); canvas.drawPath(curvePath, paint);// 绘制光滑曲线 paint.setStyle(Paint.Style.FILL); for (Point point : series.getPoints()) { if (point.willDrawing) { canvas.drawCircle(point.x, point.y, 5, paint); paint.setAlpha(80); canvas.drawCircle(point.x, point.y, 10, paint); paint.setAlpha(255); } }// 绘制结点 if (drawBesselPoint) { for (Point point : list) { if (!series.getPoints().contains(point)) { paint.setColor(Color.BLUE); paint.setAlpha(255); canvas.drawCircle(point.x, point.y, 5, paint); } }// 绘制贝塞尔控制结点 } } } /** 绘制横轴 */ private void drawHorLabels(Canvas canvas) { paint.setStyle(Paint.Style.FILL); paint.setStrokeWidth(2); paint.setColor(style.getHorizontalLabelTextColor()); paint.setTextSize(style.getHorizontalLabelTextSize()); paint.setTextAlign(Align.CENTER); float endCoordinateX = calculator.xAxisWidth; float coordinateY = getHeight() - calculator.xAxisHeight; canvas.drawLine(0, coordinateY, endCoordinateX, coordinateY, paint); for (Label label : data.getXLabels()) { // 绘制橫坐标文本 canvas.drawText(label.text, label.x, label.y, paint); } } public void updateSize() { LayoutParams lp = getLayoutParams(); lp.height = calculator.height; lp.width = calculator.xAxisWidth; setLayoutParams(lp); } public void setDrawBesselPoint(boolean drawBesselPoint) { this.drawBesselPoint = drawBesselPoint; } }