package com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.charts; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.support.annotation.UiThread; import android.util.AttributeSet; import android.view.MotionEvent; import com.mm.android.deviceaddmodule.R; import com.mm.android.deviceaddmodule.mobilecommon.utils.LogUtil; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.adapter.IValueAdapter; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.animate.LAnimator; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.data.Entry; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.data.Line; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.data.Lines; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.listener.IDragListener; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.manager.MappingManager; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.model.Axis; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.model.HighLight; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.model.XAxis; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.model.YAxis; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.GodRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.HighLightRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.LineRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.NoDataRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.XAxisRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.render.YAxisRender; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.touch.GodTouchListener; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.touch.TouchListener; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.utils.RectD; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.utils.SingleD_XY; import com.mm.android.deviceaddmodule.mobilecommon.widget.linechart.utils.Utils; import java.util.List; public class LineChart extends Chart { MappingManager _MappingManager; Lines _lines; //////////////////////////// listener /////////////////////////// IDragListener _dragListener; ////////////////////////// function ////////////////////////// boolean isHighLightEnabled = true; boolean isTouchEnabled = true; boolean isDragable = true; boolean isScaleable = true; ChartMode _ChartMode; ///////////////////////////////// parts //////////////////////////////// XAxis _XAxis; YAxis _YAxis; HighLight _HighLight; ////////////////////////////////// render ///////////////////////////// NoDataRender _NoDataRender; XAxisRender _XAxisRender; YAxisRender _YAxisRender; LineRender _LineRender; HighLightRender _HighLightRender; GodRender _GodRender; ////////////////////////////// touch ///////////////////////////// TouchListener _TouchListener; GodTouchListener _GodTouchListener; //////////////////////////// 区域 /////////////////////////// RectF _MainPlotRect;// 主要的绘图区域 float _paddingLeft = 40; float _paddingRight = 5; // float _paddingTop = 17; float _paddingTop = 37; // float _paddingBottom = 15; float _paddingBottom = 25; RectF _GodRect;// ////////////////////////////// animator //////////////////////////// LAnimator _LAnimator; public LineChart(Context context) { super(context); } public LineChart(Context context, AttributeSet attrs) { super(context, attrs); } public LineChart(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void init(Context context, AttributeSet attributeSet) { super.init(context, attributeSet); TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.LineChart); boolean isGodMode = typedArray.getBoolean(R.styleable.LineChart_god_mode, false); _ChartMode = isGodMode ? ChartMode.God : ChartMode.Normal; typedArray.recycle(); // animator _LAnimator = new LAnimator(this); // init v _MainPlotRect = new RectF(); _MappingManager = new MappingManager(_MainPlotRect); _GodRect = new RectF(); // models _XAxis = new XAxis(); _YAxis = new YAxis(); _HighLight = new HighLight(); // render _NoDataRender = new NoDataRender(_MainPlotRect, _MappingManager); _XAxisRender = new XAxisRender(_MainPlotRect, _MappingManager, _XAxis); _YAxisRender = new YAxisRender(_MainPlotRect, _MappingManager, _YAxis); _LineRender = new LineRender(_MainPlotRect, _MappingManager, _lines, this); _HighLightRender = new HighLightRender(context,_MainPlotRect, _MappingManager, _lines, _HighLight); _GodRender = new GodRender(_MainPlotRect, _MappingManager, _GodRect); // touch listener _TouchListener = new TouchListener(this); _GodTouchListener = new GodTouchListener(this); // other _paddingLeft = Utils.dp2px(_paddingLeft); _paddingRight = Utils.dp2px(_paddingRight); _paddingTop = Utils.dp2px(_paddingTop); _paddingBottom = Utils.dp2px(_paddingBottom); } @Override public void computeScroll() { super.computeScroll(); if (_ChartMode == ChartMode.Normal) { _TouchListener.computeScroll(); } } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); if (_lines == null) { return false; } if (!isTouchEnabled) { return false; } if (_ChartMode == ChartMode.Normal) { return _TouchListener.onTouch(this, event); } else if (_ChartMode == ChartMode.God) { return _GodTouchListener.onTouch(this, event); } return false; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 1. render no data if (_lines == null || _lines.getLines() == null || _lines.getLines().size() == 0) { // _NoDataRender.render(canvas); _XAxis.calValues(getVisiableMinX(), getVisiableMaxX(), null); _YAxis.calValues(getVisiableMinY(), getVisiableMaxY(), null); _YAxisRender.renderGridline(canvas); // render labels _XAxisRender.renderLabels(canvas); _YAxisRender.renderLabels(canvas); // render unit _XAxisRender.renderUnit(canvas); _YAxisRender.renderUnit(canvas); return; } // 计算轴线上的数值 Line line = _lines.getLines().get(0); _XAxis.calValues(getVisiableMinX(), getVisiableMaxX(), line); _YAxis.calValues(getVisiableMinY(), getVisiableMaxY(), line); // render grid line // _XAxisRender.renderGridline(canvas); _YAxisRender.renderGridline(canvas); // render line _LineRender.render(canvas); // render god if (_ChartMode == ChartMode.God) { _GodRender.render(canvas); } // render high light,放到刻度虚线后面画,防止被Gridline遮挡 _HighLightRender.render(canvas); // render warn line _XAxisRender.renderWarnLine(canvas); _YAxisRender.renderWarnLine(canvas); // render Axis // _XAxisRender.renderAxisLine(canvas); // _YAxisRender.renderAxisLine(canvas); // render labels _XAxisRender.renderLabels(canvas); _YAxisRender.renderLabels(canvas); // render unit _XAxisRender.renderUnit(canvas); _YAxisRender.renderUnit(canvas); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); _LineRender.onChartSizeChanged(w, h); notifyDataChanged(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); _lines = null; } /** * 通知数据改变 */ public void notifyDataChanged() { limitMainPlotArea(); if (_lines == null) { return; } _lines.calMinMax(); prepareMap(); // 2. onDataChanged _LineRender.onDataChanged(_lines); _HighLightRender.onDataChanged(_lines); } /** * 限制 主绘图区域的边界 */ private void limitMainPlotArea() { _MainPlotRect.setEmpty(); _MainPlotRect.right += _MainPlotRect.left + getWidth(); _MainPlotRect.bottom += _MainPlotRect.top + getHeight(); // 0. padding offsetPadding(); // 1. 计算label,unit的宽高 offsetArea(); if (_ChartMode == ChartMode.God) { _GodRect.set(_MainPlotRect); _GodRect.right = _GodRect.right / 3; } } private void offsetPadding() { // 考虑图例 if (_lines != null) { int h = 0; for (Line line : _lines.getLines()) { if (line.getLegendHeight() > h) { h = line.getLegendHeight(); } } if (h > 0) { _paddingTop = h; } } _MainPlotRect.left += _paddingLeft; _MainPlotRect.top += _paddingTop; _MainPlotRect.right -= _paddingRight; _MainPlotRect.bottom -= _paddingBottom; } private void offsetArea() { _YAxisRender.paint_label(); _YAxisRender.paint_unit(); Paint paintLabel = _YAxisRender.get_PaintLabel(); Paint paintUnit = _YAxisRender.get_PaintUnit(); /******************************* 取一堆label中的head middle tail 中的最大值 ***********************************/ float labelDimen = _YAxis.getLabelDimen(); if (_lines != null && _lines.getLines().size() > 0) { Line line = _lines.getLines().get(0); if (line.getEntries().size() > 0) { List entryList = line.getEntries(); Entry head = entryList.get(0); Entry tail = entryList.get(entryList.size() - 1); Entry middle = entryList.get(( entryList.size() - 1) / 2); IValueAdapter yAdapter = _YAxis.get_ValueAdapter(); String s1 = yAdapter.value2String(head.getY()); float w1 = Utils.textWidth(paintLabel, s1); if (labelDimen < w1) { labelDimen = w1; } String s2 = yAdapter.value2String(middle.getY()); float w2 = Utils.textWidth(paintLabel, s2); if (labelDimen < w2) { labelDimen = w2; } String s3 = yAdapter.value2String(tail.getY()); float w3 = Utils.textWidth(paintLabel, s3); if (labelDimen < w3) { labelDimen = w3; } } } // 考虑y label和unit _MainPlotRect.left += _YAxis.offsetLeft(labelDimen, Utils.textHeight(paintUnit)); // 考虑x label和unit _MainPlotRect.bottom -= _XAxis.offsetBottom(Utils.textHeight(paintLabel), Utils.textHeight(paintUnit)); } /** * 准备映射关系 */ private void prepareMap() { double xMin = _lines.getmXMin(); double xMax = _lines.getmXMax(); double yMin = _lines.getmYMin(); double yMax = _lines.getmYMax(); if (yMin == 0 && yMax == 0){ yMax = 10; yMin = 0; } if (_lines.getLines().size() == 0){ yMax = 10; yMin = 0; Axis.CalWay calWay = get_XAxis().getCalWay(); if (calWay == Axis.CalWay.lc_day){ xMin = - 23.d/30; xMax = 23.d*31/30; }else if (calWay == Axis.CalWay.lc_week){ xMin = - 6.d/30; xMax = 6.d*31/30; }else if (calWay == Axis.CalWay.lc_month){ xMin = - 30.d/30; xMax = 30.d*31/30; }else if (calWay == Axis.CalWay.lc_year){ xMin = - 11.d/30; xMax = 11.d*31/30; } } _MappingManager.prepareRelation(xMin, xMax, yMin, yMax); } /** * 设置数据源 * * @param lines */ public void setLines(Lines lines) { _lines = lines; notifyDataChanged(); postInvalidate(); } public Lines getlines() { return _lines; } /** * 清空数据 */ public void clearData() { _lines = null; invalidate(); } private void A______________________________________________() { } /** * Y方向进行动画 */ @UiThread public void animateY() { _LAnimator.animateY(1000); } /** * Y方向进行动画 */ @UiThread public void animateY(long duration) { _LAnimator.animateY(duration); } /** * X方向进行动画 */ @UiThread public void animateX() { _LAnimator.animateX(1000); } /** * X方向进行动画 */ @UiThread public void animateX(long duration) { _LAnimator.animateX(duration); } /** * X,Y方向进行动画 */ @UiThread public void animateXY() { _LAnimator.animateXY(1000); } /** * X,Y方向进行动画 */ @UiThread public void animateXY(long duration) { _LAnimator.animateXY(duration); } public LAnimator get_LAnimator() { return _LAnimator; } public void set_LAnimator(LAnimator _LAnimator) { this._LAnimator = _LAnimator; } private void B_________________________________________________() { } private void a______________________________________________() { } public boolean isCanX_drag() { return _TouchListener.isCanX_drag(); } /** * 设置x方向是否可拖动 * * @param canX_drag */ public void setCanX_drag(boolean canX_drag) { _TouchListener.setCanX_drag(canX_drag); } public boolean isCanY_drag() { return _TouchListener.isCanY_drag(); } /** * 设置y方向是否可拖动 * * @param canY_drag */ public void setCanY_drag(boolean canY_drag) { _TouchListener.setCanY_drag(canY_drag); } public boolean isZoom_alone() { return _TouchListener.isZoom_alone(); } /** * 设置x,y方向是否可以独立缩放 * * @param zoom_independent */ public void setZoom_alone(boolean zoom_independent) { _TouchListener.setZoom_alone(zoom_independent); } public boolean isCanX_zoom() { return _TouchListener.isCanX_zoom(); } /** * 设置x方向是否可以缩放 * * @param canX_zoom */ public void setCanX_zoom(boolean canX_zoom) { _TouchListener.setCanX_zoom(canX_zoom); } public boolean isCanY_zoom() { return _TouchListener.isCanY_zoom(); } /** * 设置y方向是否可以缩放 * * @param canY_zoom */ public void setCanY_zoom(boolean canY_zoom) { _TouchListener.setCanY_zoom(canY_zoom); } public boolean isDragable() { return isDragable; } public void setDragable(boolean dragable) { isDragable = dragable; } public boolean isScaleable() { return isScaleable; } public void setScaleable(boolean scaleable) { isScaleable = scaleable; } public IDragListener get_dragListener() { return _dragListener; } public void set_dragListener(IDragListener _dragListener) { this._dragListener = _dragListener; } private void b______________________________________________() { } public float get_paddingLeft() { return _paddingLeft; } public void set_paddingLeft(float _paddingLeft) { this._paddingLeft = _paddingLeft; } public float get_paddingRight() { return _paddingRight; } public void set_paddingRight(float _paddingRight) { this._paddingRight = _paddingRight; } public float get_paddingTop() { return _paddingTop; } public void set_paddingTop(float _paddingTop) { this._paddingTop = _paddingTop; } public float get_paddingBottom() { return _paddingBottom; } public void set_paddingBottom(float _paddingBottom) { this._paddingBottom = _paddingBottom; } private void c______________________________________________() { } public double getVisiableMinX() { float px = _MainPlotRect.left; SingleD_XY xy = _MappingManager.getValueByPx(px, 0); return xy.getX(); } public double getVisiableMaxX() { float px = _MainPlotRect.right; SingleD_XY xy = _MappingManager.getValueByPx(px, 0); return xy.getX(); } public double getVisiableMinY() { float py = _MainPlotRect.bottom; SingleD_XY xy = _MappingManager.getValueByPx(0, py); return xy.getY(); } public double getVisiableMaxY() { float py = _MainPlotRect.top; SingleD_XY xy = _MappingManager.getValueByPx(0, py); return xy.getY(); } public RectD get_currentViewPort() { return _MappingManager.get_currentViewPort(); } public void set_currentViewPort(RectD currentViewPort) { _MappingManager.set_currentViewPort(currentViewPort); } /** * 设置y轴的范围 * * @param yMin * @param yMax */ public void setYMax_Min(double yMin, double yMax) { if (_lines != null) { _lines.setYCustomMaxMin(true); _lines.setmYMin(yMin); _lines.setmYMax(yMax); postInvalidate(); } else { LogUtil.errorLog("setAxisMaxMin:", " 请在lines设置后,调用此方法!"); } } /** * 设置轴线的范围 * * @param xMin * @param xMax */ public void setXAix_MaxMin(double xMin, double xMax) { if (_lines != null) { _lines.setXCustomMaxMin(true); _lines.setmXMin(xMin); _lines.setmXMax(xMax); postInvalidate(); } else { LogUtil.errorLog("setAxisMaxMin:"," 请在lines设置后,调用此方法!"); } } private void d______________________________________________() { } /** * 获取映射管家 * * @return */ public MappingManager get_MappingManager() { return _MappingManager; } //////////////////////////////// 便捷的方法 ////////////////////////////////// public void highLight_PixXY(float px, float py) { SingleD_XY xy = _MappingManager.getValueByPx(px, py); highLight_ValueXY(xy.getX(), xy.getY()); } public void highLight_ValueXY(double x, double y) { _HighLightRender.highLight_ValueXY(x, y); invalidate(); } public void highLightLeft() { _HighLightRender.highLightLeft(); invalidate(); } public void highLightRight() { _HighLightRender.highLightRight(); invalidate(); } public HighLight get_HighLight() { return _HighLight; } public void set_HighLight(HighLight _HighLight) { this._HighLight = _HighLight; } public XAxis get_XAxis() { return _XAxis; } public YAxis get_YAxis() { return _YAxis; } public RectF get_GodRect() { return _GodRect; } public void set_GodRect(RectF _GodRect) { this._GodRect = _GodRect; } public RectF get_MainPlotRect() { return _MainPlotRect; } public void set_MainPlotRect(RectF _MainPlotRect) { this._MainPlotRect = _MainPlotRect; } public ChartMode get_ChartMode() { return _ChartMode; } public LineChart set_ChartMode(ChartMode _ChartMode) { this._ChartMode = _ChartMode; return this; } LineChart _ob_linechart; public void registObserver(LineChart lineChartOb) { this._ob_linechart = lineChartOb; notifyDataChanged_FromOb(lineChartOb); } public void notifyDataChanged_FromOb(LineChart lineChartOb) { // x,y轴上的单位 XAxis xAxis = lineChartOb.get_XAxis(); this.get_XAxis().set_unit(xAxis.get_unit()); YAxis yAxis = lineChartOb.get_YAxis(); this.get_YAxis().set_unit(yAxis.get_unit()); this.setLines(lineChartOb.getlines()); } public void notifyOB_ViewportChanged(RectD _currentViewPort) { _ob_linechart.set_CurrentViewPort(_currentViewPort); _ob_linechart.invalidate(); } public LineChart set_CurrentViewPort(RectD _currentViewPort) { _MappingManager.set_currentViewPort(_currentViewPort); return this; } public enum ChartMode { Normal, God } }