replace graphview module by aar
This commit is contained in:
parent
5f8abca2ca
commit
eb9646cc7e
37 changed files with 11 additions and 6364 deletions
|
@ -169,8 +169,11 @@ allprojects {
|
||||||
dependencies {
|
dependencies {
|
||||||
wearApp project(':wear')
|
wearApp project(':wear')
|
||||||
|
|
||||||
|
// https://github.com/nightscout/graphview.git
|
||||||
|
// in order to use internet's version you'd need to enable Jetifier again
|
||||||
|
implementation(files("${rootProject.rootDir}/libs/graphview.aar"))
|
||||||
|
|
||||||
implementation project(':iconify')
|
implementation project(':iconify')
|
||||||
implementation project(':graphview')
|
|
||||||
implementation project(':shared')
|
implementation project(':shared')
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
implementation project(':automation')
|
implementation project(':automation')
|
||||||
|
|
|
@ -12,10 +12,10 @@ android {
|
||||||
namespace 'info.nightscout.androidaps.automation'
|
namespace 'info.nightscout.androidaps.automation'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(files("${rootProject.rootDir}/libs/graphview.aar"))
|
||||||
|
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
implementation project(':database')
|
implementation project(':database')
|
||||||
implementation project(':shared')
|
implementation project(':shared')
|
||||||
implementation project(':graphview')
|
|
||||||
}
|
}
|
|
@ -12,9 +12,10 @@ apply from: "${project.rootDir}/core/test_dependencies.gradle"
|
||||||
apply from: "${project.rootDir}/core/jacoco_global.gradle"
|
apply from: "${project.rootDir}/core/jacoco_global.gradle"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(files("${rootProject.rootDir}/libs/graphview.aar"))
|
||||||
|
|
||||||
implementation project(':shared')
|
implementation project(':shared')
|
||||||
implementation project(':database')
|
implementation project(':database')
|
||||||
implementation project(':graphview')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
1
graphview/.gitignore
vendored
1
graphview/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,20 +0,0 @@
|
||||||
apply plugin: 'com.android.library'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply plugin: 'kotlin-allopen'
|
|
||||||
apply plugin: 'com.hiya.jacoco-android'
|
|
||||||
apply plugin: 'kotlinx-serialization'
|
|
||||||
|
|
||||||
apply from: "${project.rootDir}/core/android_dependencies.gradle"
|
|
||||||
apply from: "${project.rootDir}/core/android_module_dependencies.gradle"
|
|
||||||
apply from: "${project.rootDir}/core/test_dependencies.gradle"
|
|
||||||
apply from: "${project.rootDir}/core/jacoco_global.gradle"
|
|
||||||
|
|
||||||
android {
|
|
||||||
|
|
||||||
namespace 'com.jjoe64.graphview'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api "androidx.core:core-ktx:$core_version"
|
|
||||||
}
|
|
21
graphview/proguard-rules.pro
vendored
21
graphview/proguard-rules.pro
vendored
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
</manifest>
|
|
|
@ -1,105 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label formatter that will be used
|
|
||||||
* by default.
|
|
||||||
* It will use the NumberFormat from Android
|
|
||||||
* and sets the maximal fraction digits
|
|
||||||
* depending on the range between min and max
|
|
||||||
* value of the current viewport.
|
|
||||||
*
|
|
||||||
* It is recommended to use this label formatter
|
|
||||||
* as base class to implement a custom formatter.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class DefaultLabelFormatter implements LabelFormatter {
|
|
||||||
/**
|
|
||||||
* number formatter for x and y values
|
|
||||||
*/
|
|
||||||
protected NumberFormat[] mNumberFormatter = new NumberFormat[2];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reference to the viewport of the
|
|
||||||
* graph.
|
|
||||||
* Will be used to calculate the current
|
|
||||||
* range of values.
|
|
||||||
*/
|
|
||||||
protected Viewport mViewport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* uses the default number format for the labels
|
|
||||||
*/
|
|
||||||
public DefaultLabelFormatter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* use custom number format
|
|
||||||
*
|
|
||||||
* @param xFormat the number format for the x labels
|
|
||||||
* @param yFormat the number format for the y labels
|
|
||||||
*/
|
|
||||||
public DefaultLabelFormatter(NumberFormat xFormat, NumberFormat yFormat) {
|
|
||||||
mNumberFormatter[0] = yFormat;
|
|
||||||
mNumberFormatter[1] = xFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param viewport the viewport of the graph
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setViewport(Viewport viewport) {
|
|
||||||
mViewport = viewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the raw value to a nice
|
|
||||||
* looking label, depending on the
|
|
||||||
* current range of the viewport.
|
|
||||||
*
|
|
||||||
* @param value raw value
|
|
||||||
* @param isValueX true if it's a x value, otherwise false
|
|
||||||
* @return the formatted value as string
|
|
||||||
*/
|
|
||||||
public String formatLabel(double value, boolean isValueX) {
|
|
||||||
int i = isValueX ? 1 : 0;
|
|
||||||
if (mNumberFormatter[i] == null) {
|
|
||||||
mNumberFormatter[i] = NumberFormat.getNumberInstance();
|
|
||||||
double highestvalue = isValueX ? mViewport.getMaxX(false) : mViewport.getMaxY(false);
|
|
||||||
double lowestvalue = isValueX ? mViewport.getMinX(false) : mViewport.getMinY(false);
|
|
||||||
if (highestvalue - lowestvalue < 0.1) {
|
|
||||||
mNumberFormatter[i].setMaximumFractionDigits(6);
|
|
||||||
} else if (highestvalue - lowestvalue < 1) {
|
|
||||||
mNumberFormatter[i].setMaximumFractionDigits(4);
|
|
||||||
} else if (highestvalue - lowestvalue < 20) {
|
|
||||||
mNumberFormatter[i].setMaximumFractionDigits(3);
|
|
||||||
} else if (highestvalue - lowestvalue < 100) {
|
|
||||||
mNumberFormatter[i].setMaximumFractionDigits(1);
|
|
||||||
} else {
|
|
||||||
mNumberFormatter[i].setMaximumFractionDigits(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mNumberFormatter[i].format(value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,548 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.PointF;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.series.Series;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author jjoe64
|
|
||||||
* @version 4.0.0
|
|
||||||
*/
|
|
||||||
public class GraphView extends View {
|
|
||||||
/**
|
|
||||||
* Class to wrap style options that are general
|
|
||||||
* to graphs.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
private static final class Styles {
|
|
||||||
/**
|
|
||||||
* The font size of the title that can be displayed
|
|
||||||
* above the graph.
|
|
||||||
*
|
|
||||||
* @see GraphView#setTitle(String)
|
|
||||||
*/
|
|
||||||
float titleTextSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The font color of the title that can be displayed
|
|
||||||
* above the graph.
|
|
||||||
*
|
|
||||||
* @see GraphView#setTitle(String)
|
|
||||||
*/
|
|
||||||
int titleColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to detect tap events on the
|
|
||||||
* graph.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
private class TapDetector {
|
|
||||||
/**
|
|
||||||
* save the time of the last down event
|
|
||||||
*/
|
|
||||||
private long lastDown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* point of the tap down event
|
|
||||||
*/
|
|
||||||
private PointF lastPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* to be called to process the events
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
* @return true if there was a tap event. otherwise returns false.
|
|
||||||
*/
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
||||||
lastDown = System.currentTimeMillis();
|
|
||||||
lastPoint = new PointF(event.getX(), event.getY());
|
|
||||||
} else if (lastDown > 0 && event.getAction() == MotionEvent.ACTION_MOVE) {
|
|
||||||
if (Math.abs(event.getX() - lastPoint.x) > 60
|
|
||||||
|| Math.abs(event.getY() - lastPoint.y) > 60) {
|
|
||||||
lastDown = 0;
|
|
||||||
}
|
|
||||||
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
||||||
if (System.currentTimeMillis() - lastDown < 400) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* our series (this does not contain the series
|
|
||||||
* that can be displayed on the right side. The
|
|
||||||
* right side series is a special feature of
|
|
||||||
* the {@link SecondScale} feature.
|
|
||||||
*/
|
|
||||||
private List<Series> mSeries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the renderer for the grid and labels
|
|
||||||
*/
|
|
||||||
private GridLabelRenderer mGridLabelRenderer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* viewport that holds the current bounds of
|
|
||||||
* view.
|
|
||||||
*/
|
|
||||||
private Viewport mViewport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* title of the graph that will be shown above
|
|
||||||
*/
|
|
||||||
private String mTitle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wraps the general styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* feature to have a second scale e.g. on the
|
|
||||||
* right side
|
|
||||||
*/
|
|
||||||
protected SecondScale mSecondScale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tap detector
|
|
||||||
*/
|
|
||||||
private TapDetector mTapDetector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* renderer for the legend
|
|
||||||
*/
|
|
||||||
private LegendRenderer mLegendRenderer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* paint for the graph title
|
|
||||||
*/
|
|
||||||
private Paint mPaintTitle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* paint for the preview (in the SDK)
|
|
||||||
*/
|
|
||||||
private Paint mPreviewPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the GraphView view
|
|
||||||
* @param context
|
|
||||||
*/
|
|
||||||
public GraphView(Context context) {
|
|
||||||
super(context);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the GraphView view.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
*/
|
|
||||||
public GraphView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the GraphView view
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
* @param defStyle
|
|
||||||
*/
|
|
||||||
public GraphView(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* initialize the internal objects.
|
|
||||||
* This method has to be called directly
|
|
||||||
* in the constructors.
|
|
||||||
*/
|
|
||||||
protected void init() {
|
|
||||||
mPreviewPaint = new Paint();
|
|
||||||
mPreviewPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
mPreviewPaint.setColor(Color.BLACK);
|
|
||||||
mPreviewPaint.setTextSize(50);
|
|
||||||
|
|
||||||
mStyles = new Styles();
|
|
||||||
mViewport = new Viewport(this);
|
|
||||||
mGridLabelRenderer = new GridLabelRenderer(this);
|
|
||||||
mLegendRenderer = new LegendRenderer(this);
|
|
||||||
|
|
||||||
mSeries = new ArrayList<Series>();
|
|
||||||
mPaintTitle = new Paint();
|
|
||||||
|
|
||||||
mTapDetector = new TapDetector();
|
|
||||||
|
|
||||||
loadStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* loads the font
|
|
||||||
*/
|
|
||||||
protected void loadStyles() {
|
|
||||||
mStyles.titleColor = mGridLabelRenderer.getHorizontalLabelsColor();
|
|
||||||
mStyles.titleTextSize = mGridLabelRenderer.getTextSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the renderer for the grid and labels
|
|
||||||
*/
|
|
||||||
public GridLabelRenderer getGridLabelRenderer() {
|
|
||||||
return mGridLabelRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new series to the graph. This will
|
|
||||||
* automatically redraw the graph.
|
|
||||||
* @param s the series to be added
|
|
||||||
*/
|
|
||||||
public void addSeries(Series s) {
|
|
||||||
s.onGraphViewAttached(this);
|
|
||||||
mSeries.add(s);
|
|
||||||
onDataChanged(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* important: do not do modifications on the list
|
|
||||||
* object that will be returned.
|
|
||||||
* Use {@link #removeSeries(com.jjoe64.graphview.series.Series)} and {@link #addSeries(com.jjoe64.graphview.series.Series)}
|
|
||||||
*
|
|
||||||
* @return all series
|
|
||||||
*/
|
|
||||||
public List<Series> getSeries() {
|
|
||||||
// TODO immutable array
|
|
||||||
return mSeries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* call this to let the graph redraw and
|
|
||||||
* recalculate the viewport.
|
|
||||||
* This will be called when a new series
|
|
||||||
* was added or removed and when data
|
|
||||||
* was appended via {@link com.jjoe64.graphview.series.BaseSeries#appendData(com.jjoe64.graphview.series.DataPointInterface, boolean, int)}
|
|
||||||
* or {@link com.jjoe64.graphview.series.BaseSeries#resetData(com.jjoe64.graphview.series.DataPointInterface[])}.
|
|
||||||
*
|
|
||||||
* @param keepLabelsSize true if you don't want
|
|
||||||
* to recalculate the size of
|
|
||||||
* the labels. It is recommended
|
|
||||||
* to use "true" because this will
|
|
||||||
* improve performance and prevent
|
|
||||||
* a flickering.
|
|
||||||
* @param keepViewport true if you don't want that
|
|
||||||
* the viewport will be recalculated.
|
|
||||||
* It is recommended to use "true" for
|
|
||||||
* performance.
|
|
||||||
*/
|
|
||||||
public void onDataChanged(boolean keepLabelsSize, boolean keepViewport) {
|
|
||||||
// adjust grid system
|
|
||||||
mViewport.calcCompleteRange();
|
|
||||||
mGridLabelRenderer.invalidate(keepLabelsSize, keepViewport);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be called from Android system.
|
|
||||||
*
|
|
||||||
* @param canvas Canvas
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
if (isInEditMode()) {
|
|
||||||
canvas.drawColor(Color.rgb(200, 200, 200));
|
|
||||||
canvas.drawText("GraphView: No Preview available", canvas.getWidth()/2, canvas.getHeight()/2, mPreviewPaint);
|
|
||||||
} else {
|
|
||||||
drawTitle(canvas);
|
|
||||||
mViewport.drawFirst(canvas);
|
|
||||||
mGridLabelRenderer.draw(canvas);
|
|
||||||
for (Series s : mSeries) {
|
|
||||||
s.draw(this, canvas, false);
|
|
||||||
}
|
|
||||||
if (mSecondScale != null) {
|
|
||||||
for (Series s : mSecondScale.getSeries()) {
|
|
||||||
s.draw(this, canvas, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mViewport.draw(canvas);
|
|
||||||
mLegendRenderer.draw(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the Graphs title that will be
|
|
||||||
* shown above the viewport.
|
|
||||||
* Will be called by GraphView.
|
|
||||||
*
|
|
||||||
* @param canvas Canvas
|
|
||||||
*/
|
|
||||||
protected void drawTitle(Canvas canvas) {
|
|
||||||
if (mTitle != null && mTitle.length()>0) {
|
|
||||||
mPaintTitle.setColor(mStyles.titleColor);
|
|
||||||
mPaintTitle.setTextSize(mStyles.titleTextSize);
|
|
||||||
mPaintTitle.setTextAlign(Paint.Align.CENTER);
|
|
||||||
float x = canvas.getWidth()/2;
|
|
||||||
float y = mPaintTitle.getTextSize();
|
|
||||||
canvas.drawText(mTitle, x, y, mPaintTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the height of the title.
|
|
||||||
*
|
|
||||||
* @return the actual size of the title.
|
|
||||||
* if there is no title, 0 will be
|
|
||||||
* returned.
|
|
||||||
*/
|
|
||||||
protected int getTitleHeight() {
|
|
||||||
if (mTitle != null && mTitle.length()>0) {
|
|
||||||
return (int) mPaintTitle.getTextSize();
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the viewport of the Graph.
|
|
||||||
* @see com.jjoe64.graphview.Viewport
|
|
||||||
*/
|
|
||||||
public Viewport getViewport() {
|
|
||||||
return mViewport;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Android system if the size
|
|
||||||
* of the view was changed. Will recalculate
|
|
||||||
* the viewport and labels.
|
|
||||||
*
|
|
||||||
* @param w
|
|
||||||
* @param h
|
|
||||||
* @param oldw
|
|
||||||
* @param oldh
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
|
||||||
onDataChanged(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the space on the left side of the
|
|
||||||
* view from the left border to the
|
|
||||||
* beginning of the graph viewport.
|
|
||||||
*/
|
|
||||||
public int getGraphContentLeft() {
|
|
||||||
int border = getGridLabelRenderer().getStyles().padding;
|
|
||||||
return border + getGridLabelRenderer().getLabelVerticalWidth() + getGridLabelRenderer().getVerticalAxisTitleWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the space on the top of the
|
|
||||||
* view from the top border to the
|
|
||||||
* beginning of the graph viewport.
|
|
||||||
*/
|
|
||||||
public int getGraphContentTop() {
|
|
||||||
int border = getGridLabelRenderer().getStyles().padding + getTitleHeight();
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the height of the graph viewport.
|
|
||||||
*/
|
|
||||||
public int getGraphContentHeight() {
|
|
||||||
int border = getGridLabelRenderer().getStyles().padding;
|
|
||||||
int graphheight = getHeight() - (2 * border) - getGridLabelRenderer().getLabelHorizontalHeight() - getTitleHeight();
|
|
||||||
graphheight -= getGridLabelRenderer().getHorizontalAxisTitleHeight();
|
|
||||||
return graphheight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the width of the graph viewport.
|
|
||||||
*/
|
|
||||||
public int getGraphContentWidth() {
|
|
||||||
int border = getGridLabelRenderer().getStyles().padding;
|
|
||||||
int graphwidth = getWidth() - (2 * border) - getGridLabelRenderer().getLabelVerticalWidth();
|
|
||||||
if (mSecondScale != null) {
|
|
||||||
graphwidth -= getGridLabelRenderer().getLabelVerticalSecondScaleWidth();
|
|
||||||
}
|
|
||||||
return graphwidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be called from Android system.
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
boolean b = mViewport.onTouchEvent(event);
|
|
||||||
boolean a = super.onTouchEvent(event);
|
|
||||||
|
|
||||||
// is it a click?
|
|
||||||
if (mTapDetector.onTouchEvent(event)) {
|
|
||||||
for (Series s : mSeries) {
|
|
||||||
s.onTap(event.getX(), event.getY());
|
|
||||||
}
|
|
||||||
if (mSecondScale != null) {
|
|
||||||
for (Series s : mSecondScale.getSeries()) {
|
|
||||||
s.onTap(event.getX(), event.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b || a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void computeScroll() {
|
|
||||||
super.computeScroll();
|
|
||||||
mViewport.computeScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the legend renderer.
|
|
||||||
* @see com.jjoe64.graphview.LegendRenderer
|
|
||||||
*/
|
|
||||||
public LegendRenderer getLegendRenderer() {
|
|
||||||
return mLegendRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* use a specific legend renderer
|
|
||||||
*
|
|
||||||
* @param mLegendRenderer the new legend renderer
|
|
||||||
*/
|
|
||||||
public void setLegendRenderer(LegendRenderer mLegendRenderer) {
|
|
||||||
this.mLegendRenderer = mLegendRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the title that will be shown
|
|
||||||
* above the graph.
|
|
||||||
*/
|
|
||||||
public String getTitle() {
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the title of the graph that will
|
|
||||||
* be shown above the graph's viewport.
|
|
||||||
*
|
|
||||||
* @param mTitle the title
|
|
||||||
* @see #setTitleColor(int) to set the font color
|
|
||||||
* @see #setTitleTextSize(float) to set the font size
|
|
||||||
*/
|
|
||||||
public void setTitle(String mTitle) {
|
|
||||||
this.mTitle = mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the title font size
|
|
||||||
*/
|
|
||||||
public float getTitleTextSize() {
|
|
||||||
return mStyles.titleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the title's font size
|
|
||||||
*
|
|
||||||
* @param titleTextSize font size
|
|
||||||
* @see #setTitle(String)
|
|
||||||
*/
|
|
||||||
public void setTitleTextSize(float titleTextSize) {
|
|
||||||
mStyles.titleTextSize = titleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return font color of the title
|
|
||||||
*/
|
|
||||||
public int getTitleColor() {
|
|
||||||
return mStyles.titleColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the title's font color
|
|
||||||
*
|
|
||||||
* @param titleColor font color of the title
|
|
||||||
* @see #setTitle(String)
|
|
||||||
*/
|
|
||||||
public void setTitleColor(int titleColor) {
|
|
||||||
mStyles.titleColor = titleColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public SecondScale getSecondScale() {
|
|
||||||
if (mSecondScale == null) {
|
|
||||||
mSecondScale = new SecondScale(mViewport);
|
|
||||||
}
|
|
||||||
return mSecondScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all series of the graph.
|
|
||||||
*/
|
|
||||||
public void removeAllSeries() {
|
|
||||||
mSeries.clear();
|
|
||||||
onDataChanged(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a specific series of the graph.
|
|
||||||
* This will also re-render the graph, but
|
|
||||||
* without recalculating the viewport and
|
|
||||||
* label sizes.
|
|
||||||
* If you want this, you have to call {@link #onDataChanged(boolean, boolean)}
|
|
||||||
* manually.
|
|
||||||
*
|
|
||||||
* @param series
|
|
||||||
*/
|
|
||||||
public void removeSeries(Series<?> series) {
|
|
||||||
mSeries.remove(series);
|
|
||||||
onDataChanged(false, false);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,54 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface to use as label formatter.
|
|
||||||
* Implement this in order to generate
|
|
||||||
* your own labels format.
|
|
||||||
* It is recommended to override {@link com.jjoe64.graphview.DefaultLabelFormatter}.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public interface LabelFormatter {
|
|
||||||
/**
|
|
||||||
* converts a raw number as input to
|
|
||||||
* a formatted string for the label.
|
|
||||||
*
|
|
||||||
* @param value raw input number
|
|
||||||
* @param isValueX true if it is a value for the x axis
|
|
||||||
* false if it is a value for the y axis
|
|
||||||
* @return the formatted number as string
|
|
||||||
*/
|
|
||||||
public String formatLabel(double value, boolean isValueX);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be called in order to have a
|
|
||||||
* reference to the current viewport.
|
|
||||||
* This is useful if you need the bounds
|
|
||||||
* to generate your labels.
|
|
||||||
* You store this viewport in as member variable
|
|
||||||
* and access it e.g. in the {@link #formatLabel(double, boolean)}
|
|
||||||
* method.
|
|
||||||
*
|
|
||||||
* @param viewport the used viewport
|
|
||||||
*/
|
|
||||||
public void setViewport(Viewport viewport);
|
|
||||||
}
|
|
|
@ -1,394 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.series.Series;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default renderer for the legend box
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class LegendRenderer {
|
|
||||||
/**
|
|
||||||
* wrapped styles regarding to the
|
|
||||||
* legend
|
|
||||||
*/
|
|
||||||
private final class Styles {
|
|
||||||
float textSize;
|
|
||||||
int spacing;
|
|
||||||
int padding;
|
|
||||||
int width;
|
|
||||||
int backgroundColor;
|
|
||||||
int textColor;
|
|
||||||
int margin;
|
|
||||||
LegendAlign align;
|
|
||||||
Point fixedPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* alignment of the legend
|
|
||||||
*/
|
|
||||||
public enum LegendAlign {
|
|
||||||
/**
|
|
||||||
* top right corner
|
|
||||||
*/
|
|
||||||
TOP,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* middle right
|
|
||||||
*/
|
|
||||||
MIDDLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bottom right corner
|
|
||||||
*/
|
|
||||||
BOTTOM
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reference to the graphview
|
|
||||||
*/
|
|
||||||
private final GraphView mGraphView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether legend will be
|
|
||||||
* drawn
|
|
||||||
*/
|
|
||||||
private boolean mIsVisible;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* paint for the drawing
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cached legend width
|
|
||||||
* this will be filled in the drawing.
|
|
||||||
* Can be cleared via {@link #resetStyles()}
|
|
||||||
*/
|
|
||||||
private int cachedLegendWidth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates legend renderer
|
|
||||||
*
|
|
||||||
* @param graphView regarding graphview
|
|
||||||
*/
|
|
||||||
public LegendRenderer(GraphView graphView) {
|
|
||||||
mGraphView = graphView;
|
|
||||||
mIsVisible = false;
|
|
||||||
mPaint = new Paint();
|
|
||||||
mPaint.setTextAlign(Paint.Align.LEFT);
|
|
||||||
mStyles = new Styles();
|
|
||||||
cachedLegendWidth = 0;
|
|
||||||
resetStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resets the styles to the defaults
|
|
||||||
* and clears the legend width cache
|
|
||||||
*/
|
|
||||||
public void resetStyles() {
|
|
||||||
mStyles.align = LegendAlign.MIDDLE;
|
|
||||||
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 / 5);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws the legend if it is visible
|
|
||||||
*
|
|
||||||
* @param canvas canvas
|
|
||||||
* @see #setVisible(boolean)
|
|
||||||
*/
|
|
||||||
public void draw(Canvas canvas) {
|
|
||||||
if (!mIsVisible) return;
|
|
||||||
|
|
||||||
mPaint.setTextSize(mStyles.textSize);
|
|
||||||
|
|
||||||
int shapeSize = (int) (mStyles.textSize*0.8d);
|
|
||||||
|
|
||||||
List<Series> allSeries = new ArrayList<Series>();
|
|
||||||
allSeries.addAll(mGraphView.getSeries());
|
|
||||||
if (mGraphView.mSecondScale != null) {
|
|
||||||
allSeries.addAll(mGraphView.getSecondScale().getSeries());
|
|
||||||
}
|
|
||||||
|
|
||||||
// width
|
|
||||||
int legendWidth = mStyles.width;
|
|
||||||
if (legendWidth == 0) {
|
|
||||||
// auto
|
|
||||||
legendWidth = cachedLegendWidth;
|
|
||||||
|
|
||||||
if (legendWidth == 0) {
|
|
||||||
Rect textBounds = new Rect();
|
|
||||||
for (Series s : allSeries) {
|
|
||||||
if (s.getTitle() != null) {
|
|
||||||
mPaint.getTextBounds(s.getTitle(), 0, s.getTitle().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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rect
|
|
||||||
float legendHeight = (mStyles.textSize+mStyles.spacing)*allSeries.size() -mStyles.spacing;
|
|
||||||
float lLeft;
|
|
||||||
float lTop;
|
|
||||||
if (mStyles.fixedPosition != null) {
|
|
||||||
// use fied position
|
|
||||||
lLeft = mGraphView.getGraphContentLeft() + mStyles.margin + mStyles.fixedPosition.x;
|
|
||||||
lTop = mGraphView.getGraphContentTop() + mStyles.margin + mStyles.fixedPosition.y;
|
|
||||||
} else {
|
|
||||||
lLeft = mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth() - legendWidth - mStyles.margin;
|
|
||||||
switch (mStyles.align) {
|
|
||||||
case TOP:
|
|
||||||
lTop = mGraphView.getGraphContentTop() + mStyles.margin;
|
|
||||||
break;
|
|
||||||
case MIDDLE:
|
|
||||||
lTop = mGraphView.getHeight() / 2 - legendHeight / 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
lTop = mGraphView.getGraphContentTop() + mGraphView.getGraphContentHeight() - mStyles.margin - legendHeight - 2*mStyles.padding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
float lRight = lLeft+legendWidth;
|
|
||||||
float lBottom = lTop+legendHeight+2*mStyles.padding;
|
|
||||||
mPaint.setColor(mStyles.backgroundColor);
|
|
||||||
canvas.drawRoundRect(new RectF(lLeft, lTop, lRight, lBottom), 8, 8, mPaint);
|
|
||||||
|
|
||||||
int i=0;
|
|
||||||
for (Series series : allSeries) {
|
|
||||||
mPaint.setColor(series.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), mPaint);
|
|
||||||
if (series.getTitle() != null) {
|
|
||||||
mPaint.setColor(mStyles.textColor);
|
|
||||||
canvas.drawText(series.getTitle(), lLeft+mStyles.padding+shapeSize+mStyles.spacing, lTop+mStyles.padding+mStyles.textSize+(i*(mStyles.textSize+mStyles.spacing)), mPaint);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the flag whether the legend will be drawn
|
|
||||||
*/
|
|
||||||
public boolean isVisible() {
|
|
||||||
return mIsVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the flag whether the legend will be drawn
|
|
||||||
*
|
|
||||||
* @param mIsVisible visible flag
|
|
||||||
*/
|
|
||||||
public void setVisible(boolean mIsVisible) {
|
|
||||||
this.mIsVisible = mIsVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return font size
|
|
||||||
*/
|
|
||||||
public float getTextSize() {
|
|
||||||
return mStyles.textSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets the font size. this will clear
|
|
||||||
* the internal legend width cache
|
|
||||||
*
|
|
||||||
* @param textSize font size
|
|
||||||
*/
|
|
||||||
public void setTextSize(float textSize) {
|
|
||||||
mStyles.textSize = textSize;
|
|
||||||
cachedLegendWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the spacing between the text lines
|
|
||||||
*/
|
|
||||||
public int getSpacing() {
|
|
||||||
return mStyles.spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the spacing between the text lines
|
|
||||||
*
|
|
||||||
* @param spacing the spacing between the text lines
|
|
||||||
*/
|
|
||||||
public void setSpacing(int spacing) {
|
|
||||||
mStyles.spacing = spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* padding is the space between the edge of the box
|
|
||||||
* and the beginning of the text
|
|
||||||
*
|
|
||||||
* @return padding from edge to text
|
|
||||||
*/
|
|
||||||
public int getPadding() {
|
|
||||||
return mStyles.padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* padding is the space between the edge of the box
|
|
||||||
* and the beginning of the text
|
|
||||||
*
|
|
||||||
* @param padding padding from edge to text
|
|
||||||
*/
|
|
||||||
public void setPadding(int padding) {
|
|
||||||
mStyles.padding = padding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the width of the box exclusive padding
|
|
||||||
*
|
|
||||||
* @return the width of the box
|
|
||||||
* 0 => auto
|
|
||||||
*/
|
|
||||||
public int getWidth() {
|
|
||||||
return mStyles.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the width of the box exclusive padding
|
|
||||||
* @param width the width of the box exclusive padding
|
|
||||||
* 0 => auto
|
|
||||||
*/
|
|
||||||
public void setWidth(int width) {
|
|
||||||
mStyles.width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return background color of the box
|
|
||||||
* it is recommended to use semi-transparent
|
|
||||||
* color.
|
|
||||||
*/
|
|
||||||
public int getBackgroundColor() {
|
|
||||||
return mStyles.backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param backgroundColor background color of the box
|
|
||||||
* it is recommended to use semi-transparent
|
|
||||||
* color.
|
|
||||||
*/
|
|
||||||
public void setBackgroundColor(int backgroundColor) {
|
|
||||||
mStyles.backgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return margin from the edge of the box
|
|
||||||
* to the corner of the graphview
|
|
||||||
*/
|
|
||||||
public int getMargin() {
|
|
||||||
return mStyles.margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param margin margin from the edge of the box
|
|
||||||
* to the corner of the graphview
|
|
||||||
*/
|
|
||||||
public void setMargin(int margin) {
|
|
||||||
mStyles.margin = margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the vertical alignment of the box
|
|
||||||
*/
|
|
||||||
public LegendAlign getAlign() {
|
|
||||||
return mStyles.align;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param align the vertical alignment of the box
|
|
||||||
*/
|
|
||||||
public void setAlign(LegendAlign align) {
|
|
||||||
mStyles.align = align;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return font color
|
|
||||||
*/
|
|
||||||
public int getTextColor() {
|
|
||||||
return mStyles.textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param textColor font color
|
|
||||||
*/
|
|
||||||
public void setTextColor(int textColor) {
|
|
||||||
mStyles.textColor = textColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use fixed coordinates to position the legend.
|
|
||||||
* This will override the align setting.
|
|
||||||
*
|
|
||||||
* @param x x coordinates in pixel
|
|
||||||
* @param y y coordinates in pixel
|
|
||||||
*/
|
|
||||||
public void setFixedPosition(int x, int y) {
|
|
||||||
mStyles.fixedPosition = new Point(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.series.Series;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To be used to plot a second scale
|
|
||||||
* on the graph.
|
|
||||||
* The second scale has always to have
|
|
||||||
* manual bounds.
|
|
||||||
* Use {@link #setMinY(double)} and {@link #setMaxY(double)}
|
|
||||||
* to set them.
|
|
||||||
* The second scale has it's own array of series.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class SecondScale {
|
|
||||||
/**
|
|
||||||
* reference to the viewport of the graph
|
|
||||||
*/
|
|
||||||
protected final Viewport mViewport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* array of series for the second
|
|
||||||
* scale
|
|
||||||
*/
|
|
||||||
protected List<Series> mSeries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the y axis bounds
|
|
||||||
* are manual.
|
|
||||||
* For the current version this is always
|
|
||||||
* true.
|
|
||||||
*/
|
|
||||||
private boolean mYAxisBoundsManual = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* min y value for the y axis bounds
|
|
||||||
*/
|
|
||||||
private double mMinY;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* max y value for the y axis bounds
|
|
||||||
*/
|
|
||||||
private double mMaxY;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* label formatter for the y labels
|
|
||||||
* on the right side
|
|
||||||
*/
|
|
||||||
protected LabelFormatter mLabelFormatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the second scale.
|
|
||||||
* normally you do not call this contructor.
|
|
||||||
* Use {@link com.jjoe64.graphview.GraphView#getSecondScale()}
|
|
||||||
* in order to get the instance.
|
|
||||||
*/
|
|
||||||
SecondScale(Viewport viewport) {
|
|
||||||
mViewport = viewport;
|
|
||||||
mSeries = new ArrayList<Series>();
|
|
||||||
mLabelFormatter = new DefaultLabelFormatter();
|
|
||||||
mLabelFormatter.setViewport(mViewport);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* add a series to the second scale.
|
|
||||||
* Don't add this series also to the GraphView
|
|
||||||
* object.
|
|
||||||
*
|
|
||||||
* @param s the series
|
|
||||||
*/
|
|
||||||
public void addSeries(Series s) {
|
|
||||||
mSeries.add(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
//public void setYAxisBoundsManual(boolean mYAxisBoundsManual) {
|
|
||||||
// this.mYAxisBoundsManual = mYAxisBoundsManual;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the min y bounds
|
|
||||||
*
|
|
||||||
* @param d min y value
|
|
||||||
*/
|
|
||||||
public void setMinY(double d) {
|
|
||||||
mMinY = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the max y bounds
|
|
||||||
*
|
|
||||||
* @param d max y value
|
|
||||||
*/
|
|
||||||
public void setMaxY(double d) {
|
|
||||||
mMaxY = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the series of the second scale
|
|
||||||
*/
|
|
||||||
public List<Series> getSeries() {
|
|
||||||
return mSeries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return min y bound
|
|
||||||
*/
|
|
||||||
public double getMinY() {
|
|
||||||
return mMinY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return max y bound
|
|
||||||
*/
|
|
||||||
public double getMaxY() {
|
|
||||||
return mMaxY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return always true for the current implementation
|
|
||||||
*/
|
|
||||||
public boolean isYAxisBoundsManual() {
|
|
||||||
return mYAxisBoundsManual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return label formatter for the y labels on the right side
|
|
||||||
*/
|
|
||||||
public LabelFormatter getLabelFormatter() {
|
|
||||||
return mLabelFormatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a custom label formatter that is used
|
|
||||||
* for the y labels on the right side.
|
|
||||||
*
|
|
||||||
* @param formatter label formatter for the y labels
|
|
||||||
*/
|
|
||||||
public void setLabelFormatter(LabelFormatter formatter) {
|
|
||||||
mLabelFormatter = formatter;
|
|
||||||
mLabelFormatter.setViewport(mViewport);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.series.DataPointInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* you can change the color depending on the value.
|
|
||||||
* takes only effect for BarGraphSeries.
|
|
||||||
*
|
|
||||||
* @see com.jjoe64.graphview.series.BarGraphSeries#setValueDependentColor(ValueDependentColor)
|
|
||||||
*/
|
|
||||||
public interface ValueDependentColor<T extends DataPointInterface> {
|
|
||||||
/**
|
|
||||||
* this is called when a bar is about to draw
|
|
||||||
* and the color is be loaded.
|
|
||||||
*
|
|
||||||
* @param data the current input value
|
|
||||||
* @return the color that the bar should be drawn with
|
|
||||||
* Generate the int via the android.graphics.Color class.
|
|
||||||
*/
|
|
||||||
public int get(T data);
|
|
||||||
}
|
|
|
@ -1,996 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.GestureDetector;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.ScaleGestureDetector;
|
|
||||||
import android.widget.OverScroller;
|
|
||||||
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.core.widget.EdgeEffectCompat;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.compat.OverScrollerCompat;
|
|
||||||
import com.jjoe64.graphview.series.DataPointInterface;
|
|
||||||
import com.jjoe64.graphview.series.Series;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the default implementation for the viewport.
|
|
||||||
* This implementation so for a normal viewport
|
|
||||||
* where there is a horizontal x-axis and a
|
|
||||||
* vertical y-axis.
|
|
||||||
* This viewport is compatible with
|
|
||||||
* - {@link com.jjoe64.graphview.series.BarGraphSeries}
|
|
||||||
* - {@link com.jjoe64.graphview.series.LineGraphSeries}
|
|
||||||
* - {@link com.jjoe64.graphview.series.PointsGraphSeries}
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class Viewport {
|
|
||||||
/**
|
|
||||||
* listener for the scale gesture
|
|
||||||
*/
|
|
||||||
private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener
|
|
||||||
= new ScaleGestureDetector.OnScaleGestureListener() {
|
|
||||||
/**
|
|
||||||
* called by android
|
|
||||||
* @param detector detector
|
|
||||||
* @return always true
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onScale(ScaleGestureDetector detector) {
|
|
||||||
float viewportWidth = mCurrentViewport.width();
|
|
||||||
float center = mCurrentViewport.left + viewportWidth / 2;
|
|
||||||
viewportWidth /= detector.getScaleFactor();
|
|
||||||
mCurrentViewport.left = center - viewportWidth / 2;
|
|
||||||
mCurrentViewport.right = mCurrentViewport.left+viewportWidth;
|
|
||||||
|
|
||||||
// viewportStart must not be < minX
|
|
||||||
float minX = (float) getMinX(true);
|
|
||||||
if (mCurrentViewport.left < minX) {
|
|
||||||
mCurrentViewport.left = minX;
|
|
||||||
mCurrentViewport.right = mCurrentViewport.left+viewportWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// viewportStart + viewportSize must not be > maxX
|
|
||||||
float maxX = (float) getMaxX(true);
|
|
||||||
if (viewportWidth == 0) {
|
|
||||||
mCurrentViewport.right = maxX;
|
|
||||||
}
|
|
||||||
double overlap = mCurrentViewport.left + viewportWidth - maxX;
|
|
||||||
if (overlap > 0) {
|
|
||||||
// scroll left
|
|
||||||
if (mCurrentViewport.left-overlap > minX) {
|
|
||||||
mCurrentViewport.left -= overlap;
|
|
||||||
mCurrentViewport.right = mCurrentViewport.left+viewportWidth;
|
|
||||||
} else {
|
|
||||||
// maximal scale
|
|
||||||
mCurrentViewport.left = minX;
|
|
||||||
mCurrentViewport.right = maxX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adjust viewport, labels, etc.
|
|
||||||
mGraphView.onDataChanged(true, false);
|
|
||||||
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called when scaling begins
|
|
||||||
*
|
|
||||||
* @param detector detector
|
|
||||||
* @return true if it is scalable
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
|
||||||
if (mIsScalable) {
|
|
||||||
mScalingBeginWidth = mCurrentViewport.width();
|
|
||||||
mScalingBeginLeft = mCurrentViewport.left;
|
|
||||||
mScalingActive = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called when sacling ends
|
|
||||||
* This will re-adjust the viewport.
|
|
||||||
*
|
|
||||||
* @param detector detector
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
|
||||||
mScalingActive = false;
|
|
||||||
|
|
||||||
// re-adjust
|
|
||||||
mXAxisBoundsStatus = AxisBoundsStatus.READJUST_AFTER_SCALE;
|
|
||||||
|
|
||||||
mScrollingReferenceX = Float.NaN;
|
|
||||||
|
|
||||||
// adjust viewport, labels, etc.
|
|
||||||
mGraphView.onDataChanged(true, false);
|
|
||||||
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* simple gesture listener to track scroll events
|
|
||||||
*/
|
|
||||||
private final GestureDetector.SimpleOnGestureListener mGestureListener
|
|
||||||
= new GestureDetector.SimpleOnGestureListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onDown(MotionEvent e) {
|
|
||||||
if (!mIsScrollable || mScalingActive) return false;
|
|
||||||
|
|
||||||
// Initiates the decay phase of any active edge effects.
|
|
||||||
releaseEdgeEffects();
|
|
||||||
mScrollerStartViewport.set(mCurrentViewport);
|
|
||||||
// Aborts any active scroll animations and invalidates.
|
|
||||||
mScroller.forceFinished(true);
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
|
||||||
if (!mIsScrollable || mScalingActive) return false;
|
|
||||||
|
|
||||||
if (Float.isNaN(mScrollingReferenceX)) {
|
|
||||||
mScrollingReferenceX = mCurrentViewport.left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scrolling uses math based on the viewport (as opposed to math using pixels).
|
|
||||||
/**
|
|
||||||
* Pixel offset is the offset in screen pixels, while viewport offset is the
|
|
||||||
* offset within the current viewport. For additional information on surface sizes
|
|
||||||
* and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For
|
|
||||||
* additional information about the viewport, see the comments for
|
|
||||||
* {@link mCurrentViewport}.
|
|
||||||
*/
|
|
||||||
float viewportOffsetX = distanceX * mCurrentViewport.width() / mGraphView.getGraphContentWidth();
|
|
||||||
float viewportOffsetY = -distanceY * mCurrentViewport.height() / mGraphView.getGraphContentHeight();
|
|
||||||
|
|
||||||
int completeWidth = (int)((mCompleteRange.width()/mCurrentViewport.width()) * (float) mGraphView.getGraphContentWidth());
|
|
||||||
int completeHeight = (int)((mCompleteRange.height()/mCurrentViewport.height()) * (float) mGraphView.getGraphContentHeight());
|
|
||||||
|
|
||||||
int scrolledX = (int) (completeWidth
|
|
||||||
* (mCurrentViewport.left + viewportOffsetX - mCompleteRange.left)
|
|
||||||
/ mCompleteRange.width());
|
|
||||||
int scrolledY = (int) (completeHeight
|
|
||||||
* (mCompleteRange.bottom - mCurrentViewport.bottom - viewportOffsetY)
|
|
||||||
/ mCompleteRange.height());
|
|
||||||
boolean canScrollX = mCurrentViewport.left > mCompleteRange.left
|
|
||||||
|| mCurrentViewport.right < mCompleteRange.right;
|
|
||||||
boolean canScrollY = mCurrentViewport.bottom > mCompleteRange.bottom
|
|
||||||
|| mCurrentViewport.top < mCompleteRange.top;
|
|
||||||
|
|
||||||
if (canScrollX) {
|
|
||||||
if (viewportOffsetX < 0) {
|
|
||||||
float tooMuch = mCurrentViewport.left+viewportOffsetX - mCompleteRange.left;
|
|
||||||
if (tooMuch < 0) {
|
|
||||||
viewportOffsetX -= tooMuch;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
float tooMuch = mCurrentViewport.right+viewportOffsetX - mCompleteRange.right;
|
|
||||||
if (tooMuch > 0) {
|
|
||||||
viewportOffsetX -= tooMuch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCurrentViewport.left += viewportOffsetX;
|
|
||||||
mCurrentViewport.right += viewportOffsetX;
|
|
||||||
}
|
|
||||||
if (canScrollY) {
|
|
||||||
//mCurrentViewport.top += viewportOffsetX;
|
|
||||||
//mCurrentViewport.bottom -= viewportOffsetX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canScrollX && scrolledX < 0) {
|
|
||||||
mEdgeEffectLeft.onPull(scrolledX / (float) mGraphView.getGraphContentWidth());
|
|
||||||
mEdgeEffectLeftActive = true;
|
|
||||||
}
|
|
||||||
if (canScrollY && scrolledY < 0) {
|
|
||||||
mEdgeEffectBottom.onPull(scrolledY / (float) mGraphView.getGraphContentHeight());
|
|
||||||
mEdgeEffectBottomActive = true;
|
|
||||||
}
|
|
||||||
if (canScrollX && scrolledX > completeWidth - mGraphView.getGraphContentWidth()) {
|
|
||||||
mEdgeEffectRight.onPull((scrolledX - completeWidth + mGraphView.getGraphContentWidth())
|
|
||||||
/ (float) mGraphView.getGraphContentWidth());
|
|
||||||
mEdgeEffectRightActive = true;
|
|
||||||
}
|
|
||||||
//if (canScrollY && scrolledY > mSurfaceSizeBuffer.y - mContentRect.height()) {
|
|
||||||
// mEdgeEffectTop.onPull((scrolledY - mSurfaceSizeBuffer.y + mContentRect.height())
|
|
||||||
// / (float) mContentRect.height());
|
|
||||||
// mEdgeEffectTopActive = true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// adjust viewport, labels, etc.
|
|
||||||
mGraphView.onDataChanged(true, false);
|
|
||||||
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onFling(MotionEvent e1, MotionEvent e2,
|
|
||||||
float velocityX, float velocityY) {
|
|
||||||
//fling((int) -velocityX, (int) -velocityY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the state of the axis bounds
|
|
||||||
*/
|
|
||||||
public enum AxisBoundsStatus {
|
|
||||||
/**
|
|
||||||
* initial means that the bounds gets
|
|
||||||
* auto adjusted if they are not manual.
|
|
||||||
* After adjusting the status comes to
|
|
||||||
* #AUTO_ADJUSTED.
|
|
||||||
*/
|
|
||||||
INITIAL,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* after the bounds got auto-adjusted,
|
|
||||||
* this status will set.
|
|
||||||
*/
|
|
||||||
AUTO_ADJUSTED,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this flags the status that a scale was
|
|
||||||
* done and the bounds has to be auto-adjusted
|
|
||||||
* afterwards.
|
|
||||||
*/
|
|
||||||
READJUST_AFTER_SCALE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* means that the bounds are fix (manually) and
|
|
||||||
* are not to be auto-adjusted.
|
|
||||||
*/
|
|
||||||
FIX
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* paint to draw background
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reference to the graphview
|
|
||||||
*/
|
|
||||||
private final GraphView mGraphView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this holds the current visible viewport
|
|
||||||
* left = minX, right = maxX
|
|
||||||
* bottom = minY, top = maxY
|
|
||||||
*/
|
|
||||||
protected RectF mCurrentViewport = new RectF();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this holds the whole range of the data
|
|
||||||
* left = minX, right = maxX
|
|
||||||
* bottom = minY, top = maxY
|
|
||||||
*/
|
|
||||||
protected RectF mCompleteRange = new RectF();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether scaling is currently active
|
|
||||||
*/
|
|
||||||
protected boolean mScalingActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the width of the viewport at the time
|
|
||||||
* of beginning of the scaling.
|
|
||||||
*/
|
|
||||||
protected float mScalingBeginWidth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the viewport left at the time of
|
|
||||||
* beginning of the scaling.
|
|
||||||
*/
|
|
||||||
protected float mScalingBeginLeft;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the viewport is scrollable
|
|
||||||
*/
|
|
||||||
private boolean mIsScrollable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the viewport is scalable
|
|
||||||
*/
|
|
||||||
private boolean mIsScalable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* gesture detector to detect scrolling
|
|
||||||
*/
|
|
||||||
protected GestureDetector mGestureDetector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* detect scaling
|
|
||||||
*/
|
|
||||||
protected ScaleGestureDetector mScaleGestureDetector;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used - for fling
|
|
||||||
*/
|
|
||||||
protected OverScroller mScroller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used
|
|
||||||
*/
|
|
||||||
private EdgeEffectCompat mEdgeEffectTop;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used
|
|
||||||
*/
|
|
||||||
private EdgeEffectCompat mEdgeEffectBottom;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* glow effect when scrolling left
|
|
||||||
*/
|
|
||||||
private EdgeEffectCompat mEdgeEffectLeft;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* glow effect when scrolling right
|
|
||||||
*/
|
|
||||||
private EdgeEffectCompat mEdgeEffectRight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used
|
|
||||||
*/
|
|
||||||
private boolean mEdgeEffectTopActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used
|
|
||||||
*/
|
|
||||||
private boolean mEdgeEffectBottomActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* glow effect when scrolling left
|
|
||||||
*/
|
|
||||||
private boolean mEdgeEffectLeftActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* glow effect when scrolling right
|
|
||||||
*/
|
|
||||||
private boolean mEdgeEffectRightActive;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the viewport at the time of
|
|
||||||
* the beginning of scaling
|
|
||||||
*/
|
|
||||||
private RectF mScrollerStartViewport = new RectF();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the viewport left value at the
|
|
||||||
* time of beginning of the scrolling
|
|
||||||
*/
|
|
||||||
protected float mScrollingReferenceX = Float.NaN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* state of the x axis
|
|
||||||
*/
|
|
||||||
private AxisBoundsStatus mXAxisBoundsStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* state of the y axis
|
|
||||||
*/
|
|
||||||
private AxisBoundsStatus mYAxisBoundsStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the x axis bounds are manual
|
|
||||||
*/
|
|
||||||
private boolean mXAxisBoundsManual;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the y axis bounds are manual
|
|
||||||
*/
|
|
||||||
private boolean mYAxisBoundsManual;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* background color of the viewport area
|
|
||||||
* it is recommended to use a semi-transparent color
|
|
||||||
*/
|
|
||||||
private int mBackgroundColor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the viewport
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
*/
|
|
||||||
Viewport(GraphView graphView) {
|
|
||||||
mScroller = new OverScroller(graphView.getContext());
|
|
||||||
mEdgeEffectTop = new EdgeEffectCompat(graphView.getContext());
|
|
||||||
mEdgeEffectBottom = new EdgeEffectCompat(graphView.getContext());
|
|
||||||
mEdgeEffectLeft = new EdgeEffectCompat(graphView.getContext());
|
|
||||||
mEdgeEffectRight = new EdgeEffectCompat(graphView.getContext());
|
|
||||||
mGestureDetector = new GestureDetector(graphView.getContext(), mGestureListener);
|
|
||||||
mScaleGestureDetector = new ScaleGestureDetector(graphView.getContext(), mScaleGestureListener);
|
|
||||||
|
|
||||||
mGraphView = graphView;
|
|
||||||
mXAxisBoundsStatus = AxisBoundsStatus.INITIAL;
|
|
||||||
mYAxisBoundsStatus = AxisBoundsStatus.INITIAL;
|
|
||||||
mBackgroundColor = Color.TRANSPARENT;
|
|
||||||
mPaint = new Paint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be called on a touch event.
|
|
||||||
* needed to use scaling and scrolling
|
|
||||||
*
|
|
||||||
* @param event
|
|
||||||
* @return true if it was consumed
|
|
||||||
*/
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
boolean b = mScaleGestureDetector.onTouchEvent(event);
|
|
||||||
b |= mGestureDetector.onTouchEvent(event);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change the state of the x axis.
|
|
||||||
* normally you do not call this method.
|
|
||||||
* If you want to set manual axis use
|
|
||||||
* {@link #setXAxisBoundsManual(boolean)} and {@link #setYAxisBoundsManual(boolean)}
|
|
||||||
*
|
|
||||||
* @param s state
|
|
||||||
*/
|
|
||||||
public void setXAxisBoundsStatus(AxisBoundsStatus s) {
|
|
||||||
mXAxisBoundsStatus = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* change the state of the y axis.
|
|
||||||
* normally you do not call this method.
|
|
||||||
* If you want to set manual axis use
|
|
||||||
* {@link #setXAxisBoundsManual(boolean)} and {@link #setYAxisBoundsManual(boolean)}
|
|
||||||
*
|
|
||||||
* @param s state
|
|
||||||
*/
|
|
||||||
public void setYAxisBoundsStatus(AxisBoundsStatus s) {
|
|
||||||
mYAxisBoundsStatus = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the viewport is scrollable
|
|
||||||
*/
|
|
||||||
public boolean isScrollable() {
|
|
||||||
return mIsScrollable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mIsScrollable whether is viewport is scrollable
|
|
||||||
*/
|
|
||||||
public void setScrollable(boolean mIsScrollable) {
|
|
||||||
this.mIsScrollable = mIsScrollable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the x axis state
|
|
||||||
*/
|
|
||||||
public AxisBoundsStatus getXAxisBoundsStatus() {
|
|
||||||
return mXAxisBoundsStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the y axis state
|
|
||||||
*/
|
|
||||||
public AxisBoundsStatus getYAxisBoundsStatus() {
|
|
||||||
return mYAxisBoundsStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* caches the complete range (minX, maxX, minY, maxY)
|
|
||||||
* by iterating all series and all datapoints and
|
|
||||||
* stores it into #mCompleteRange
|
|
||||||
*/
|
|
||||||
public void calcCompleteRange() {
|
|
||||||
List<Series> series = mGraphView.getSeries();
|
|
||||||
mCompleteRange.set(0, 0, 0, 0);
|
|
||||||
if (!series.isEmpty() && !series.get(0).isEmpty()) {
|
|
||||||
double d = series.get(0).getLowestValueX();
|
|
||||||
for (Series s : series) {
|
|
||||||
if (!s.isEmpty() && d > s.getLowestValueX()) {
|
|
||||||
d = s.getLowestValueX();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCompleteRange.left = (float) d;
|
|
||||||
|
|
||||||
d = series.get(0).getHighestValueX();
|
|
||||||
for (Series s : series) {
|
|
||||||
if (!s.isEmpty() && d < s.getHighestValueX()) {
|
|
||||||
d = s.getHighestValueX();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCompleteRange.right = (float) d;
|
|
||||||
|
|
||||||
d = series.get(0).getLowestValueY();
|
|
||||||
for (Series s : series) {
|
|
||||||
if (!s.isEmpty() && d > s.getLowestValueY()) {
|
|
||||||
d = s.getLowestValueY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCompleteRange.bottom = (float) d;
|
|
||||||
|
|
||||||
d = series.get(0).getHighestValueY();
|
|
||||||
for (Series s : series) {
|
|
||||||
if (!s.isEmpty() && d < s.getHighestValueY()) {
|
|
||||||
d = s.getHighestValueY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCompleteRange.top = (float) d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc current viewport bounds
|
|
||||||
if (mYAxisBoundsStatus == AxisBoundsStatus.AUTO_ADJUSTED) {
|
|
||||||
mYAxisBoundsStatus = AxisBoundsStatus.INITIAL;
|
|
||||||
}
|
|
||||||
if (mYAxisBoundsStatus == AxisBoundsStatus.INITIAL) {
|
|
||||||
mCurrentViewport.top = mCompleteRange.top;
|
|
||||||
mCurrentViewport.bottom = mCompleteRange.bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mXAxisBoundsStatus == AxisBoundsStatus.AUTO_ADJUSTED) {
|
|
||||||
mXAxisBoundsStatus = AxisBoundsStatus.INITIAL;
|
|
||||||
}
|
|
||||||
if (mXAxisBoundsStatus == AxisBoundsStatus.INITIAL) {
|
|
||||||
mCurrentViewport.left = mCompleteRange.left;
|
|
||||||
mCurrentViewport.right = mCompleteRange.right;
|
|
||||||
} else if (mXAxisBoundsManual && !mYAxisBoundsManual && mCompleteRange.width() != 0) {
|
|
||||||
// get highest/lowest of current viewport
|
|
||||||
// lowest
|
|
||||||
double d = Double.MAX_VALUE;
|
|
||||||
for (Series s : series) {
|
|
||||||
Iterator<DataPointInterface> values = s.getValues(mCurrentViewport.left, mCurrentViewport.right);
|
|
||||||
while (values.hasNext()) {
|
|
||||||
double v = values.next().getY();
|
|
||||||
if (d > v) {
|
|
||||||
d = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentViewport.bottom = (float) d;
|
|
||||||
|
|
||||||
// highest
|
|
||||||
d = Double.MIN_VALUE;
|
|
||||||
for (Series s : series) {
|
|
||||||
Iterator<DataPointInterface> values = s.getValues(mCurrentViewport.left, mCurrentViewport.right);
|
|
||||||
while (values.hasNext()) {
|
|
||||||
double v = values.next().getY();
|
|
||||||
if (d < v) {
|
|
||||||
d = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mCurrentViewport.top = (float) d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixes blank screen when range is zero
|
|
||||||
if (mCurrentViewport.left == mCurrentViewport.right) mCurrentViewport.right++;
|
|
||||||
if (mCurrentViewport.top == mCurrentViewport.bottom) mCurrentViewport.top++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param completeRange if true => minX of the complete range of all series
|
|
||||||
* if false => minX of the current visible viewport
|
|
||||||
* @return the min x value
|
|
||||||
*/
|
|
||||||
public double getMinX(boolean completeRange) {
|
|
||||||
if (completeRange) {
|
|
||||||
return (double) mCompleteRange.left;
|
|
||||||
} else {
|
|
||||||
return (double) mCurrentViewport.left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param completeRange if true => maxX of the complete range of all series
|
|
||||||
* if false => maxX of the current visible viewport
|
|
||||||
* @return the max x value
|
|
||||||
*/
|
|
||||||
public double getMaxX(boolean completeRange) {
|
|
||||||
if (completeRange) {
|
|
||||||
return (double) mCompleteRange.right;
|
|
||||||
} else {
|
|
||||||
return mCurrentViewport.right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param completeRange if true => minY of the complete range of all series
|
|
||||||
* if false => minY of the current visible viewport
|
|
||||||
* @return the min y value
|
|
||||||
*/
|
|
||||||
public double getMinY(boolean completeRange) {
|
|
||||||
if (completeRange) {
|
|
||||||
return (double) mCompleteRange.bottom;
|
|
||||||
} else {
|
|
||||||
return mCurrentViewport.bottom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param completeRange if true => maxY of the complete range of all series
|
|
||||||
* if false => maxY of the current visible viewport
|
|
||||||
* @return the max y value
|
|
||||||
*/
|
|
||||||
public double getMaxY(boolean completeRange) {
|
|
||||||
if (completeRange) {
|
|
||||||
return (double) mCompleteRange.top;
|
|
||||||
} else {
|
|
||||||
return mCurrentViewport.top;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the maximal y value for the current viewport.
|
|
||||||
* Make sure to set the y bounds to manual via
|
|
||||||
* {@link #setYAxisBoundsManual(boolean)}
|
|
||||||
* @param y max / highest value
|
|
||||||
*/
|
|
||||||
public void setMaxY(double y) {
|
|
||||||
mCurrentViewport.top = (float) y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the minimal y value for the current viewport.
|
|
||||||
* Make sure to set the y bounds to manual via
|
|
||||||
* {@link #setYAxisBoundsManual(boolean)}
|
|
||||||
* @param y min / lowest value
|
|
||||||
*/
|
|
||||||
public void setMinY(double y) {
|
|
||||||
mCurrentViewport.bottom = (float) y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the maximal x value for the current viewport.
|
|
||||||
* Make sure to set the x bounds to manual via
|
|
||||||
* {@link #setXAxisBoundsManual(boolean)}
|
|
||||||
* @param x max / highest value
|
|
||||||
*/
|
|
||||||
public void setMaxX(double x) {
|
|
||||||
mCurrentViewport.right = (float) x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the minimal x value for the current viewport.
|
|
||||||
* Make sure to set the x bounds to manual via
|
|
||||||
* {@link #setXAxisBoundsManual(boolean)}
|
|
||||||
* @param x min / lowest value
|
|
||||||
*/
|
|
||||||
public void setMinX(double x) {
|
|
||||||
mCurrentViewport.left = (float) x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* release the glowing effects
|
|
||||||
*/
|
|
||||||
private void releaseEdgeEffects() {
|
|
||||||
mEdgeEffectLeftActive
|
|
||||||
= mEdgeEffectRightActive
|
|
||||||
= false;
|
|
||||||
mEdgeEffectLeft.onRelease();
|
|
||||||
mEdgeEffectRight.onRelease();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used currently
|
|
||||||
*
|
|
||||||
* @param velocityX
|
|
||||||
* @param velocityY
|
|
||||||
*/
|
|
||||||
private void fling(int velocityX, int velocityY) {
|
|
||||||
velocityY = 0;
|
|
||||||
releaseEdgeEffects();
|
|
||||||
// Flings use math in pixels (as opposed to math based on the viewport).
|
|
||||||
mScrollerStartViewport.set(mCurrentViewport);
|
|
||||||
int maxX = (int)((mCurrentViewport.width()/mCompleteRange.width())*(float)mGraphView.getGraphContentWidth()) - mGraphView.getGraphContentWidth();
|
|
||||||
int maxY = (int)((mCurrentViewport.height()/mCompleteRange.height())*(float)mGraphView.getGraphContentHeight()) - mGraphView.getGraphContentHeight();
|
|
||||||
int startX = (int)((mCurrentViewport.left - mCompleteRange.left)/mCompleteRange.width())*maxX;
|
|
||||||
int startY = (int)((mCurrentViewport.top - mCompleteRange.top)/mCompleteRange.height())*maxY;
|
|
||||||
mScroller.forceFinished(true);
|
|
||||||
mScroller.fling(
|
|
||||||
startX,
|
|
||||||
startY,
|
|
||||||
velocityX,
|
|
||||||
velocityY,
|
|
||||||
0, maxX,
|
|
||||||
0, maxY,
|
|
||||||
mGraphView.getGraphContentWidth() / 2,
|
|
||||||
mGraphView.getGraphContentHeight() / 2);
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* not used currently
|
|
||||||
*/
|
|
||||||
public void computeScroll() {
|
|
||||||
if (true) return;
|
|
||||||
|
|
||||||
boolean needsInvalidate = false;
|
|
||||||
|
|
||||||
if (mScroller.computeScrollOffset()) {
|
|
||||||
// The scroller isn't finished, meaning a fling or programmatic pan operation is
|
|
||||||
// currently active.
|
|
||||||
|
|
||||||
int completeWidth = (int)((mCompleteRange.width()/mCurrentViewport.width()) * (float) mGraphView.getGraphContentWidth());
|
|
||||||
int completeHeight = (int)((mCompleteRange.height()/mCurrentViewport.height()) * (float) mGraphView.getGraphContentHeight());
|
|
||||||
|
|
||||||
int currX = mScroller.getCurrX();
|
|
||||||
int currY = mScroller.getCurrY();
|
|
||||||
|
|
||||||
boolean canScrollX = mCurrentViewport.left > mCompleteRange.left
|
|
||||||
|| mCurrentViewport.right < mCompleteRange.right;
|
|
||||||
boolean canScrollY = mCurrentViewport.bottom > mCompleteRange.bottom
|
|
||||||
|| mCurrentViewport.top < mCompleteRange.top;
|
|
||||||
|
|
||||||
if (canScrollX
|
|
||||||
&& currX < 0
|
|
||||||
&& mEdgeEffectLeft.isFinished()
|
|
||||||
&& !mEdgeEffectLeftActive) {
|
|
||||||
mEdgeEffectLeft.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));
|
|
||||||
mEdgeEffectLeftActive = true;
|
|
||||||
needsInvalidate = true;
|
|
||||||
} else if (canScrollX
|
|
||||||
&& currX > (completeWidth - mGraphView.getGraphContentWidth())
|
|
||||||
&& mEdgeEffectRight.isFinished()
|
|
||||||
&& !mEdgeEffectRightActive) {
|
|
||||||
mEdgeEffectRight.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));
|
|
||||||
mEdgeEffectRightActive = true;
|
|
||||||
needsInvalidate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canScrollY
|
|
||||||
&& currY < 0
|
|
||||||
&& mEdgeEffectTop.isFinished()
|
|
||||||
&& !mEdgeEffectTopActive) {
|
|
||||||
mEdgeEffectTop.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));
|
|
||||||
mEdgeEffectTopActive = true;
|
|
||||||
needsInvalidate = true;
|
|
||||||
} else if (canScrollY
|
|
||||||
&& currY > (completeHeight - mGraphView.getGraphContentHeight())
|
|
||||||
&& mEdgeEffectBottom.isFinished()
|
|
||||||
&& !mEdgeEffectBottomActive) {
|
|
||||||
mEdgeEffectBottom.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller));
|
|
||||||
mEdgeEffectBottomActive = true;
|
|
||||||
needsInvalidate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float currXRange = mCompleteRange.left + mCompleteRange.width()
|
|
||||||
* currX / completeWidth;
|
|
||||||
float currYRange = mCompleteRange.top - mCompleteRange.height()
|
|
||||||
* currY / completeHeight;
|
|
||||||
|
|
||||||
float currWidth = mCurrentViewport.width();
|
|
||||||
float currHeight = mCurrentViewport.height();
|
|
||||||
mCurrentViewport.left = currXRange;
|
|
||||||
mCurrentViewport.right = currXRange + currWidth;
|
|
||||||
//mCurrentViewport.bottom = currYRange;
|
|
||||||
//mCurrentViewport.top = currYRange + currHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsInvalidate) {
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the overscroll "glow" at the four edges of the chart region, if necessary.
|
|
||||||
*
|
|
||||||
* @see EdgeEffectCompat
|
|
||||||
*/
|
|
||||||
private void drawEdgeEffectsUnclipped(Canvas canvas) {
|
|
||||||
// The methods below rotate and translate the canvas as needed before drawing the glow,
|
|
||||||
// since EdgeEffectCompat always draws a top-glow at 0,0.
|
|
||||||
|
|
||||||
boolean needsInvalidate = false;
|
|
||||||
|
|
||||||
if (!mEdgeEffectTop.isFinished()) {
|
|
||||||
final int restoreCount = canvas.save();
|
|
||||||
canvas.translate(mGraphView.getGraphContentLeft(), mGraphView.getGraphContentTop());
|
|
||||||
mEdgeEffectTop.setSize(mGraphView.getGraphContentWidth(), mGraphView.getGraphContentHeight());
|
|
||||||
if (mEdgeEffectTop.draw(canvas)) {
|
|
||||||
needsInvalidate = true;
|
|
||||||
}
|
|
||||||
canvas.restoreToCount(restoreCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (!mEdgeEffectBottom.isFinished()) {
|
|
||||||
// final int restoreCount = canvas.save();
|
|
||||||
// canvas.translate(2 * mContentRect.left - mContentRect.right, mContentRect.bottom);
|
|
||||||
// canvas.rotate(180, mContentRect.width(), 0);
|
|
||||||
// mEdgeEffectBottom.setSize(mContentRect.width(), mContentRect.height());
|
|
||||||
// if (mEdgeEffectBottom.draw(canvas)) {
|
|
||||||
// needsInvalidate = true;
|
|
||||||
// }
|
|
||||||
// canvas.restoreToCount(restoreCount);
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (!mEdgeEffectLeft.isFinished()) {
|
|
||||||
final int restoreCount = canvas.save();
|
|
||||||
canvas.translate(mGraphView.getGraphContentLeft(), mGraphView.getGraphContentTop()+ mGraphView.getGraphContentHeight());
|
|
||||||
canvas.rotate(-90, 0, 0);
|
|
||||||
mEdgeEffectLeft.setSize(mGraphView.getGraphContentHeight(), mGraphView.getGraphContentWidth());
|
|
||||||
if (mEdgeEffectLeft.draw(canvas)) {
|
|
||||||
needsInvalidate = true;
|
|
||||||
}
|
|
||||||
canvas.restoreToCount(restoreCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mEdgeEffectRight.isFinished()) {
|
|
||||||
final int restoreCount = canvas.save();
|
|
||||||
canvas.translate(mGraphView.getGraphContentLeft()+ mGraphView.getGraphContentWidth(), mGraphView.getGraphContentTop());
|
|
||||||
canvas.rotate(90, 0, 0);
|
|
||||||
mEdgeEffectRight.setSize(mGraphView.getGraphContentHeight(), mGraphView.getGraphContentWidth());
|
|
||||||
if (mEdgeEffectRight.draw(canvas)) {
|
|
||||||
needsInvalidate = true;
|
|
||||||
}
|
|
||||||
canvas.restoreToCount(restoreCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsInvalidate) {
|
|
||||||
ViewCompat.postInvalidateOnAnimation(mGraphView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* will be first called in order to draw
|
|
||||||
* the canvas
|
|
||||||
* Used to draw the background
|
|
||||||
*
|
|
||||||
* @param c canvas.
|
|
||||||
*/
|
|
||||||
public void drawFirst(Canvas c) {
|
|
||||||
// draw background
|
|
||||||
if (mBackgroundColor != Color.TRANSPARENT) {
|
|
||||||
mPaint.setColor(mBackgroundColor);
|
|
||||||
c.drawRect(
|
|
||||||
mGraphView.getGraphContentLeft(),
|
|
||||||
mGraphView.getGraphContentTop(),
|
|
||||||
mGraphView.getGraphContentLeft()+mGraphView.getGraphContentWidth(),
|
|
||||||
mGraphView.getGraphContentTop()+mGraphView.getGraphContentHeight(),
|
|
||||||
mPaint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws the glowing edge effect
|
|
||||||
*
|
|
||||||
* @param c canvas
|
|
||||||
*/
|
|
||||||
public void draw(Canvas c) {
|
|
||||||
drawEdgeEffectsUnclipped(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return background of the viewport area
|
|
||||||
*/
|
|
||||||
public int getBackgroundColor() {
|
|
||||||
return mBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mBackgroundColor background of the viewport area
|
|
||||||
* use transparent to have no background
|
|
||||||
*/
|
|
||||||
public void setBackgroundColor(int mBackgroundColor) {
|
|
||||||
this.mBackgroundColor = mBackgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the viewport is scalable
|
|
||||||
*/
|
|
||||||
public boolean isScalable() {
|
|
||||||
return mIsScalable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* active the scaling/zooming feature
|
|
||||||
* notice: sets the x axis bounds to manual
|
|
||||||
*
|
|
||||||
* @param mIsScalable whether the viewport is scalable
|
|
||||||
*/
|
|
||||||
public void setScalable(boolean mIsScalable) {
|
|
||||||
this.mIsScalable = mIsScalable;
|
|
||||||
if (mIsScalable) {
|
|
||||||
mIsScrollable = true;
|
|
||||||
|
|
||||||
// set viewport to manual
|
|
||||||
setXAxisBoundsManual(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the x axis bounds are manual.
|
|
||||||
* @see #setMinX(double)
|
|
||||||
* @see #setMaxX(double)
|
|
||||||
*/
|
|
||||||
public boolean isXAxisBoundsManual() {
|
|
||||||
return mXAxisBoundsManual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mXAxisBoundsManual whether the x axis bounds are manual.
|
|
||||||
* @see #setMinX(double)
|
|
||||||
* @see #setMaxX(double)
|
|
||||||
*/
|
|
||||||
public void setXAxisBoundsManual(boolean mXAxisBoundsManual) {
|
|
||||||
this.mXAxisBoundsManual = mXAxisBoundsManual;
|
|
||||||
if (mXAxisBoundsManual) {
|
|
||||||
mXAxisBoundsStatus = AxisBoundsStatus.FIX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the y axis bound are manual
|
|
||||||
*/
|
|
||||||
public boolean isYAxisBoundsManual() {
|
|
||||||
return mYAxisBoundsManual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mYAxisBoundsManual whether the y axis bounds are manual
|
|
||||||
* @see #setMaxY(double)
|
|
||||||
* @see #setMinY(double)
|
|
||||||
*/
|
|
||||||
public void setYAxisBoundsManual(boolean mYAxisBoundsManual) {
|
|
||||||
this.mYAxisBoundsManual = mYAxisBoundsManual;
|
|
||||||
if (mYAxisBoundsManual) {
|
|
||||||
mYAxisBoundsStatus = AxisBoundsStatus.FIX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* forces the viewport to scroll to the end
|
|
||||||
* of the range by keeping the current viewport size.
|
|
||||||
*
|
|
||||||
* Important: Only takes effect if x axis bounds are manual.
|
|
||||||
*
|
|
||||||
* @see #setXAxisBoundsManual(boolean)
|
|
||||||
*/
|
|
||||||
public void scrollToEnd() {
|
|
||||||
if (mXAxisBoundsManual) {
|
|
||||||
float size = mCurrentViewport.width();
|
|
||||||
mCurrentViewport.right = mCompleteRange.right;
|
|
||||||
mCurrentViewport.left = mCompleteRange.right - size;
|
|
||||||
mScrollingReferenceX = Float.NaN;
|
|
||||||
mGraphView.onDataChanged(true, false);
|
|
||||||
} else {
|
|
||||||
Log.w("GraphView", "scrollToEnd works only with manual x axis bounds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.compat;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.widget.OverScroller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility class for using {@link android.widget.OverScroller} in a backward-compatible fashion.
|
|
||||||
*/
|
|
||||||
public class OverScrollerCompat {
|
|
||||||
/**
|
|
||||||
* Disallow instantiation.
|
|
||||||
*/
|
|
||||||
private OverScrollerCompat() {
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @see android.view.ScaleGestureDetector#getCurrentSpanY()
|
|
||||||
*/
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
|
||||||
public static float getCurrVelocity(OverScroller overScroller) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
return overScroller.getCurrVelocity();
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.helper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.DefaultLabelFormatter;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to use date objects as x-values.
|
|
||||||
* This will use your own Date Format or by default
|
|
||||||
* the Android default date format to convert
|
|
||||||
* the x-values (that has to be millis from
|
|
||||||
* 01-01-1970) into a formatted date string.
|
|
||||||
*
|
|
||||||
* See the DateAsXAxis example in the GraphView-Demos project
|
|
||||||
* to see a working example.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class DateAsXAxisLabelFormatter extends DefaultLabelFormatter {
|
|
||||||
/**
|
|
||||||
* the date format that will convert
|
|
||||||
* the unix timestamp to string
|
|
||||||
*/
|
|
||||||
protected final DateFormat mDateFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* calendar to avoid creating new date objects
|
|
||||||
*/
|
|
||||||
protected final Calendar mCalendar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create the formatter with the Android default date format to convert
|
|
||||||
* the x-values.
|
|
||||||
*
|
|
||||||
* @param context the application context
|
|
||||||
*/
|
|
||||||
public DateAsXAxisLabelFormatter(Context context) {
|
|
||||||
mDateFormat = android.text.format.DateFormat.getDateFormat(context);
|
|
||||||
mCalendar = Calendar.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create the formatter with your own custom
|
|
||||||
* date format to convert the x-values.
|
|
||||||
*
|
|
||||||
* @param context the application context
|
|
||||||
* @param dateFormat custom date format
|
|
||||||
*/
|
|
||||||
public DateAsXAxisLabelFormatter(Context context, DateFormat dateFormat) {
|
|
||||||
mDateFormat = dateFormat;
|
|
||||||
mCalendar = Calendar.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* formats the x-values as date string.
|
|
||||||
*
|
|
||||||
* @param value raw value
|
|
||||||
* @param isValueX true if it's a x value, otherwise false
|
|
||||||
* @return value converted to string
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String formatLabel(double value, boolean isValueX) {
|
|
||||||
if (isValueX) {
|
|
||||||
// format as date
|
|
||||||
mCalendar.setTimeInMillis((long) value);
|
|
||||||
return mDateFormat.format(mCalendar.getTimeInMillis());
|
|
||||||
} else {
|
|
||||||
return super.formatLabel(value, isValueX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,137 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.helper;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
import com.jjoe64.graphview.R;
|
|
||||||
import com.jjoe64.graphview.series.BarGraphSeries;
|
|
||||||
import com.jjoe64.graphview.series.BaseSeries;
|
|
||||||
import com.jjoe64.graphview.series.DataPoint;
|
|
||||||
import com.jjoe64.graphview.series.DataPointInterface;
|
|
||||||
import com.jjoe64.graphview.series.LineGraphSeries;
|
|
||||||
import com.jjoe64.graphview.series.PointsGraphSeries;
|
|
||||||
import com.jjoe64.graphview.series.Series;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper class to use GraphView directly
|
|
||||||
* in a XML layout file.
|
|
||||||
*
|
|
||||||
* You can set the data via attribute <b>app:seriesData</b>
|
|
||||||
* in the format: "X=Y;X=Y;..." e.g. "0=5.0;1=5;2=4;3=9"
|
|
||||||
*
|
|
||||||
* Other styling options:
|
|
||||||
* <li>app:seriesType="line|bar|points"</li>
|
|
||||||
* <li>app:seriesColor="#ff0000"</li>
|
|
||||||
* <li>app:seriesTitle="foobar" - if this is set, the legend will be drawn</li>
|
|
||||||
* <li>android:title="foobar"</li>
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
* <pre>
|
|
||||||
* {@code
|
|
||||||
* <com.jjoe64.graphview.helper.GraphViewXML
|
|
||||||
* android:layout_width="match_parent"
|
|
||||||
* android:layout_height="100dip"
|
|
||||||
* app:seriesData="0=5;2=5;3=0;4=2"
|
|
||||||
* app:seriesType="line"
|
|
||||||
* app:seriesColor="#ee0000" />
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class GraphViewXML extends GraphView {
|
|
||||||
/**
|
|
||||||
* creates the graphview object with data and
|
|
||||||
* other options from xml attributes.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param attrs
|
|
||||||
*/
|
|
||||||
public GraphViewXML(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
// get attributes
|
|
||||||
TypedArray a=context.obtainStyledAttributes(
|
|
||||||
attrs,
|
|
||||||
R.styleable.GraphViewXML);
|
|
||||||
|
|
||||||
String dataStr = a.getString(R.styleable.GraphViewXML_seriesData);
|
|
||||||
int color = a.getColor(R.styleable.GraphViewXML_seriesColor, Color.TRANSPARENT);
|
|
||||||
String type = a.getString(R.styleable.GraphViewXML_seriesType);
|
|
||||||
String seriesTitle = a.getString(R.styleable.GraphViewXML_seriesTitle);
|
|
||||||
String title = a.getString(R.styleable.GraphViewXML_android_title);
|
|
||||||
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
// decode data
|
|
||||||
DataPoint[] data;
|
|
||||||
if (dataStr == null || dataStr.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Attribute seriesData is required in the format: 0=5.0;1=5;2=4;3=9");
|
|
||||||
} else {
|
|
||||||
String[] d = dataStr.split(";");
|
|
||||||
try {
|
|
||||||
data = new DataPoint[d.length];
|
|
||||||
int i = 0;
|
|
||||||
for (String dd : d) {
|
|
||||||
String[] xy = dd.split("=");
|
|
||||||
data[i] = new DataPoint(Double.parseDouble(xy[0]), Double.parseDouble(xy[1]));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("GraphViewXML", e.toString());
|
|
||||||
throw new IllegalArgumentException("Attribute seriesData is broken. Use this format: 0=5.0;1=5;2=4;3=9");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create series
|
|
||||||
BaseSeries<DataPoint> series;
|
|
||||||
if (type == null || type.isEmpty()) {
|
|
||||||
type = "line";
|
|
||||||
}
|
|
||||||
if (type.equals("line")) {
|
|
||||||
series = new LineGraphSeries<DataPoint>(data);
|
|
||||||
} else if (type.equals("bar")) {
|
|
||||||
series = new BarGraphSeries<DataPoint>(data);
|
|
||||||
} else if (type.equals("points")) {
|
|
||||||
series = new PointsGraphSeries<DataPoint>(data);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("unknown graph type: "+type+". Possible is line|bar|points");
|
|
||||||
}
|
|
||||||
if (color != Color.TRANSPARENT) {
|
|
||||||
series.setColor(color);
|
|
||||||
}
|
|
||||||
addSeries(series);
|
|
||||||
|
|
||||||
if (seriesTitle != null && !seriesTitle.isEmpty()) {
|
|
||||||
series.setTitle(seriesTitle);
|
|
||||||
getLegendRenderer().setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (title != null && !title.isEmpty()) {
|
|
||||||
setTitle(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,209 +0,0 @@
|
||||||
package com.jjoe64.graphview.helper;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.DefaultLabelFormatter;
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
import com.jjoe64.graphview.LabelFormatter;
|
|
||||||
import com.jjoe64.graphview.Viewport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this label formatter to show static labels.
|
|
||||||
* Static labels are not bound to the data. It is typical used
|
|
||||||
* for show text like "low", "middle", "high".
|
|
||||||
*
|
|
||||||
* You can set the static labels for vertical or horizontal
|
|
||||||
* individually and you can define a label formatter that
|
|
||||||
* is to be used if you don't define static labels.
|
|
||||||
*
|
|
||||||
* For example if you only use static labels for horizontal labels,
|
|
||||||
* graphview will use the dynamicLabelFormatter for the vertical labels.
|
|
||||||
*/
|
|
||||||
public class StaticLabelsFormatter implements LabelFormatter {
|
|
||||||
/**
|
|
||||||
* reference to the viewport
|
|
||||||
*/
|
|
||||||
protected Viewport mViewport;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the vertical labels, ordered from bottom to the top
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
*/
|
|
||||||
protected String[] mVerticalLabels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the horizontal labels, ordered form the left to the right
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
*/
|
|
||||||
protected String[] mHorizontalLabels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the label formatter that will format the labels
|
|
||||||
* for that there are no static labels defined.
|
|
||||||
*/
|
|
||||||
protected LabelFormatter mDynamicLabelFormatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reference to the graphview
|
|
||||||
*/
|
|
||||||
protected final GraphView mGraphView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the formatter without any static labels
|
|
||||||
* define your static labels via {@link #setHorizontalLabels(String[])} and {@link #setVerticalLabels(String[])}
|
|
||||||
*
|
|
||||||
* @param graphView reference to the graphview
|
|
||||||
*/
|
|
||||||
public StaticLabelsFormatter(GraphView graphView) {
|
|
||||||
mGraphView = graphView;
|
|
||||||
init(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the formatter without any static labels.
|
|
||||||
* define your static labels via {@link #setHorizontalLabels(String[])} and {@link #setVerticalLabels(String[])}
|
|
||||||
*
|
|
||||||
* @param graphView reference to the graphview
|
|
||||||
* @param dynamicLabelFormatter the label formatter that will format the labels
|
|
||||||
* for that there are no static labels defined.
|
|
||||||
*/
|
|
||||||
public StaticLabelsFormatter(GraphView graphView, LabelFormatter dynamicLabelFormatter) {
|
|
||||||
mGraphView = graphView;
|
|
||||||
init(null, null, dynamicLabelFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the formatter with static labels defined.
|
|
||||||
*
|
|
||||||
* @param graphView reference to the graphview
|
|
||||||
* @param horizontalLabels the horizontal labels, ordered form the left to the right
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
* @param verticalLabels the vertical labels, ordered from bottom to the top
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
*/
|
|
||||||
public StaticLabelsFormatter(GraphView graphView, String[] horizontalLabels, String[] verticalLabels) {
|
|
||||||
mGraphView = graphView;
|
|
||||||
init(horizontalLabels, verticalLabels, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the formatter with static labels defined.
|
|
||||||
*
|
|
||||||
* @param graphView reference to the graphview
|
|
||||||
* @param horizontalLabels the horizontal labels, ordered form the left to the right
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
* @param verticalLabels the vertical labels, ordered from bottom to the top
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
* @param dynamicLabelFormatter the label formatter that will format the labels
|
|
||||||
* for that there are no static labels defined.
|
|
||||||
*/
|
|
||||||
public StaticLabelsFormatter(GraphView graphView, String[] horizontalLabels, String[] verticalLabels, LabelFormatter dynamicLabelFormatter) {
|
|
||||||
mGraphView = graphView;
|
|
||||||
init(horizontalLabels, verticalLabels, dynamicLabelFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param horizontalLabels the horizontal labels, ordered form the left to the right
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
* @param verticalLabels the vertical labels, ordered from bottom to the top
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
* @param dynamicLabelFormatter the label formatter that will format the labels
|
|
||||||
* for that there are no static labels defined.
|
|
||||||
*/
|
|
||||||
protected void init(String[] horizontalLabels, String[] verticalLabels, LabelFormatter dynamicLabelFormatter) {
|
|
||||||
mDynamicLabelFormatter = dynamicLabelFormatter;
|
|
||||||
if (mDynamicLabelFormatter == null) {
|
|
||||||
mDynamicLabelFormatter = new DefaultLabelFormatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
mHorizontalLabels = horizontalLabels;
|
|
||||||
mVerticalLabels = verticalLabels;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a label formatter that will be used for the labels
|
|
||||||
* that don't have static labels.
|
|
||||||
*
|
|
||||||
* For example if you only use static labels for horizontal labels,
|
|
||||||
* graphview will use the dynamicLabelFormatter for the vertical labels.
|
|
||||||
*
|
|
||||||
* @param dynamicLabelFormatter the label formatter that will format the labels
|
|
||||||
* for that there are no static labels defined.
|
|
||||||
*/
|
|
||||||
public void setDynamicLabelFormatter(LabelFormatter dynamicLabelFormatter) {
|
|
||||||
this.mDynamicLabelFormatter = dynamicLabelFormatter;
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param horizontalLabels the horizontal labels, ordered form the left to the right
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
*/
|
|
||||||
public void setHorizontalLabels(String[] horizontalLabels) {
|
|
||||||
this.mHorizontalLabels = horizontalLabels;
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param verticalLabels the vertical labels, ordered from bottom to the top
|
|
||||||
* if it is null, the labels will be generated via the #dynamicLabelFormatter
|
|
||||||
*/
|
|
||||||
public void setVerticalLabels(String[] verticalLabels) {
|
|
||||||
this.mVerticalLabels = verticalLabels;
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param value raw input number
|
|
||||||
* @param isValueX true if it is a value for the x axis
|
|
||||||
* false if it is a value for the y axis
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String formatLabel(double value, boolean isValueX) {
|
|
||||||
if (isValueX && mHorizontalLabels != null) {
|
|
||||||
double minX = mViewport.getMinX(false);
|
|
||||||
double maxX = mViewport.getMaxX(false);
|
|
||||||
double range = maxX - minX;
|
|
||||||
value = value-minX;
|
|
||||||
int idx = (int)((value/range) * (mHorizontalLabels.length-1));
|
|
||||||
return mHorizontalLabels[idx];
|
|
||||||
} else if (!isValueX && mVerticalLabels != null) {
|
|
||||||
double minY = mViewport.getMinY(false);
|
|
||||||
double maxY = mViewport.getMaxY(false);
|
|
||||||
double range = maxY - minY;
|
|
||||||
value = value-minY;
|
|
||||||
int idx = (int)((value/range) * (mVerticalLabels.length-1));
|
|
||||||
return mVerticalLabels[idx];
|
|
||||||
} else {
|
|
||||||
return mDynamicLabelFormatter.formatLabel(value, isValueX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param viewport the used viewport
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setViewport(Viewport viewport) {
|
|
||||||
mViewport = viewport;
|
|
||||||
adjust();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* adjusts the number of vertical/horizontal labels
|
|
||||||
*/
|
|
||||||
protected void adjust() {
|
|
||||||
mDynamicLabelFormatter.setViewport(mViewport);
|
|
||||||
if (mVerticalLabels != null) {
|
|
||||||
if (mVerticalLabels.length < 2) {
|
|
||||||
throw new IllegalStateException("You need at least 2 vertical labels if you use static label formatter.");
|
|
||||||
}
|
|
||||||
mGraphView.getGridLabelRenderer().setNumVerticalLabels(mVerticalLabels.length);
|
|
||||||
}
|
|
||||||
if (mHorizontalLabels != null) {
|
|
||||||
if (mHorizontalLabels.length < 2) {
|
|
||||||
throw new IllegalStateException("You need at least 2 horizontal labels if you use static label formatter.");
|
|
||||||
}
|
|
||||||
mGraphView.getGridLabelRenderer().setNumHorizontalLabels(mHorizontalLabels.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,379 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
import com.jjoe64.graphview.ValueDependentColor;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Series with Bars to visualize the data.
|
|
||||||
* The Bars are always vertical.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class BarGraphSeries<E extends DataPointInterface> extends BaseSeries<E> {
|
|
||||||
/**
|
|
||||||
* paint to do drawing on canvas
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spacing between the bars in percentage.
|
|
||||||
* 0 => no spacing
|
|
||||||
* 100 => the space bewetten the bars is as big as the bars itself
|
|
||||||
*/
|
|
||||||
private int mSpacing;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* callback to generate value-dependent colors
|
|
||||||
* of the bars
|
|
||||||
*/
|
|
||||||
private ValueDependentColor<E> mValueDependentColor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the values should drawn
|
|
||||||
* above the bars as text
|
|
||||||
*/
|
|
||||||
private boolean mDrawValuesOnTop;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* color of the text above the bars.
|
|
||||||
*
|
|
||||||
* @see #mDrawValuesOnTop
|
|
||||||
*/
|
|
||||||
private int mValuesOnTopColor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* font size of the text above the bars.
|
|
||||||
*
|
|
||||||
* @see #mDrawValuesOnTop
|
|
||||||
*/
|
|
||||||
private float mValuesOnTopSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the coordinates of the bars to
|
|
||||||
* trigger tap on series events.
|
|
||||||
*/
|
|
||||||
private Map<RectF, E> mDataPoints = new HashMap<RectF, E>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates bar series without any data
|
|
||||||
*/
|
|
||||||
public BarGraphSeries() {
|
|
||||||
mPaint = new Paint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates bar series with data
|
|
||||||
*
|
|
||||||
* @param data values
|
|
||||||
*/
|
|
||||||
public BarGraphSeries(E[] data) {
|
|
||||||
super(data);
|
|
||||||
mPaint = new Paint();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws the bars on the canvas
|
|
||||||
*
|
|
||||||
* @param graphView corresponding graphview
|
|
||||||
* @param canvas canvas
|
|
||||||
* @param isSecondScale whether we are plotting the second scale or not
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
|
|
||||||
mPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
if (mValuesOnTopSize == 0) {
|
|
||||||
mValuesOnTopSize = graphView.getGridLabelRenderer().getTextSize();
|
|
||||||
}
|
|
||||||
mPaint.setTextSize(mValuesOnTopSize);
|
|
||||||
|
|
||||||
// get data
|
|
||||||
double maxX = graphView.getViewport().getMaxX(false);
|
|
||||||
double minX = graphView.getViewport().getMinX(false);
|
|
||||||
|
|
||||||
double maxY;
|
|
||||||
double minY;
|
|
||||||
if (isSecondScale) {
|
|
||||||
maxY = graphView.getSecondScale().getMaxY();
|
|
||||||
minY = graphView.getSecondScale().getMinY();
|
|
||||||
} else {
|
|
||||||
maxY = graphView.getViewport().getMaxY(false);
|
|
||||||
minY = graphView.getViewport().getMinY(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through all bar graph series
|
|
||||||
// so we know how wide to make our bar,
|
|
||||||
// and in what position to put it in
|
|
||||||
int numBarSeries = 0;
|
|
||||||
int currentSeriesOrder = 0;
|
|
||||||
int numValues = 0;
|
|
||||||
boolean isCurrentSeries;
|
|
||||||
SortedSet<Double> xVals = new TreeSet<Double>();
|
|
||||||
for(Series inspectedSeries: graphView.getSeries()) {
|
|
||||||
if(inspectedSeries instanceof BarGraphSeries) {
|
|
||||||
isCurrentSeries = (inspectedSeries == this);
|
|
||||||
if(isCurrentSeries) {
|
|
||||||
currentSeriesOrder = numBarSeries;
|
|
||||||
}
|
|
||||||
numBarSeries++;
|
|
||||||
|
|
||||||
// calculate the number of slots for bars based on the minimum distance between
|
|
||||||
// x coordinates in the series. This is divided into the range to find
|
|
||||||
// the placement and width of bar slots
|
|
||||||
// (sections of the x axis for each bar or set of bars)
|
|
||||||
// TODO: Move this somewhere more general and cache it, so we don't recalculate it for each series
|
|
||||||
Iterator<E> curValues = inspectedSeries.getValues(minX, maxX);
|
|
||||||
if (curValues.hasNext()) {
|
|
||||||
xVals.add(curValues.next().getX());
|
|
||||||
if(isCurrentSeries) { numValues++; }
|
|
||||||
while (curValues.hasNext()) {
|
|
||||||
xVals.add(curValues.next().getX());
|
|
||||||
if(isCurrentSeries) { numValues++; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (numValues == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Double lastVal = null;
|
|
||||||
double minGap = 0;
|
|
||||||
for(Double curVal: xVals) {
|
|
||||||
if(lastVal != null) {
|
|
||||||
double curGap = Math.abs(curVal - lastVal);
|
|
||||||
if (minGap == 0 || (curGap > 0 && curGap < minGap)) {
|
|
||||||
minGap = curGap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastVal = curVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numBarSlots = (minGap == 0) ? 1 : (int)Math.round((maxX - minX)/minGap) + 1;
|
|
||||||
|
|
||||||
Iterator<E> values = getValues(minX, maxX);
|
|
||||||
|
|
||||||
// Calculate the overall bar slot width - this includes all bars across
|
|
||||||
// all series, and any spacing between sets of bars
|
|
||||||
float barSlotWidth = numBarSlots == 1
|
|
||||||
? graphView.getGraphContentWidth()
|
|
||||||
: graphView.getGraphContentWidth() / (numBarSlots-1);
|
|
||||||
Log.d("BarGraphSeries", "numBars=" + numBarSlots);
|
|
||||||
|
|
||||||
// Total spacing (both sides) between sets of bars
|
|
||||||
float spacing = Math.min((float) barSlotWidth*mSpacing/100, barSlotWidth*0.98f);
|
|
||||||
// Width of an individual bar
|
|
||||||
float barWidth = (barSlotWidth - spacing) / numBarSeries;
|
|
||||||
// Offset from the center of a given bar to start drawing
|
|
||||||
float offset = barSlotWidth/2;
|
|
||||||
|
|
||||||
double diffY = maxY - minY;
|
|
||||||
double diffX = maxX - minX;
|
|
||||||
float contentHeight = graphView.getGraphContentHeight();
|
|
||||||
float contentWidth = graphView.getGraphContentWidth();
|
|
||||||
float contentLeft = graphView.getGraphContentLeft();
|
|
||||||
float contentTop = graphView.getGraphContentTop();
|
|
||||||
|
|
||||||
// draw data
|
|
||||||
int i=0;
|
|
||||||
while (values.hasNext()) {
|
|
||||||
E value = values.next();
|
|
||||||
|
|
||||||
double valY = value.getY() - minY;
|
|
||||||
double ratY = valY / diffY;
|
|
||||||
double y = contentHeight * ratY;
|
|
||||||
|
|
||||||
double valY0 = 0 - minY;
|
|
||||||
double ratY0 = valY0 / diffY;
|
|
||||||
double y0 = contentHeight * ratY0;
|
|
||||||
|
|
||||||
double valX = value.getX() - minX;
|
|
||||||
double ratX = valX / diffX;
|
|
||||||
double x = contentWidth * ratX;
|
|
||||||
|
|
||||||
// hook for value dependent color
|
|
||||||
if (getValueDependentColor() != null) {
|
|
||||||
mPaint.setColor(getValueDependentColor().get(value));
|
|
||||||
} else {
|
|
||||||
mPaint.setColor(getColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
float left = (float)x + contentLeft - offset + spacing/2 + currentSeriesOrder*barWidth;
|
|
||||||
float top = (contentTop - (float)y) + contentHeight;
|
|
||||||
float right = left + barWidth;
|
|
||||||
float bottom = (contentTop - (float)y0) + contentHeight - (graphView.getGridLabelRenderer().isHighlightZeroLines()?4:1);
|
|
||||||
|
|
||||||
boolean reverse = top > bottom;
|
|
||||||
if (reverse) {
|
|
||||||
float tmp = top;
|
|
||||||
top = bottom + (graphView.getGridLabelRenderer().isHighlightZeroLines()?4:1);
|
|
||||||
bottom = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// overdraw
|
|
||||||
left = Math.max(left, contentLeft);
|
|
||||||
right = Math.min(right, contentLeft+contentWidth);
|
|
||||||
bottom = Math.min(bottom, contentTop+contentHeight);
|
|
||||||
top = Math.max(top, contentTop);
|
|
||||||
|
|
||||||
mDataPoints.put(new RectF(left, top, right, bottom), value);
|
|
||||||
|
|
||||||
canvas.drawRect(left, top, right, bottom, mPaint);
|
|
||||||
|
|
||||||
// set values on top of graph
|
|
||||||
if (mDrawValuesOnTop) {
|
|
||||||
if (reverse) {
|
|
||||||
top = bottom + mValuesOnTopSize + 4;
|
|
||||||
if (top > contentTop+contentHeight) top = contentTop + contentHeight;
|
|
||||||
} else {
|
|
||||||
top -= 4;
|
|
||||||
if (top<=contentTop) top+=contentTop+4;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPaint.setColor(mValuesOnTopColor);
|
|
||||||
canvas.drawText(
|
|
||||||
graphView.getGridLabelRenderer().getLabelFormatter().formatLabel(value.getY(), false)
|
|
||||||
, (left+right)/2, top, mPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the hook to generate value-dependent color. default null
|
|
||||||
*/
|
|
||||||
public ValueDependentColor<E> getValueDependentColor() {
|
|
||||||
return mValueDependentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a hook to make the color of the bars depending
|
|
||||||
* on the actually value/data.
|
|
||||||
*
|
|
||||||
* @param mValueDependentColor hook
|
|
||||||
* null to disable
|
|
||||||
*/
|
|
||||||
public void setValueDependentColor(ValueDependentColor<E> mValueDependentColor) {
|
|
||||||
this.mValueDependentColor = mValueDependentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the spacing between the bars in percentage
|
|
||||||
*/
|
|
||||||
public int getSpacing() {
|
|
||||||
return mSpacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mSpacing spacing between the bars in percentage.
|
|
||||||
* 0 => no spacing
|
|
||||||
* 100 => the space between the bars is as big as the bars itself
|
|
||||||
*/
|
|
||||||
public void setSpacing(int mSpacing) {
|
|
||||||
this.mSpacing = mSpacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether the values should be drawn above the bars
|
|
||||||
*/
|
|
||||||
public boolean isDrawValuesOnTop() {
|
|
||||||
return mDrawValuesOnTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mDrawValuesOnTop flag whether the values should drawn
|
|
||||||
* above the bars as text
|
|
||||||
*/
|
|
||||||
public void setDrawValuesOnTop(boolean mDrawValuesOnTop) {
|
|
||||||
this.mDrawValuesOnTop = mDrawValuesOnTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return font color of the values on top of the bars
|
|
||||||
* @see #setDrawValuesOnTop(boolean)
|
|
||||||
*/
|
|
||||||
public int getValuesOnTopColor() {
|
|
||||||
return mValuesOnTopColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mValuesOnTopColor the font color of the values on top of the bars
|
|
||||||
* @see #setDrawValuesOnTop(boolean)
|
|
||||||
*/
|
|
||||||
public void setValuesOnTopColor(int mValuesOnTopColor) {
|
|
||||||
this.mValuesOnTopColor = mValuesOnTopColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return font size of the values above the bars
|
|
||||||
* @see #setDrawValuesOnTop(boolean)
|
|
||||||
*/
|
|
||||||
public float getValuesOnTopSize() {
|
|
||||||
return mValuesOnTopSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mValuesOnTopSize font size of the values above the bars
|
|
||||||
* @see #setDrawValuesOnTop(boolean)
|
|
||||||
*/
|
|
||||||
public void setValuesOnTopSize(float mValuesOnTopSize) {
|
|
||||||
this.mValuesOnTopSize = mValuesOnTopSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resets the cached coordinates of the bars
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void resetDataPoints() {
|
|
||||||
mDataPoints.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find the corresponding data point by
|
|
||||||
* coordinates.
|
|
||||||
*
|
|
||||||
* @param x pixels
|
|
||||||
* @param y pixels
|
|
||||||
* @return datapoint or null
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected E findDataPoint(float x, float y) {
|
|
||||||
for (Map.Entry<RectF, E> entry : mDataPoints.entrySet()) {
|
|
||||||
if (x >= entry.getKey().left && x <= entry.getKey().right
|
|
||||||
&& y >= entry.getKey().top && y <= entry.getKey().bottom) {
|
|
||||||
return entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,448 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.graphics.PointF;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basis implementation for series.
|
|
||||||
* Used for series that are plotted on
|
|
||||||
* a default x/y 2d viewport.
|
|
||||||
*
|
|
||||||
* Extend this class to implement your own custom
|
|
||||||
* graph type.
|
|
||||||
*
|
|
||||||
* This implementation uses a internal Array to store
|
|
||||||
* the data. If you want to implement a custom data provider
|
|
||||||
* you may want to implement {@link com.jjoe64.graphview.series.Series}.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public abstract class BaseSeries<E extends DataPointInterface> implements Series<E> {
|
|
||||||
/**
|
|
||||||
* holds the data
|
|
||||||
*/
|
|
||||||
final private List<E> mData = new ArrayList<E>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the used coordinates to find the
|
|
||||||
* corresponding data point on a tap
|
|
||||||
*
|
|
||||||
* Key => x/y pixel
|
|
||||||
* Value => Plotted Datapoint
|
|
||||||
*
|
|
||||||
* will be filled while drawing via {@link #registerDataPoint(float, float, DataPointInterface)}
|
|
||||||
*/
|
|
||||||
private Map<PointF, E> mDataPoints = new HashMap<PointF, E>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* title for this series that can be displayed
|
|
||||||
* in the legend.
|
|
||||||
*/
|
|
||||||
private String mTitle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* base color for this series. will be used also in
|
|
||||||
* the legend
|
|
||||||
*/
|
|
||||||
private int mColor = 0xff0077cc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* listener to handle tap events on a data point
|
|
||||||
*/
|
|
||||||
protected OnDataPointTapListener mOnDataPointTapListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* stores the graphviews where this series is used.
|
|
||||||
* Can be more than one.
|
|
||||||
*/
|
|
||||||
private List<GraphView> mGraphViews;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates series without data
|
|
||||||
*/
|
|
||||||
public BaseSeries() {
|
|
||||||
mGraphViews = new ArrayList<GraphView>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates series with data
|
|
||||||
*
|
|
||||||
* @param data data points
|
|
||||||
* important: array has to be sorted from lowest x-value to the highest
|
|
||||||
*/
|
|
||||||
public BaseSeries(E[] data) {
|
|
||||||
mGraphViews = new ArrayList<GraphView>();
|
|
||||||
for (E d : data) {
|
|
||||||
mData.add(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the lowest x value, or 0 if there is no data
|
|
||||||
*/
|
|
||||||
public double getLowestValueX() {
|
|
||||||
if (mData.isEmpty()) return 0d;
|
|
||||||
return mData.get(0).getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the highest x value, or 0 if there is no data
|
|
||||||
*/
|
|
||||||
public double getHighestValueX() {
|
|
||||||
if (mData.isEmpty()) return 0d;
|
|
||||||
return mData.get(mData.size()-1).getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the lowest y value, or 0 if there is no data
|
|
||||||
*/
|
|
||||||
public double getLowestValueY() {
|
|
||||||
if (mData.isEmpty()) return 0d;
|
|
||||||
double l = mData.get(0).getY();
|
|
||||||
for (int i = 1; i < mData.size(); i++) {
|
|
||||||
double c = mData.get(i).getY();
|
|
||||||
if (l > c) {
|
|
||||||
l = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the highest y value, or 0 if there is no data
|
|
||||||
*/
|
|
||||||
public double getHighestValueY() {
|
|
||||||
if (mData.isEmpty()) return 0d;
|
|
||||||
double h = mData.get(0).getY();
|
|
||||||
for (int i = 1; i < mData.size(); i++) {
|
|
||||||
double c = mData.get(i).getY();
|
|
||||||
if (h < c) {
|
|
||||||
h = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the values for a given x range. if from and until are bigger or equal than
|
|
||||||
* all the data, the original data is returned.
|
|
||||||
* If it is only a part of the data, the range is returned plus one datapoint
|
|
||||||
* before and after to get a nice scrolling.
|
|
||||||
*
|
|
||||||
* @param from minimal x-value
|
|
||||||
* @param until maximal x-value
|
|
||||||
* @return data for the range +/- 1 datapoint
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<E> getValues(final double from, final double until) {
|
|
||||||
if (from <= getLowestValueX() && until >= getHighestValueX()) {
|
|
||||||
return mData.iterator();
|
|
||||||
} else {
|
|
||||||
return new Iterator<E>() {
|
|
||||||
Iterator<E> org = mData.iterator();
|
|
||||||
E nextValue = null;
|
|
||||||
E nextNextValue = null;
|
|
||||||
boolean plusOne = true;
|
|
||||||
|
|
||||||
{
|
|
||||||
// go to first
|
|
||||||
boolean found = false;
|
|
||||||
E prevValue = null;
|
|
||||||
if (org.hasNext()) {
|
|
||||||
prevValue = org.next();
|
|
||||||
}
|
|
||||||
if (prevValue.getX() >= from) {
|
|
||||||
nextValue = prevValue;
|
|
||||||
found = true;
|
|
||||||
} else {
|
|
||||||
while (org.hasNext()) {
|
|
||||||
nextValue = org.next();
|
|
||||||
if (nextValue.getX() >= from) {
|
|
||||||
found = true;
|
|
||||||
nextNextValue = nextValue;
|
|
||||||
nextValue = prevValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prevValue = nextValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
nextValue = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public E next() {
|
|
||||||
if (hasNext()) {
|
|
||||||
E r = nextValue;
|
|
||||||
if (r.getX() > until) {
|
|
||||||
plusOne = false;
|
|
||||||
}
|
|
||||||
if (nextNextValue != null) {
|
|
||||||
nextValue = nextNextValue;
|
|
||||||
nextNextValue = null;
|
|
||||||
} else if (org.hasNext()) nextValue = org.next();
|
|
||||||
else nextValue = null;
|
|
||||||
return r;
|
|
||||||
} else {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return nextValue != null && (nextValue.getX() <= until || plusOne);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the title of the series
|
|
||||||
*/
|
|
||||||
public String getTitle() {
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the title of the series. This will be used in
|
|
||||||
* the legend.
|
|
||||||
*
|
|
||||||
* @param mTitle title of the series
|
|
||||||
*/
|
|
||||||
public void setTitle(String mTitle) {
|
|
||||||
this.mTitle = mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return color of the series
|
|
||||||
*/
|
|
||||||
public int getColor() {
|
|
||||||
return mColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the color of the series. This will be used in
|
|
||||||
* plotting (depends on the series implementation) and
|
|
||||||
* is used in the legend.
|
|
||||||
*
|
|
||||||
* @param mColor
|
|
||||||
*/
|
|
||||||
public void setColor(int mColor) {
|
|
||||||
this.mColor = mColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a listener for tap on a data point.
|
|
||||||
*
|
|
||||||
* @param l listener
|
|
||||||
*/
|
|
||||||
public void setOnDataPointTapListener(OnDataPointTapListener l) {
|
|
||||||
this.mOnDataPointTapListener = l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called by the tap detector in order to trigger
|
|
||||||
* the on tap on datapoint event.
|
|
||||||
*
|
|
||||||
* @param x pixel
|
|
||||||
* @param y pixel
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onTap(float x, float y) {
|
|
||||||
if (mOnDataPointTapListener != null) {
|
|
||||||
E p = findDataPoint(x, y);
|
|
||||||
if (p != null) {
|
|
||||||
mOnDataPointTapListener.onTap(this, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find the data point which is next to the
|
|
||||||
* coordinates
|
|
||||||
*
|
|
||||||
* @param x pixel
|
|
||||||
* @param y pixel
|
|
||||||
* @return the data point or null if nothing was found
|
|
||||||
*/
|
|
||||||
protected E findDataPoint(float x, float y) {
|
|
||||||
float shortestDistance = Float.NaN;
|
|
||||||
E shortest = null;
|
|
||||||
for (Map.Entry<PointF, E> entry : mDataPoints.entrySet()) {
|
|
||||||
float x1 = entry.getKey().x;
|
|
||||||
float y1 = entry.getKey().y;
|
|
||||||
float x2 = x;
|
|
||||||
float y2 = y;
|
|
||||||
|
|
||||||
float distance = (float) Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
|
|
||||||
if (shortest == null || distance < shortestDistance) {
|
|
||||||
shortestDistance = distance;
|
|
||||||
shortest = entry.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shortest != null) {
|
|
||||||
if (shortestDistance < 120) {
|
|
||||||
return shortest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* register the datapoint to find it at a tap
|
|
||||||
*
|
|
||||||
* @param x pixel
|
|
||||||
* @param y pixel
|
|
||||||
* @param dp the data point to save
|
|
||||||
*/
|
|
||||||
protected void registerDataPoint(float x, float y, E dp) {
|
|
||||||
mDataPoints.put(new PointF(x, y), dp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clears the cached data point coordinates
|
|
||||||
*/
|
|
||||||
protected void resetDataPoints() {
|
|
||||||
mDataPoints.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clears the data of this series and sets new.
|
|
||||||
* will redraw the graph
|
|
||||||
*
|
|
||||||
* @param data the values must be in the correct order!
|
|
||||||
* x-value has to be ASC. First the lowest x value and at least the highest x value.
|
|
||||||
*/
|
|
||||||
public void resetData(E[] data) {
|
|
||||||
mData.clear();
|
|
||||||
for (E d : data) {
|
|
||||||
mData.add(d);
|
|
||||||
}
|
|
||||||
checkValueOrder(null);
|
|
||||||
|
|
||||||
// update graphview
|
|
||||||
for (GraphView gv : mGraphViews) {
|
|
||||||
gv.onDataChanged(true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called when the series was added to a graph
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onGraphViewAttached(GraphView graphView) {
|
|
||||||
mGraphViews.add(graphView);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param dataPoint values the values must be in the correct order!
|
|
||||||
* x-value has to be ASC. First the lowest x value and at least the highest x value.
|
|
||||||
* @param scrollToEnd true => graphview will scroll to the end (maxX)
|
|
||||||
* @param maxDataPoints if max data count is reached, the oldest data
|
|
||||||
* value will be lost to avoid memory leaks
|
|
||||||
*/
|
|
||||||
public void appendData(E dataPoint, boolean scrollToEnd, int maxDataPoints) {
|
|
||||||
checkValueOrder(dataPoint);
|
|
||||||
|
|
||||||
if (!mData.isEmpty() && dataPoint.getX() < mData.get(mData.size()-1).getX()) {
|
|
||||||
throw new IllegalArgumentException("new x-value must be greater then the last value. x-values has to be ordered in ASC.");
|
|
||||||
}
|
|
||||||
synchronized (mData) {
|
|
||||||
int curDataCount = mData.size();
|
|
||||||
if (curDataCount < maxDataPoints) {
|
|
||||||
// enough space
|
|
||||||
mData.add(dataPoint);
|
|
||||||
} else {
|
|
||||||
// we have to trim one data
|
|
||||||
mData.remove(0);
|
|
||||||
mData.add(dataPoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recalc the labels when it was the first data
|
|
||||||
boolean keepLabels = mData.size() != 1;
|
|
||||||
|
|
||||||
// update linked graph views
|
|
||||||
// update graphview
|
|
||||||
for (GraphView gv : mGraphViews) {
|
|
||||||
gv.onDataChanged(keepLabels, scrollToEnd);
|
|
||||||
if (scrollToEnd) {
|
|
||||||
gv.getViewport().scrollToEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether there are data points
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return mData.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* checks that the data is in the correct order
|
|
||||||
*
|
|
||||||
* @param onlyLast if not null, it will only check that this
|
|
||||||
* datapoint is after the last point.
|
|
||||||
*/
|
|
||||||
protected void checkValueOrder(DataPointInterface onlyLast) {
|
|
||||||
if (mData.size()>1) {
|
|
||||||
if (onlyLast != null) {
|
|
||||||
// only check last
|
|
||||||
if (onlyLast.getX() < mData.get(mData.size()-1).getX()) {
|
|
||||||
throw new IllegalArgumentException("new x-value must be greater then the last value. x-values has to be ordered in ASC.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
double lx = mData.get(0).getX();
|
|
||||||
|
|
||||||
for (int i = 1; i < mData.size(); i++) {
|
|
||||||
if (mData.get(i).getX() != Double.NaN) {
|
|
||||||
if (lx > mData.get(i).getX()) {
|
|
||||||
throw new IllegalArgumentException("The order of the values is not correct. X-Values have to be ordered ASC. First the lowest x value and at least the highest x value.");
|
|
||||||
}
|
|
||||||
lx = mData.get(i).getX();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.provider.ContactsContract;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* default data point implementation.
|
|
||||||
* This stores the x and y values.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class DataPoint implements DataPointInterface, Serializable {
|
|
||||||
private static final long serialVersionUID=1428263322645L;
|
|
||||||
|
|
||||||
private double x;
|
|
||||||
private double y;
|
|
||||||
|
|
||||||
public DataPoint(double x, double y) {
|
|
||||||
this.x=x;
|
|
||||||
this.y=y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataPoint(Date x, double y) {
|
|
||||||
this.x = x.getTime();
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "["+x+"/"+y+"]";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* interface of data points. Implement this in order
|
|
||||||
* to use your class in {@link com.jjoe64.graphview.series.Series}.
|
|
||||||
*
|
|
||||||
* You can also use the default implementation {@link com.jjoe64.graphview.series.DataPoint} so
|
|
||||||
* you do not have to implement it for yourself.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public interface DataPointInterface {
|
|
||||||
/**
|
|
||||||
* @return the x value
|
|
||||||
*/
|
|
||||||
public double getX();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the y value
|
|
||||||
*/
|
|
||||||
public double getY();
|
|
||||||
}
|
|
|
@ -1,409 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Series to plot the data as line.
|
|
||||||
* The line can be styled with many options.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class LineGraphSeries<E extends DataPointInterface> extends BaseSeries<E> {
|
|
||||||
/**
|
|
||||||
* wrapped styles regarding the line
|
|
||||||
*/
|
|
||||||
private final class Styles {
|
|
||||||
/**
|
|
||||||
* the thickness of the line.
|
|
||||||
* This option will be ignored if you are
|
|
||||||
* using a custom paint via {@link #setCustomPaint(android.graphics.Paint)}
|
|
||||||
*/
|
|
||||||
private int thickness = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the area under the line to the bottom
|
|
||||||
* of the viewport will be filled with a
|
|
||||||
* specific background color.
|
|
||||||
*
|
|
||||||
* @see #backgroundColor
|
|
||||||
*/
|
|
||||||
private boolean drawBackground = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the data points are highlighted as
|
|
||||||
* a visible point.
|
|
||||||
*
|
|
||||||
* @see #dataPointsRadius
|
|
||||||
*/
|
|
||||||
private boolean drawDataPoints = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the radius for the data points.
|
|
||||||
*
|
|
||||||
* @see #drawDataPoints
|
|
||||||
*/
|
|
||||||
private float dataPointsRadius = 10f;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the background color for the filling under
|
|
||||||
* the line.
|
|
||||||
*
|
|
||||||
* @see #drawBackground
|
|
||||||
*/
|
|
||||||
private int backgroundColor = Color.argb(100, 172, 218, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* internal paint object
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* paint for the background
|
|
||||||
*/
|
|
||||||
private Paint mPaintBackground;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* path for the background filling
|
|
||||||
*/
|
|
||||||
private Path mPathBackground;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* path to the line
|
|
||||||
*/
|
|
||||||
private Path mPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* custom paint that can be used.
|
|
||||||
* this will ignore the thickness and color styles.
|
|
||||||
*/
|
|
||||||
private Paint mCustomPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates a series without data
|
|
||||||
*/
|
|
||||||
public LineGraphSeries() {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates a series with data
|
|
||||||
*
|
|
||||||
* @param data data points
|
|
||||||
*/
|
|
||||||
public LineGraphSeries(E[] data) {
|
|
||||||
super(data);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* do the initialization
|
|
||||||
* creates internal objects
|
|
||||||
*/
|
|
||||||
protected void init() {
|
|
||||||
mStyles = new Styles();
|
|
||||||
mPaint = new Paint();
|
|
||||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
mPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
mPaintBackground = new Paint();
|
|
||||||
|
|
||||||
mPathBackground = new Path();
|
|
||||||
mPath = new Path();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* plots the series
|
|
||||||
* draws the line and the background
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
* @param canvas canvas
|
|
||||||
* @param isSecondScale flag if it is the second scale
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
|
|
||||||
resetDataPoints();
|
|
||||||
|
|
||||||
// get data
|
|
||||||
double maxX = graphView.getViewport().getMaxX(false);
|
|
||||||
double minX = graphView.getViewport().getMinX(false);
|
|
||||||
|
|
||||||
double maxY;
|
|
||||||
double minY;
|
|
||||||
if (isSecondScale) {
|
|
||||||
maxY = graphView.getSecondScale().getMaxY();
|
|
||||||
minY = graphView.getSecondScale().getMinY();
|
|
||||||
} else {
|
|
||||||
maxY = graphView.getViewport().getMaxY(false);
|
|
||||||
minY = graphView.getViewport().getMinY(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<E> values = getValues(minX, maxX);
|
|
||||||
|
|
||||||
// draw background
|
|
||||||
double lastEndY = 0;
|
|
||||||
double lastEndX = 0;
|
|
||||||
|
|
||||||
// draw data
|
|
||||||
mPaint.setStrokeWidth(mStyles.thickness);
|
|
||||||
mPaint.setColor(getColor());
|
|
||||||
mPaintBackground.setColor(mStyles.backgroundColor);
|
|
||||||
|
|
||||||
Paint paint;
|
|
||||||
if (mCustomPaint != null) {
|
|
||||||
paint = mCustomPaint;
|
|
||||||
} else {
|
|
||||||
paint = mPaint;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStyles.drawBackground) {
|
|
||||||
mPathBackground.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
double diffY = maxY - minY;
|
|
||||||
double diffX = maxX - minX;
|
|
||||||
|
|
||||||
float graphHeight = graphView.getGraphContentHeight();
|
|
||||||
float graphWidth = graphView.getGraphContentWidth();
|
|
||||||
float graphLeft = graphView.getGraphContentLeft();
|
|
||||||
float graphTop = graphView.getGraphContentTop();
|
|
||||||
|
|
||||||
lastEndY = 0;
|
|
||||||
lastEndX = 0;
|
|
||||||
double lastUsedEndX = 0;
|
|
||||||
float firstX = 0;
|
|
||||||
int i=0;
|
|
||||||
while (values.hasNext()) {
|
|
||||||
E value = values.next();
|
|
||||||
|
|
||||||
double valY = value.getY() - minY;
|
|
||||||
double ratY = valY / diffY;
|
|
||||||
double y = graphHeight * ratY;
|
|
||||||
|
|
||||||
double valX = value.getX() - minX;
|
|
||||||
double ratX = valX / diffX;
|
|
||||||
double x = graphWidth * ratX;
|
|
||||||
|
|
||||||
double orgX = x;
|
|
||||||
double orgY = y;
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
// overdraw
|
|
||||||
if (x > graphWidth) { // end right
|
|
||||||
double b = ((graphWidth - lastEndX) * (y - lastEndY)/(x - lastEndX));
|
|
||||||
y = lastEndY+b;
|
|
||||||
x = graphWidth;
|
|
||||||
}
|
|
||||||
if (y < 0) { // end bottom
|
|
||||||
double b = ((0 - lastEndY) * (x - lastEndX)/(y - lastEndY));
|
|
||||||
x = lastEndX+b;
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
if (y > graphHeight) { // end top
|
|
||||||
double b = ((graphHeight - lastEndY) * (x - lastEndX)/(y - lastEndY));
|
|
||||||
x = lastEndX+b;
|
|
||||||
y = graphHeight;
|
|
||||||
}
|
|
||||||
if (lastEndY < 0) { // start bottom
|
|
||||||
double b = ((0 - y) * (x - lastEndX)/(lastEndY - y));
|
|
||||||
lastEndX = x-b;
|
|
||||||
lastEndY = 0;
|
|
||||||
}
|
|
||||||
if (lastEndX < 0) { // start left
|
|
||||||
double b = ((0 - x) * (y - lastEndY)/(lastEndX - x));
|
|
||||||
lastEndY = y-b;
|
|
||||||
lastEndX = 0;
|
|
||||||
}
|
|
||||||
if (lastEndY > graphHeight) { // start top
|
|
||||||
double b = ((graphHeight - y) * (x - lastEndX)/(lastEndY - y));
|
|
||||||
lastEndX = x-b;
|
|
||||||
lastEndY = graphHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
float startX = (float) lastEndX + (graphLeft + 1);
|
|
||||||
float startY = (float) (graphTop - lastEndY) + graphHeight;
|
|
||||||
float endX = (float) x + (graphLeft + 1);
|
|
||||||
float endY = (float) (graphTop - y) + graphHeight;
|
|
||||||
|
|
||||||
// draw data point
|
|
||||||
if (mStyles.drawDataPoints) {
|
|
||||||
//fix: last value was not drawn. Draw here now the end values
|
|
||||||
canvas.drawCircle(endX, endY, mStyles.dataPointsRadius, mPaint);
|
|
||||||
}
|
|
||||||
registerDataPoint(endX, endY, value);
|
|
||||||
|
|
||||||
mPath.reset();
|
|
||||||
mPath.moveTo(startX, startY);
|
|
||||||
mPath.lineTo(endX, endY);
|
|
||||||
canvas.drawPath(mPath, paint);
|
|
||||||
if (mStyles.drawBackground) {
|
|
||||||
if (i==1) {
|
|
||||||
firstX = startX;
|
|
||||||
mPathBackground.moveTo(startX, startY);
|
|
||||||
}
|
|
||||||
mPathBackground.lineTo(endX, endY);
|
|
||||||
}
|
|
||||||
lastUsedEndX = endX;
|
|
||||||
} else if (mStyles.drawDataPoints) {
|
|
||||||
//fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above)
|
|
||||||
float first_X = (float) x + (graphLeft + 1);
|
|
||||||
float first_Y = (float) (graphTop - y) + graphHeight;
|
|
||||||
//TODO canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint);
|
|
||||||
}
|
|
||||||
lastEndY = orgY;
|
|
||||||
lastEndX = orgX;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStyles.drawBackground) {
|
|
||||||
// end / close path
|
|
||||||
mPathBackground.lineTo((float) lastUsedEndX, graphHeight + graphTop);
|
|
||||||
mPathBackground.lineTo(firstX, graphHeight + graphTop);
|
|
||||||
mPathBackground.close();
|
|
||||||
canvas.drawPath(mPathBackground, mPaintBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the thickness of the line.
|
|
||||||
* This option will be ignored if you are
|
|
||||||
* using a custom paint via {@link #setCustomPaint(android.graphics.Paint)}
|
|
||||||
*
|
|
||||||
* @return the thickness of the line
|
|
||||||
*/
|
|
||||||
public int getThickness() {
|
|
||||||
return mStyles.thickness;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the thickness of the line.
|
|
||||||
* This option will be ignored if you are
|
|
||||||
* using a custom paint via {@link #setCustomPaint(android.graphics.Paint)}
|
|
||||||
*
|
|
||||||
* @param thickness thickness of the line
|
|
||||||
*/
|
|
||||||
public void setThickness(int thickness) {
|
|
||||||
mStyles.thickness = thickness;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the area under the line to the bottom
|
|
||||||
* of the viewport will be filled with a
|
|
||||||
* specific background color.
|
|
||||||
*
|
|
||||||
* @return whether the background will be drawn
|
|
||||||
* @see #getBackgroundColor()
|
|
||||||
*/
|
|
||||||
public boolean isDrawBackground() {
|
|
||||||
return mStyles.drawBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the area under the line to the bottom
|
|
||||||
* of the viewport will be filled with a
|
|
||||||
* specific background color.
|
|
||||||
*
|
|
||||||
* @param drawBackground whether the background will be drawn
|
|
||||||
* @see #setBackgroundColor(int)
|
|
||||||
*/
|
|
||||||
public void setDrawBackground(boolean drawBackground) {
|
|
||||||
mStyles.drawBackground = drawBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the data points are highlighted as
|
|
||||||
* a visible point.
|
|
||||||
*
|
|
||||||
* @return flag whether the data points are highlighted
|
|
||||||
* @see #setDataPointsRadius(float)
|
|
||||||
*/
|
|
||||||
public boolean isDrawDataPoints() {
|
|
||||||
return mStyles.drawDataPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* flag whether the data points are highlighted as
|
|
||||||
* a visible point.
|
|
||||||
*
|
|
||||||
* @param drawDataPoints flag whether the data points are highlighted
|
|
||||||
* @see #setDataPointsRadius(float)
|
|
||||||
*/
|
|
||||||
public void setDrawDataPoints(boolean drawDataPoints) {
|
|
||||||
mStyles.drawDataPoints = drawDataPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the radius for the data points.
|
|
||||||
* @see #setDrawDataPoints(boolean)
|
|
||||||
*/
|
|
||||||
public float getDataPointsRadius() {
|
|
||||||
return mStyles.dataPointsRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param dataPointsRadius the radius for the data points.
|
|
||||||
* @see #setDrawDataPoints(boolean)
|
|
||||||
*/
|
|
||||||
public void setDataPointsRadius(float dataPointsRadius) {
|
|
||||||
mStyles.dataPointsRadius = dataPointsRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the background color for the filling under
|
|
||||||
* the line.
|
|
||||||
* @see #setDrawBackground(boolean)
|
|
||||||
*/
|
|
||||||
public int getBackgroundColor() {
|
|
||||||
return mStyles.backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param backgroundColor the background color for the filling under
|
|
||||||
* the line.
|
|
||||||
* @see #setDrawBackground(boolean)
|
|
||||||
*/
|
|
||||||
public void setBackgroundColor(int backgroundColor) {
|
|
||||||
mStyles.backgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* custom paint that can be used.
|
|
||||||
* this will ignore the thickness and color styles.
|
|
||||||
*
|
|
||||||
* @param customPaint the custom paint to be used for rendering the line
|
|
||||||
*/
|
|
||||||
public void setCustomPaint(Paint customPaint) {
|
|
||||||
this.mCustomPaint = customPaint;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for the tap event which will be
|
|
||||||
* triggered when the user touches on a datapoint.
|
|
||||||
*
|
|
||||||
* Use this in {@link com.jjoe64.graphview.series.BaseSeries#setOnDataPointTapListener(OnDataPointTapListener)}
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public interface OnDataPointTapListener {
|
|
||||||
/**
|
|
||||||
* gets called when the user touches on a datapoint.
|
|
||||||
*
|
|
||||||
* @param series the corresponding series
|
|
||||||
* @param dataPoint the data point that was tapped on
|
|
||||||
*/
|
|
||||||
void onTap(Series series, DataPointInterface dataPoint);
|
|
||||||
}
|
|
|
@ -1,312 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.Point;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Series that plots the data as points.
|
|
||||||
* The points can be different shapes or a
|
|
||||||
* complete custom drawing.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public class PointsGraphSeries<E extends DataPointInterface> extends BaseSeries<E> {
|
|
||||||
/**
|
|
||||||
* interface to implement a custom
|
|
||||||
* drawing for the data points.
|
|
||||||
*/
|
|
||||||
public static interface CustomShape {
|
|
||||||
/**
|
|
||||||
* called when drawing a single data point.
|
|
||||||
* use the x and y coordinates to render your
|
|
||||||
* drawing at this point.
|
|
||||||
*
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param paint internal paint object. this has the correct color.
|
|
||||||
* But you can use your own paint.
|
|
||||||
* @param x x-coordinate the point has to be drawn to
|
|
||||||
* @param y y-coordinate the point has to be drawn to
|
|
||||||
* @param dataPoint the related data point
|
|
||||||
*/
|
|
||||||
void draw(Canvas canvas, Paint paint, float x, float y, DataPointInterface dataPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* choose a predefined shape to render for
|
|
||||||
* each data point.
|
|
||||||
* You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape}
|
|
||||||
*/
|
|
||||||
public enum Shape {
|
|
||||||
/**
|
|
||||||
* draws a point / circle
|
|
||||||
*/
|
|
||||||
POINT,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws a triangle
|
|
||||||
*/
|
|
||||||
TRIANGLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* draws a rectangle
|
|
||||||
*/
|
|
||||||
RECTANGLE
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles for this series
|
|
||||||
*/
|
|
||||||
private final class Styles {
|
|
||||||
/**
|
|
||||||
* this is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*/
|
|
||||||
float size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the shape that will be drawn for each point.
|
|
||||||
*/
|
|
||||||
Shape shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* wrapped styles
|
|
||||||
*/
|
|
||||||
private Styles mStyles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* internal paint object
|
|
||||||
*/
|
|
||||||
private Paint mPaint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
private CustomShape mCustomShape;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the series without data
|
|
||||||
*/
|
|
||||||
public PointsGraphSeries() {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* creates the series with data
|
|
||||||
*
|
|
||||||
* @param data datapoints
|
|
||||||
*/
|
|
||||||
public PointsGraphSeries(E[] data) {
|
|
||||||
super(data);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* inits the internal objects
|
|
||||||
* set the defaults
|
|
||||||
*/
|
|
||||||
protected void init() {
|
|
||||||
mStyles = new Styles();
|
|
||||||
mStyles.size = 20f;
|
|
||||||
mPaint = new Paint();
|
|
||||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
setShape(Shape.POINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* plot the data to the viewport
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param isSecondScale whether it is the second scale
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) {
|
|
||||||
resetDataPoints();
|
|
||||||
|
|
||||||
// get data
|
|
||||||
double maxX = graphView.getViewport().getMaxX(false);
|
|
||||||
double minX = graphView.getViewport().getMinX(false);
|
|
||||||
|
|
||||||
double maxY;
|
|
||||||
double minY;
|
|
||||||
if (isSecondScale) {
|
|
||||||
maxY = graphView.getSecondScale().getMaxY();
|
|
||||||
minY = graphView.getSecondScale().getMinY();
|
|
||||||
} else {
|
|
||||||
maxY = graphView.getViewport().getMaxY(false);
|
|
||||||
minY = graphView.getViewport().getMinY(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<E> values = getValues(minX, maxX);
|
|
||||||
|
|
||||||
// draw background
|
|
||||||
double lastEndY = 0;
|
|
||||||
double lastEndX = 0;
|
|
||||||
|
|
||||||
// draw data
|
|
||||||
mPaint.setColor(getColor());
|
|
||||||
|
|
||||||
double diffY = maxY - minY;
|
|
||||||
double diffX = maxX - minX;
|
|
||||||
|
|
||||||
float graphHeight = graphView.getGraphContentHeight();
|
|
||||||
float graphWidth = graphView.getGraphContentWidth();
|
|
||||||
float graphLeft = graphView.getGraphContentLeft();
|
|
||||||
float graphTop = graphView.getGraphContentTop();
|
|
||||||
|
|
||||||
lastEndY = 0;
|
|
||||||
lastEndX = 0;
|
|
||||||
float firstX = 0;
|
|
||||||
int i=0;
|
|
||||||
while (values.hasNext()) {
|
|
||||||
E value = values.next();
|
|
||||||
|
|
||||||
double valY = value.getY() - minY;
|
|
||||||
double ratY = valY / diffY;
|
|
||||||
double y = graphHeight * ratY;
|
|
||||||
|
|
||||||
double valX = value.getX() - minX;
|
|
||||||
double ratX = valX / diffX;
|
|
||||||
double x = graphWidth * ratX;
|
|
||||||
|
|
||||||
double orgX = x;
|
|
||||||
double orgY = y;
|
|
||||||
|
|
||||||
// overdraw
|
|
||||||
boolean overdraw = false;
|
|
||||||
if (x > graphWidth) { // end right
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
if (y < 0) { // end bottom
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
if (y > graphHeight) { // end top
|
|
||||||
overdraw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
float endX = (float) x + (graphLeft + 1);
|
|
||||||
float endY = (float) (graphTop - y) + graphHeight;
|
|
||||||
registerDataPoint(endX, endY, value);
|
|
||||||
|
|
||||||
// draw data point
|
|
||||||
if (!overdraw) {
|
|
||||||
if (mCustomShape != null) {
|
|
||||||
mCustomShape.draw(canvas, mPaint, endX, endY, value);
|
|
||||||
} else if (mStyles.shape == Shape.POINT) {
|
|
||||||
canvas.drawCircle(endX, endY, mStyles.size, mPaint);
|
|
||||||
} else if (mStyles.shape == Shape.RECTANGLE) {
|
|
||||||
canvas.drawRect(endX-mStyles.size, endY-mStyles.size, endX+mStyles.size, endY+mStyles.size, mPaint);
|
|
||||||
} else if (mStyles.shape == Shape.TRIANGLE) {
|
|
||||||
Point[] points = new Point[3];
|
|
||||||
points[0] = new Point((int)endX, (int)(endY-getSize()));
|
|
||||||
points[1] = new Point((int)(endX+getSize()), (int)(endY+getSize()*0.67));
|
|
||||||
points[2] = new Point((int)(endX-getSize()), (int)(endY+getSize()*0.67));
|
|
||||||
drawArrows(points, canvas, mPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper to render triangle
|
|
||||||
*
|
|
||||||
* @param point array with 3 coordinates
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param paint paint object
|
|
||||||
*/
|
|
||||||
private void drawArrows(Point[] point, Canvas canvas, Paint paint) {
|
|
||||||
float [] points = new float[8];
|
|
||||||
points[0] = point[0].x;
|
|
||||||
points[1] = point[0].y;
|
|
||||||
points[2] = point[1].x;
|
|
||||||
points[3] = point[1].y;
|
|
||||||
points[4] = point[2].x;
|
|
||||||
points[5] = point[2].y;
|
|
||||||
points[6] = point[0].x;
|
|
||||||
points[7] = point[0].y;
|
|
||||||
|
|
||||||
canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint);
|
|
||||||
Path path = new Path();
|
|
||||||
path.moveTo(point[0].x , point[0].y);
|
|
||||||
path.lineTo(point[1].x,point[1].y);
|
|
||||||
path.lineTo(point[2].x,point[2].y);
|
|
||||||
canvas.drawPath(path,paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @return the size of the shape
|
|
||||||
*/
|
|
||||||
public float getSize() {
|
|
||||||
return mStyles.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used for the size of the shape that
|
|
||||||
* will be drawn.
|
|
||||||
* This is useless if you are using a custom shape.
|
|
||||||
*
|
|
||||||
* @param radius the size of the shape
|
|
||||||
*/
|
|
||||||
public void setSize(float radius) {
|
|
||||||
mStyles.size = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public Shape getShape() {
|
|
||||||
return mStyles.shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param s the shape that will be drawn for each point
|
|
||||||
*/
|
|
||||||
public void setShape(Shape s) {
|
|
||||||
mStyles.shape = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a custom handler to render your own
|
|
||||||
* drawing for each data point.
|
|
||||||
*
|
|
||||||
* @param shape handler to use a custom drawing
|
|
||||||
*/
|
|
||||||
public void setCustomShape(CustomShape shape) {
|
|
||||||
mCustomShape = shape;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
/**
|
|
||||||
* GraphView
|
|
||||||
* Copyright (C) 2014 Jonas Gehring
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License,
|
|
||||||
* with the "Linking Exception", which can be found at the license.txt
|
|
||||||
* file in this program.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* with the "Linking Exception" along with this program; if not,
|
|
||||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
|
||||||
*/
|
|
||||||
package com.jjoe64.graphview.series;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
|
|
||||||
import com.jjoe64.graphview.GraphView;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basis interface for series that can be plotted
|
|
||||||
* on the graph.
|
|
||||||
* You can implement this in order to create a completely
|
|
||||||
* custom series type.
|
|
||||||
* But it is recommended to extend {@link com.jjoe64.graphview.series.BaseSeries} or another
|
|
||||||
* implemented Series class to save time.
|
|
||||||
* Anyway this interface can make sense if you want to implement
|
|
||||||
* a custom data provider, because BaseSeries uses a internal Array to store
|
|
||||||
* the data.
|
|
||||||
*
|
|
||||||
* @author jjoe64
|
|
||||||
*/
|
|
||||||
public interface Series<E extends DataPointInterface> {
|
|
||||||
/**
|
|
||||||
* @return the lowest x-value of the data
|
|
||||||
*/
|
|
||||||
public double getLowestValueX();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the highest x-value of the data
|
|
||||||
*/
|
|
||||||
public double getHighestValueX();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the lowest y-value of the data
|
|
||||||
*/
|
|
||||||
public double getLowestValueY();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the highest y-value of the data
|
|
||||||
*/
|
|
||||||
public double getHighestValueY();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the values for a specific range. It is
|
|
||||||
* important that the data comes in the sorted order
|
|
||||||
* (from lowest to highest x-value).
|
|
||||||
*
|
|
||||||
* @param from the minimal x-value
|
|
||||||
* @param until the maximal x-value
|
|
||||||
* @return all datapoints between the from and until x-value
|
|
||||||
* including the from and until data points.
|
|
||||||
*/
|
|
||||||
public Iterator<E> getValues(double from, double until);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plots the series to the viewport.
|
|
||||||
* You have to care about overdrawing.
|
|
||||||
* This method may be called 2 times: one for
|
|
||||||
* the default scale and one time for the
|
|
||||||
* second scale.
|
|
||||||
*
|
|
||||||
* @param graphView corresponding graphview
|
|
||||||
* @param canvas canvas to draw on
|
|
||||||
* @param isSecondScale true if the drawing is for the second scale
|
|
||||||
*/
|
|
||||||
public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the title of the series. Used in the legend
|
|
||||||
*/
|
|
||||||
public String getTitle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the color of the series. Used in the legend and should
|
|
||||||
* be used for the plotted points or lines.
|
|
||||||
*/
|
|
||||||
public int getColor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set a listener for tap on a data point.
|
|
||||||
*
|
|
||||||
* @param l listener
|
|
||||||
*/
|
|
||||||
public void setOnDataPointTapListener(OnDataPointTapListener l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called by the tap detector in order to trigger
|
|
||||||
* the on tap on datapoint event.
|
|
||||||
*
|
|
||||||
* @param x pixel
|
|
||||||
* @param y pixel
|
|
||||||
*/
|
|
||||||
void onTap(float x, float y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* called when the series was added to a graph
|
|
||||||
*
|
|
||||||
* @param graphView graphview
|
|
||||||
*/
|
|
||||||
void onGraphViewAttached(GraphView graphView);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return whether there are data points
|
|
||||||
*/
|
|
||||||
boolean isEmpty();
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<declare-styleable name="GraphViewXML">
|
|
||||||
<attr name="seriesData" format="string" />
|
|
||||||
<attr name="seriesType" format="string" />
|
|
||||||
<attr name="seriesTitle" format="string" />
|
|
||||||
<attr name="android:title" />
|
|
||||||
<attr name="seriesColor" format="color" />
|
|
||||||
</declare-styleable>
|
|
||||||
</resources>
|
|
|
@ -1,229 +0,0 @@
|
||||||
package com.joanzapata.iconify;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.*;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import com.joanzapata.iconify.internal.IconFontDescriptorWrapper;
|
|
||||||
|
|
||||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Embed an icon into a Drawable that can be used as TextView icons, or ActionBar icons.
|
|
||||||
* <pre>
|
|
||||||
* new IconDrawable(context, IconValue.icon_star)
|
|
||||||
* .colorRes(R.color.white)
|
|
||||||
* .actionBarSize();
|
|
||||||
* </pre>
|
|
||||||
* If you don't set the size of the drawable, it will use the size
|
|
||||||
* that is given to him. Note that in an ActionBar, if you don't
|
|
||||||
* set the size explicitly it uses 0, so please use actionBarSize().
|
|
||||||
*/
|
|
||||||
public class IconDrawable extends Drawable {
|
|
||||||
|
|
||||||
public static final int ANDROID_ACTIONBAR_ICON_SIZE_DP = 24;
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
private Icon icon;
|
|
||||||
|
|
||||||
private TextPaint paint;
|
|
||||||
|
|
||||||
private int size = -1;
|
|
||||||
|
|
||||||
private int alpha = 255;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an IconDrawable.
|
|
||||||
* @param context Your activity or application context.
|
|
||||||
* @param iconKey The icon key you want this drawable to display.
|
|
||||||
* @throws IllegalArgumentException if the key doesn't match any icon.
|
|
||||||
*/
|
|
||||||
public IconDrawable(Context context, String iconKey) {
|
|
||||||
Icon icon = Iconify.findIconForKey(iconKey);
|
|
||||||
if (icon == null) {
|
|
||||||
throw new IllegalArgumentException("No icon with that key \"" + iconKey + "\".");
|
|
||||||
}
|
|
||||||
init(context, icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an IconDrawable.
|
|
||||||
* @param context Your activity or application context.
|
|
||||||
* @param icon The icon you want this drawable to display.
|
|
||||||
*/
|
|
||||||
public IconDrawable(Context context, Icon icon) {
|
|
||||||
init(context, icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(Context context, Icon icon) {
|
|
||||||
this.context = context;
|
|
||||||
this.icon = icon;
|
|
||||||
paint = new TextPaint();
|
|
||||||
IconFontDescriptorWrapper descriptor = Iconify.findTypefaceOf(icon);
|
|
||||||
if (descriptor == null) {
|
|
||||||
throw new IllegalStateException("Unable to find the module associated " +
|
|
||||||
"with icon " + icon.key() + ", have you registered the module " +
|
|
||||||
"you are trying to use with Iconify.with(...) in your Application?");
|
|
||||||
}
|
|
||||||
paint.setTypeface(descriptor.getTypeface(context));
|
|
||||||
paint.setStyle(Paint.Style.FILL);
|
|
||||||
paint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
paint.setUnderlineText(false);
|
|
||||||
paint.setColor(Color.BLACK);
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of this icon to the standard Android ActionBar.
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable actionBarSize() {
|
|
||||||
return sizeDp(ANDROID_ACTIONBAR_ICON_SIZE_DP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of the drawable.
|
|
||||||
* @param dimenRes The dimension resource.
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable sizeRes(int dimenRes) {
|
|
||||||
return sizePx(context.getResources().getDimensionPixelSize(dimenRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of the drawable.
|
|
||||||
* @param size The size in density-independent pixels (dp).
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable sizeDp(int size) {
|
|
||||||
return sizePx(convertDpToPx(context, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of the drawable.
|
|
||||||
* @param size The size in pixels (px).
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable sizePx(int size) {
|
|
||||||
this.size = size;
|
|
||||||
setBounds(0, 0, size, size);
|
|
||||||
invalidateSelf();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the color of the drawable.
|
|
||||||
* @param color The color, usually from android.graphics.Color or 0xFF012345.
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable color(int color) {
|
|
||||||
paint.setColor(color);
|
|
||||||
invalidateSelf();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the color of the drawable.
|
|
||||||
* @param colorRes The color resource, from your R file.
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable colorRes(int colorRes) {
|
|
||||||
paint.setColor(context.getColor(colorRes));
|
|
||||||
invalidateSelf();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the alpha of this drawable.
|
|
||||||
* @param alpha The alpha, between 0 (transparent) and 255 (opaque).
|
|
||||||
* @return The current IconDrawable for chaining.
|
|
||||||
*/
|
|
||||||
public IconDrawable alpha(int alpha) {
|
|
||||||
setAlpha(alpha);
|
|
||||||
invalidateSelf();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIntrinsicHeight() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIntrinsicWidth() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void draw(Canvas canvas) {
|
|
||||||
Rect bounds = getBounds();
|
|
||||||
int height = bounds.height();
|
|
||||||
paint.setTextSize(height);
|
|
||||||
Rect textBounds = new Rect();
|
|
||||||
String textValue = String.valueOf(icon.character());
|
|
||||||
paint.getTextBounds(textValue, 0, 1, textBounds);
|
|
||||||
int textHeight = textBounds.height();
|
|
||||||
float textBottom = bounds.top + (height - textHeight) / 2f + textHeight - textBounds.bottom;
|
|
||||||
canvas.drawText(textValue, bounds.exactCenterX(), textBottom, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStateful() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean setState(int[] stateSet) {
|
|
||||||
int oldValue = paint.getAlpha();
|
|
||||||
int newValue = isEnabled(stateSet) ? alpha : alpha / 2;
|
|
||||||
paint.setAlpha(newValue);
|
|
||||||
return oldValue != newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAlpha(int alpha) {
|
|
||||||
this.alpha = alpha;
|
|
||||||
paint.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColorFilter(ColorFilter cf) {
|
|
||||||
paint.setColorFilter(cf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearColorFilter() {
|
|
||||||
paint.setColorFilter(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("WrongConstant") @Override
|
|
||||||
public int getOpacity() {
|
|
||||||
return this.alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets paint style.
|
|
||||||
* @param style to be applied
|
|
||||||
*/
|
|
||||||
public void setStyle(Paint.Style style) {
|
|
||||||
paint.setStyle(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Util
|
|
||||||
private boolean isEnabled(int[] stateSet) {
|
|
||||||
for (int state : stateSet)
|
|
||||||
if (state == android.R.attr.state_enabled)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Util
|
|
||||||
private int convertDpToPx(Context context, float dp) {
|
|
||||||
return (int) TypedValue.applyDimension(
|
|
||||||
COMPLEX_UNIT_DIP, dp,
|
|
||||||
context.getResources().getDisplayMetrics());
|
|
||||||
}
|
|
||||||
}
|
|
BIN
libs/graphview.aar
Normal file
BIN
libs/graphview.aar
Normal file
Binary file not shown.
|
@ -19,4 +19,3 @@ include ':diaconn'
|
||||||
include ':openhumans'
|
include ':openhumans'
|
||||||
include ':shared'
|
include ':shared'
|
||||||
include ':iconify'
|
include ':iconify'
|
||||||
include ':graphview'
|
|
||||||
|
|
|
@ -107,8 +107,8 @@ dependencies {
|
||||||
compileOnly "com.google.android.wearable:wearable:$wearable_version"
|
compileOnly "com.google.android.wearable:wearable:$wearable_version"
|
||||||
implementation "com.google.android.support:wearable:$wearable_version"
|
implementation "com.google.android.support:wearable:$wearable_version"
|
||||||
implementation "com.google.android.gms:play-services-wearable:$play_services_wearable_version"
|
implementation "com.google.android.gms:play-services-wearable:$play_services_wearable_version"
|
||||||
implementation(files('libs/ustwo-clockwise-debug.aar'))
|
implementation(files("${rootProject.rootDir}/libs/ustwo-clockwise-debug.aar"))
|
||||||
implementation(files('libs/wearpreferenceactivity-0.5.0.aar'))
|
implementation(files("${rootProject.rootDir}/libs/wearpreferenceactivity-0.5.0.aar"))
|
||||||
implementation('com.github.lecho:hellocharts-library:1.5.8@aar')
|
implementation('com.github.lecho:hellocharts-library:1.5.8@aar')
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue