/* * Copyright (C) 2012 www.amsoft.cn * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ab.view.chart; import java.util.List; import java.util.Map; import java.util.Map.Entry; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.FontMetrics; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.os.Handler; import android.text.TextPaint; import android.view.MotionEvent; import android.view.View; import com.ab.util.AbFileUtil; import com.ab.util.AbGraphicUtil; import com.ab.util.AbViewUtil; // TODO: Auto-generated Javadoc /** * The view that encapsulates the graphical chart. */ public class GraphicalView extends View { /** The m context. */ private Context mContext; /** The chart to be drawn. */ private AbstractChart mChart; /** The chart renderer. */ private DefaultRenderer mRenderer; /** The view bounds. */ private Rect mRect = new Rect(); /** The user interface thread handler. */ private Handler mHandler; /** The zoom buttons rectangle. */ private RectF mZoomR = new RectF(); /** The zoom buttons background color. */ private static final int ZOOM_BUTTONS_COLOR = Color.argb(175, 150, 150, 150); /** The zoom in tool. */ private Zoom mZoomIn; /** The zoom out tool. */ private Zoom mZoomOut; /** The fit zoom tool. */ private FitZoom mFitZoom; /** The paint to be used when drawing the chart. */ private Paint mPaint = new Paint(); /** The touch handler. */ private ITouchHandler mTouchHandler; /** The old x coordinate. */ private float oldX; /** The old y coordinate. */ private float oldY; /** * Creates a new graphical view. * @param context the context * @param chart the chart to be drawn */ public GraphicalView(Context context, AbstractChart chart) { super(context); mContext = context; mChart = chart; mHandler = new Handler(); if (mChart instanceof XYChart) { mRenderer = ((XYChart) mChart).getRenderer(); } else { mRenderer = ((RoundChart) mChart).getRenderer(); } if (mRenderer instanceof XYMultipleSeriesRenderer && ((XYMultipleSeriesRenderer) mRenderer).getMarginsColor() == XYMultipleSeriesRenderer.NO_COLOR) { ((XYMultipleSeriesRenderer) mRenderer).setMarginsColor(mPaint.getColor()); } //缩放控制器 if (mRenderer.isZoomEnabled()) { mZoomIn = new Zoom(mChart, true, mRenderer.getZoomRate()); mZoomOut = new Zoom(mChart, false, mRenderer.getZoomRate()); mFitZoom = new FitZoom(mChart); } mTouchHandler = new TouchHandler(this, mChart); if (mChart instanceof XYChart) { XYMultipleSeriesRenderer mXYMultipleSeriesRenderer = ((XYChart) mChart).getRenderer(); //根据屏幕大小重置所有尺寸 int explainTextSize1 = mXYMultipleSeriesRenderer.getExplainTextSize1(); int explainTextSize2 = mXYMultipleSeriesRenderer.getExplainTextSize2(); int scaleCircleRadius = mXYMultipleSeriesRenderer.getScaleCircleRadius(); int scaleRectWidth = mXYMultipleSeriesRenderer.getScaleRectWidth(); int scaleRectHeight = mXYMultipleSeriesRenderer.getScaleRectHeight(); //按分辨率转换 mXYMultipleSeriesRenderer.setExplainTextSize1(AbViewUtil.scaleTextValue(mContext, explainTextSize1)); mXYMultipleSeriesRenderer.setExplainTextSize2(AbViewUtil.scaleTextValue(mContext, explainTextSize2)); mXYMultipleSeriesRenderer.setScaleCircleRadius(AbViewUtil.scaleValue(mContext, scaleCircleRadius)); mXYMultipleSeriesRenderer.setScaleRectWidth(AbViewUtil.scaleValue(mContext, scaleRectWidth)); mXYMultipleSeriesRenderer.setScaleRectHeight(AbViewUtil.scaleValue(mContext, scaleRectHeight)); SimpleSeriesRenderer[] mSimpleSeriesRenderers = mRenderer.getSeriesRenderers(); if(mSimpleSeriesRenderers!=null && mSimpleSeriesRenderers.length>0){ for(int i=0;i<mSimpleSeriesRenderers.length;i++){ SimpleSeriesRenderer mSimpleSeriesRenderer = mSimpleSeriesRenderers[i]; int mChartValuesTextSize = (int)mSimpleSeriesRenderer.getChartValuesTextSize(); mSimpleSeriesRenderer.setChartValuesTextSize(AbViewUtil.scaleTextValue(mContext, mChartValuesTextSize)); } } } int chartTitleTextSize = (int)mRenderer.getChartTitleTextSize(); mRenderer.setChartTitleTextSize(AbViewUtil.scaleTextValue(mContext, chartTitleTextSize)); //轴线上标签文字大小 int mLabelsTextSize = (int)mRenderer.getLabelsTextSize(); mRenderer.setLabelsTextSize(AbViewUtil.scaleTextValue(mContext, mLabelsTextSize)); //说明文字大小 int mLegendTextSize = (int)mRenderer.getLegendTextSize(); mRenderer.setLegendTextSize(AbViewUtil.scaleTextValue(mContext, mLegendTextSize)); } /** * Returns the current series selection object. * * @return the series selection */ public SeriesSelection getCurrentSeriesAndPoint() { return mChart.getSeriesAndPointForScreenCoordinate(new Point(oldX, oldY)); } /** * Transforms the currently selected screen point to a real point. * * @param scale the scale * @return the currently selected real point */ public double[] toRealPoint(int scale) { if (mChart instanceof XYChart) { XYChart chart = (XYChart) mChart; return chart.toRealPoint(oldX, oldY, scale); } return null; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.getClipBounds(mRect); int top = mRect.top; int left = mRect.left; int width = mRect.width(); int height = mRect.height(); if (mRenderer.isInScroll()) { top = 0; left = 0; width = getMeasuredWidth(); height = getMeasuredHeight(); } mChart.draw(canvas, left, top, width, height, mPaint); if (mChart instanceof XYChart) { XYMultipleSeriesRenderer mXYMultipleSeriesRenderer = ((XYChart) mChart).getRenderer(); if(mXYMultipleSeriesRenderer.isScaleLineEnabled()){ oldX = ((TouchHandler) mTouchHandler).getOldX(); int scaleTopPadding = 50; //从坐标轴往下 int scaleBottomPadding = 20; int explainTextSize1 = mXYMultipleSeriesRenderer.getExplainTextSize1(); int explainTextSize2 = mXYMultipleSeriesRenderer.getExplainTextSize2(); int scaleCircleRadius = mXYMultipleSeriesRenderer.getScaleCircleRadius(); //按分辨率转换 scaleTopPadding = AbViewUtil.scaleValue(mContext, scaleTopPadding); scaleBottomPadding = AbViewUtil.scaleValue(mContext, scaleBottomPadding); //Y轴位置 int bottomY = 0; int topY = 0; if (mChart instanceof XYChart) { Rect mScreenR = ((XYChart) mChart).getScreenR(); bottomY = mScreenR.bottom; topY = mScreenR.top; if(oldX == 0){ oldX = mScreenR.right; } } else { } //标尺线与拖手 mPaint.setColor(mXYMultipleSeriesRenderer.getScaleLineColor()); canvas.drawLine(oldX,scaleTopPadding,oldX, bottomY+scaleBottomPadding, mPaint); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(ZOOM_BUTTONS_COLOR); canvas.drawCircle(oldX, bottomY+scaleBottomPadding+scaleCircleRadius, scaleCircleRadius, mPaint); //判断挂到哪个点上 if(mChart instanceof XYChart){ List<Float> mPoints = null; List<Double> mValues = null; List<String> mExplains = null; float minValue = -1; int minIndex = -1; //要显示的文本 String showValue = ""; String showExplain = ""; Map<Integer, List<Float>> points = ((XYChart) mChart).getPoints(); Map<Integer, List<Double>> values = ((XYChart) mChart).getValues(); Map<Integer, List<String>> explains = ((XYChart) mChart).getExplains(); for (Entry<Integer, List<Float>> value : points.entrySet()) { int index = value.getKey(); mPoints = value.getValue(); mValues = values.get(value.getKey()); mExplains = explains.get(value.getKey()); //判断距离在2范围的点的索引 minValue = 1000; minIndex = -1; for(int i=0;i<mPoints.size();i+=2){ Float f = mPoints.get(i); if(Math.abs(f-oldX)<minValue){ minValue = Math.abs(f-oldX); minIndex = i; } } if(index==0){ showValue = String.valueOf((double)mValues.get(minIndex+1)).replace(".0", ""); String showExplainT = mExplains.get(minIndex/2); if(showExplainT!=null && !"".equals(showExplainT.trim())){ showExplain = mExplains.get(minIndex/2); } }else{ showValue = showValue+"/"+String.valueOf((double)mValues.get(minIndex+1)).replace(".0", ""); String showExplainT = mExplains.get(minIndex/2); if(showExplainT!=null && !"".equals(showExplainT.trim())){ if(showExplain!=null && !"".equals(showExplain.trim())){ showExplain = showExplain+"/"+mExplains.get(minIndex/2); }else{ showExplain = mExplains.get(minIndex/2); } } } } if(minValue<5){ boolean showRect = false; if(mXYMultipleSeriesRenderer.isDisplayValue0()){ showRect = true; }else{ if("0".equals(showValue) || "0/0".equals(showValue)){ showRect = false; }else{ showRect = true; } } if(showRect){ //画框框和点 //获取文本的高度 TextPaint mTextPaint1 = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint1.setColor(Color.WHITE); mTextPaint1.setTypeface(Typeface.DEFAULT); mTextPaint1.setTextSize(explainTextSize1); TextPaint mTextPaint2 = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint2.setColor(Color.WHITE); mTextPaint2.setTypeface(Typeface.DEFAULT); mTextPaint2.setTextSize(explainTextSize2); //行高 float hSize1 = AbGraphicUtil.getDesiredHeight(mTextPaint1); //行高 float hSize2 = AbGraphicUtil.getDesiredHeight(mTextPaint2); int textPadding = 8; textPadding = AbViewUtil.scaleValue(mContext, textPadding); //设置个新的长方形 //判断文字是否超出设置的框框 int row1 = AbGraphicUtil.getDrawRowCount(showValue,mXYMultipleSeriesRenderer.getScaleRectWidth()-10,mTextPaint1); int row2 = AbGraphicUtil.getDrawRowCount(showExplain,mXYMultipleSeriesRenderer.getScaleRectWidth()-10,mTextPaint2); RectF mRectF = null; int textHeight = (int)(row1*hSize1+row2*hSize2+textPadding); //提示框的宽度和高度 int realScaleRectWidth = mXYMultipleSeriesRenderer.getScaleRectWidth(); int realScaleRectHeight = mXYMultipleSeriesRenderer.getScaleRectHeight(); //画圆角矩形 //充满 mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(mXYMultipleSeriesRenderer.getScaleRectColor()); //设置画笔的锯齿效果 mPaint.setAntiAlias(true); int mRectLeft = (int)(double)mPoints.get(minIndex); int mRectTop = (int)(double)mPoints.get(minIndex+1); int mRectRight = mRectLeft+realScaleRectWidth; int mRectBottom = mRectTop+realScaleRectHeight; if(textHeight > realScaleRectHeight){ realScaleRectHeight = textHeight; mRectBottom = mRectTop + realScaleRectHeight; } //判断是否会超出屏幕 if(mRectRight>width){ //超出了 mRectLeft = (int)(double)mPoints.get(minIndex)-5-mXYMultipleSeriesRenderer.getScaleRectWidth(); mRectRight = (int)(double)mPoints.get(minIndex)-5; } if(mRectBottom>bottomY){ //超出了 mRectTop = (int)(double)mPoints.get(minIndex+1)-5-realScaleRectHeight; mRectBottom = (int)(double)mPoints.get(minIndex+1)-5; //是否超出Y=0的位置的 if(mRectTop<topY){ mRectTop = mRectTop+realScaleRectHeight/2; mRectBottom = mRectBottom+realScaleRectHeight/2; } } mRectF = new RectF(mRectLeft,mRectTop, mRectRight,mRectBottom); //第二个参数是x半径,第三个参数是y半径 canvas.drawRoundRect(mRectF, 5, 5, mPaint); AbGraphicUtil.drawText(canvas,showValue,mXYMultipleSeriesRenderer.getScaleRectWidth(),mTextPaint1,mRectLeft+textPadding, mRectTop+textPadding); AbGraphicUtil.drawText(canvas,showExplain,mXYMultipleSeriesRenderer.getScaleRectWidth(),mTextPaint2,mRectLeft+textPadding, (int)(mRectTop+textPadding+row1*hSize1)); } } } } } } /** * Sets the zoom rate. * * @param rate the zoom rate */ public void setZoomRate(float rate) { if (mZoomIn != null && mZoomOut != null) { mZoomIn.setZoomRate(rate); mZoomOut.setZoomRate(rate); } } /** * Do a chart zoom in. */ public void zoomIn() { if (mZoomIn != null) { mZoomIn.apply(Zoom.ZOOM_AXIS_XY); repaint(); } } /** * Do a chart zoom out. */ public void zoomOut() { if (mZoomOut != null) { mZoomOut.apply(Zoom.ZOOM_AXIS_XY); repaint(); } } /** * Do a chart zoom reset / fit zoom. */ public void zoomReset() { if (mFitZoom != null) { mFitZoom.apply(); mZoomIn.notifyZoomResetListeners(); repaint(); } } /** * Adds a new zoom listener. * * @param listener zoom listener * @param onButtons the on buttons * @param onPinch the on pinch */ public void addZoomListener(ZoomListener listener, boolean onButtons, boolean onPinch) { if (onButtons) { if (mZoomIn != null) { mZoomIn.addZoomListener(listener); mZoomOut.addZoomListener(listener); } if (onPinch) { mTouchHandler.addZoomListener(listener); } } } /** * Removes a zoom listener. * * @param listener zoom listener */ public synchronized void removeZoomListener(ZoomListener listener) { if (mZoomIn != null) { mZoomIn.removeZoomListener(listener); mZoomOut.removeZoomListener(listener); } mTouchHandler.removeZoomListener(listener); } /** * Adds a new pan listener. * * @param listener pan listener */ public void addPanListener(PanListener listener) { mTouchHandler.addPanListener(listener); } /** * Removes a pan listener. * * @param listener pan listener */ public void removePanListener(PanListener listener) { mTouchHandler.removePanListener(listener); } /** * Gets the zoom rectangle. * * @return the zoom rectangle */ protected RectF getZoomRectangle() { return mZoomR; } /** * 描述:TODO. * * @version v1.0 * @param event the event * @return true, if successful * @see android.view.View#onTouchEvent(android.view.MotionEvent) * @author: amsoft.cn * @date:2013-6-17 上午9:04:51 */ @Override public boolean onTouchEvent(MotionEvent event) { //判断事件的位置 是标尺 还是图表 if(mRenderer != null){ //分解事件 float newX = event.getX(); float newY = event.getY(); //坐标轴之外事件 //int bottom = y + height - margins[2] - legendSize; int bottom = 0; if (mChart instanceof XYChart) { Rect mScreenR = ((XYChart) mChart).getScreenR(); bottom = mScreenR.bottom; } else { } if(newY >= bottom-10){ //不是图表事件 if (mTouchHandler.handleTouchControl(event)) { return true; } }else{ //可拖动,缩放打开默认拖动也打开 if ((mRenderer.isPanEnabled() || mRenderer.isZoomEnabled())) { if (mTouchHandler.handleTouch(event)) { return true; } } } } return super.onTouchEvent(event); } /** * Schedule a view content repaint. */ public void repaint() { mHandler.post(new Runnable() { public void run() { invalidate(); } }); } /** * Schedule a view content repaint, in the specified rectangle area. * * @param left the left position of the area to be repainted * @param top the top position of the area to be repainted * @param right the right position of the area to be repainted * @param bottom the bottom position of the area to be repainted */ public void repaint(final int left, final int top, final int right, final int bottom) { mHandler.post(new Runnable() { public void run() { invalidate(left, top, right, bottom); } }); } /** * Saves the content of the graphical view to a bitmap. * * @return the bitmap */ public Bitmap toBitmap() { setDrawingCacheEnabled(false); if (!isDrawingCacheEnabled()) { setDrawingCacheEnabled(true); } if (mRenderer.isApplyBackgroundColor()) { setDrawingCacheBackgroundColor(mRenderer.getBackgroundColor()); } setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH); return getDrawingCache(true); } }