package com.jjoe64.graphview; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import com.jjoe64.graphview.series.BaseSeries; import com.jjoe64.graphview.series.DataPointInterface; import com.jjoe64.graphview.series.Series; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by jonas on 22/02/2017. */ public class CursorMode { private final static class Styles { public float textSize; public int spacing; public int padding; public int width; public int backgroundColor; public int margin; public int textColor; } protected final Paint mPaintLine; protected final GraphView mGraphView; protected float mPosX; protected float mPosY; protected boolean mCursorVisible; protected final Map<BaseSeries, DataPointInterface> mCurrentSelection; protected final Paint mRectPaint; protected final Paint mTextPaint; protected double mCurrentSelectionX; protected Styles mStyles; protected int cachedLegendWidth; public CursorMode(GraphView graphView) { mStyles = new Styles(); mGraphView = graphView; mPaintLine = new Paint(); mPaintLine.setColor(Color.argb(128, 180, 180, 180)); mPaintLine.setStrokeWidth(10f); mCurrentSelection = new HashMap<>(); mRectPaint = new Paint(); mTextPaint = new Paint(); resetStyles(); } /** * resets the styles to the defaults * and clears the legend width cache */ public void resetStyles() { mStyles.textSize = mGraphView.getGridLabelRenderer().getTextSize(); mStyles.spacing = (int) (mStyles.textSize / 5); mStyles.padding = (int) (mStyles.textSize / 2); mStyles.width = 0; mStyles.backgroundColor = Color.argb(180, 100, 100, 100); mStyles.margin = (int) (mStyles.textSize); // get matching styles from theme TypedValue typedValue = new TypedValue(); mGraphView.getContext().getTheme().resolveAttribute(android.R.attr.textAppearanceSmall, typedValue, true); int color1; try { TypedArray array = mGraphView.getContext().obtainStyledAttributes(typedValue.data, new int[]{ android.R.attr.textColorPrimary}); color1 = array.getColor(0, Color.BLACK); array.recycle(); } catch (Exception e) { color1 = Color.BLACK; } mStyles.textColor = color1; cachedLegendWidth = 0; } public void onDown(MotionEvent e) { mPosX = Math.max(e.getX(), mGraphView.getGraphContentLeft()); mPosX = Math.min(mPosX, mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth()); mPosY = e.getY(); mCursorVisible = true; findCurrentDataPoint(); mGraphView.invalidate(); } public void onMove(MotionEvent e) { if (mCursorVisible) { mPosX = Math.max(e.getX(), mGraphView.getGraphContentLeft()); mPosX = Math.min(mPosX, mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth()); mPosY = e.getY(); findCurrentDataPoint(); mGraphView.invalidate(); } } public void draw(Canvas canvas) { if (mCursorVisible) { canvas.drawLine(mPosX, 0, mPosX, canvas.getHeight(), mPaintLine); } // selection for (Map.Entry<BaseSeries, DataPointInterface> entry : mCurrentSelection.entrySet()) { entry.getKey().drawSelection(mGraphView, canvas, false, entry.getValue()); } if (!mCurrentSelection.isEmpty()) { drawLegend(canvas); } } protected String getTextForSeries(Series s, DataPointInterface value) { StringBuffer txt = new StringBuffer(); if (s.getTitle() != null) { txt.append(s.getTitle()); txt.append(": "); } txt.append(mGraphView.getGridLabelRenderer().getLabelFormatter().formatLabel(value.getY(), false)); return txt.toString(); } protected void drawLegend(Canvas canvas) { mTextPaint.setTextSize(mStyles.textSize); mTextPaint.setColor(mStyles.textColor); int shapeSize = (int) (mStyles.textSize*0.8d); // width int legendWidth = mStyles.width; if (legendWidth == 0) { // auto legendWidth = cachedLegendWidth; if (legendWidth == 0) { Rect textBounds = new Rect(); for (Map.Entry<BaseSeries, DataPointInterface> entry : mCurrentSelection.entrySet()) { String txt = getTextForSeries(entry.getKey(), entry.getValue()); mTextPaint.getTextBounds(txt, 0, txt.length(), textBounds); legendWidth = Math.max(legendWidth, textBounds.width()); } if (legendWidth == 0) legendWidth = 1; // add shape size legendWidth += shapeSize+mStyles.padding*2 + mStyles.spacing; cachedLegendWidth = legendWidth; } } float legendPosX = mPosX - mStyles.margin - legendWidth; if (legendPosX < 0) { legendPosX = 0; } // rect float legendHeight = (mStyles.textSize+mStyles.spacing) * (mCurrentSelection.size() + 1) -mStyles.spacing; float legendPosY = mPosY - legendHeight - 4.5f * mStyles.textSize; if (legendPosY < 0) { legendPosY = 0; } float lLeft; float lTop; lLeft = legendPosX; lTop = legendPosY; float lRight = lLeft+legendWidth; float lBottom = lTop+legendHeight+2*mStyles.padding; mRectPaint.setColor(mStyles.backgroundColor); canvas.drawRoundRect(new RectF(lLeft, lTop, lRight, lBottom), 8, 8, mRectPaint); mTextPaint.setFakeBoldText(true); canvas.drawText(mGraphView.getGridLabelRenderer().getLabelFormatter().formatLabel(mCurrentSelectionX, true), lLeft+mStyles.padding, lTop+mStyles.padding/2+mStyles.textSize, mTextPaint); mTextPaint.setFakeBoldText(false); int i=1; for (Map.Entry<BaseSeries, DataPointInterface> entry : mCurrentSelection.entrySet()) { mRectPaint.setColor(entry.getKey().getColor()); canvas.drawRect(new RectF(lLeft+mStyles.padding, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing)), lLeft+mStyles.padding+shapeSize, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing))+shapeSize), mRectPaint); canvas.drawText(getTextForSeries(entry.getKey(), entry.getValue()), lLeft+mStyles.padding+shapeSize+mStyles.spacing, lTop+mStyles.padding/2+mStyles.textSize+(i*(mStyles.textSize+mStyles.spacing)), mTextPaint); i++; } } public boolean onUp(MotionEvent event) { mCursorVisible = false; findCurrentDataPoint(); mGraphView.invalidate(); return true; } private void findCurrentDataPoint() { double selX = 0; mCurrentSelection.clear(); for (Series series : mGraphView.getSeries()) { if (series instanceof BaseSeries) { DataPointInterface p = ((BaseSeries) series).findDataPointAtX(mPosX); if (p != null) { selX = p.getX(); mCurrentSelection.put((BaseSeries) series, p); } } } if (!mCurrentSelection.isEmpty()) { mCurrentSelectionX = selX; } } public void setTextSize(float t) { mStyles.textSize = t; } public void setTextColor(int color) { mStyles.textColor = color; } public void setBackgroundColor(int color) { mStyles.backgroundColor = color; } public void setSpacing(int s) { mStyles.spacing = s; } public void setPadding(int s) { mStyles.padding = s; } public void setMargin(int s) { mStyles.margin = s; } public void setWidth(int s) { mStyles.width = s; } }