Browse Source

//步数,股票表格控件(从github上clone下来,修改源码满足我们的产品需求)
api 'com.airsmart.github.lib:MPAndroidChart:1.0.4'
//日历控件(从github上clone下来,修改源码满足我们的产品需求)
api 'com.airsmart.github.lib:calendarview:1.0.0'

houjie 4 years ago
parent
commit
a606000778
100 changed files with 18102 additions and 34 deletions
  1. 14 34
      .gitignore
  2. 125 0
      .idea/codeStyles/Project.xml
  3. 5 0
      .idea/codeStyles/codeStyleConfig.xml
  4. 22 0
      .idea/gradle.xml
  5. 9 0
      .idea/misc.xml
  6. 12 0
      .idea/runConfigurations.xml
  7. 1 0
      MPChartLib/.gitignore
  8. 71 0
      MPChartLib/build.gradle
  9. BIN
      MPChartLib/ic_launcher-web.png
  10. 89 0
      MPChartLib/pom.xml
  11. 13 0
      MPChartLib/src/main/AndroidManifest.xml
  12. 207 0
      MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java
  13. 309 0
      MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java
  14. 91 0
      MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java
  15. 130 0
      MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java
  16. 94 0
      MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java
  17. 258 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java
  18. 1674 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java
  19. 43 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java
  20. 44 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java
  21. 1821 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java
  22. 272 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java
  23. 346 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java
  24. 50 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java
  25. 804 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java
  26. 499 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java
  27. 362 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java
  28. 77 0
      MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java
  29. 815 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java
  30. 173 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java
  31. 95 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java
  32. 47 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java
  33. 825 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java
  34. 78 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java
  35. 215 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java
  36. 167 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java
  37. 129 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java
  38. 118 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java
  39. 467 0
      MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java
  40. 119 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java
  41. 299 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java
  42. 310 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java
  43. 27 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java
  44. 48 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java
  45. 506 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java
  46. 97 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java
  47. 34 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java
  48. 71 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java
  49. 91 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java
  50. 21 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java
  51. 295 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java
  52. 193 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java
  53. 820 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java
  54. 272 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java
  55. 456 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java
  56. 173 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java
  57. 27 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java
  58. 417 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java
  59. 135 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java
  60. 120 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java
  61. 100 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java
  62. 263 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java
  63. 86 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java
  64. 58 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java
  65. 122 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java
  66. 44 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java
  67. 40 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java
  68. 157 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java
  69. 102 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java
  70. 146 0
      MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java
  71. 14 0
      MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java
  72. 24 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java
  73. 54 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java
  74. 45 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java
  75. 67 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java
  76. 29 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java
  77. 24 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java
  78. 31 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java
  79. 63 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java
  80. 91 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java
  81. 54 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java
  82. 71 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java
  83. 137 0
      MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java
  84. 163 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java
  85. 246 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java
  86. 93 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java
  87. 243 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java
  88. 85 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java
  89. 17 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java
  90. 25 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java
  91. 66 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java
  92. 79 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java
  93. 38 0
      MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java
  94. 11 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java
  95. 16 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java
  96. 8 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java
  97. 8 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java
  98. 69 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java
  99. 11 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java
  100. 0 0
      MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java

+ 14 - 34
.gitignore

@@ -1,34 +1,14 @@
-# ---> Android
-# Built application files
-*.apk
-*.ap_
-
-# Files for the Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin/
-gen/
-
-# Gradle files
-.gradle/
-build/
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation/
-
-# Android Studio captures folder
-captures/
-
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx

+ 125 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,125 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <AndroidXmlCodeStyleSettings>
+      <option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
+    </AndroidXmlCodeStyleSettings>
+    <JetCodeStyleSettings>
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </JetCodeStyleSettings>
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+    <codeStyleSettings language="kotlin">
+      <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 5 - 0
.idea/codeStyles/codeStyleConfig.xml

@@ -0,0 +1,5 @@
+<component name="ProjectCodeStyleConfiguration">
+  <state>
+    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+  </state>
+</component>

+ 22 - 0
.idea/gradle.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/MPChartLib" />
+            <option value="$PROJECT_DIR$/app" />
+            <option value="$PROJECT_DIR$/appBarChart" />
+            <option value="$PROJECT_DIR$/calendarview" />
+          </set>
+        </option>
+        <option name="resolveModulePerSourceSet" value="false" />
+        <option name="testRunner" value="PLATFORM" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 9 - 0
.idea/misc.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 12 - 0
.idea/runConfigurations.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 1 - 0
MPChartLib/.gitignore

@@ -0,0 +1 @@
+/build

+ 71 - 0
MPChartLib/build.gradle

@@ -0,0 +1,71 @@
+apply plugin: 'com.android.library'
+//apply plugin: 'com.github.dcendents.android-maven'
+apply plugin: 'maven'
+group = 'com.github.philjay'
+
+android {
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
+    defaultConfig {
+        minSdkVersion 14
+        targetSdkVersion 28
+        versionCode 3
+        versionName '3.1.0'
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    testOptions {
+        unitTests.returnDefaultValues = true // this prevents "not mocked" error
+    }
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            repository(url: "https://repo.rdc.aliyun.com/repository/84521-release-D7jNC2/") {
+                authentication(userName: "icYZUR", password: "Xgk5Pc7PcV")
+            }
+            pom.version = "1.0.4"
+            pom.artifactId = "MPAndroidChart" // 对应 appcompat-v7
+            pom.groupId = "com.airsmart.github.lib" // com.android.support
+        }
+    }
+}
+
+
+dependencies {
+    //implementation 'androidx.annotation:annotation:1.0.0'
+
+    implementation 'com.android.support:support-annotations:28.0.0'
+    //javadocDeps 'com.android.support:support-annotations:28.0.0'
+    //implementation 'com.android.support:appcompat-v7:28.0.0'
+    testImplementation 'junit:junit:4.12'
+}
+
+task sourcesJar(type: Jar) {
+    from android.sourceSets.main.java.srcDirs
+    classifier = 'sources'
+}
+
+task javadoc(type: Javadoc) {
+    options.charSet = 'UTF-8'
+    failOnError false
+    source = android.sourceSets.main.java.sourceFiles
+    //classpath += javadocDeps
+    configurations.implementation.setCanBeResolved(true)
+    classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + configurations.implementation
+}
+
+task javadocJar(type: Jar, dependsOn: javadoc) {
+    classifier = 'javadoc'
+    from javadoc.destinationDir
+}
+
+artifacts {
+//    archives sourcesJar
+//    archives javadocJar
+}

BIN
MPChartLib/ic_launcher-web.png


+ 89 - 0
MPChartLib/pom.xml

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 Philipp Jahoda <philjay.librarysup@gmail.com>
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+	<version>1.4.2-SNAPSHOT</version>
+    <groupId>com.github.mikephil</groupId>
+    <artifactId>MPAndroidChart</artifactId>
+    <name>MPAndroidChart</name>
+	<description>A simple Android chart view/graph view library, supporting line- bar- and piecharts as well as scaling, dragging and animations</description>
+	<url>https://github.com/PhilJay/MPAndroidChart</url>
+    <packaging>apklib</packaging>
+    <!--<packaging>aar</packaging>-->
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+    	<sourceDirectory>src</sourceDirectory>
+        <plugins>
+            <plugin>
+                <groupId>com.jayway.maven.plugins.android.generation2</groupId>
+                <artifactId>android-maven-plugin</artifactId>
+                <version>3.9.0-rc.2</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <!--<sdk>-->
+                        <!--<path>${env.ANDROID_HOME}</path>-->
+                        <!--<platform>16</platform>-->
+                    <!--</sdk>-->
+                    <undeployBeforeDeploy>true</undeployBeforeDeploy>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.android</groupId>
+            <artifactId>android</artifactId>
+            <scope>provided</scope>
+            <version>4.1.1.4</version>
+        </dependency>
+    </dependencies>
+
+
+    <issueManagement>
+        <url>https://github.com/PhilJay/MPAndroidChart/issues</url>
+        <system>GitHub Issues</system>
+    </issueManagement>
+
+    <licenses>
+        <license>
+            <name>Apache License Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <scm>
+        <url>https://github.com/PhilJay/MPAndroidChart</url>
+        <connection>scm:git:git://github.com/PhilJay/MPAndroidChart.git</connection>
+        <developerConnection>scm:git:git@github.com:PhilJay/MPAndroidChart.git</developerConnection>
+    </scm>
+
+    <developers>
+        <developer>
+            <name>Philipp Jahoda</name>
+            <email>philjay.librarysup@gmail.com</email>
+            <url>http://stackoverflow.com/users/1590502/philipp-jahoda</url>
+            <id>PhilJay</id>
+        </developer>
+    </developers>
+</project>

+ 13 - 0
MPChartLib/src/main/AndroidManifest.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="com.github.mikephil.charting">
+
+ <!--  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+    </application> -->
+
+</manifest>

+ 207 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java

@@ -0,0 +1,207 @@
+package com.github.mikephil.charting.animation;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.support.annotation.RequiresApi;
+
+import com.github.mikephil.charting.animation.Easing.EasingFunction;
+
+/**
+ * Object responsible for all animations in the Chart. Animations require API level 11.
+ *
+ * @author Philipp Jahoda
+ * @author Mick Ashton
+ */
+public class ChartAnimator {
+
+    /** object that is updated upon animation update */
+    private AnimatorUpdateListener mListener;
+
+    /** The phase of drawn values on the y-axis. 0 - 1 */
+    @SuppressWarnings("WeakerAccess")
+    protected float mPhaseY = 1f;
+
+    /** The phase of drawn values on the x-axis. 0 - 1 */
+    @SuppressWarnings("WeakerAccess")
+    protected float mPhaseX = 1f;
+
+    public ChartAnimator() { }
+
+    @RequiresApi(11)
+    public ChartAnimator(AnimatorUpdateListener listener) {
+        mListener = listener;
+    }
+
+    @RequiresApi(11)
+    private ObjectAnimator xAnimator(int duration, EasingFunction easing) {
+
+        ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f);
+        animatorX.setInterpolator(easing);
+        animatorX.setDuration(duration);
+
+        return animatorX;
+    }
+
+    @RequiresApi(11)
+    private ObjectAnimator yAnimator(int duration, EasingFunction easing) {
+
+        ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f);
+        animatorY.setInterpolator(easing);
+        animatorY.setDuration(duration);
+
+        return animatorY;
+    }
+
+    /**
+     * Animates values along the X axis, in a linear fashion.
+     *
+     * @param durationMillis animation duration
+     */
+    @RequiresApi(11)
+    public void animateX(int durationMillis) {
+        animateX(durationMillis, Easing.Linear);
+    }
+
+    /**
+     * Animates values along the X axis.
+     *
+     * @param durationMillis animation duration
+     * @param easing EasingFunction
+     */
+    @RequiresApi(11)
+    public void animateX(int durationMillis, EasingFunction easing) {
+
+        ObjectAnimator animatorX = xAnimator(durationMillis, easing);
+        animatorX.addUpdateListener(mListener);
+        animatorX.start();
+    }
+
+    /**
+     * Animates values along both the X and Y axes, in a linear fashion.
+     *
+     * @param durationMillisX animation duration along the X axis
+     * @param durationMillisY animation duration along the Y axis
+     */
+    @RequiresApi(11)
+    public void animateXY(int durationMillisX, int durationMillisY) {
+        animateXY(durationMillisX, durationMillisY, Easing.Linear, Easing.Linear);
+    }
+
+    /**
+     * Animates values along both the X and Y axes.
+     *
+     * @param durationMillisX animation duration along the X axis
+     * @param durationMillisY animation duration along the Y axis
+     * @param easing EasingFunction for both axes
+     */
+    @RequiresApi(11)
+    public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) {
+
+        ObjectAnimator xAnimator = xAnimator(durationMillisX, easing);
+        ObjectAnimator yAnimator = yAnimator(durationMillisY, easing);
+
+        if (durationMillisX > durationMillisY) {
+            xAnimator.addUpdateListener(mListener);
+        } else {
+            yAnimator.addUpdateListener(mListener);
+        }
+
+        xAnimator.start();
+        yAnimator.start();
+    }
+
+    /**
+     * Animates values along both the X and Y axes.
+     *
+     * @param durationMillisX animation duration along the X axis
+     * @param durationMillisY animation duration along the Y axis
+     * @param easingX EasingFunction for the X axis
+     * @param easingY EasingFunction for the Y axis
+     */
+    @RequiresApi(11)
+    public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX,
+                          EasingFunction easingY) {
+
+        ObjectAnimator xAnimator = xAnimator(durationMillisX, easingX);
+        ObjectAnimator yAnimator = yAnimator(durationMillisY, easingY);
+
+        if (durationMillisX > durationMillisY) {
+            xAnimator.addUpdateListener(mListener);
+        } else {
+            yAnimator.addUpdateListener(mListener);
+        }
+
+        xAnimator.start();
+        yAnimator.start();
+    }
+
+    /**
+     * Animates values along the Y axis, in a linear fashion.
+     *
+     * @param durationMillis animation duration
+     */
+    @RequiresApi(11)
+    public void animateY(int durationMillis) {
+        animateY(durationMillis, Easing.Linear);
+    }
+
+    /**
+     * Animates values along the Y axis.
+     *
+     * @param durationMillis animation duration
+     * @param easing EasingFunction
+     */
+    @RequiresApi(11)
+    public void animateY(int durationMillis, EasingFunction easing) {
+
+        ObjectAnimator animatorY = yAnimator(durationMillis, easing);
+        animatorY.addUpdateListener(mListener);
+        animatorY.start();
+    }
+
+    /**
+     * Gets the Y axis phase of the animation.
+     *
+     * @return float value of {@link #mPhaseY}
+     */
+    public float getPhaseY() {
+        return mPhaseY;
+    }
+
+    /**
+     * Sets the Y axis phase of the animation.
+     *
+     * @param phase float value between 0 - 1
+     */
+    public void setPhaseY(float phase) {
+        if (phase > 1f) {
+            phase = 1f;
+        } else if (phase < 0f) {
+            phase = 0f;
+        }
+        mPhaseY = phase;
+    }
+
+    /**
+     * Gets the X axis phase of the animation.
+     *
+     * @return float value of {@link #mPhaseX}
+     */
+    public float getPhaseX() {
+        return mPhaseX;
+    }
+
+    /**
+     * Sets the X axis phase of the animation.
+     *
+     * @param phase float value between 0 - 1
+     */
+    public void setPhaseX(float phase) {
+        if (phase > 1f) {
+            phase = 1f;
+        } else if (phase < 0f) {
+            phase = 0f;
+        }
+        mPhaseX = phase;
+    }
+}

+ 309 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java

@@ -0,0 +1,309 @@
+package com.github.mikephil.charting.animation;
+
+import android.animation.TimeInterpolator;
+import android.support.annotation.RequiresApi;
+
+/**
+ * Easing options.
+ *
+ * @author Daniel Cohen Gindi
+ * @author Mick Ashton
+ */
+@SuppressWarnings("WeakerAccess")
+@RequiresApi(11)
+public class Easing {
+
+    public interface EasingFunction extends TimeInterpolator {
+        @Override
+        float getInterpolation(float input);
+    }
+
+    private static final float DOUBLE_PI = 2f * (float) Math.PI;
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction Linear = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return input;
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInQuad = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return input * input;
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutQuad = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return -input * (input - 2f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutQuad = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input *= 2f;
+
+            if (input < 1f) {
+                return 0.5f * input * input;
+            }
+
+            return -0.5f * ((--input) * (input - 2f) - 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInCubic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return (float) Math.pow(input, 3);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutCubic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input--;
+            return (float) Math.pow(input, 3) + 1f;
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutCubic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input *= 2f;
+            if (input < 1f) {
+                return 0.5f * (float) Math.pow(input, 3);
+            }
+            input -= 2f;
+            return 0.5f * ((float) Math.pow(input, 3) + 2f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInQuart = new EasingFunction() {
+
+        public float getInterpolation(float input) {
+            return (float) Math.pow(input, 4);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutQuart = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input--;
+            return -((float) Math.pow(input, 4) - 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutQuart = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input *= 2f;
+            if (input < 1f) {
+                return 0.5f * (float) Math.pow(input, 4);
+            }
+            input -= 2f;
+            return -0.5f * ((float) Math.pow(input, 4) - 2f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInSine = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return -(float) Math.cos(input * (Math.PI / 2f)) + 1f;
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutSine = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return (float) Math.sin(input * (Math.PI / 2f));
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutSine = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return -0.5f * ((float) Math.cos(Math.PI * input) - 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInExpo = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return (input == 0) ? 0f : (float) Math.pow(2f, 10f * (input - 1f));
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutExpo = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return (input == 1f) ? 1f : (-(float) Math.pow(2f, -10f * (input + 1f)));
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutExpo = new EasingFunction() {
+        public float getInterpolation(float input) {
+            if (input == 0) {
+                return 0f;
+            } else if (input == 1f) {
+                return 1f;
+            }
+
+            input *= 2f;
+            if (input < 1f) {
+                return 0.5f * (float) Math.pow(2f, 10f * (input - 1f));
+            }
+            return 0.5f * (-(float) Math.pow(2f, -10f * --input) + 2f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInCirc = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return -((float) Math.sqrt(1f - input * input) - 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutCirc = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input--;
+            return (float) Math.sqrt(1f - input * input);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutCirc = new EasingFunction() {
+        public float getInterpolation(float input) {
+            input *= 2f;
+            if (input < 1f) {
+                return -0.5f * ((float) Math.sqrt(1f - input * input) - 1f);
+            }
+            return 0.5f * ((float) Math.sqrt(1f - (input -= 2f) * input) + 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInElastic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            if (input == 0) {
+                return 0f;
+            } else if (input == 1) {
+                return 1f;
+            }
+
+            float p = 0.3f;
+            float s = p / DOUBLE_PI * (float) Math.asin(1f);
+            return -((float) Math.pow(2f, 10f * (input -= 1f))
+                    *(float) Math.sin((input - s) * DOUBLE_PI / p));
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutElastic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            if (input == 0) {
+                return 0f;
+            } else if (input == 1) {
+                return 1f;
+            }
+
+            float p = 0.3f;
+            float s = p / DOUBLE_PI * (float) Math.asin(1f);
+            return 1f
+                    + (float) Math.pow(2f, -10f * input)
+                    * (float) Math.sin((input - s) * DOUBLE_PI / p);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutElastic = new EasingFunction() {
+        public float getInterpolation(float input) {
+            if (input == 0) {
+                return 0f;
+            }
+
+            input *= 2f;
+            if (input == 2) {
+                return 1f;
+            }
+
+            float p = 1f / 0.45f;
+            float s = 0.45f / DOUBLE_PI * (float) Math.asin(1f);
+            if (input < 1f) {
+                return -0.5f
+                        * ((float) Math.pow(2f, 10f * (input -= 1f))
+                        * (float) Math.sin((input * 1f - s) * DOUBLE_PI * p));
+            }
+            return 1f + 0.5f
+                    * (float) Math.pow(2f, -10f * (input -= 1f))
+                    * (float) Math.sin((input * 1f - s) * DOUBLE_PI * p);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInBack = new EasingFunction() {
+        public float getInterpolation(float input) {
+            final float s = 1.70158f;
+            return input * input * ((s + 1f) * input - s);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutBack = new EasingFunction() {
+        public float getInterpolation(float input) {
+            final float s = 1.70158f;
+            input--;
+            return (input * input * ((s + 1f) * input + s) + 1f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutBack = new EasingFunction() {
+        public float getInterpolation(float input) {
+            float s = 1.70158f;
+            input *= 2f;
+            if (input < 1f) {
+                return 0.5f * (input * input * (((s *= (1.525f)) + 1f) * input - s));
+            }
+            return 0.5f * ((input -= 2f) * input * (((s *= (1.525f)) + 1f) * input + s) + 2f);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInBounce = new EasingFunction() {
+        public float getInterpolation(float input) {
+            return 1f - EaseOutBounce.getInterpolation(1f - input);
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseOutBounce = new EasingFunction() {
+        public float getInterpolation(float input) {
+            float s = 7.5625f;
+            if (input < (1f / 2.75f)) {
+                return s * input * input;
+            } else if (input < (2f / 2.75f)) {
+                return s * (input -= (1.5f / 2.75f)) * input + 0.75f;
+            } else if (input < (2.5f / 2.75f)) {
+                return s * (input -= (2.25f / 2.75f)) * input + 0.9375f;
+            }
+            return s * (input -= (2.625f / 2.75f)) * input + 0.984375f;
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static final EasingFunction EaseInOutBounce = new EasingFunction() {
+        public float getInterpolation(float input) {
+            if (input < 0.5f) {
+                return EaseInBounce.getInterpolation(input * 2f) * 0.5f;
+            }
+            return EaseOutBounce.getInterpolation(input * 2f - 1f) * 0.5f + 0.5f;
+        }
+    };
+
+}

+ 91 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java

@@ -0,0 +1,91 @@
+
+package com.github.mikephil.charting.buffer;
+
+import java.util.List;
+
+/**
+ * Buffer class to boost performance while drawing. Concept: Replace instead of
+ * recreate.
+ * 
+ * @author Philipp Jahoda
+ * @param <T> The data the buffer accepts to be fed with.
+ */
+public abstract class AbstractBuffer<T> {
+
+    /** index in the buffer */
+    protected int index = 0;
+
+    /** float-buffer that holds the data points to draw, order: x,y,x,y,... */
+    public final float[] buffer;
+
+    /** animation phase x-axis */
+    protected float phaseX = 1f;
+
+    /** animation phase y-axis */
+    protected float phaseY = 1f;
+
+    /** indicates from which x-index the visible data begins */
+    protected int mFrom = 0;
+
+    /** indicates to which x-index the visible data ranges */
+    protected int mTo = 0;
+
+    /**
+     * Initialization with buffer-size.
+     * 
+     * @param size
+     */
+    public AbstractBuffer(int size) {
+        index = 0;
+        buffer = new float[size];
+    }
+
+    /** limits the drawing on the x-axis */
+    public void limitFrom(int from) {
+        if (from < 0)
+            from = 0;
+        mFrom = from;
+    }
+
+    /** limits the drawing on the x-axis */
+    public void limitTo(int to) {
+        if (to < 0)
+            to = 0;
+        mTo = to;
+    }
+
+    /**
+     * Resets the buffer index to 0 and makes the buffer reusable.
+     */
+    public void reset() {
+        index = 0;
+    }
+
+    /**
+     * Returns the size (length) of the buffer array.
+     * 
+     * @return
+     */
+    public int size() {
+        return buffer.length;
+    }
+
+    /**
+     * Set the phases used for animations.
+     * 
+     * @param phaseX
+     * @param phaseY
+     */
+    public void setPhases(float phaseX, float phaseY) {
+        this.phaseX = phaseX;
+        this.phaseY = phaseY;
+    }
+
+    /**
+     * Builds up the buffer with the provided data and resets the buffer-index
+     * after feed-completion. This needs to run FAST.
+     * 
+     * @param data
+     */
+    public abstract void feed(T data);
+}

+ 130 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java

@@ -0,0 +1,130 @@
+
+package com.github.mikephil.charting.buffer;
+
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+
+public class BarBuffer extends AbstractBuffer<IBarDataSet> {
+
+    protected int mDataSetIndex = 0;
+    protected int mDataSetCount = 1;
+    protected boolean mContainsStacks = false;
+    protected boolean mInverted = false;
+
+    /** width of the bar on the x-axis, in values (not pixels) */
+    protected float mBarWidth = 1f;
+
+    public BarBuffer(int size, int dataSetCount, boolean containsStacks) {
+        super(size);
+        this.mDataSetCount = dataSetCount;
+        this.mContainsStacks = containsStacks;
+    }
+
+    public void setBarWidth(float barWidth) {
+        this.mBarWidth = barWidth;
+    }
+
+    public void setDataSet(int index) {
+        this.mDataSetIndex = index;
+    }
+
+    public void setInverted(boolean inverted) {
+        this.mInverted = inverted;
+    }
+
+    protected void addBar(float left, float top, float right, float bottom) {
+
+        buffer[index++] = left;
+        buffer[index++] = top;
+        buffer[index++] = right;
+        buffer[index++] = bottom;
+    }
+
+    @Override
+    public void feed(IBarDataSet data) {
+
+        float size = data.getEntryCount() * phaseX;
+        float barWidthHalf = mBarWidth / 2f;
+
+        for (int i = 0; i < size; i++) {
+
+            BarEntry e = data.getEntryForIndex(i);
+
+            if(e == null)
+                continue;
+
+            float x = e.getX();
+            float y = e.getY();
+            float[] vals = e.getYVals();
+
+            if (!mContainsStacks || vals == null) {
+
+                float left = x - barWidthHalf;
+                float right = x + barWidthHalf;
+                float bottom, top;
+
+                if (mInverted) {
+                    bottom = y >= 0 ? y : 0;
+                    top = y <= 0 ? y : 0;
+                } else {
+                    top = y >= 0 ? y : 0;
+                    bottom = y <= 0 ? y : 0;
+                }
+
+                // multiply the height of the rect with the phase
+                if (top > 0)
+                    top *= phaseY;
+                else
+                    bottom *= phaseY;
+
+                addBar(left, top, right, bottom);
+
+            } else {
+
+                float posY = 0f;
+                float negY = -e.getNegativeSum();
+                float yStart = 0f;
+
+                // fill the stack
+                for (int k = 0; k < vals.length; k++) {
+
+                    float value = vals[k];
+
+                    if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) {
+                        // Take care of the situation of a 0.0 value, which overlaps a non-zero bar
+                        y = value;
+                        yStart = y;
+                    } else if (value >= 0.0f) {
+                        y = posY;
+                        yStart = posY + value;
+                        posY = yStart;
+                    } else {
+                        y = negY;
+                        yStart = negY + Math.abs(value);
+                        negY += Math.abs(value);
+                    }
+
+                    float left = x - barWidthHalf;
+                    float right = x + barWidthHalf;
+                    float bottom, top;
+
+                    if (mInverted) {
+                        bottom = y >= yStart ? y : yStart;
+                        top = y <= yStart ? y : yStart;
+                    } else {
+                        top = y >= yStart ? y : yStart;
+                        bottom = y <= yStart ? y : yStart;
+                    }
+
+                    // multiply the height of the rect with the phase
+                    top *= phaseY;
+                    bottom *= phaseY;
+
+                    addBar(left, top, right, bottom);
+                }
+            }
+        }
+
+        reset();
+    }
+}

+ 94 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java

@@ -0,0 +1,94 @@
+
+package com.github.mikephil.charting.buffer;
+
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+
+public class HorizontalBarBuffer extends BarBuffer {
+
+    public HorizontalBarBuffer(int size, int dataSetCount, boolean containsStacks) {
+        super(size, dataSetCount, containsStacks);
+    }
+
+    @Override
+    public void feed(IBarDataSet data) {
+
+        float size = data.getEntryCount() * phaseX;
+        float barWidthHalf = mBarWidth / 2f;
+
+        for (int i = 0; i < size; i++) {
+
+            BarEntry e = data.getEntryForIndex(i);
+
+            if(e == null)
+                continue;
+
+            float x = e.getX();
+            float y = e.getY();
+            float[] vals = e.getYVals();
+
+            if (!mContainsStacks || vals == null) {
+
+                float bottom = x - barWidthHalf;
+                float top = x + barWidthHalf;
+                float left, right;
+                if (mInverted) {
+                    left = y >= 0 ? y : 0;
+                    right = y <= 0 ? y : 0;
+                } else {
+                    right = y >= 0 ? y : 0;
+                    left = y <= 0 ? y : 0;
+                }
+
+                // multiply the height of the rect with the phase
+                if (right > 0)
+                    right *= phaseY;
+                else
+                    left *= phaseY;
+
+                addBar(left, top, right, bottom);
+
+            } else {
+
+                float posY = 0f;
+                float negY = -e.getNegativeSum();
+                float yStart = 0f;
+
+                // fill the stack
+                for (int k = 0; k < vals.length; k++) {
+
+                    float value = vals[k];
+
+                    if (value >= 0f) {
+                        y = posY;
+                        yStart = posY + value;
+                        posY = yStart;
+                    } else {
+                        y = negY;
+                        yStart = negY + Math.abs(value);
+                        negY += Math.abs(value);
+                    }
+
+                    float bottom = x - barWidthHalf;
+                    float top = x + barWidthHalf;
+                    float left, right;
+                    if (mInverted) {
+                        left = y >= yStart ? y : yStart;
+                        right = y <= yStart ? y : yStart;
+                    } else {
+                        right = y >= yStart ? y : yStart;
+                        left = y <= yStart ? y : yStart;
+                    }
+
+                    // multiply the height of the rect with the phase
+                    right *= phaseY;
+                    left *= phaseY;
+
+                    addBar(left, top, right, bottom);
+                }
+            }
+        }
+
+        reset();
+    }
+}

+ 258 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java

@@ -0,0 +1,258 @@
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.highlight.BarHighlighter;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+import com.github.mikephil.charting.renderer.BarChartRenderer;
+
+/**
+ * Chart that draws bars.
+ *
+ * @author Philipp Jahoda
+ */
+public class BarChart extends BarLineChartBase<BarData> implements BarDataProvider {
+
+    /**
+     * flag that indicates whether the highlight should be full-bar oriented, or single-value?
+     */
+    protected boolean mHighlightFullBarEnabled = false;
+
+    /**
+     * if set to true, all values are drawn above their bars, instead of below their top
+     */
+    private boolean mDrawValueAboveBar = true;
+
+    /**
+     * if set to true, a grey area is drawn behind each bar that indicates the maximum value
+     */
+    private boolean mDrawBarShadow = false;
+
+    private boolean mFitBars = false;
+
+    public BarChart(Context context) {
+        super(context);
+    }
+
+    public BarChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BarChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler);
+
+        setHighlighter(new BarHighlighter(this));
+
+        getXAxis().setSpaceMin(0.5f);
+        getXAxis().setSpaceMax(0.5f);
+    }
+
+    @Override
+    protected void calcMinMax() {
+
+        if (mFitBars) {
+            mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f);
+        } else {
+            mXAxis.calculate(mData.getXMin(), mData.getXMax());
+        }
+
+        // calculate axis range (min / max) according to provided data
+        mAxisLeft.calculate(mData.getYMin(YAxis.AxisDependency.LEFT), mData.getYMax(YAxis.AxisDependency.LEFT));
+        mAxisRight.calculate(mData.getYMin(YAxis.AxisDependency.RIGHT), mData.getYMax(YAxis.AxisDependency
+                .RIGHT));
+    }
+
+    /**
+     * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch
+     * point
+     * inside the BarChart.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    @Override
+    public Highlight getHighlightByTouchPoint(float x, float y) {
+
+        if (mData == null) {
+            Log.e(LOG_TAG, "Can't select by touch. No data set.");
+            return null;
+        } else {
+            Highlight h = getHighlighter().getHighlight(x, y);
+            if (h == null || !isHighlightFullBarEnabled()) return h;
+
+            // For isHighlightFullBarEnabled, remove stackIndex
+            return new Highlight(h.getX(), h.getY(),
+                    h.getXPx(), h.getYPx(),
+                    h.getDataSetIndex(), -1, h.getAxis());
+        }
+    }
+
+    /**
+     * Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be
+     * found in the charts data.  Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead.
+     *
+     * @param e
+     * @return
+     */
+    public RectF getBarBounds(BarEntry e) {
+
+        RectF bounds = new RectF();
+        getBarBounds(e, bounds);
+
+        return bounds;
+    }
+
+    /**
+     * The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet.
+     * The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data.
+     *
+     * @param e
+     * @return
+     */
+    public void getBarBounds(BarEntry e, RectF outputRect) {
+
+        RectF bounds = outputRect;
+
+        IBarDataSet set = mData.getDataSetForEntry(e);
+
+        if (set == null) {
+            bounds.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
+            return;
+        }
+
+        float y = e.getY();
+        float x = e.getX();
+
+        float barWidth = mData.getBarWidth();
+
+        float left = x - barWidth / 2f;
+        float right = x + barWidth / 2f;
+        float top = y >= 0 ? y : 0;
+        float bottom = y <= 0 ? y : 0;
+
+        bounds.set(left, top, right, bottom);
+
+        getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect);
+    }
+
+    /**
+     * If set to true, all values are drawn above their bars, instead of below their top.
+     *
+     * @param enabled
+     */
+    public void setDrawValueAboveBar(boolean enabled) {
+        mDrawValueAboveBar = enabled;
+    }
+
+    /**
+     * returns true if drawing values above bars is enabled, false if not
+     *
+     * @return
+     */
+    public boolean isDrawValueAboveBarEnabled() {
+        return mDrawValueAboveBar;
+    }
+
+    /**
+     * If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce
+     * performance by about 50%.
+     *
+     * @param enabled
+     */
+    public void setDrawBarShadow(boolean enabled) {
+        mDrawBarShadow = enabled;
+    }
+
+    /**
+     * returns true if drawing shadows (maxvalue) for each bar is enabled, false if not
+     *
+     * @return
+     */
+    public boolean isDrawBarShadowEnabled() {
+        return mDrawBarShadow;
+    }
+
+    /**
+     * Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant
+     * only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry
+     * was tapped.
+     * Default: false
+     *
+     * @param enabled
+     */
+    public void setHighlightFullBarEnabled(boolean enabled) {
+        mHighlightFullBarEnabled = enabled;
+    }
+
+    /**
+     * @return true the highlight operation is be full-bar oriented, false if single-value
+     */
+    @Override
+    public boolean isHighlightFullBarEnabled() {
+        return mHighlightFullBarEnabled;
+    }
+
+    /**
+     * Highlights the value at the given x-value in the given DataSet. Provide
+     * -1 as the dataSetIndex to undo all highlighting.
+     *
+     * @param x
+     * @param dataSetIndex
+     * @param stackIndex   the index inside the stack - only relevant for stacked entries
+     */
+    public void highlightValue(float x, int dataSetIndex, int stackIndex) {
+        highlightValue(new Highlight(x, dataSetIndex, stackIndex), false);
+    }
+
+    @Override
+    public BarData getBarData() {
+        return mData;
+    }
+
+    /**
+     * Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be
+     * fully displayed.
+     * Default: false
+     *
+     * @param enabled
+     */
+    public void setFitBars(boolean enabled) {
+        mFitBars = enabled;
+    }
+
+    /**
+     * Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries.
+     * Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified
+     * by the parameters.
+     * Calls notifyDataSetChanged() afterwards.
+     *
+     * @param fromX      the starting point on the x-axis where the grouping should begin
+     * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f
+     * @param barSpace   the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f
+     */
+    public void groupBars(float fromX, float groupSpace, float barSpace) {
+
+        if (getBarData() == null) {
+            throw new RuntimeException("You need to set data for the chart before grouping bars.");
+        } else {
+            getBarData().groupBars(fromX, groupSpace, barSpace);
+            notifyDataSetChanged();
+        }
+    }
+}

File diff suppressed because it is too large
+ 1674 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java


+ 43 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java

@@ -0,0 +1,43 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.data.BubbleData;
+import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider;
+import com.github.mikephil.charting.renderer.BubbleChartRenderer;
+
+/**
+ * The BubbleChart. Draws bubbles. Bubble chart implementation: Copyright 2015
+ * Pierre-Marc Airoldi Licensed under Apache License 2.0. In the BubbleChart, it
+ * is the area of the bubble, not the radius or diameter of the bubble that
+ * conveys the data.
+ *
+ * @author Philipp Jahoda
+ */
+public class BubbleChart extends BarLineChartBase<BubbleData> implements BubbleDataProvider {
+
+    public BubbleChart(Context context) {
+        super(context);
+    }
+
+    public BubbleChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BubbleChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler);
+    }
+
+    public BubbleData getBubbleData() {
+        return mData;
+    }
+}

+ 44 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java

@@ -0,0 +1,44 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.data.CandleData;
+import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider;
+import com.github.mikephil.charting.renderer.CandleStickChartRenderer;
+
+/**
+ * Financial chart type that draws candle-sticks (OHCL chart).
+ *
+ * @author Philipp Jahoda
+ */
+public class CandleStickChart extends BarLineChartBase<CandleData> implements CandleDataProvider {
+
+    public CandleStickChart(Context context) {
+        super(context);
+    }
+
+    public CandleStickChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CandleStickChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler);
+
+        getXAxis().setSpaceMin(0.5f);
+        getXAxis().setSpaceMax(0.5f);
+    }
+
+    @Override
+    public CandleData getCandleData() {
+        return mData;
+    }
+}

File diff suppressed because it is too large
+ 1821 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java


+ 272 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java

@@ -0,0 +1,272 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BubbleData;
+import com.github.mikephil.charting.data.CandleData;
+import com.github.mikephil.charting.data.CombinedData;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.ScatterData;
+import com.github.mikephil.charting.highlight.CombinedHighlighter;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.renderer.CombinedChartRenderer;
+
+/**
+ * This chart class allows the combination of lines, bars, scatter and candle
+ * data all displayed in one chart area.
+ *
+ * @author Philipp Jahoda
+ */
+public class CombinedChart extends BarLineChartBase<CombinedData> implements CombinedDataProvider {
+
+    /**
+     * if set to true, all values are drawn above their bars, instead of below
+     * their top
+     */
+    private boolean mDrawValueAboveBar = true;
+
+
+    /**
+     * flag that indicates whether the highlight should be full-bar oriented, or single-value?
+     */
+    protected boolean mHighlightFullBarEnabled = false;
+
+    /**
+     * if set to true, a grey area is drawn behind each bar that indicates the
+     * maximum value
+     */
+    private boolean mDrawBarShadow = false;
+
+    protected DrawOrder[] mDrawOrder;
+
+    /**
+     * enum that allows to specify the order in which the different data objects
+     * for the combined-chart are drawn
+     */
+    public enum DrawOrder {
+        BAR, BUBBLE, LINE, CANDLE, SCATTER
+    }
+
+    public CombinedChart(Context context) {
+        super(context);
+    }
+
+    public CombinedChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CombinedChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        // Default values are not ready here yet
+        mDrawOrder = new DrawOrder[]{
+                DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER
+        };
+
+        setHighlighter(new CombinedHighlighter(this, this));
+
+        // Old default behaviour
+        setHighlightFullBarEnabled(true);
+
+        mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler);
+    }
+
+    @Override
+    public CombinedData getCombinedData() {
+        return mData;
+    }
+
+    @Override
+    public void setData(CombinedData data) {
+        super.setData(data);
+        setHighlighter(new CombinedHighlighter(this, this));
+        ((CombinedChartRenderer)mRenderer).createRenderers();
+        mRenderer.initBuffers();
+    }
+
+    /**
+     * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch
+     * point
+     * inside the CombinedChart.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    @Override
+    public Highlight getHighlightByTouchPoint(float x, float y) {
+
+        if (mData == null) {
+            Log.e(LOG_TAG, "Can't select by touch. No data set.");
+            return null;
+        } else {
+            Highlight h = getHighlighter().getHighlight(x, y);
+            if (h == null || !isHighlightFullBarEnabled()) return h;
+
+            // For isHighlightFullBarEnabled, remove stackIndex
+            return new Highlight(h.getX(), h.getY(),
+                    h.getXPx(), h.getYPx(),
+                    h.getDataSetIndex(), -1, h.getAxis());
+        }
+    }
+
+    @Override
+    public LineData getLineData() {
+        if (mData == null)
+            return null;
+        return mData.getLineData();
+    }
+
+    @Override
+    public BarData getBarData() {
+        if (mData == null)
+            return null;
+        return mData.getBarData();
+    }
+
+    @Override
+    public ScatterData getScatterData() {
+        if (mData == null)
+            return null;
+        return mData.getScatterData();
+    }
+
+    @Override
+    public CandleData getCandleData() {
+        if (mData == null)
+            return null;
+        return mData.getCandleData();
+    }
+
+    @Override
+    public BubbleData getBubbleData() {
+        if (mData == null)
+            return null;
+        return mData.getBubbleData();
+    }
+
+    @Override
+    public boolean isDrawBarShadowEnabled() {
+        return mDrawBarShadow;
+    }
+
+    @Override
+    public boolean isDrawValueAboveBarEnabled() {
+        return mDrawValueAboveBar;
+    }
+
+    /**
+     * If set to true, all values are drawn above their bars, instead of below
+     * their top.
+     *
+     * @param enabled
+     */
+    public void setDrawValueAboveBar(boolean enabled) {
+        mDrawValueAboveBar = enabled;
+    }
+
+
+    /**
+     * If set to true, a grey area is drawn behind each bar that indicates the
+     * maximum value. Enabling his will reduce performance by about 50%.
+     *
+     * @param enabled
+     */
+    public void setDrawBarShadow(boolean enabled) {
+        mDrawBarShadow = enabled;
+    }
+
+    /**
+     * Set this to true to make the highlight operation full-bar oriented,
+     * false to make it highlight single values (relevant only for stacked).
+     *
+     * @param enabled
+     */
+    public void setHighlightFullBarEnabled(boolean enabled) {
+        mHighlightFullBarEnabled = enabled;
+    }
+
+    /**
+     * @return true the highlight operation is be full-bar oriented, false if single-value
+     */
+    @Override
+    public boolean isHighlightFullBarEnabled() {
+        return mHighlightFullBarEnabled;
+    }
+
+    /**
+     * Returns the currently set draw order.
+     *
+     * @return
+     */
+    public DrawOrder[] getDrawOrder() {
+        return mDrawOrder;
+    }
+
+    /**
+     * Sets the order in which the provided data objects should be drawn. The
+     * earlier you place them in the provided array, the further they will be in
+     * the background. e.g. if you provide new DrawOrer[] { DrawOrder.BAR,
+     * DrawOrder.LINE }, the bars will be drawn behind the lines.
+     *
+     * @param order
+     */
+    public void setDrawOrder(DrawOrder[] order) {
+        if (order == null || order.length <= 0)
+            return;
+        mDrawOrder = order;
+    }
+
+    /**
+     * draws all MarkerViews on the highlighted positions
+     */
+    protected void drawMarkers(Canvas canvas) {
+
+        // if there is no marker view or drawing marker is disabled
+        if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
+            return;
+
+        for (int i = 0; i < mIndicesToHighlight.length; i++) {
+
+            Highlight highlight = mIndicesToHighlight[i];
+
+            IDataSet set = mData.getDataSetByHighlight(highlight);
+
+            Entry e = mData.getEntryForHighlight(highlight);
+            if (e == null)
+                continue;
+
+            int entryIndex = set.getEntryIndex(e);
+
+            // make sure entry not null
+            if (entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
+                continue;
+
+            float[] pos = getMarkerPosition(highlight);
+
+            // check bounds
+            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
+                continue;
+
+            // callbacks to update the content
+            mMarker.refreshContent(e, highlight);
+
+            // draw the marker
+            mMarker.draw(canvas, pos[0], pos[1]);
+        }
+    }
+
+}

+ 346 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java

@@ -0,0 +1,346 @@
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.github.mikephil.charting.components.XAxis.XAxisPosition;
+import com.github.mikephil.charting.components.YAxis.AxisDependency;
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.highlight.HorizontalBarHighlighter;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer;
+import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart;
+import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart;
+import com.github.mikephil.charting.utils.HorizontalViewPortHandler;
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.TransformerHorizontalBarChart;
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class
+ * represents the horizontal values and the XAxis class represents the vertical values.
+ *
+ * @author Philipp Jahoda
+ */
+public class HorizontalBarChart extends BarChart {
+
+    public HorizontalBarChart(Context context) {
+        super(context);
+    }
+
+    public HorizontalBarChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+
+        mViewPortHandler = new HorizontalViewPortHandler();
+
+        super.init();
+
+        mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler);
+        mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler);
+
+        mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler);
+        setHighlighter(new HorizontalBarHighlighter(this));
+
+        mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer);
+        mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer);
+        mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this);
+    }
+
+    private RectF mOffsetsBuffer = new RectF();
+
+    protected void calculateLegendOffsets(RectF offsets) {
+
+        offsets.left = 0.f;
+        offsets.right = 0.f;
+        offsets.top = 0.f;
+        offsets.bottom = 0.f;
+
+        if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled())
+            return;
+
+        switch (mLegend.getOrientation()) {
+            case VERTICAL:
+
+                switch (mLegend.getHorizontalAlignment()) {
+                    case LEFT:
+                        offsets.left += Math.min(mLegend.mNeededWidth,
+                                mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+                                + mLegend.getXOffset();
+                        break;
+
+                    case RIGHT:
+                        offsets.right += Math.min(mLegend.mNeededWidth,
+                                mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+                                + mLegend.getXOffset();
+                        break;
+
+                    case CENTER:
+
+                        switch (mLegend.getVerticalAlignment()) {
+                            case TOP:
+                                offsets.top += Math.min(mLegend.mNeededHeight,
+                                        mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+                                        + mLegend.getYOffset();
+                                break;
+
+                            case BOTTOM:
+                                offsets.bottom += Math.min(mLegend.mNeededHeight,
+                                        mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+                                        + mLegend.getYOffset();
+                                break;
+
+                            default:
+                                break;
+                        }
+                }
+
+                break;
+
+            case HORIZONTAL:
+
+                switch (mLegend.getVerticalAlignment()) {
+                    case TOP:
+                        offsets.top += Math.min(mLegend.mNeededHeight,
+                                mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+                                + mLegend.getYOffset();
+
+                        if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLabelsEnabled())
+                            offsets.top += mAxisLeft.getRequiredHeightSpace(
+                                    mAxisRendererLeft.getPaintAxisLabels());
+                        break;
+
+                    case BOTTOM:
+                        offsets.bottom += Math.min(mLegend.mNeededHeight,
+                                mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+                                + mLegend.getYOffset();
+
+                        if (mAxisRight.isEnabled() && mAxisRight.isDrawLabelsEnabled())
+                            offsets.bottom += mAxisRight.getRequiredHeightSpace(
+                                    mAxisRendererRight.getPaintAxisLabels());
+                        break;
+
+                    default:
+                        break;
+                }
+                break;
+        }
+    }
+
+    @Override
+    public void calculateOffsets() {
+
+        float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f;
+
+        calculateLegendOffsets(mOffsetsBuffer);
+
+        offsetLeft += mOffsetsBuffer.left;
+        offsetTop += mOffsetsBuffer.top;
+        offsetRight += mOffsetsBuffer.right;
+        offsetBottom += mOffsetsBuffer.bottom;
+
+        // offsets for y-labels
+        if (mAxisLeft.needsOffset()) {
+            offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels());
+        }
+
+        if (mAxisRight.needsOffset()) {
+            offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels());
+        }
+
+        float xlabelwidth = mXAxis.mLabelRotatedWidth;
+
+        if (mXAxis.isEnabled()) {
+
+            // offsets for x-labels
+            if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
+
+                offsetLeft += xlabelwidth;
+
+            } else if (mXAxis.getPosition() == XAxisPosition.TOP) {
+
+                offsetRight += xlabelwidth;
+
+            } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) {
+
+                offsetLeft += xlabelwidth;
+                offsetRight += xlabelwidth;
+            }
+        }
+
+        offsetTop += getExtraTopOffset();
+        offsetRight += getExtraRightOffset();
+        offsetBottom += getExtraBottomOffset();
+        offsetLeft += getExtraLeftOffset();
+
+        float minOffset = Utils.convertDpToPixel(mMinOffset);
+
+        mViewPortHandler.restrainViewPort(
+                Math.max(minOffset, offsetLeft),
+                Math.max(minOffset, offsetTop),
+                Math.max(minOffset, offsetRight),
+                Math.max(minOffset, offsetBottom));
+
+        if (mLogEnabled) {
+            Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " +
+                    offsetRight + ", offsetBottom: "
+                    + offsetBottom);
+            Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString());
+        }
+
+        prepareOffsetMatrix();
+        prepareValuePxMatrix();
+    }
+
+    @Override
+    protected void prepareValuePxMatrix() {
+        mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange,
+                mXAxis.mAxisMinimum);
+        mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange,
+                mXAxis.mAxisMinimum);
+    }
+
+    @Override
+    protected float[] getMarkerPosition(Highlight high) {
+        return new float[]{high.getDrawY(), high.getDrawX()};
+    }
+
+    @Override
+    public void getBarBounds(BarEntry e, RectF outputRect) {
+
+        RectF bounds = outputRect;
+        IBarDataSet set = mData.getDataSetForEntry(e);
+
+        if (set == null) {
+            outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
+            return;
+        }
+
+        float y = e.getY();
+        float x = e.getX();
+
+        float barWidth = mData.getBarWidth();
+
+        float top = x - barWidth / 2f;
+        float bottom = x + barWidth / 2f;
+        float left = y >= 0 ? y : 0;
+        float right = y <= 0 ? y : 0;
+
+        bounds.set(left, top, right, bottom);
+
+        getTransformer(set.getAxisDependency()).rectValueToPixel(bounds);
+
+    }
+
+    protected float[] mGetPositionBuffer = new float[2];
+
+    /**
+     * Returns a recyclable MPPointF instance.
+     *
+     * @param e
+     * @param axis
+     * @return
+     */
+    @Override
+    public MPPointF getPosition(Entry e, AxisDependency axis) {
+
+        if (e == null)
+            return null;
+
+        float[] vals = mGetPositionBuffer;
+        vals[0] = e.getY();
+        vals[1] = e.getX();
+
+        getTransformer(axis).pointValuesToPixel(vals);
+
+        return MPPointF.getInstance(vals[0], vals[1]);
+    }
+
+    /**
+     * Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point
+     * inside the BarChart.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    @Override
+    public Highlight getHighlightByTouchPoint(float x, float y) {
+
+        if (mData == null) {
+            if (mLogEnabled)
+                Log.e(LOG_TAG, "Can't select by touch. No data set.");
+            return null;
+        } else
+            return getHighlighter().getHighlight(y, x); // switch x and y
+    }
+
+    @Override
+    public float getLowestVisibleX() {
+        getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
+                mViewPortHandler.contentBottom(), posForGetLowestVisibleX);
+        float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y);
+        return result;
+    }
+
+    @Override
+    public float getHighestVisibleX() {
+        getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
+                mViewPortHandler.contentTop(), posForGetHighestVisibleX);
+        float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y);
+        return result;
+    }
+
+    /**
+     * ###### VIEWPORT METHODS BELOW THIS ######
+     */
+
+    @Override
+    public void setVisibleXRangeMaximum(float maxXRange) {
+        float xScale = mXAxis.mAxisRange / (maxXRange);
+        mViewPortHandler.setMinimumScaleY(xScale);
+    }
+
+    @Override
+    public void setVisibleXRangeMinimum(float minXRange) {
+        float xScale = mXAxis.mAxisRange / (minXRange);
+        mViewPortHandler.setMaximumScaleY(xScale);
+    }
+
+    @Override
+    public void setVisibleXRange(float minXRange, float maxXRange) {
+        float minScale = mXAxis.mAxisRange / minXRange;
+        float maxScale = mXAxis.mAxisRange / maxXRange;
+        mViewPortHandler.setMinMaxScaleY(minScale, maxScale);
+    }
+
+    @Override
+    public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) {
+        float yScale = getAxisRange(axis) / maxYRange;
+        mViewPortHandler.setMinimumScaleX(yScale);
+    }
+
+    @Override
+    public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) {
+        float yScale = getAxisRange(axis) / minYRange;
+        mViewPortHandler.setMaximumScaleX(yScale);
+    }
+
+    @Override
+    public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) {
+        float minScale = getAxisRange(axis) / minYRange;
+        float maxScale = getAxisRange(axis) / maxYRange;
+        mViewPortHandler.setMinMaxScaleX(minScale, maxScale);
+    }
+}

+ 50 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java

@@ -0,0 +1,50 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
+import com.github.mikephil.charting.renderer.LineChartRenderer;
+
+/**
+ * Chart that draws lines, surfaces, circles, ...
+ *
+ * @author Philipp Jahoda
+ */
+public class LineChart extends BarLineChartBase<LineData> implements LineDataProvider {
+
+    public LineChart(Context context) {
+        super(context);
+    }
+
+    public LineChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public LineChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler);
+    }
+
+    @Override
+    public LineData getLineData() {
+        return mData;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        // releases the bitmap in the renderer to avoid oom error
+        if (mRenderer != null && mRenderer instanceof LineChartRenderer) {
+            ((LineChartRenderer) mRenderer).releaseBitmap();
+        }
+        super.onDetachedFromWindow();
+    }
+}

+ 804 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java

@@ -0,0 +1,804 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.data.PieData;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.highlight.PieHighlighter;
+import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
+import com.github.mikephil.charting.renderer.PieChartRenderer;
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.List;
+
+/**
+ * View that represents a pie chart. Draws cake like slices.
+ *
+ * @author Philipp Jahoda
+ */
+public class PieChart extends PieRadarChartBase<PieData> {
+
+    /**
+     * rect object that represents the bounds of the piechart, needed for
+     * drawing the circle
+     */
+    private RectF mCircleBox = new RectF();
+
+    /**
+     * flag indicating if entry labels should be drawn or not
+     */
+    private boolean mDrawEntryLabels = true;
+
+    /**
+     * array that holds the width of each pie-slice in degrees
+     */
+    private float[] mDrawAngles = new float[1];
+
+    /**
+     * array that holds the absolute angle in degrees of each slice
+     */
+    private float[] mAbsoluteAngles = new float[1];
+
+    /**
+     * if true, the white hole inside the chart will be drawn
+     */
+    private boolean mDrawHole = true;
+
+    /**
+     * if true, the hole will see-through to the inner tips of the slices
+     */
+    private boolean mDrawSlicesUnderHole = false;
+
+    /**
+     * if true, the values inside the piechart are drawn as percent values
+     */
+    private boolean mUsePercentValues = false;
+
+    /**
+     * if true, the slices of the piechart are rounded
+     */
+    private boolean mDrawRoundedSlices = false;
+
+    /**
+     * variable for the text that is drawn in the center of the pie-chart
+     */
+    private CharSequence mCenterText = "";
+
+    private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0);
+
+    /**
+     * indicates the size of the hole in the center of the piechart, default:
+     * radius / 2
+     */
+    private float mHoleRadiusPercent = 50f;
+
+    /**
+     * the radius of the transparent circle next to the chart-hole in the center
+     */
+    protected float mTransparentCircleRadiusPercent = 55f;
+
+    /**
+     * if enabled, centertext is drawn
+     */
+    private boolean mDrawCenterText = true;
+
+    private float mCenterTextRadiusPercent = 100.f;
+
+    protected float mMaxAngle = 360f;
+
+    /**
+     * Minimum angle to draw slices, this only works if there is enough room for all slices to have
+     * the minimum angle, default 0f.
+     */
+    private float mMinAngleForSlices = 0f;
+
+    public PieChart(Context context) {
+        super(context);
+    }
+
+    public PieChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PieChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler);
+        mXAxis = null;
+
+        mHighlighter = new PieHighlighter(this);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mData == null)
+            return;
+
+        mRenderer.drawData(canvas);
+
+        if (valuesToHighlight())
+            mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
+
+        mRenderer.drawExtras(canvas);
+
+        mRenderer.drawValues(canvas);
+
+        mLegendRenderer.renderLegend(canvas);
+
+        drawDescription(canvas);
+
+        drawMarkers(canvas);
+    }
+
+    @Override
+    public void calculateOffsets() {
+        super.calculateOffsets();
+
+        // prevent nullpointer when no data set
+        if (mData == null)
+            return;
+
+        float diameter = getDiameter();
+        float radius = diameter / 2f;
+
+        MPPointF c = getCenterOffsets();
+
+        float shift = mData.getDataSet().getSelectionShift();
+
+        // create the circle box that will contain the pie-chart (the bounds of
+        // the pie-chart)
+        mCircleBox.set(c.x - radius + shift,
+                c.y - radius + shift,
+                c.x + radius - shift,
+                c.y + radius - shift);
+
+        MPPointF.recycleInstance(c);
+    }
+
+    @Override
+    protected void calcMinMax() {
+        calcAngles();
+    }
+
+    @Override
+    protected float[] getMarkerPosition(Highlight highlight) {
+
+        MPPointF center = getCenterCircleBox();
+        float r = getRadius();
+
+        float off = r / 10f * 3.6f;
+
+        if (isDrawHoleEnabled()) {
+            off = (r - (r / 100f * getHoleRadius())) / 2f;
+        }
+
+        r -= off; // offset to keep things inside the chart
+
+        float rotationAngle = getRotationAngle();
+
+        int entryIndex = (int) highlight.getX();
+
+        // offset needed to center the drawn text in the slice
+        float offset = mDrawAngles[entryIndex] / 2;
+
+        // calculate the text position
+        float x = (float) (r
+                * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
+                * mAnimator.getPhaseY())) + center.x);
+        float y = (float) (r
+                * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
+                * mAnimator.getPhaseY())) + center.y);
+
+        MPPointF.recycleInstance(center);
+        return new float[]{x, y};
+    }
+
+    /**
+     * calculates the needed angles for the chart slices
+     */
+    private void calcAngles() {
+
+        int entryCount = mData.getEntryCount();
+
+        if (mDrawAngles.length != entryCount) {
+            mDrawAngles = new float[entryCount];
+        } else {
+            for (int i = 0; i < entryCount; i++) {
+                mDrawAngles[i] = 0;
+            }
+        }
+        if (mAbsoluteAngles.length != entryCount) {
+            mAbsoluteAngles = new float[entryCount];
+        } else {
+            for (int i = 0; i < entryCount; i++) {
+                mAbsoluteAngles[i] = 0;
+            }
+        }
+
+        float yValueSum = mData.getYValueSum();
+
+        List<IPieDataSet> dataSets = mData.getDataSets();
+
+        boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle;
+        float[] minAngles = new float[entryCount];
+
+        int cnt = 0;
+        float offset = 0f;
+        float diff = 0f;
+
+        for (int i = 0; i < mData.getDataSetCount(); i++) {
+
+            IPieDataSet set = dataSets.get(i);
+
+            for (int j = 0; j < set.getEntryCount(); j++) {
+
+                float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum);
+
+                if (hasMinAngle) {
+                    float temp = drawAngle - mMinAngleForSlices;
+                    if (temp <= 0) {
+                        minAngles[cnt] = mMinAngleForSlices;
+                        offset += -temp;
+                    } else {
+                        minAngles[cnt] = drawAngle;
+                        diff += temp;
+                    }
+                }
+
+                mDrawAngles[cnt] = drawAngle;
+
+                if (cnt == 0) {
+                    mAbsoluteAngles[cnt] = mDrawAngles[cnt];
+                } else {
+                    mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt];
+                }
+
+                cnt++;
+            }
+        }
+
+        if (hasMinAngle) {
+            // Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract
+            // This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work!
+            for (int i = 0; i < entryCount; i++) {
+                minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset;
+                if (i == 0) {
+                    mAbsoluteAngles[0] = minAngles[0];
+                } else {
+                    mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i];
+                }
+            }
+
+            mDrawAngles = minAngles;
+        }
+    }
+
+    /**
+     * Checks if the given index is set to be highlighted.
+     *
+     * @param index
+     * @return
+     */
+    public boolean needsHighlight(int index) {
+
+        // no highlight
+        if (!valuesToHighlight())
+            return false;
+
+        for (int i = 0; i < mIndicesToHighlight.length; i++)
+
+            // check if the xvalue for the given dataset needs highlight
+            if ((int) mIndicesToHighlight[i].getX() == index)
+                return true;
+
+        return false;
+    }
+
+    /**
+     * calculates the needed angle for a given value
+     *
+     * @param value
+     * @return
+     */
+    private float calcAngle(float value) {
+        return calcAngle(value, mData.getYValueSum());
+    }
+
+    /**
+     * calculates the needed angle for a given value
+     *
+     * @param value
+     * @param yValueSum
+     * @return
+     */
+    private float calcAngle(float value, float yValueSum) {
+        return value / yValueSum * mMaxAngle;
+    }
+
+    /**
+     * This will throw an exception, PieChart has no XAxis object.
+     *
+     * @return
+     */
+    @Deprecated
+    @Override
+    public XAxis getXAxis() {
+        throw new RuntimeException("PieChart has no XAxis");
+    }
+
+    @Override
+    public int getIndexForAngle(float angle) {
+
+        // take the current angle of the chart into consideration
+        float a = Utils.getNormalizedAngle(angle - getRotationAngle());
+
+        for (int i = 0; i < mAbsoluteAngles.length; i++) {
+            if (mAbsoluteAngles[i] > a)
+                return i;
+        }
+
+        return -1; // return -1 if no index found
+    }
+
+    /**
+     * Returns the index of the DataSet this x-index belongs to.
+     *
+     * @param xIndex
+     * @return
+     */
+    public int getDataSetIndexForIndex(int xIndex) {
+
+        List<IPieDataSet> dataSets = mData.getDataSets();
+
+        for (int i = 0; i < dataSets.size(); i++) {
+            if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null)
+                return i;
+        }
+
+        return -1;
+    }
+
+    /**
+     * returns an integer array of all the different angles the chart slices
+     * have the angles in the returned array determine how much space (of 360°)
+     * each slice takes
+     *
+     * @return
+     */
+    public float[] getDrawAngles() {
+        return mDrawAngles;
+    }
+
+    /**
+     * returns the absolute angles of the different chart slices (where the
+     * slices end)
+     *
+     * @return
+     */
+    public float[] getAbsoluteAngles() {
+        return mAbsoluteAngles;
+    }
+
+    /**
+     * Sets the color for the hole that is drawn in the center of the PieChart
+     * (if enabled).
+     *
+     * @param color
+     */
+    public void setHoleColor(int color) {
+        ((PieChartRenderer) mRenderer).getPaintHole().setColor(color);
+    }
+
+    /**
+     * Enable or disable the visibility of the inner tips of the slices behind the hole
+     */
+    public void setDrawSlicesUnderHole(boolean enable) {
+        mDrawSlicesUnderHole = enable;
+    }
+
+    /**
+     * Returns true if the inner tips of the slices are visible behind the hole,
+     * false if not.
+     *
+     * @return true if slices are visible behind the hole.
+     */
+    public boolean isDrawSlicesUnderHoleEnabled() {
+        return mDrawSlicesUnderHole;
+    }
+
+    /**
+     * set this to true to draw the pie center empty
+     *
+     * @param enabled
+     */
+    public void setDrawHoleEnabled(boolean enabled) {
+        this.mDrawHole = enabled;
+    }
+
+    /**
+     * returns true if the hole in the center of the pie-chart is set to be
+     * visible, false if not
+     *
+     * @return
+     */
+    public boolean isDrawHoleEnabled() {
+        return mDrawHole;
+    }
+
+    /**
+     * Sets the text String that is displayed in the center of the PieChart.
+     *
+     * @param text
+     */
+    public void setCenterText(CharSequence text) {
+        if (text == null)
+            mCenterText = "";
+        else
+            mCenterText = text;
+    }
+
+    /**
+     * returns the text that is drawn in the center of the pie-chart
+     *
+     * @return
+     */
+    public CharSequence getCenterText() {
+        return mCenterText;
+    }
+
+    /**
+     * set this to true to draw the text that is displayed in the center of the
+     * pie chart
+     *
+     * @param enabled
+     */
+    public void setDrawCenterText(boolean enabled) {
+        this.mDrawCenterText = enabled;
+    }
+
+    /**
+     * returns true if drawing the center text is enabled
+     *
+     * @return
+     */
+    public boolean isDrawCenterTextEnabled() {
+        return mDrawCenterText;
+    }
+
+    @Override
+    protected float getRequiredLegendOffset() {
+        return mLegendRenderer.getLabelPaint().getTextSize() * 2.f;
+    }
+
+    @Override
+    protected float getRequiredBaseOffset() {
+        return 0;
+    }
+
+    @Override
+    public float getRadius() {
+        if (mCircleBox == null)
+            return 0;
+        else
+            return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f);
+    }
+
+    /**
+     * returns the circlebox, the boundingbox of the pie-chart slices
+     *
+     * @return
+     */
+    public RectF getCircleBox() {
+        return mCircleBox;
+    }
+
+    /**
+     * returns the center of the circlebox
+     *
+     * @return
+     */
+    public MPPointF getCenterCircleBox() {
+        return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY());
+    }
+
+    /**
+     * sets the typeface for the center-text paint
+     *
+     * @param t
+     */
+    public void setCenterTextTypeface(Typeface t) {
+        ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t);
+    }
+
+    /**
+     * Sets the size of the center text of the PieChart in dp.
+     *
+     * @param sizeDp
+     */
+    public void setCenterTextSize(float sizeDp) {
+        ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(
+                Utils.convertDpToPixel(sizeDp));
+    }
+
+    /**
+     * Sets the size of the center text of the PieChart in pixels.
+     *
+     * @param sizePixels
+     */
+    public void setCenterTextSizePixels(float sizePixels) {
+        ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels);
+    }
+
+    /**
+     * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0
+     *
+     * @param x
+     * @param y
+     */
+    public void setCenterTextOffset(float x, float y) {
+        mCenterTextOffset.x = Utils.convertDpToPixel(x);
+        mCenterTextOffset.y = Utils.convertDpToPixel(y);
+    }
+
+    /**
+     * Returns the offset on the x- and y-axis the center text has in dp.
+     *
+     * @return
+     */
+    public MPPointF getCenterTextOffset() {
+        return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y);
+    }
+
+    /**
+     * Sets the color of the center text of the PieChart.
+     *
+     * @param color
+     */
+    public void setCenterTextColor(int color) {
+        ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color);
+    }
+
+    /**
+     * sets the radius of the hole in the center of the piechart in percent of
+     * the maximum radius (max = the radius of the whole chart), default 50%
+     *
+     * @param percent
+     */
+    public void setHoleRadius(final float percent) {
+        mHoleRadiusPercent = percent;
+    }
+
+    /**
+     * Returns the size of the hole radius in percent of the total radius.
+     *
+     * @return
+     */
+    public float getHoleRadius() {
+        return mHoleRadiusPercent;
+    }
+
+    /**
+     * Sets the color the transparent-circle should have.
+     *
+     * @param color
+     */
+    public void setTransparentCircleColor(int color) {
+
+        Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle();
+        int alpha = p.getAlpha();
+        p.setColor(color);
+        p.setAlpha(alpha);
+    }
+
+    /**
+     * sets the radius of the transparent circle that is drawn next to the hole
+     * in the piechart in percent of the maximum radius (max = the radius of the
+     * whole chart), default 55% -> means 5% larger than the center-hole by
+     * default
+     *
+     * @param percent
+     */
+    public void setTransparentCircleRadius(final float percent) {
+        mTransparentCircleRadiusPercent = percent;
+    }
+
+    public float getTransparentCircleRadius() {
+        return mTransparentCircleRadiusPercent;
+    }
+
+    /**
+     * Sets the amount of transparency the transparent circle should have 0 = fully transparent,
+     * 255 = fully opaque.
+     * Default value is 100.
+     *
+     * @param alpha 0-255
+     */
+    public void setTransparentCircleAlpha(int alpha) {
+        ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha);
+    }
+
+    /**
+     * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
+     * Deprecated -> use setDrawEntryLabels(...) instead.
+     *
+     * @param enabled
+     */
+    @Deprecated
+    public void setDrawSliceText(boolean enabled) {
+        mDrawEntryLabels = enabled;
+    }
+
+    /**
+     * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
+     *
+     * @param enabled
+     */
+    public void setDrawEntryLabels(boolean enabled) {
+        mDrawEntryLabels = enabled;
+    }
+
+    /**
+     * Returns true if drawing the entry labels is enabled, false if not.
+     *
+     * @return
+     */
+    public boolean isDrawEntryLabelsEnabled() {
+        return mDrawEntryLabels;
+    }
+
+    /**
+     * Sets the color the entry labels are drawn with.
+     *
+     * @param color
+     */
+    public void setEntryLabelColor(int color) {
+        ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color);
+    }
+
+    /**
+     * Sets a custom Typeface for the drawing of the entry labels.
+     *
+     * @param tf
+     */
+    public void setEntryLabelTypeface(Typeface tf) {
+        ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf);
+    }
+
+    /**
+     * Sets the size of the entry labels in dp. Default: 13dp
+     *
+     * @param size
+     */
+    public void setEntryLabelTextSize(float size) {
+        ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size));
+    }
+
+    /**
+     * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled
+     * and if the slices are not drawn under the hole.
+     *
+     * @param enabled draw curved ends of slices
+     */
+    public void setDrawRoundedSlices(boolean enabled) {
+        mDrawRoundedSlices = enabled;
+    }
+
+    /**
+     * Returns true if the chart is set to draw each end of a pie-slice
+     * "rounded".
+     *
+     * @return
+     */
+    public boolean isDrawRoundedSlicesEnabled() {
+        return mDrawRoundedSlices;
+    }
+
+    /**
+     * If this is enabled, values inside the PieChart are drawn in percent and
+     * not with their original value. Values provided for the IValueFormatter to
+     * format are then provided in percent.
+     *
+     * @param enabled
+     */
+    public void setUsePercentValues(boolean enabled) {
+        mUsePercentValues = enabled;
+    }
+
+    /**
+     * Returns true if using percentage values is enabled for the chart.
+     *
+     * @return
+     */
+    public boolean isUsePercentValuesEnabled() {
+        return mUsePercentValues;
+    }
+
+    /**
+     * the rectangular radius of the bounding box for the center text, as a percentage of the pie
+     * hole
+     * default 1.f (100%)
+     */
+    public void setCenterTextRadiusPercent(float percent) {
+        mCenterTextRadiusPercent = percent;
+    }
+
+    /**
+     * the rectangular radius of the bounding box for the center text, as a percentage of the pie
+     * hole
+     * default 1.f (100%)
+     */
+    public float getCenterTextRadiusPercent() {
+        return mCenterTextRadiusPercent;
+    }
+
+    public float getMaxAngle() {
+        return mMaxAngle;
+    }
+
+    /**
+     * Sets the max angle that is used for calculating the pie-circle. 360f means
+     * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f
+     *
+     * @param maxangle min 90, max 360
+     */
+    public void setMaxAngle(float maxangle) {
+
+        if (maxangle > 360)
+            maxangle = 360f;
+
+        if (maxangle < 90)
+            maxangle = 90f;
+
+        this.mMaxAngle = maxangle;
+    }
+
+    /**
+     * The minimum angle slices on the chart are rendered with, default is 0f.
+     *
+     * @return minimum angle for slices
+     */
+    public float getMinAngleForSlices() {
+        return mMinAngleForSlices;
+    }
+
+    /**
+     * Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()}
+     * and {@link #invalidate()} when changing this, only works if there is enough room for all
+     * slices to have the minimum angle.
+     *
+     * @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)}
+     */
+    public void setMinAngleForSlices(float minAngle) {
+
+        if (minAngle > (mMaxAngle / 2f))
+            minAngle = mMaxAngle / 2f;
+        else if (minAngle < 0)
+            minAngle = 0f;
+
+        this.mMinAngleForSlices = minAngle;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        // releases the bitmap in the renderer to avoid oom error
+        if (mRenderer != null && mRenderer instanceof PieChartRenderer) {
+            ((PieChartRenderer) mRenderer).releaseBitmap();
+        }
+        super.onDetachedFromWindow();
+    }
+}

+ 499 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java

@@ -0,0 +1,499 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.github.mikephil.charting.animation.Easing;
+import com.github.mikephil.charting.animation.Easing.EasingFunction;
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.components.XAxis;
+import com.github.mikephil.charting.data.ChartData;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Baseclass of PieChart and RadarChart.
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<? extends Entry>>>
+        extends Chart<T> {
+
+    /**
+     * holds the normalized version of the current rotation angle of the chart
+     */
+    private float mRotationAngle = 270f;
+
+    /**
+     * holds the raw version of the current rotation angle of the chart
+     */
+    private float mRawRotationAngle = 270f;
+
+    /**
+     * flag that indicates if rotation is enabled or not
+     */
+    protected boolean mRotateEnabled = true;
+
+    /**
+     * Sets the minimum offset (padding) around the chart, defaults to 0.f
+     */
+    protected float mMinOffset = 0.f;
+
+    public PieRadarChartBase(Context context) {
+        super(context);
+    }
+
+    public PieRadarChartBase(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mChartTouchListener = new PieRadarChartTouchListener(this);
+    }
+
+    @Override
+    protected void calcMinMax() {
+        //mXAxis.mAxisRange = mData.getXVals().size() - 1;
+    }
+
+    @Override
+    public int getMaxVisibleCount() {
+        return mData.getEntryCount();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        // use the pie- and radarchart listener own listener
+        if (mTouchEnabled && mChartTouchListener != null)
+            return mChartTouchListener.onTouch(this, event);
+        else
+            return super.onTouchEvent(event);
+    }
+
+    @Override
+    public void computeScroll() {
+
+        if (mChartTouchListener instanceof PieRadarChartTouchListener)
+            ((PieRadarChartTouchListener) mChartTouchListener).computeScroll();
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        if (mData == null)
+            return;
+
+        calcMinMax();
+
+        if (mLegend != null)
+            mLegendRenderer.computeLegend(mData);
+
+        calculateOffsets();
+    }
+
+    @Override
+    public void calculateOffsets() {
+
+        float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f;
+
+        if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) {
+
+            float fullLegendWidth = Math.min(mLegend.mNeededWidth,
+                    mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent());
+
+            switch (mLegend.getOrientation()) {
+                case VERTICAL: {
+                    float xLegendOffset = 0.f;
+
+                    if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT
+                            || mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT) {
+                        if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.CENTER) {
+                            // this is the space between the legend and the chart
+                            final float spacing = Utils.convertDpToPixel(13f);
+
+                            xLegendOffset = fullLegendWidth + spacing;
+
+                        } else {
+                            // this is the space between the legend and the chart
+                            float spacing = Utils.convertDpToPixel(8f);
+
+                            float legendWidth = fullLegendWidth + spacing;
+                            float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax;
+
+                            MPPointF center = getCenter();
+
+                            float bottomX = mLegend.getHorizontalAlignment() ==
+                                    Legend.LegendHorizontalAlignment.RIGHT
+                                    ? getWidth() - legendWidth + 15.f
+                                    : legendWidth - 15.f;
+                            float bottomY = legendHeight + 15.f;
+                            float distLegend = distanceToCenter(bottomX, bottomY);
+
+                            MPPointF reference = getPosition(center, getRadius(),
+                                    getAngleForPoint(bottomX, bottomY));
+
+                            float distReference = distanceToCenter(reference.x, reference.y);
+                            float minOffset = Utils.convertDpToPixel(5f);
+
+                            if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) {
+                                xLegendOffset = legendWidth;
+                            } else if (distLegend < distReference) {
+
+                                float diff = distReference - distLegend;
+                                xLegendOffset = minOffset + diff;
+                            }
+
+                            MPPointF.recycleInstance(center);
+                            MPPointF.recycleInstance(reference);
+                        }
+                    }
+
+                    switch (mLegend.getHorizontalAlignment()) {
+                        case LEFT:
+                            legendLeft = xLegendOffset;
+                            break;
+
+                        case RIGHT:
+                            legendRight = xLegendOffset;
+                            break;
+
+                        case CENTER:
+                            switch (mLegend.getVerticalAlignment()) {
+                                case TOP:
+                                    legendTop = Math.min(mLegend.mNeededHeight,
+                                            mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
+                                    break;
+                                case BOTTOM:
+                                    legendBottom = Math.min(mLegend.mNeededHeight,
+                                            mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
+                                    break;
+                            }
+                            break;
+                    }
+                }
+                break;
+
+                case HORIZONTAL:
+                    float yLegendOffset = 0.f;
+
+                    if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.TOP ||
+                            mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.BOTTOM) {
+
+                        // It's possible that we do not need this offset anymore as it
+                        //   is available through the extraOffsets, but changing it can mean
+                        //   changing default visibility for existing apps.
+                        float yOffset = getRequiredLegendOffset();
+
+                        yLegendOffset = Math.min(mLegend.mNeededHeight + yOffset,
+                                mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
+
+                        switch (mLegend.getVerticalAlignment()) {
+                            case TOP:
+                                legendTop = yLegendOffset;
+                                break;
+                            case BOTTOM:
+                                legendBottom = yLegendOffset;
+                                break;
+                        }
+                    }
+                    break;
+            }
+
+            legendLeft += getRequiredBaseOffset();
+            legendRight += getRequiredBaseOffset();
+            legendTop += getRequiredBaseOffset();
+            legendBottom += getRequiredBaseOffset();
+        }
+
+        float minOffset = Utils.convertDpToPixel(mMinOffset);
+
+        if (this instanceof RadarChart) {
+            XAxis x = this.getXAxis();
+
+            if (x.isEnabled() && x.isDrawLabelsEnabled()) {
+                minOffset = Math.max(minOffset, x.mLabelRotatedWidth);
+            }
+        }
+
+        legendTop += getExtraTopOffset();
+        legendRight += getExtraRightOffset();
+        legendBottom += getExtraBottomOffset();
+        legendLeft += getExtraLeftOffset();
+
+        float offsetLeft = Math.max(minOffset, legendLeft);
+        float offsetTop = Math.max(minOffset, legendTop);
+        float offsetRight = Math.max(minOffset, legendRight);
+        float offsetBottom = Math.max(minOffset, Math.max(getRequiredBaseOffset(), legendBottom));
+
+        mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom);
+
+        if (mLogEnabled)
+            Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop
+                    + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom);
+    }
+
+    /**
+     * returns the angle relative to the chart center for the given point on the
+     * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH,
+     * 90° is EAST, ...
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    public float getAngleForPoint(float x, float y) {
+
+        MPPointF c = getCenterOffsets();
+
+        double tx = x - c.x, ty = y - c.y;
+        double length = Math.sqrt(tx * tx + ty * ty);
+        double r = Math.acos(ty / length);
+
+        float angle = (float) Math.toDegrees(r);
+
+        if (x > c.x)
+            angle = 360f - angle;
+
+        // add 90° because chart starts EAST
+        angle = angle + 90f;
+
+        // neutralize overflow
+        if (angle > 360f)
+            angle = angle - 360f;
+
+        MPPointF.recycleInstance(c);
+
+        return angle;
+    }
+
+    /**
+     * Returns a recyclable MPPointF instance.
+     * Calculates the position around a center point, depending on the distance
+     * from the center, and the angle of the position around the center.
+     *
+     * @param center
+     * @param dist
+     * @param angle  in degrees, converted to radians internally
+     * @return
+     */
+    public MPPointF getPosition(MPPointF center, float dist, float angle) {
+
+        MPPointF p = MPPointF.getInstance(0, 0);
+        getPosition(center, dist, angle, p);
+        return p;
+    }
+
+    public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) {
+        outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle)));
+        outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle)));
+    }
+
+    /**
+     * Returns the distance of a certain point on the chart to the center of the
+     * chart.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    public float distanceToCenter(float x, float y) {
+
+        MPPointF c = getCenterOffsets();
+
+        float dist = 0f;
+
+        float xDist = 0f;
+        float yDist = 0f;
+
+        if (x > c.x) {
+            xDist = x - c.x;
+        } else {
+            xDist = c.x - x;
+        }
+
+        if (y > c.y) {
+            yDist = y - c.y;
+        } else {
+            yDist = c.y - y;
+        }
+
+        // pythagoras
+        dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0));
+
+        MPPointF.recycleInstance(c);
+
+        return dist;
+    }
+
+    /**
+     * Returns the xIndex for the given angle around the center of the chart.
+     * Returns -1 if not found / outofbounds.
+     *
+     * @param angle
+     * @return
+     */
+    public abstract int getIndexForAngle(float angle);
+
+    /**
+     * Set an offset for the rotation of the RadarChart in degrees. Default 270f
+     * --> top (NORTH)
+     *
+     * @param angle
+     */
+    public void setRotationAngle(float angle) {
+        mRawRotationAngle = angle;
+        mRotationAngle = Utils.getNormalizedAngle(mRawRotationAngle);
+    }
+
+    /**
+     * gets the raw version of the current rotation angle of the pie chart the
+     * returned value could be any value, negative or positive, outside of the
+     * 360 degrees. this is used when working with rotation direction, mainly by
+     * gestures and animations.
+     *
+     * @return
+     */
+    public float getRawRotationAngle() {
+        return mRawRotationAngle;
+    }
+
+    /**
+     * gets a normalized version of the current rotation angle of the pie chart,
+     * which will always be between 0.0 < 360.0
+     *
+     * @return
+     */
+    public float getRotationAngle() {
+        return mRotationAngle;
+    }
+
+    /**
+     * Set this to true to enable the rotation / spinning of the chart by touch.
+     * Set it to false to disable it. Default: true
+     *
+     * @param enabled
+     */
+    public void setRotationEnabled(boolean enabled) {
+        mRotateEnabled = enabled;
+    }
+
+    /**
+     * Returns true if rotation of the chart by touch is enabled, false if not.
+     *
+     * @return
+     */
+    public boolean isRotationEnabled() {
+        return mRotateEnabled;
+    }
+
+    /**
+     * Gets the minimum offset (padding) around the chart, defaults to 0.f
+     */
+    public float getMinOffset() {
+        return mMinOffset;
+    }
+
+    /**
+     * Sets the minimum offset (padding) around the chart, defaults to 0.f
+     */
+    public void setMinOffset(float minOffset) {
+        mMinOffset = minOffset;
+    }
+
+    /**
+     * returns the diameter of the pie- or radar-chart
+     *
+     * @return
+     */
+    public float getDiameter() {
+        RectF content = mViewPortHandler.getContentRect();
+        content.left += getExtraLeftOffset();
+        content.top += getExtraTopOffset();
+        content.right -= getExtraRightOffset();
+        content.bottom -= getExtraBottomOffset();
+        return Math.min(content.width(), content.height());
+    }
+
+    /**
+     * Returns the radius of the chart in pixels.
+     *
+     * @return
+     */
+    public abstract float getRadius();
+
+    /**
+     * Returns the required offset for the chart legend.
+     *
+     * @return
+     */
+    protected abstract float getRequiredLegendOffset();
+
+    /**
+     * Returns the base offset needed for the chart without calculating the
+     * legend size.
+     *
+     * @return
+     */
+    protected abstract float getRequiredBaseOffset();
+
+    @Override
+    public float getYChartMax() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public float getYChartMin() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * ################ ################ ################ ################
+     */
+    /** CODE BELOW THIS RELATED TO ANIMATION */
+
+    /**
+     * Applys a spin animation to the Chart.
+     *
+     * @param durationmillis
+     * @param fromangle
+     * @param toangle
+     */
+    @SuppressLint("NewApi")
+    public void spin(int durationmillis, float fromangle, float toangle, EasingFunction easing) {
+
+        setRotationAngle(fromangle);
+
+        ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle,
+                toangle);
+        spinAnimator.setDuration(durationmillis);
+        spinAnimator.setInterpolator(easing);
+
+        spinAnimator.addUpdateListener(new AnimatorUpdateListener() {
+
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                postInvalidate();
+            }
+        });
+        spinAnimator.start();
+    }
+}

+ 362 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java

@@ -0,0 +1,362 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.components.YAxis.AxisDependency;
+import com.github.mikephil.charting.data.RadarData;
+import com.github.mikephil.charting.highlight.RadarHighlighter;
+import com.github.mikephil.charting.renderer.RadarChartRenderer;
+import com.github.mikephil.charting.renderer.XAxisRendererRadarChart;
+import com.github.mikephil.charting.renderer.YAxisRendererRadarChart;
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Implementation of the RadarChart, a "spidernet"-like chart. It works best
+ * when displaying 5-10 entries per DataSet.
+ *
+ * @author Philipp Jahoda
+ */
+public class RadarChart extends PieRadarChartBase<RadarData> {
+
+    /**
+     * width of the main web lines
+     */
+    private float mWebLineWidth = 2.5f;
+
+    /**
+     * width of the inner web lines
+     */
+    private float mInnerWebLineWidth = 1.5f;
+
+    /**
+     * color for the main web lines
+     */
+    private int mWebColor = Color.rgb(122, 122, 122);
+
+    /**
+     * color for the inner web
+     */
+    private int mWebColorInner = Color.rgb(122, 122, 122);
+
+    /**
+     * transparency the grid is drawn with (0-255)
+     */
+    private int mWebAlpha = 150;
+
+    /**
+     * flag indicating if the web lines should be drawn or not
+     */
+    private boolean mDrawWeb = true;
+
+    /**
+     * modulus that determines how many labels and web-lines are skipped before the next is drawn
+     */
+    private int mSkipWebLineCount = 0;
+
+    /**
+     * the object reprsenting the y-axis labels
+     */
+    private YAxis mYAxis;
+
+    protected YAxisRendererRadarChart mYAxisRenderer;
+    protected XAxisRendererRadarChart mXAxisRenderer;
+
+    public RadarChart(Context context) {
+        super(context);
+    }
+
+    public RadarChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public RadarChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mYAxis = new YAxis(AxisDependency.LEFT);
+        mYAxis.setLabelXOffset(10f);
+
+        mWebLineWidth = Utils.convertDpToPixel(1.5f);
+        mInnerWebLineWidth = Utils.convertDpToPixel(0.75f);
+
+        mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler);
+        mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this);
+        mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this);
+
+        mHighlighter = new RadarHighlighter(this);
+    }
+
+    @Override
+    protected void calcMinMax() {
+        super.calcMinMax();
+
+        mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT));
+        mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount());
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        if (mData == null)
+            return;
+
+        calcMinMax();
+
+        mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted());
+        mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);
+
+        if (mLegend != null && !mLegend.isLegendCustom())
+            mLegendRenderer.computeLegend(mData);
+
+        calculateOffsets();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (mData == null)
+            return;
+
+//        if (mYAxis.isEnabled())
+//            mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted());
+
+        if (mXAxis.isEnabled())
+            mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);
+
+        mXAxisRenderer.renderAxisLabels(canvas);
+
+        if (mDrawWeb)
+            mRenderer.drawExtras(canvas);
+
+        if (mYAxis.isEnabled() && mYAxis.isDrawLimitLinesBehindDataEnabled())
+            mYAxisRenderer.renderLimitLines(canvas);
+
+        mRenderer.drawData(canvas);
+
+        if (valuesToHighlight())
+            mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
+
+        if (mYAxis.isEnabled() && !mYAxis.isDrawLimitLinesBehindDataEnabled())
+            mYAxisRenderer.renderLimitLines(canvas);
+
+        mYAxisRenderer.renderAxisLabels(canvas);
+
+        mRenderer.drawValues(canvas);
+
+        mLegendRenderer.renderLegend(canvas);
+
+        drawDescription(canvas);
+
+        drawMarkers(canvas);
+    }
+
+    /**
+     * Returns the factor that is needed to transform values into pixels.
+     *
+     * @return
+     */
+    public float getFactor() {
+        RectF content = mViewPortHandler.getContentRect();
+        return Math.min(content.width() / 2f, content.height() / 2f) / mYAxis.mAxisRange;
+    }
+
+    /**
+     * Returns the angle that each slice in the radar chart occupies.
+     *
+     * @return
+     */
+    public float getSliceAngle() {
+        return 360f / (float) mData.getMaxEntryCountSet().getEntryCount();
+    }
+
+    @Override
+    public int getIndexForAngle(float angle) {
+
+        // take the current angle of the chart into consideration
+        float a = Utils.getNormalizedAngle(angle - getRotationAngle());
+
+        float sliceangle = getSliceAngle();
+
+        int max = mData.getMaxEntryCountSet().getEntryCount();
+
+        int index = 0;
+
+        for (int i = 0; i < max; i++) {
+
+            float referenceAngle = sliceangle * (i + 1) - sliceangle / 2f;
+
+            if (referenceAngle > a) {
+                index = i;
+                break;
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Returns the object that represents all y-labels of the RadarChart.
+     *
+     * @return
+     */
+    public YAxis getYAxis() {
+        return mYAxis;
+    }
+
+    /**
+     * Sets the width of the web lines that come from the center.
+     *
+     * @param width
+     */
+    public void setWebLineWidth(float width) {
+        mWebLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    public float getWebLineWidth() {
+        return mWebLineWidth;
+    }
+
+    /**
+     * Sets the width of the web lines that are in between the lines coming from
+     * the center.
+     *
+     * @param width
+     */
+    public void setWebLineWidthInner(float width) {
+        mInnerWebLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    public float getWebLineWidthInner() {
+        return mInnerWebLineWidth;
+    }
+
+    /**
+     * Sets the transparency (alpha) value for all web lines, default: 150, 255
+     * = 100% opaque, 0 = 100% transparent
+     *
+     * @param alpha
+     */
+    public void setWebAlpha(int alpha) {
+        mWebAlpha = alpha;
+    }
+
+    /**
+     * Returns the alpha value for all web lines.
+     *
+     * @return
+     */
+    public int getWebAlpha() {
+        return mWebAlpha;
+    }
+
+    /**
+     * Sets the color for the web lines that come from the center. Don't forget
+     * to use getResources().getColor(...) when loading a color from the
+     * resources. Default: Color.rgb(122, 122, 122)
+     *
+     * @param color
+     */
+    public void setWebColor(int color) {
+        mWebColor = color;
+    }
+
+    public int getWebColor() {
+        return mWebColor;
+    }
+
+    /**
+     * Sets the color for the web lines in between the lines that come from the
+     * center. Don't forget to use getResources().getColor(...) when loading a
+     * color from the resources. Default: Color.rgb(122, 122, 122)
+     *
+     * @param color
+     */
+    public void setWebColorInner(int color) {
+        mWebColorInner = color;
+    }
+
+    public int getWebColorInner() {
+        return mWebColorInner;
+    }
+
+    /**
+     * If set to true, drawing the web is enabled, if set to false, drawing the
+     * whole web is disabled. Default: true
+     *
+     * @param enabled
+     */
+    public void setDrawWeb(boolean enabled) {
+        mDrawWeb = enabled;
+    }
+
+    /**
+     * Sets the number of web-lines that should be skipped on chart web before the
+     * next one is drawn. This targets the lines that come from the center of the RadarChart.
+     *
+     * @param count if count = 1 -> 1 line is skipped in between
+     */
+    public void setSkipWebLineCount(int count) {
+
+        mSkipWebLineCount = Math.max(0, count);
+    }
+
+    /**
+     * Returns the modulus that is used for skipping web-lines.
+     *
+     * @return
+     */
+    public int getSkipWebLineCount() {
+        return mSkipWebLineCount;
+    }
+
+    @Override
+    protected float getRequiredLegendOffset() {
+        return mLegendRenderer.getLabelPaint().getTextSize() * 4.f;
+    }
+
+    @Override
+    protected float getRequiredBaseOffset() {
+        return mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled() ?
+                mXAxis.mLabelRotatedWidth :
+                Utils.convertDpToPixel(10f);
+    }
+
+    @Override
+    public float getRadius() {
+        RectF content = mViewPortHandler.getContentRect();
+        return Math.min(content.width() / 2f, content.height() / 2f);
+    }
+
+    /**
+     * Returns the maximum value this chart can display on it's y-axis.
+     */
+    public float getYChartMax() {
+        return mYAxis.mAxisMaximum;
+    }
+
+    /**
+     * Returns the minimum value this chart can display on it's y-axis.
+     */
+    public float getYChartMin() {
+        return mYAxis.mAxisMinimum;
+    }
+
+    /**
+     * Returns the range of y-values this chart can display.
+     *
+     * @return
+     */
+    public float getYRange() {
+        return mYAxis.mAxisRange;
+    }
+}

+ 77 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java

@@ -0,0 +1,77 @@
+
+package com.github.mikephil.charting.charts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.github.mikephil.charting.data.ScatterData;
+import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider;
+import com.github.mikephil.charting.renderer.ScatterChartRenderer;
+
+/**
+ * The ScatterChart. Draws dots, triangles, squares and custom shapes into the
+ * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the
+ * worst performance.
+ *
+ * @author Philipp Jahoda
+ */
+public class ScatterChart extends BarLineChartBase<ScatterData> implements ScatterDataProvider {
+
+    public ScatterChart(Context context) {
+        super(context);
+    }
+
+    public ScatterChart(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ScatterChart(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+
+    @Override
+    protected void init() {
+        super.init();
+
+        mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler);
+
+        getXAxis().setSpaceMin(0.5f);
+        getXAxis().setSpaceMax(0.5f);
+    }
+
+    @Override
+    public ScatterData getScatterData() {
+        return mData;
+    }
+
+    /**
+     * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with.
+     * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set.
+     */
+    public enum ScatterShape {
+
+        SQUARE("SQUARE"),
+        CIRCLE("CIRCLE"),
+        TRIANGLE("TRIANGLE"),
+        CROSS("CROSS"),
+        X("X"),
+        CHEVRON_UP("CHEVRON_UP"),
+        CHEVRON_DOWN("CHEVRON_DOWN");
+
+        private final String shapeIdentifier;
+
+        ScatterShape(final String shapeIdentifier) {
+            this.shapeIdentifier = shapeIdentifier;
+        }
+
+        @Override
+        public String toString() {
+            return shapeIdentifier;
+        }
+
+        public static ScatterShape[] getAllDefaultShapes() {
+            return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN};
+        }
+    }
+}

+ 815 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java

@@ -0,0 +1,815 @@
+package com.github.mikephil.charting.components;
+
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.util.Log;
+
+import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter;
+import com.github.mikephil.charting.formatter.ValueFormatter;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base-class of all axes (previously called labels).
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class AxisBase extends ComponentBase {
+
+    /**
+     * custom formatter that is used instead of the auto-formatter if set
+     */
+    protected ValueFormatter mAxisValueFormatter;
+
+    private int mGridColor = Color.GRAY;
+
+    private float mGridLineWidth = 1f;
+
+    private int mAxisLineColor = Color.GRAY;
+
+    private float mAxisLineWidth = 1f;
+
+    /**
+     * the actual array of entries
+     */
+    public float[] mEntries = new float[]{};
+
+    /**
+     * axis label entries only used for centered labels
+     */
+    public float[] mCenteredEntries = new float[]{};
+
+    /**
+     * the number of entries the legend contains
+     */
+    public int mEntryCount;
+
+    /**
+     * the number of decimal digits to use
+     */
+    public int mDecimals;
+
+    /**
+     * the number of label entries the axis should have, default 6
+     */
+    private int mLabelCount = 6;
+
+    /**
+     * the minimum interval between axis values
+     */
+    protected float mGranularity = 1.0f;
+
+    /**
+     * When true, axis labels are controlled by the `granularity` property.
+     * When false, axis values could possibly be repeated.
+     * This could happen if two adjacent axis values are rounded to same value.
+     * If using granularity this could be avoided by having fewer axis values visible.
+     */
+    protected boolean mGranularityEnabled = false;
+
+    /**
+     * if true, the set number of y-labels will be forced
+     */
+    protected boolean mForceLabels = false;
+
+    /**
+     * flag indicating if the grid lines for this axis should be drawn
+     */
+    protected boolean mDrawGridLines = true;
+
+    /**
+     * flag that indicates if the line alongside the axis is drawn or not
+     */
+    protected boolean mDrawAxisLine = true;
+
+    /**
+     * flag that indicates of the labels of this axis should be drawn or not
+     */
+    protected boolean mDrawLabels = true;
+
+    protected boolean mCenterAxisLabels = false;
+
+    /**
+     * the path effect of the axis line that makes dashed lines possible
+     */
+    private DashPathEffect mAxisLineDashPathEffect = null;
+
+    /**
+     * the path effect of the grid lines that makes dashed lines possible
+     */
+    private DashPathEffect mGridDashPathEffect = null;
+
+    /**
+     * array of limit lines that can be set for the axis
+     */
+    protected List<LimitLine> mLimitLines;
+
+    /**
+     * flag indicating the limit lines layer depth
+     */
+    protected boolean mDrawLimitLineBehindData = false;
+
+    /**
+     * flag indicating the grid lines layer depth
+     */
+    protected boolean mDrawGridLinesBehindData = true;
+
+    /**
+     * Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
+     */
+    protected float mSpaceMin = 0.f;
+
+    /**
+     * Extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
+     */
+    protected float mSpaceMax = 0.f;
+
+    /**
+     * flag indicating that the axis-min value has been customized
+     */
+    protected boolean mCustomAxisMin = false;
+
+    /**
+     * flag indicating that the axis-max value has been customized
+     */
+    protected boolean mCustomAxisMax = false;
+
+    /**
+     * don't touch this direclty, use setter
+     */
+    public float mAxisMaximum = 0f;
+
+    /**
+     * don't touch this directly, use setter
+     */
+    public float mAxisMinimum = 0f;
+
+    /**
+     * the total range of values this axis covers
+     */
+    public float mAxisRange = 0f;
+
+    private int mAxisMinLabels = 2;
+    private int mAxisMaxLabels = 25;
+
+    /**
+     * The minumum number of labels on the axis
+     */
+    public int getAxisMinLabels() {
+        return mAxisMinLabels;
+    }
+
+    /**
+     * The minumum number of labels on the axis
+     */
+    public void setAxisMinLabels(int labels) {
+        if (labels > 0)
+            mAxisMinLabels = labels;
+    }
+
+    /**
+     * The maximum number of labels on the axis
+     */
+    public int getAxisMaxLabels() {
+        return mAxisMaxLabels;
+    }
+
+    /**
+     * The maximum number of labels on the axis
+     */
+    public void setAxisMaxLabels(int labels) {
+        if (labels > 0)
+            mAxisMaxLabels = labels;
+    }
+
+    /**
+     * default constructor
+     */
+    public AxisBase() {
+        this.mTextSize = Utils.convertDpToPixel(10f);
+        this.mXOffset = Utils.convertDpToPixel(5f);
+        this.mYOffset = Utils.convertDpToPixel(5f);
+        this.mLimitLines = new ArrayList<LimitLine>();
+    }
+
+    /**
+     * Set this to true to enable drawing the grid lines for this axis.
+     *
+     * @param enabled
+     */
+    public void setDrawGridLines(boolean enabled) {
+        mDrawGridLines = enabled;
+    }
+
+    /**
+     * Returns true if drawing grid lines is enabled for this axis.
+     *
+     * @return
+     */
+    public boolean isDrawGridLinesEnabled() {
+        return mDrawGridLines;
+    }
+
+    /**
+     * Set this to true if the line alongside the axis should be drawn or not.
+     *
+     * @param enabled
+     */
+    public void setDrawAxisLine(boolean enabled) {
+        mDrawAxisLine = enabled;
+    }
+
+    /**
+     * Returns true if the line alongside the axis should be drawn.
+     *
+     * @return
+     */
+    public boolean isDrawAxisLineEnabled() {
+        return mDrawAxisLine;
+    }
+
+    /**
+     * Centers the axis labels instead of drawing them at their original position.
+     * This is useful especially for grouped BarChart.
+     *
+     * @param enabled
+     */
+    public void setCenterAxisLabels(boolean enabled) {
+        mCenterAxisLabels = enabled;
+    }
+
+    public boolean isCenterAxisLabelsEnabled() {
+        return mCenterAxisLabels && mEntryCount > 0;
+    }
+
+    /**
+     * Sets the color of the grid lines for this axis (the horizontal lines
+     * coming from each label).
+     *
+     * @param color
+     */
+    public void setGridColor(int color) {
+        mGridColor = color;
+    }
+
+    /**
+     * Returns the color of the grid lines for this axis (the horizontal lines
+     * coming from each label).
+     *
+     * @return
+     */
+    public int getGridColor() {
+        return mGridColor;
+    }
+
+    /**
+     * Sets the width of the border surrounding the chart in dp.
+     *
+     * @param width
+     */
+    public void setAxisLineWidth(float width) {
+        mAxisLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    /**
+     * Returns the width of the axis line (line alongside the axis).
+     *
+     * @return
+     */
+    public float getAxisLineWidth() {
+        return mAxisLineWidth;
+    }
+
+    /**
+     * Sets the width of the grid lines that are drawn away from each axis
+     * label.
+     *
+     * @param width
+     */
+    public void setGridLineWidth(float width) {
+        mGridLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    /**
+     * Returns the width of the grid lines that are drawn away from each axis
+     * label.
+     *
+     * @return
+     */
+    public float getGridLineWidth() {
+        return mGridLineWidth;
+    }
+
+    /**
+     * Sets the color of the border surrounding the chart.
+     *
+     * @param color
+     */
+    public void setAxisLineColor(int color) {
+        mAxisLineColor = color;
+    }
+
+    /**
+     * Returns the color of the axis line (line alongside the axis).
+     *
+     * @return
+     */
+    public int getAxisLineColor() {
+        return mAxisLineColor;
+    }
+
+    /**
+     * Set this to true to enable drawing the labels of this axis (this will not
+     * affect drawing the grid lines or axis lines).
+     *
+     * @param enabled
+     */
+    public void setDrawLabels(boolean enabled) {
+        mDrawLabels = enabled;
+    }
+
+    /**
+     * Returns true if drawing the labels is enabled for this axis.
+     *
+     * @return
+     */
+    public boolean isDrawLabelsEnabled() {
+        return mDrawLabels;
+    }
+
+    /**
+     * Sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware
+     * that this number is not fixed.
+     *
+     * @param count the number of y-axis labels that should be displayed
+     */
+    public void setLabelCount(int count) {
+
+        if (count > getAxisMaxLabels())
+            count = getAxisMaxLabels();
+        if (count < getAxisMinLabels())
+            count = getAxisMinLabels();
+
+        mLabelCount = count;
+        mForceLabels = false;
+    }
+
+    /**
+     * sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware
+     * that this number is not
+     * fixed (if force == false) and can only be approximated.
+     *
+     * @param count the number of y-axis labels that should be displayed
+     * @param force if enabled, the set label count will be forced, meaning that the exact
+     *              specified count of labels will
+     *              be drawn and evenly distributed alongside the axis - this might cause labels
+     *              to have uneven values
+     */
+    public void setLabelCount(int count, boolean force) {
+
+        setLabelCount(count);
+        mForceLabels = force;
+    }
+
+    /**
+     * Returns true if focing the y-label count is enabled. Default: false
+     *
+     * @return
+     */
+    public boolean isForceLabelsEnabled() {
+        return mForceLabels;
+    }
+
+    /**
+     * Returns the number of label entries the y-axis should have
+     *
+     * @return
+     */
+    public int getLabelCount() {
+        return mLabelCount;
+    }
+
+    /**
+     * @return true if granularity is enabled
+     */
+    public boolean isGranularityEnabled() {
+        return mGranularityEnabled;
+    }
+
+    /**
+     * Enabled/disable granularity control on axis value intervals. If enabled, the axis
+     * interval is not allowed to go below a certain granularity. Default: false
+     *
+     * @param enabled
+     */
+    public void setGranularityEnabled(boolean enabled) {
+        mGranularityEnabled = enabled;
+    }
+
+    /**
+     * @return the minimum interval between axis values
+     */
+    public float getGranularity() {
+        return mGranularity;
+    }
+
+    /**
+     * Set a minimum interval for the axis when zooming in. The axis is not allowed to go below
+     * that limit. This can be used to avoid label duplicating when zooming in.
+     *
+     * @param granularity
+     */
+    public void setGranularity(float granularity) {
+        mGranularity = granularity;
+        // set this to true if it was disabled, as it makes no sense to call this method with granularity disabled
+        mGranularityEnabled = true;
+    }
+
+    /**
+     * Adds a new LimitLine to this axis.
+     *
+     * @param l
+     */
+    public void addLimitLine(LimitLine l) {
+        mLimitLines.add(l);
+
+        if (mLimitLines.size() > 6) {
+            Log.e("MPAndroiChart",
+                    "Warning! You have more than 6 LimitLines on your axis, do you really want " +
+                            "that?");
+        }
+    }
+
+    /**
+     * Removes the specified LimitLine from the axis.
+     *
+     * @param l
+     */
+    public void removeLimitLine(LimitLine l) {
+        mLimitLines.remove(l);
+    }
+
+    /**
+     * Removes all LimitLines from the axis.
+     */
+    public void removeAllLimitLines() {
+        mLimitLines.clear();
+    }
+
+    /**
+     * Returns the LimitLines of this axis.
+     *
+     * @return
+     */
+    public List<LimitLine> getLimitLines() {
+        return mLimitLines;
+    }
+
+    /**
+     * If this is set to true, the LimitLines are drawn behind the actual data,
+     * otherwise on top. Default: false
+     *
+     * @param enabled
+     */
+    public void setDrawLimitLinesBehindData(boolean enabled) {
+        mDrawLimitLineBehindData = enabled;
+    }
+
+    public boolean isDrawLimitLinesBehindDataEnabled() {
+        return mDrawLimitLineBehindData;
+    }
+
+    /**
+     * If this is set to false, the grid lines are draw on top of the actual data,
+     * otherwise behind. Default: true
+     *
+     * @param enabled
+     */
+    public void setDrawGridLinesBehindData(boolean enabled) { mDrawGridLinesBehindData = enabled; }
+
+    public boolean isDrawGridLinesBehindDataEnabled() {
+        return mDrawGridLinesBehindData;
+    }
+
+    /**
+     * Returns the longest formatted label (in terms of characters), this axis
+     * contains.
+     *
+     * @return
+     */
+    public String getLongestLabel() {
+
+        String longest = "";
+
+        for (int i = 0; i < mEntries.length; i++) {
+            String text = getFormattedLabel(i);
+
+            if (text != null && longest.length() < text.length())
+                longest = text;
+        }
+
+        return longest;
+    }
+
+    public String getFormattedLabel(int index) {
+
+        if (index < 0 || index >= mEntries.length)
+            return "";
+        else
+            return getValueFormatter().getAxisLabel(mEntries[index], this);
+    }
+
+    /**
+     * Sets the formatter to be used for formatting the axis labels. If no formatter is set, the
+     * chart will
+     * automatically determine a reasonable formatting (concerning decimals) for all the values
+     * that are drawn inside
+     * the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart.
+     *
+     * @param f
+     */
+    public void setValueFormatter(ValueFormatter f) {
+
+        if (f == null)
+            mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals);
+        else
+            mAxisValueFormatter = f;
+    }
+
+    /**
+     * Returns the formatter used for formatting the axis labels.
+     *
+     * @return
+     */
+    public ValueFormatter getValueFormatter() {
+
+        if (mAxisValueFormatter == null ||
+                (mAxisValueFormatter instanceof DefaultAxisValueFormatter &&
+                        ((DefaultAxisValueFormatter)mAxisValueFormatter).getDecimalDigits() != mDecimals))
+            mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals);
+
+        return mAxisValueFormatter;
+    }
+
+    /**
+     * Enables the grid line to be drawn in dashed mode, e.g. like this
+     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
+     * Keep in mind that hardware acceleration boosts performance.
+     *
+     * @param lineLength  the length of the line pieces
+     * @param spaceLength the length of space in between the pieces
+     * @param phase       offset, in degrees (normally, use 0)
+     */
+    public void enableGridDashedLine(float lineLength, float spaceLength, float phase) {
+        mGridDashPathEffect = new DashPathEffect(new float[]{
+                lineLength, spaceLength
+        }, phase);
+    }
+
+    /**
+     * Enables the grid line to be drawn in dashed mode, e.g. like this
+     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
+     * Keep in mind that hardware acceleration boosts performance.
+     *
+     * @param effect the DashPathEffect
+     */
+    public void setGridDashedLine(DashPathEffect effect) {
+        mGridDashPathEffect = effect;
+    }
+
+    /**
+     * Disables the grid line to be drawn in dashed mode.
+     */
+    public void disableGridDashedLine() {
+        mGridDashPathEffect = null;
+    }
+
+    /**
+     * Returns true if the grid dashed-line effect is enabled, false if not.
+     *
+     * @return
+     */
+    public boolean isGridDashedLineEnabled() {
+        return mGridDashPathEffect == null ? false : true;
+    }
+
+    /**
+     * returns the DashPathEffect that is set for grid line
+     *
+     * @return
+     */
+    public DashPathEffect getGridDashPathEffect() {
+        return mGridDashPathEffect;
+    }
+
+
+    /**
+     * Enables the axis line to be drawn in dashed mode, e.g. like this
+     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
+     * Keep in mind that hardware acceleration boosts performance.
+     *
+     * @param lineLength  the length of the line pieces
+     * @param spaceLength the length of space in between the pieces
+     * @param phase       offset, in degrees (normally, use 0)
+     */
+    public void enableAxisLineDashedLine(float lineLength, float spaceLength, float phase) {
+        mAxisLineDashPathEffect = new DashPathEffect(new float[]{
+                lineLength, spaceLength
+        }, phase);
+    }
+
+    /**
+     * Enables the axis line to be drawn in dashed mode, e.g. like this
+     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
+     * Keep in mind that hardware acceleration boosts performance.
+     *
+     * @param effect the DashPathEffect
+     */
+    public void setAxisLineDashedLine(DashPathEffect effect) {
+        mAxisLineDashPathEffect = effect;
+    }
+
+    /**
+     * Disables the axis line to be drawn in dashed mode.
+     */
+    public void disableAxisLineDashedLine() {
+        mAxisLineDashPathEffect = null;
+    }
+
+    /**
+     * Returns true if the axis dashed-line effect is enabled, false if not.
+     *
+     * @return
+     */
+    public boolean isAxisLineDashedLineEnabled() {
+        return mAxisLineDashPathEffect == null ? false : true;
+    }
+
+    /**
+     * returns the DashPathEffect that is set for axis line
+     *
+     * @return
+     */
+    public DashPathEffect getAxisLineDashPathEffect() {
+        return mAxisLineDashPathEffect;
+    }
+
+    /**
+     * ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ######
+     */
+
+    public float getAxisMaximum() {
+        return mAxisMaximum;
+    }
+
+    public float getAxisMinimum() {
+        return mAxisMinimum;
+    }
+
+    /**
+     * By calling this method, any custom maximum value that has been previously set is reseted,
+     * and the calculation is
+     * done automatically.
+     */
+    public void resetAxisMaximum() {
+        mCustomAxisMax = false;
+    }
+
+    /**
+     * Returns true if the axis max value has been customized (and is not calculated automatically)
+     *
+     * @return
+     */
+    public boolean isAxisMaxCustom() {
+        return mCustomAxisMax;
+    }
+
+    /**
+     * By calling this method, any custom minimum value that has been previously set is reseted,
+     * and the calculation is
+     * done automatically.
+     */
+    public void resetAxisMinimum() {
+        mCustomAxisMin = false;
+    }
+
+    /**
+     * Returns true if the axis min value has been customized (and is not calculated automatically)
+     *
+     * @return
+     */
+    public boolean isAxisMinCustom() {
+        return mCustomAxisMin;
+    }
+
+    /**
+     * Set a custom minimum value for this axis. If set, this value will not be calculated
+     * automatically depending on
+     * the provided data. Use resetAxisMinValue() to undo this. Do not forget to call
+     * setStartAtZero(false) if you use
+     * this method. Otherwise, the axis-minimum value will still be forced to 0.
+     *
+     * @param min
+     */
+    public void setAxisMinimum(float min) {
+        mCustomAxisMin = true;
+        mAxisMinimum = min;
+        this.mAxisRange = Math.abs(mAxisMaximum - min);
+    }
+
+    /**
+     * Use setAxisMinimum(...) instead.
+     *
+     * @param min
+     */
+    @Deprecated
+    public void setAxisMinValue(float min) {
+        setAxisMinimum(min);
+    }
+
+    /**
+     * Set a custom maximum value for this axis. If set, this value will not be calculated
+     * automatically depending on
+     * the provided data. Use resetAxisMaxValue() to undo this.
+     *
+     * @param max
+     */
+    public void setAxisMaximum(float max) {
+        mCustomAxisMax = true;
+        mAxisMaximum = max;
+        this.mAxisRange = Math.abs(max - mAxisMinimum);
+    }
+
+    /**
+     * Use setAxisMaximum(...) instead.
+     *
+     * @param max
+     */
+    @Deprecated
+    public void setAxisMaxValue(float max) {
+        setAxisMaximum(max);
+    }
+
+    /**
+     * Calculates the minimum / maximum  and range values of the axis with the given
+     * minimum and maximum values from the chart data.
+     *
+     * @param dataMin the min value according to chart data
+     * @param dataMax the max value according to chart data
+     */
+    public void calculate(float dataMin, float dataMax) {
+
+        // if custom, use value as is, else use data value
+        float min = mCustomAxisMin ? mAxisMinimum : (dataMin - mSpaceMin);
+        float max = mCustomAxisMax ? mAxisMaximum : (dataMax + mSpaceMax);
+
+        // temporary range (before calculations)
+        float range = Math.abs(max - min);
+
+        // in case all values are equal
+        if (range == 0f) {
+            max = max + 1f;
+            min = min - 1f;
+        }
+
+        this.mAxisMinimum = min;
+        this.mAxisMaximum = max;
+
+        // actual range
+        this.mAxisRange = Math.abs(max - min);
+    }
+
+    /**
+     * Gets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
+     */
+    public float getSpaceMin()
+    {
+        return mSpaceMin;
+    }
+
+    /**
+     * Sets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
+     */
+    public void setSpaceMin(float mSpaceMin)
+    {
+        this.mSpaceMin = mSpaceMin;
+    }
+
+    /**
+     * Gets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
+     */
+    public float getSpaceMax()
+    {
+        return mSpaceMax;
+    }
+
+    /**
+     * Sets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
+     */
+    public void setSpaceMax(float mSpaceMax)
+    {
+        this.mSpaceMax = mSpaceMax;
+    }
+}

+ 173 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java

@@ -0,0 +1,173 @@
+
+package com.github.mikephil.charting.components;
+
+import android.graphics.Color;
+import android.graphics.Typeface;
+
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * This class encapsulates everything both Axis, Legend and LimitLines have in common.
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class ComponentBase {
+
+    /**
+     * flag that indicates if this axis / legend is enabled or not
+     */
+    protected boolean mEnabled = true;
+
+    /**
+     * the offset in pixels this component has on the x-axis
+     */
+    protected float mXOffset = 5f;
+
+    /**
+     * the offset in pixels this component has on the Y-axis
+     */
+    protected float mYOffset = 5f;
+
+    /**
+     * the typeface used for the labels
+     */
+    protected Typeface mTypeface = null;
+
+    /**
+     * the text size of the labels
+     */
+    protected float mTextSize = Utils.convertDpToPixel(10f);
+
+    /**
+     * the text color to use for the labels
+     */
+    protected int mTextColor = Color.BLACK;
+
+
+    public ComponentBase() {
+
+    }
+
+    /**
+     * Returns the used offset on the x-axis for drawing the axis or legend
+     * labels. This offset is applied before and after the label.
+     *
+     * @return
+     */
+    public float getXOffset() {
+        return mXOffset;
+    }
+
+    /**
+     * Sets the used x-axis offset for the labels on this axis.
+     *
+     * @param xOffset
+     */
+    public void setXOffset(float xOffset) {
+        mXOffset = Utils.convertDpToPixel(xOffset);
+    }
+
+    /**
+     * Returns the used offset on the x-axis for drawing the axis labels. This
+     * offset is applied before and after the label.
+     *
+     * @return
+     */
+    public float getYOffset() {
+        return mYOffset;
+    }
+
+    /**
+     * Sets the used y-axis offset for the labels on this axis. For the legend,
+     * higher offset means the legend as a whole will be placed further away
+     * from the top.
+     *
+     * @param yOffset
+     */
+    public void setYOffset(float yOffset) {
+        mYOffset = Utils.convertDpToPixel(yOffset);
+    }
+
+    /**
+     * returns the Typeface used for the labels, returns null if none is set
+     *
+     * @return
+     */
+    public Typeface getTypeface() {
+        return mTypeface;
+    }
+
+    /**
+     * sets a specific Typeface for the labels
+     *
+     * @param tf
+     */
+    public void setTypeface(Typeface tf) {
+        mTypeface = tf;
+    }
+
+    /**
+     * sets the size of the label text in density pixels min = 6f, max = 24f, default
+     * 10f
+     *
+     * @param size the text size, in DP
+     */
+    public void setTextSize(float size) {
+
+        if (size > 24f)
+            size = 24f;
+        if (size < 6f)
+            size = 6f;
+
+        mTextSize = Utils.convertDpToPixel(size);
+    }
+
+    /**
+     * returns the text size that is currently set for the labels, in pixels
+     *
+     * @return
+     */
+    public float getTextSize() {
+        return mTextSize;
+    }
+
+
+    /**
+     * Sets the text color to use for the labels. Make sure to use
+     * getResources().getColor(...) when using a color from the resources.
+     *
+     * @param color
+     */
+    public void setTextColor(int color) {
+        mTextColor = color;
+    }
+
+    /**
+     * Returns the text color that is set for the labels.
+     *
+     * @return
+     */
+    public int getTextColor() {
+        return mTextColor;
+    }
+
+    /**
+     * Set this to true if this component should be enabled (should be drawn),
+     * false if not. If disabled, nothing of this component will be drawn.
+     * Default: true
+     *
+     * @param enabled
+     */
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    /**
+     * Returns true if this comonent is enabled (should be drawn), false if not.
+     *
+     * @return
+     */
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+}

+ 95 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java

@@ -0,0 +1,95 @@
+package com.github.mikephil.charting.components;
+
+import android.graphics.Paint;
+
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Created by Philipp Jahoda on 17/09/16.
+ */
+public class Description extends ComponentBase {
+
+    /**
+     * the text used in the description
+     */
+    private String text = "Description Label";
+
+    /**
+     * the custom position of the description text
+     */
+    private MPPointF mPosition;
+
+    /**
+     * the alignment of the description text
+     */
+    private Paint.Align mTextAlign = Paint.Align.RIGHT;
+
+    public Description() {
+        super();
+
+        // default size
+        mTextSize = Utils.convertDpToPixel(8f);
+    }
+
+    /**
+     * Sets the text to be shown as the description.
+     * Never set this to null as this will cause nullpointer exception when drawing with Android Canvas.
+     *
+     * @param text
+     */
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    /**
+     * Returns the description text.
+     *
+     * @return
+     */
+    public String getText() {
+        return text;
+    }
+
+    /**
+     * Sets a custom position for the description text in pixels on the screen.
+     *
+     * @param x - xcoordinate
+     * @param y - ycoordinate
+     */
+    public void setPosition(float x, float y) {
+        if (mPosition == null) {
+            mPosition = MPPointF.getInstance(x, y);
+        } else {
+            mPosition.x = x;
+            mPosition.y = y;
+        }
+    }
+
+    /**
+     * Returns the customized position of the description, or null if none set.
+     *
+     * @return
+     */
+    public MPPointF getPosition() {
+        return mPosition;
+    }
+
+    /**
+     * Sets the text alignment of the description text. Default RIGHT.
+     *
+     * @param align
+     */
+    public void setTextAlign(Paint.Align align) {
+        this.mTextAlign = align;
+    }
+
+    /**
+     * Returns the text alignment of the description.
+     *
+     * @return
+     */
+    public Paint.Align getTextAlign() {
+        return mTextAlign;
+    }
+}

+ 47 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java

@@ -0,0 +1,47 @@
+package com.github.mikephil.charting.components;
+
+import android.graphics.Canvas;
+
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.utils.MPPointF;
+
+public interface IMarker {
+
+    /**
+     * @return The desired (general) offset you wish the IMarker to have on the x- and y-axis.
+     *         By returning x: -(width / 2) you will center the IMarker horizontally.
+     *         By returning y: -(height / 2) you will center the IMarker vertically.
+     */
+    MPPointF getOffset();
+
+    /**
+     * @return The offset for drawing at the specific `point`. This allows conditional adjusting of the Marker position.
+     *         If you have no adjustments to make, return getOffset().
+     *
+     * @param posX This is the X position at which the marker wants to be drawn.
+     *             You can adjust the offset conditionally based on this argument.
+     * @param posY This is the X position at which the marker wants to be drawn.
+     *             You can adjust the offset conditionally based on this argument.
+     */
+    MPPointF getOffsetForDrawingAtPoint(float posX, float posY);
+
+    /**
+     * This method enables a specified custom IMarker to update it's content every time the IMarker is redrawn.
+     *
+     * @param e         The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or
+     *                  CandleEntry, simply cast it at runtime.
+     * @param highlight The highlight object contains information about the highlighted value such as it's dataset-index, the
+     *                  selected range or stack-index (only stacked bar entries).
+     */
+    void refreshContent(Entry e, Highlight highlight);
+
+    /**
+     * Draws the IMarker on the given position on the screen with the given Canvas object.
+     *
+     * @param canvas
+     * @param posX
+     * @param posY
+     */
+    void draw(Canvas canvas, float posX, float posY);
+}

+ 825 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java

@@ -0,0 +1,825 @@
+package com.github.mikephil.charting.components;
+
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+
+import com.github.mikephil.charting.utils.ColorTemplate;
+import com.github.mikephil.charting.utils.FSize;
+import com.github.mikephil.charting.utils.Utils;
+import com.github.mikephil.charting.utils.ViewPortHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class representing the legend of the chart. The legend will contain one entry
+ * per color and DataSet. Multiple colors in one DataSet are grouped together.
+ * The legend object is NOT available before setting data to the chart.
+ *
+ * @author Philipp Jahoda
+ */
+public class Legend extends ComponentBase {
+
+    public enum LegendForm {
+        /**
+         * Avoid drawing a form
+         */
+        NONE,
+
+        /**
+         * Do not draw the a form, but leave space for it
+         */
+        EMPTY,
+
+        /**
+         * Use default (default dataset's form to the legend's form)
+         */
+        DEFAULT,
+
+        /**
+         * Draw a square
+         */
+        SQUARE,
+
+        /**
+         * Draw a circle
+         */
+        CIRCLE,
+
+        /**
+         * Draw a horizontal line
+         */
+        LINE
+    }
+
+    public enum LegendHorizontalAlignment {
+        LEFT, CENTER, RIGHT
+    }
+
+    public enum LegendVerticalAlignment {
+        TOP, CENTER, BOTTOM
+    }
+
+    public enum LegendOrientation {
+        HORIZONTAL, VERTICAL
+    }
+
+    public enum LegendDirection {
+        LEFT_TO_RIGHT, RIGHT_TO_LEFT
+    }
+
+    /**
+     * The legend entries array
+     */
+    private LegendEntry[] mEntries = new LegendEntry[]{};
+
+    /**
+     * Entries that will be appended to the end of the auto calculated entries after calculating the legend.
+     * (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect)
+     */
+    private LegendEntry[] mExtraEntries;
+
+    /**
+     * Are the legend labels/colors a custom value or auto calculated? If false,
+     * then it's auto, if true, then custom. default false (automatic legend)
+     */
+    private boolean mIsLegendCustom = false;
+
+    private LegendHorizontalAlignment mHorizontalAlignment = LegendHorizontalAlignment.LEFT;
+    private LegendVerticalAlignment mVerticalAlignment = LegendVerticalAlignment.BOTTOM;
+    private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL;
+    private boolean mDrawInside = false;
+
+    /**
+     * the text direction for the legend
+     */
+    private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT;
+
+    /**
+     * the shape/form the legend colors are drawn in
+     */
+    private LegendForm mShape = LegendForm.SQUARE;
+
+    /**
+     * the size of the legend forms/shapes
+     */
+    private float mFormSize = 8f;
+
+    /**
+     * the size of the legend forms/shapes
+     */
+    private float mFormLineWidth = 3f;
+
+    /**
+     * Line dash path effect used for shapes that consist of lines.
+     */
+    private DashPathEffect mFormLineDashEffect = null;
+
+    /**
+     * the space between the legend entries on a horizontal axis, default 6f
+     */
+    private float mXEntrySpace = 6f;
+
+    /**
+     * the space between the legend entries on a vertical axis, default 5f
+     */
+    private float mYEntrySpace = 0f;
+
+    /**
+     * the space between the legend entries on a vertical axis, default 2f
+     * private float mYEntrySpace = 2f; /** the space between the form and the
+     * actual label/text
+     */
+    private float mFormToTextSpace = 5f;
+
+    /**
+     * the space that should be left between stacked forms
+     */
+    private float mStackSpace = 3f;
+
+    /**
+     * the maximum relative size out of the whole chart view in percent
+     */
+    private float mMaxSizePercent = 0.95f;
+
+    /**
+     * default constructor
+     */
+    public Legend() {
+
+        this.mTextSize = Utils.convertDpToPixel(10f);
+        this.mXOffset = Utils.convertDpToPixel(5f);
+        this.mYOffset = Utils.convertDpToPixel(3f); // 2
+    }
+
+    /**
+     * Constructor. Provide entries for the legend.
+     *
+     * @param entries
+     */
+    public Legend(LegendEntry[] entries) {
+        this();
+
+        if (entries == null) {
+            throw new IllegalArgumentException("entries array is NULL");
+        }
+
+        this.mEntries = entries;
+    }
+
+    /**
+     * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors.
+     *
+     * @param entries
+     */
+    public void setEntries(List<LegendEntry> entries) {
+        mEntries = entries.toArray(new LegendEntry[entries.size()]);
+    }
+
+    public LegendEntry[] getEntries() {
+        return mEntries;
+    }
+
+    /**
+     * returns the maximum length in pixels across all legend labels + formsize
+     * + formtotextspace
+     *
+     * @param p the paint object used for rendering the text
+     * @return
+     */
+    public float getMaximumEntryWidth(Paint p) {
+
+        float max = 0f;
+        float maxFormSize = 0f;
+        float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
+
+        for (LegendEntry entry : mEntries) {
+            final float formSize = Utils.convertDpToPixel(
+                    Float.isNaN(entry.formSize)
+                    ? mFormSize : entry.formSize);
+            if (formSize > maxFormSize)
+                maxFormSize = formSize;
+
+            String label = entry.label;
+            if (label == null) continue;
+
+            float length = (float) Utils.calcTextWidth(p, label);
+
+            if (length > max)
+                max = length;
+        }
+
+        return max + maxFormSize + formToTextSpace;
+    }
+
+    /**
+     * returns the maximum height in pixels across all legend labels
+     *
+     * @param p the paint object used for rendering the text
+     * @return
+     */
+    public float getMaximumEntryHeight(Paint p) {
+
+        float max = 0f;
+
+        for (LegendEntry entry : mEntries) {
+            String label = entry.label;
+            if (label == null) continue;
+
+            float length = (float) Utils.calcTextHeight(p, label);
+
+            if (length > max)
+                max = length;
+        }
+
+        return max;
+    }
+
+    public LegendEntry[] getExtraEntries() {
+
+        return mExtraEntries;
+    }
+
+    public void setExtra(List<LegendEntry> entries) {
+        mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
+    }
+
+    public void setExtra(LegendEntry[] entries) {
+        if (entries == null)
+            entries = new LegendEntry[]{};
+        mExtraEntries = entries;
+    }
+
+    /**
+     * Entries that will be appended to the end of the auto calculated
+     *   entries after calculating the legend.
+     * (if the legend has already been calculated, you will need to call notifyDataSetChanged()
+     *   to let the changes take effect)
+     */
+    public void setExtra(int[] colors, String[] labels) {
+
+        List<LegendEntry> entries = new ArrayList<>();
+
+        for (int i = 0; i < Math.min(colors.length, labels.length); i++) {
+            final LegendEntry entry = new LegendEntry();
+            entry.formColor = colors[i];
+            entry.label = labels[i];
+
+            if (entry.formColor == ColorTemplate.COLOR_SKIP ||
+                    entry.formColor == 0)
+                entry.form = LegendForm.NONE;
+            else if (entry.formColor == ColorTemplate.COLOR_NONE)
+                entry.form = LegendForm.EMPTY;
+
+            entries.add(entry);
+        }
+
+        mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
+    }
+
+    /**
+     * Sets a custom legend's entries array.
+     * * A null label will start a group.
+     * This will disable the feature that automatically calculates the legend
+     *   entries from the datasets.
+     * Call resetCustom() to re-enable automatic calculation (and then
+     *   notifyDataSetChanged() is needed to auto-calculate the legend again)
+     */
+    public void setCustom(LegendEntry[] entries) {
+
+        mEntries = entries;
+        mIsLegendCustom = true;
+    }
+
+    /**
+     * Sets a custom legend's entries array.
+     * * A null label will start a group.
+     * This will disable the feature that automatically calculates the legend
+     *   entries from the datasets.
+     * Call resetCustom() to re-enable automatic calculation (and then
+     *   notifyDataSetChanged() is needed to auto-calculate the legend again)
+     */
+    public void setCustom(List<LegendEntry> entries) {
+
+        mEntries = entries.toArray(new LegendEntry[entries.size()]);
+        mIsLegendCustom = true;
+    }
+
+    /**
+     * Calling this will disable the custom legend entries (set by
+     * setCustom(...)). Instead, the entries will again be calculated
+     * automatically (after notifyDataSetChanged() is called).
+     */
+    public void resetCustom() {
+        mIsLegendCustom = false;
+    }
+
+    /**
+     * @return true if a custom legend entries has been set default
+     * false (automatic legend)
+     */
+    public boolean isLegendCustom() {
+        return mIsLegendCustom;
+    }
+
+    /**
+     * returns the horizontal alignment of the legend
+     *
+     * @return
+     */
+    public LegendHorizontalAlignment getHorizontalAlignment() {
+        return mHorizontalAlignment;
+    }
+
+    /**
+     * sets the horizontal alignment of the legend
+     *
+     * @param value
+     */
+    public void setHorizontalAlignment(LegendHorizontalAlignment value) {
+        mHorizontalAlignment = value;
+    }
+
+    /**
+     * returns the vertical alignment of the legend
+     *
+     * @return
+     */
+    public LegendVerticalAlignment getVerticalAlignment() {
+        return mVerticalAlignment;
+    }
+
+    /**
+     * sets the vertical alignment of the legend
+     *
+     * @param value
+     */
+    public void setVerticalAlignment(LegendVerticalAlignment value) {
+        mVerticalAlignment = value;
+    }
+
+    /**
+     * returns the orientation of the legend
+     *
+     * @return
+     */
+    public LegendOrientation getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * sets the orientation of the legend
+     *
+     * @param value
+     */
+    public void setOrientation(LegendOrientation value) {
+        mOrientation = value;
+    }
+
+    /**
+     * returns whether the legend will draw inside the chart or outside
+     *
+     * @return
+     */
+    public boolean isDrawInsideEnabled() {
+        return mDrawInside;
+    }
+
+    /**
+     * sets whether the legend will draw inside the chart or outside
+     *
+     * @param value
+     */
+    public void setDrawInside(boolean value) {
+        mDrawInside = value;
+    }
+
+    /**
+     * returns the text direction of the legend
+     *
+     * @return
+     */
+    public LegendDirection getDirection() {
+        return mDirection;
+    }
+
+    /**
+     * sets the text direction of the legend
+     *
+     * @param pos
+     */
+    public void setDirection(LegendDirection pos) {
+        mDirection = pos;
+    }
+
+    /**
+     * returns the current form/shape that is set for the legend
+     *
+     * @return
+     */
+    public LegendForm getForm() {
+        return mShape;
+    }
+
+    /**
+     * sets the form/shape of the legend forms
+     *
+     * @param shape
+     */
+    public void setForm(LegendForm shape) {
+        mShape = shape;
+    }
+
+    /**
+     * sets the size in dp of the legend forms, default 8f
+     *
+     * @param size
+     */
+    public void setFormSize(float size) {
+        mFormSize = size;
+    }
+
+    /**
+     * returns the size in dp of the legend forms
+     *
+     * @return
+     */
+    public float getFormSize() {
+        return mFormSize;
+    }
+
+    /**
+     * sets the line width in dp for forms that consist of lines, default 3f
+     *
+     * @param size
+     */
+    public void setFormLineWidth(float size) {
+        mFormLineWidth = size;
+    }
+
+    /**
+     * returns the line width in dp for drawing forms that consist of lines
+     *
+     * @return
+     */
+    public float getFormLineWidth() {
+        return mFormLineWidth;
+    }
+
+    /**
+     * Sets the line dash path effect used for shapes that consist of lines.
+     *
+     * @param dashPathEffect
+     */
+    public void setFormLineDashEffect(DashPathEffect dashPathEffect) {
+        mFormLineDashEffect = dashPathEffect;
+    }
+
+    /**
+     * @return The line dash path effect used for shapes that consist of lines.
+     */
+    public DashPathEffect getFormLineDashEffect() {
+        return mFormLineDashEffect;
+    }
+
+    /**
+     * returns the space between the legend entries on a horizontal axis in
+     * pixels
+     *
+     * @return
+     */
+    public float getXEntrySpace() {
+        return mXEntrySpace;
+    }
+
+    /**
+     * sets the space between the legend entries on a horizontal axis in pixels,
+     * converts to dp internally
+     *
+     * @param space
+     */
+    public void setXEntrySpace(float space) {
+        mXEntrySpace = space;
+    }
+
+    /**
+     * returns the space between the legend entries on a vertical axis in pixels
+     *
+     * @return
+     */
+    public float getYEntrySpace() {
+        return mYEntrySpace;
+    }
+
+    /**
+     * sets the space between the legend entries on a vertical axis in pixels,
+     * converts to dp internally
+     *
+     * @param space
+     */
+    public void setYEntrySpace(float space) {
+        mYEntrySpace = space;
+    }
+
+    /**
+     * returns the space between the form and the actual label/text
+     *
+     * @return
+     */
+    public float getFormToTextSpace() {
+        return mFormToTextSpace;
+    }
+
+    /**
+     * sets the space between the form and the actual label/text, converts to dp
+     * internally
+     *
+     * @param space
+     */
+    public void setFormToTextSpace(float space) {
+        this.mFormToTextSpace = space;
+    }
+
+    /**
+     * returns the space that is left out between stacked forms (with no label)
+     *
+     * @return
+     */
+    public float getStackSpace() {
+        return mStackSpace;
+    }
+
+    /**
+     * sets the space that is left out between stacked forms (with no label)
+     *
+     * @param space
+     */
+    public void setStackSpace(float space) {
+        mStackSpace = space;
+    }
+
+    /**
+     * the total width of the legend (needed width space)
+     */
+    public float mNeededWidth = 0f;
+
+    /**
+     * the total height of the legend (needed height space)
+     */
+    public float mNeededHeight = 0f;
+
+    public float mTextHeightMax = 0f;
+
+    public float mTextWidthMax = 0f;
+
+    /**
+     * flag that indicates if word wrapping is enabled
+     */
+    private boolean mWordWrapEnabled = false;
+
+    /**
+     * Should the legend word wrap? / this is currently supported only for:
+     * BelowChartLeft, BelowChartRight, BelowChartCenter. / note that word
+     * wrapping a legend takes a toll on performance. / you may want to set
+     * maxSizePercent when word wrapping, to set the point where the text wraps.
+     * / default: false
+     *
+     * @param enabled
+     */
+    public void setWordWrapEnabled(boolean enabled) {
+        mWordWrapEnabled = enabled;
+    }
+
+    /**
+     * If this is set, then word wrapping the legend is enabled. This means the
+     * legend will not be cut off if too long.
+     *
+     * @return
+     */
+    public boolean isWordWrapEnabled() {
+        return mWordWrapEnabled;
+    }
+
+    /**
+     * The maximum relative size out of the whole chart view. / If the legend is
+     * to the right/left of the chart, then this affects the width of the
+     * legend. / If the legend is to the top/bottom of the chart, then this
+     * affects the height of the legend. / If the legend is the center of the
+     * piechart, then this defines the size of the rectangular bounds out of the
+     * size of the "hole". / default: 0.95f (95%)
+     *
+     * @return
+     */
+    public float getMaxSizePercent() {
+        return mMaxSizePercent;
+    }
+
+    /**
+     * The maximum relative size out of the whole chart view. / If
+     * the legend is to the right/left of the chart, then this affects the width
+     * of the legend. / If the legend is to the top/bottom of the chart, then
+     * this affects the height of the legend. / default: 0.95f (95%)
+     *
+     * @param maxSize
+     */
+    public void setMaxSizePercent(float maxSize) {
+        mMaxSizePercent = maxSize;
+    }
+
+    private List<FSize> mCalculatedLabelSizes = new ArrayList<>(16);
+    private List<Boolean> mCalculatedLabelBreakPoints = new ArrayList<>(16);
+    private List<FSize> mCalculatedLineSizes = new ArrayList<>(16);
+
+    public List<FSize> getCalculatedLabelSizes() {
+        return mCalculatedLabelSizes;
+    }
+
+    public List<Boolean> getCalculatedLabelBreakPoints() {
+        return mCalculatedLabelBreakPoints;
+    }
+
+    public List<FSize> getCalculatedLineSizes() {
+        return mCalculatedLineSizes;
+    }
+
+    /**
+     * Calculates the dimensions of the Legend. This includes the maximum width
+     * and height of a single entry, as well as the total width and height of
+     * the Legend.
+     *
+     * @param labelpaint
+     */
+    public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) {
+
+        float defaultFormSize = Utils.convertDpToPixel(mFormSize);
+        float stackSpace = Utils.convertDpToPixel(mStackSpace);
+        float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
+        float xEntrySpace = Utils.convertDpToPixel(mXEntrySpace);
+        float yEntrySpace = Utils.convertDpToPixel(mYEntrySpace);
+        boolean wordWrapEnabled = mWordWrapEnabled;
+        LegendEntry[] entries = mEntries;
+        int entryCount = entries.length;
+
+        mTextWidthMax = getMaximumEntryWidth(labelpaint);
+        mTextHeightMax = getMaximumEntryHeight(labelpaint);
+
+        switch (mOrientation) {
+            case VERTICAL: {
+
+                float maxWidth = 0f, maxHeight = 0f, width = 0f;
+                float labelLineHeight = Utils.getLineHeight(labelpaint);
+                boolean wasStacked = false;
+
+                for (int i = 0; i < entryCount; i++) {
+
+                    LegendEntry e = entries[i];
+                    boolean drawingForm = e.form != LegendForm.NONE;
+                    float formSize = Float.isNaN(e.formSize)
+                            ? defaultFormSize
+                            : Utils.convertDpToPixel(e.formSize);
+                    String label = e.label;
+
+                    if (!wasStacked)
+                        width = 0.f;
+
+                    if (drawingForm) {
+                        if (wasStacked)
+                            width += stackSpace;
+                        width += formSize;
+                    }
+
+                    // grouped forms have null labels
+                    if (label != null) {
+
+                        // make a step to the left
+                        if (drawingForm && !wasStacked)
+                            width += formToTextSpace;
+                        else if (wasStacked) {
+                            maxWidth = Math.max(maxWidth, width);
+                            maxHeight += labelLineHeight + yEntrySpace;
+                            width = 0.f;
+                            wasStacked = false;
+                        }
+
+                        width += Utils.calcTextWidth(labelpaint, label);
+
+                        maxHeight += labelLineHeight + yEntrySpace;
+                    } else {
+                        wasStacked = true;
+                        width += formSize;
+                        if (i < entryCount - 1)
+                            width += stackSpace;
+                    }
+
+                    maxWidth = Math.max(maxWidth, width);
+                }
+
+                mNeededWidth = maxWidth;
+                mNeededHeight = maxHeight;
+
+                break;
+            }
+            case HORIZONTAL: {
+
+                float labelLineHeight = Utils.getLineHeight(labelpaint);
+                float labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace;
+                float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent;
+
+                // Start calculating layout
+                float maxLineWidth = 0.f;
+                float currentLineWidth = 0.f;
+                float requiredWidth = 0.f;
+                int stackedStartIndex = -1;
+
+                mCalculatedLabelBreakPoints.clear();
+                mCalculatedLabelSizes.clear();
+                mCalculatedLineSizes.clear();
+
+                for (int i = 0; i < entryCount; i++) {
+
+                    LegendEntry e = entries[i];
+                    boolean drawingForm = e.form != LegendForm.NONE;
+                    float formSize = Float.isNaN(e.formSize)
+                            ? defaultFormSize
+                            : Utils.convertDpToPixel(e.formSize);
+                    String label = e.label;
+
+                    mCalculatedLabelBreakPoints.add(false);
+
+                    if (stackedStartIndex == -1) {
+                        // we are not stacking, so required width is for this label
+                        // only
+                        requiredWidth = 0.f;
+                    } else {
+                        // add the spacing appropriate for stacked labels/forms
+                        requiredWidth += stackSpace;
+                    }
+
+                    // grouped forms have null labels
+                    if (label != null) {
+
+                        mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label));
+                        requiredWidth += drawingForm ? formToTextSpace + formSize : 0.f;
+                        requiredWidth += mCalculatedLabelSizes.get(i).width;
+                    } else {
+
+                        mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f));
+                        requiredWidth += drawingForm ? formSize : 0.f;
+
+                        if (stackedStartIndex == -1) {
+                            // mark this index as we might want to break here later
+                            stackedStartIndex = i;
+                        }
+                    }
+
+                    if (label != null || i == entryCount - 1) {
+
+                        float requiredSpacing = currentLineWidth == 0.f ? 0.f : xEntrySpace;
+
+                        if (!wordWrapEnabled // No word wrapping, it must fit.
+                                // The line is empty, it must fit
+                                || currentLineWidth == 0.f
+                                // It simply fits
+                                || (contentWidth - currentLineWidth >=
+                                requiredSpacing + requiredWidth)) {
+                            // Expand current line
+                            currentLineWidth += requiredSpacing + requiredWidth;
+                        } else { // It doesn't fit, we need to wrap a line
+
+                            // Add current line size to array
+                            mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
+                            maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
+
+                            // Start a new line
+                            mCalculatedLabelBreakPoints.set(
+                                    stackedStartIndex > -1 ? stackedStartIndex
+                                            : i, true);
+                            currentLineWidth = requiredWidth;
+                        }
+
+                        if (i == entryCount - 1) {
+                            // Add last line size to array
+                            mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
+                            maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
+                        }
+                    }
+
+                    stackedStartIndex = label != null ? -1 : stackedStartIndex;
+                }
+
+                mNeededWidth = maxLineWidth;
+                mNeededHeight = labelLineHeight
+                        * (float) (mCalculatedLineSizes.size())
+                        + labelLineSpacing *
+                        (float) (mCalculatedLineSizes.size() == 0
+                                ? 0
+                                : (mCalculatedLineSizes.size() - 1));
+
+                break;
+            }
+        }
+
+        mNeededHeight += mYOffset;
+        mNeededWidth += mXOffset;
+    }
+}

+ 78 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java

@@ -0,0 +1,78 @@
+package com.github.mikephil.charting.components;
+
+
+import android.graphics.DashPathEffect;
+
+import com.github.mikephil.charting.utils.ColorTemplate;
+
+public class LegendEntry {
+    public LegendEntry() {
+
+    }
+
+    /**
+     *
+     * @param label The legend entry text. A `null` label will start a group.
+     * @param form The form to draw for this entry.
+     * @param formSize Set to NaN to use the legend's default.
+     * @param formLineWidth Set to NaN to use the legend's default.
+     * @param formLineDashEffect Set to nil to use the legend's default.
+     * @param formColor The color for drawing the form.
+     */
+    public LegendEntry(String label,
+                       Legend.LegendForm form,
+                       float formSize,
+                       float formLineWidth,
+                       DashPathEffect formLineDashEffect,
+                       int formColor)
+    {
+        this.label = label;
+        this.form = form;
+        this.formSize = formSize;
+        this.formLineWidth = formLineWidth;
+        this.formLineDashEffect = formLineDashEffect;
+        this.formColor = formColor;
+    }
+
+    /**
+     * The legend entry text.
+     * A `null` label will start a group.
+     */
+    public String label;
+
+    /**
+     * The form to draw for this entry.
+     *
+     * `NONE` will avoid drawing a form, and any related space.
+     * `EMPTY` will avoid drawing a form, but keep its space.
+     * `DEFAULT` will use the Legend's default.
+     */
+    public Legend.LegendForm form = Legend.LegendForm.DEFAULT;
+
+    /**
+     * Form size will be considered except for when .None is used
+     *
+     * Set as NaN to use the legend's default
+     */
+    public float formSize = Float.NaN;
+
+    /**
+     * Line width used for shapes that consist of lines.
+     *
+     * Set as NaN to use the legend's default
+     */
+    public float formLineWidth = Float.NaN;
+
+    /**
+     * Line dash path effect used for shapes that consist of lines.
+     *
+     * Set to null to use the legend's default
+     */
+    public DashPathEffect formLineDashEffect = null;
+
+    /**
+     * The color for drawing the form
+     */
+    public int formColor = ColorTemplate.COLOR_NONE;
+
+}

+ 215 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java

@@ -0,0 +1,215 @@
+
+package com.github.mikephil.charting.components;
+
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * The limit line is an additional feature for all Line-, Bar- and
+ * ScatterCharts. It allows the displaying of an additional line in the chart
+ * that marks a certain maximum / limit on the specified axis (x- or y-axis).
+ * 
+ * @author Philipp Jahoda
+ */
+public class LimitLine extends ComponentBase {
+
+    /** limit / maximum (the y-value or xIndex) */
+    private float mLimit = 0f;
+
+    /** the width of the limit line */
+    private float mLineWidth = 2f;
+
+    /** the color of the limit line */
+    private int mLineColor = Color.rgb(237, 91, 91);
+
+    /** the style of the label text */
+    private Paint.Style mTextStyle = Paint.Style.FILL_AND_STROKE;
+
+    /** label string that is drawn next to the limit line */
+    private String mLabel = "";
+
+    /** the path effect of this LimitLine that makes dashed lines possible */
+    private DashPathEffect mDashPathEffect = null;
+
+    /** indicates the position of the LimitLine label */
+    private LimitLabelPosition mLabelPosition = LimitLabelPosition.RIGHT_TOP;
+
+    /** enum that indicates the position of the LimitLine label */
+    public enum LimitLabelPosition {
+        LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM
+    }
+
+    /**
+     * Constructor with limit.
+     * 
+     * @param limit - the position (the value) on the y-axis (y-value) or x-axis
+     *            (xIndex) where this line should appear
+     */
+    public LimitLine(float limit) {
+        mLimit = limit;
+    }
+
+    /**
+     * Constructor with limit and label.
+     * 
+     * @param limit - the position (the value) on the y-axis (y-value) or x-axis
+     *            (xIndex) where this line should appear
+     * @param label - provide "" if no label is required
+     */
+    public LimitLine(float limit, String label) {
+        mLimit = limit;
+        mLabel = label;
+    }
+
+    /**
+     * Returns the limit that is set for this line.
+     * 
+     * @return
+     */
+    public float getLimit() {
+        return mLimit;
+    }
+
+    /**
+     * set the line width of the chart (min = 0.2f, max = 12f); default 2f NOTE:
+     * thinner line == better performance, thicker line == worse performance
+     * 
+     * @param width
+     */
+    public void setLineWidth(float width) {
+
+        if (width < 0.2f)
+            width = 0.2f;
+        if (width > 12.0f)
+            width = 12.0f;
+        mLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    /**
+     * returns the width of limit line
+     * 
+     * @return
+     */
+    public float getLineWidth() {
+        return mLineWidth;
+    }
+
+    /**
+     * Sets the linecolor for this LimitLine. Make sure to use
+     * getResources().getColor(...)
+     * 
+     * @param color
+     */
+    public void setLineColor(int color) {
+        mLineColor = color;
+    }
+
+    /**
+     * Returns the color that is used for this LimitLine
+     * 
+     * @return
+     */
+    public int getLineColor() {
+        return mLineColor;
+    }
+
+    /**
+     * Enables the line to be drawn in dashed mode, e.g. like this "- - - - - -"
+     * 
+     * @param lineLength the length of the line pieces
+     * @param spaceLength the length of space inbetween the pieces
+     * @param phase offset, in degrees (normally, use 0)
+     */
+    public void enableDashedLine(float lineLength, float spaceLength, float phase) {
+        mDashPathEffect = new DashPathEffect(new float[] {
+                lineLength, spaceLength
+        }, phase);
+    }
+
+    /**
+     * Disables the line to be drawn in dashed mode.
+     */
+    public void disableDashedLine() {
+        mDashPathEffect = null;
+    }
+
+    /**
+     * Returns true if the dashed-line effect is enabled, false if not. Default:
+     * disabled
+     * 
+     * @return
+     */
+    public boolean isDashedLineEnabled() {
+        return mDashPathEffect == null ? false : true;
+    }
+
+    /**
+     * returns the DashPathEffect that is set for this LimitLine
+     * 
+     * @return
+     */
+    public DashPathEffect getDashPathEffect() {
+        return mDashPathEffect;
+    }
+
+    /**
+     * Sets the color of the value-text that is drawn next to the LimitLine.
+     * Default: Paint.Style.FILL_AND_STROKE
+     *
+     * @param style
+     */
+    public void setTextStyle(Paint.Style style) {
+        this.mTextStyle = style;
+    }
+
+    /**
+     * Returns the color of the value-text that is drawn next to the LimitLine.
+     *
+     * @return
+     */
+    public Paint.Style getTextStyle() {
+        return mTextStyle;
+    }
+
+    /**
+     * Sets the position of the LimitLine value label (either on the right or on
+     * the left edge of the chart). Not supported for RadarChart.
+     * 
+     * @param pos
+     */
+    public void setLabelPosition(LimitLabelPosition pos) {
+        mLabelPosition = pos;
+    }
+
+    /**
+     * Returns the position of the LimitLine label (value).
+     * 
+     * @return
+     */
+    public LimitLabelPosition getLabelPosition() {
+        return mLabelPosition;
+    }
+
+    /**
+     * Sets the label that is drawn next to the limit line. Provide "" if no
+     * label is required.
+     * 
+     * @param label
+     */
+    public void setLabel(String label) {
+        mLabel = label;
+    }
+
+    /**
+     * Returns the label that is drawn next to the limit line.
+     * 
+     * @return
+     */
+    public String getLabel() {
+        return mLabel;
+    }
+}

+ 167 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java

@@ -0,0 +1,167 @@
+package com.github.mikephil.charting.components;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.github.mikephil.charting.charts.Chart;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.utils.FSize;
+import com.github.mikephil.charting.utils.MPPointF;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your
+ * markers.
+ *
+ * @author Philipp Jahoda
+ */
+public class MarkerImage implements IMarker {
+
+    private Context mContext;
+    private Drawable mDrawable;
+
+    private MPPointF mOffset = new MPPointF();
+    private MPPointF mOffset2 = new MPPointF();
+    private WeakReference<Chart> mWeakChart;
+
+    private FSize mSize = new FSize();
+    private Rect mDrawableBoundsCache = new Rect();
+
+    /**
+     * Constructor. Sets up the MarkerView with a custom layout resource.
+     *
+     * @param context
+     * @param drawableResourceId the drawable resource to render
+     */
+    public MarkerImage(Context context, int drawableResourceId) {
+        mContext = context;
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+        {
+            mDrawable = mContext.getResources().getDrawable(drawableResourceId, null);
+        }
+        else
+        {
+            mDrawable = mContext.getResources().getDrawable(drawableResourceId);
+        }
+    }
+
+    public void setOffset(MPPointF offset) {
+        mOffset = offset;
+
+        if (mOffset == null) {
+            mOffset = new MPPointF();
+        }
+    }
+
+    public void setOffset(float offsetX, float offsetY) {
+        mOffset.x = offsetX;
+        mOffset.y = offsetY;
+    }
+
+    @Override
+    public MPPointF getOffset() {
+        return mOffset;
+    }
+
+    public void setSize(FSize size) {
+        mSize = size;
+
+        if (mSize == null) {
+            mSize = new FSize();
+        }
+    }
+
+    public FSize getSize() {
+        return mSize;
+    }
+
+    public void setChartView(Chart chart) {
+        mWeakChart = new WeakReference<>(chart);
+    }
+
+    public Chart getChartView() {
+        return mWeakChart == null ? null : mWeakChart.get();
+    }
+
+    @Override
+    public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
+
+        MPPointF offset = getOffset();
+        mOffset2.x = offset.x;
+        mOffset2.y = offset.y;
+
+        Chart chart = getChartView();
+
+        float width = mSize.width;
+        float height = mSize.height;
+
+        if (width == 0.f && mDrawable != null) {
+            width = mDrawable.getIntrinsicWidth();
+        }
+        if (height == 0.f && mDrawable != null) {
+            height = mDrawable.getIntrinsicHeight();
+        }
+
+        if (posX + mOffset2.x < 0) {
+            mOffset2.x = - posX;
+        } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
+            mOffset2.x = chart.getWidth() - posX - width;
+        }
+
+        if (posY + mOffset2.y < 0) {
+            mOffset2.y = - posY;
+        } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
+            mOffset2.y = chart.getHeight() - posY - height;
+        }
+
+        return mOffset2;
+    }
+
+    @Override
+    public void refreshContent(Entry e, Highlight highlight) {
+
+    }
+
+    @Override
+    public void draw(Canvas canvas, float posX, float posY) {
+
+        if (mDrawable == null) return;
+
+        MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
+
+        float width = mSize.width;
+        float height = mSize.height;
+
+        if (width == 0.f) {
+            width = mDrawable.getIntrinsicWidth();
+        }
+        if (height == 0.f) {
+            height = mDrawable.getIntrinsicHeight();
+        }
+
+        mDrawable.copyBounds(mDrawableBoundsCache);
+        mDrawable.setBounds(
+                mDrawableBoundsCache.left,
+                mDrawableBoundsCache.top,
+                mDrawableBoundsCache.left + (int)width,
+                mDrawableBoundsCache.top + (int)height);
+
+        int saveId = canvas.save();
+        // translate to the correct position and draw
+        canvas.translate(posX + offset.x, posY + offset.y);
+        mDrawable.draw(canvas);
+        canvas.restoreToCount(saveId);
+
+        mDrawable.setBounds(mDrawableBoundsCache);
+    }
+}

+ 129 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java

@@ -0,0 +1,129 @@
+package com.github.mikephil.charting.components;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.github.mikephil.charting.charts.Chart;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.utils.FSize;
+import com.github.mikephil.charting.utils.MPPointF;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your
+ * markers.
+ *
+ * @author Philipp Jahoda
+ */
+public class MarkerView extends RelativeLayout implements IMarker {
+
+    private MPPointF mOffset = new MPPointF();
+    private MPPointF mOffset2 = new MPPointF();
+    private WeakReference<Chart> mWeakChart;
+
+    /**
+     * Constructor. Sets up the MarkerView with a custom layout resource.
+     *
+     * @param context
+     * @param layoutResource the layout resource to use for the MarkerView
+     */
+    public MarkerView(Context context, int layoutResource) {
+        super(context);
+        setupLayoutResource(layoutResource);
+    }
+
+    /**
+     * Sets the layout resource for a custom MarkerView.
+     *
+     * @param layoutResource
+     */
+    private void setupLayoutResource(int layoutResource) {
+
+        View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this);
+
+        inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
+        inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+
+        // measure(getWidth(), getHeight());
+        inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight());
+    }
+
+    public void setOffset(MPPointF offset) {
+        mOffset = offset;
+
+        if (mOffset == null) {
+            mOffset = new MPPointF();
+        }
+    }
+
+    public void setOffset(float offsetX, float offsetY) {
+        mOffset.x = offsetX;
+        mOffset.y = offsetY;
+    }
+
+    @Override
+    public MPPointF getOffset() {
+        return mOffset;
+    }
+
+    public void setChartView(Chart chart) {
+        mWeakChart = new WeakReference<>(chart);
+    }
+
+    public Chart getChartView() {
+        return mWeakChart == null ? null : mWeakChart.get();
+    }
+
+    @Override
+    public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
+
+        MPPointF offset = getOffset();
+        mOffset2.x = offset.x;
+        mOffset2.y = offset.y;
+
+        Chart chart = getChartView();
+
+        float width = getWidth();
+        float height = getHeight();
+
+        if (posX + mOffset2.x < 0) {
+            mOffset2.x = - posX;
+        } else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
+            mOffset2.x = chart.getWidth() - posX - width;
+        }
+
+        if (posY + mOffset2.y < 0) {
+            mOffset2.y = - posY;
+        } else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
+            mOffset2.y = chart.getHeight() - posY - height;
+        }
+
+        return mOffset2;
+    }
+
+    @Override
+    public void refreshContent(Entry e, Highlight highlight) {
+
+        measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+        layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+
+    }
+
+    @Override
+    public void draw(Canvas canvas, float posX, float posY) {
+
+        MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
+
+        int saveId = canvas.save();
+        // translate to the correct position and draw
+        canvas.translate(posX + offset.x, posY + offset.y);
+        draw(canvas);
+        canvas.restoreToCount(saveId);
+    }
+}

+ 118 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java

@@ -0,0 +1,118 @@
+
+package com.github.mikephil.charting.components;
+
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Class representing the x-axis labels settings. Only use the setter methods to
+ * modify it. Do not access public variables directly. Be aware that not all
+ * features the XLabels class provides are suitable for the RadarChart.
+ *
+ * @author Philipp Jahoda
+ */
+public class XAxis extends AxisBase {
+
+    /**
+     * width of the x-axis labels in pixels - this is automatically
+     * calculated by the computeSize() methods in the renderers
+     */
+    public int mLabelWidth = 1;
+
+    /**
+     * height of the x-axis labels in pixels - this is automatically
+     * calculated by the computeSize() methods in the renderers
+     */
+    public int mLabelHeight = 1;
+
+    /**
+     * width of the (rotated) x-axis labels in pixels - this is automatically
+     * calculated by the computeSize() methods in the renderers
+     */
+    public int mLabelRotatedWidth = 1;
+
+    /**
+     * height of the (rotated) x-axis labels in pixels - this is automatically
+     * calculated by the computeSize() methods in the renderers
+     */
+    public int mLabelRotatedHeight = 1;
+
+    /**
+     * This is the angle for drawing the X axis labels (in degrees)
+     */
+    protected float mLabelRotationAngle = 0f;
+
+    /**
+     * if set to true, the chart will avoid that the first and last label entry
+     * in the chart "clip" off the edge of the chart
+     */
+    private boolean mAvoidFirstLastClipping = false;
+
+    /**
+     * the position of the x-labels relative to the chart
+     */
+    private XAxisPosition mPosition = XAxisPosition.TOP;
+
+    /**
+     * enum for the position of the x-labels relative to the chart
+     */
+    public enum XAxisPosition {
+        TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE
+    }
+
+    public XAxis() {
+        super();
+
+        mYOffset = Utils.convertDpToPixel(4.f); // -3
+    }
+
+    /**
+     * returns the position of the x-labels
+     */
+    public XAxisPosition getPosition() {
+        return mPosition;
+    }
+
+    /**
+     * sets the position of the x-labels
+     *
+     * @param pos
+     */
+    public void setPosition(XAxisPosition pos) {
+        mPosition = pos;
+    }
+
+    /**
+     * returns the angle for drawing the X axis labels (in degrees)
+     */
+    public float getLabelRotationAngle() {
+        return mLabelRotationAngle;
+    }
+
+    /**
+     * sets the angle for drawing the X axis labels (in degrees)
+     *
+     * @param angle the angle in degrees
+     */
+    public void setLabelRotationAngle(float angle) {
+        mLabelRotationAngle = angle;
+    }
+
+    /**
+     * if set to true, the chart will avoid that the first and last label entry
+     * in the chart "clip" off the edge of the chart or the screen
+     *
+     * @param enabled
+     */
+    public void setAvoidFirstLastClipping(boolean enabled) {
+        mAvoidFirstLastClipping = enabled;
+    }
+
+    /**
+     * returns true if avoid-first-lastclipping is enabled, false if not
+     *
+     * @return
+     */
+    public boolean isAvoidFirstLastClippingEnabled() {
+        return mAvoidFirstLastClipping;
+    }
+}

+ 467 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java

@@ -0,0 +1,467 @@
+package com.github.mikephil.charting.components;
+
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Class representing the y-axis labels settings and its entries. Only use the setter methods to
+ * modify it. Do not
+ * access public variables directly. Be aware that not all features the YLabels class provides
+ * are suitable for the
+ * RadarChart. Customizations that affect the value range of the axis need to be applied before
+ * setting data for the
+ * chart.
+ *
+ * @author Philipp Jahoda
+ */
+public class YAxis extends AxisBase {
+
+    /**
+     * indicates if the bottom y-label entry is drawn or not
+     */
+    private boolean mDrawBottomYLabelEntry = true;
+
+    /**
+     * indicates if the top y-label entry is drawn or not
+     */
+    private boolean mDrawTopYLabelEntry = true;
+
+    /**
+     * flag that indicates if the axis is inverted or not
+     */
+    protected boolean mInverted = false;
+
+    /**
+     * flag that indicates if the zero-line should be drawn regardless of other grid lines
+     */
+    protected boolean mDrawZeroLine = false;
+
+    /**
+     * flag indicating that auto scale min restriction should be used
+     */
+    private boolean mUseAutoScaleRestrictionMin = false;
+
+    /**
+     * flag indicating that auto scale max restriction should be used
+     */
+    private boolean mUseAutoScaleRestrictionMax = false;
+
+    /**
+     * Color of the zero line
+     */
+    protected int mZeroLineColor = Color.GRAY;
+
+    /**
+     * Width of the zero line in pixels
+     */
+    protected float mZeroLineWidth = 1f;
+
+    /**
+     * axis space from the largest value to the top in percent of the total axis range
+     */
+    protected float mSpacePercentTop = 10f;
+
+    /**
+     * axis space from the smallest value to the bottom in percent of the total axis range
+     */
+    protected float mSpacePercentBottom = 10f;
+
+    /**
+     * the position of the y-labels relative to the chart
+     */
+    private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART;
+
+    /**
+     * the horizontal offset of the y-label
+     */
+    private float mXLabelOffset = 0.0f;
+
+    /**
+     * enum for the position of the y-labels relative to the chart
+     */
+    public enum YAxisLabelPosition {
+        OUTSIDE_CHART, INSIDE_CHART
+    }
+
+    /**
+     * the side this axis object represents
+     */
+    private AxisDependency mAxisDependency;
+
+    /**
+     * the minimum width that the axis should take (in dp).
+     * <p/>
+     * default: 0.0
+     */
+    protected float mMinWidth = 0.f;
+
+    /**
+     * the maximum width that the axis can take (in dp).
+     * use Inifinity for disabling the maximum
+     * default: Float.POSITIVE_INFINITY (no maximum specified)
+     */
+    protected float mMaxWidth = Float.POSITIVE_INFINITY;
+
+    /**
+     * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT.
+     *
+     * @author Philipp Jahoda
+     */
+    public enum AxisDependency {
+        LEFT, RIGHT
+    }
+
+    public YAxis() {
+        super();
+
+        // default left
+        this.mAxisDependency = AxisDependency.LEFT;
+        this.mYOffset = 0f;
+    }
+
+    public YAxis(AxisDependency position) {
+        super();
+        this.mAxisDependency = position;
+        this.mYOffset = 0f;
+    }
+
+    public AxisDependency getAxisDependency() {
+        return mAxisDependency;
+    }
+
+    /**
+     * @return the minimum width that the axis should take (in dp).
+     */
+    public float getMinWidth() {
+        return mMinWidth;
+    }
+
+    /**
+     * Sets the minimum width that the axis should take (in dp).
+     *
+     * @param minWidth
+     */
+    public void setMinWidth(float minWidth) {
+        mMinWidth = minWidth;
+    }
+
+    /**
+     * @return the maximum width that the axis can take (in dp).
+     */
+    public float getMaxWidth() {
+        return mMaxWidth;
+    }
+
+    /**
+     * Sets the maximum width that the axis can take (in dp).
+     *
+     * @param maxWidth
+     */
+    public void setMaxWidth(float maxWidth) {
+        mMaxWidth = maxWidth;
+    }
+
+    /**
+     * returns the position of the y-labels
+     */
+    public YAxisLabelPosition getLabelPosition() {
+        return mPosition;
+    }
+
+    /**
+     * sets the position of the y-labels
+     *
+     * @param pos
+     */
+    public void setPosition(YAxisLabelPosition pos) {
+        mPosition = pos;
+    }
+
+    /**
+     * returns the horizontal offset of the y-label
+     */
+    public float getLabelXOffset() {
+        return mXLabelOffset;
+    }
+
+    /**
+     * sets the horizontal offset of the y-label
+     *
+     * @param xOffset
+     */
+    public void setLabelXOffset(float xOffset) {
+        mXLabelOffset = xOffset;
+    }
+
+    /**
+     * returns true if drawing the top y-axis label entry is enabled
+     *
+     * @return
+     */
+    public boolean isDrawTopYLabelEntryEnabled() {
+        return mDrawTopYLabelEntry;
+    }
+
+    /**
+     * returns true if drawing the bottom y-axis label entry is enabled
+     *
+     * @return
+     */
+    public boolean isDrawBottomYLabelEntryEnabled() {
+        return mDrawBottomYLabelEntry;
+    }
+
+    /**
+     * set this to true to enable drawing the top y-label entry. Disabling this can be helpful
+     * when the top y-label and
+     * left x-label interfere with each other. default: true
+     *
+     * @param enabled
+     */
+    public void setDrawTopYLabelEntry(boolean enabled) {
+        mDrawTopYLabelEntry = enabled;
+    }
+
+    /**
+     * If this is set to true, the y-axis is inverted which means that low values are on top of
+     * the chart, high values
+     * on bottom.
+     *
+     * @param enabled
+     */
+    public void setInverted(boolean enabled) {
+        mInverted = enabled;
+    }
+
+    /**
+     * If this returns true, the y-axis is inverted.
+     *
+     * @return
+     */
+    public boolean isInverted() {
+        return mInverted;
+    }
+
+    /**
+     * This method is deprecated.
+     * Use setAxisMinimum(...) / setAxisMaximum(...) instead.
+     *
+     * @param startAtZero
+     */
+    @Deprecated
+    public void setStartAtZero(boolean startAtZero) {
+        if (startAtZero)
+            setAxisMinimum(0f);
+        else
+            resetAxisMinimum();
+    }
+
+    /**
+     * Sets the top axis space in percent of the full range. Default 10f
+     *
+     * @param percent
+     */
+    public void setSpaceTop(float percent) {
+        mSpacePercentTop = percent;
+    }
+
+    /**
+     * Returns the top axis space in percent of the full range. Default 10f
+     *
+     * @return
+     */
+    public float getSpaceTop() {
+        return mSpacePercentTop;
+    }
+
+    /**
+     * Sets the bottom axis space in percent of the full range. Default 10f
+     *
+     * @param percent
+     */
+    public void setSpaceBottom(float percent) {
+        mSpacePercentBottom = percent;
+    }
+
+    /**
+     * Returns the bottom axis space in percent of the full range. Default 10f
+     *
+     * @return
+     */
+    public float getSpaceBottom() {
+        return mSpacePercentBottom;
+    }
+
+    public boolean isDrawZeroLineEnabled() {
+        return mDrawZeroLine;
+    }
+
+    /**
+     * Set this to true to draw the zero-line regardless of weather other
+     * grid-lines are enabled or not. Default: false
+     *
+     * @param mDrawZeroLine
+     */
+    public void setDrawZeroLine(boolean mDrawZeroLine) {
+        this.mDrawZeroLine = mDrawZeroLine;
+    }
+
+    public int getZeroLineColor() {
+        return mZeroLineColor;
+    }
+
+    /**
+     * Sets the color of the zero line
+     *
+     * @param color
+     */
+    public void setZeroLineColor(int color) {
+        mZeroLineColor = color;
+    }
+
+    public float getZeroLineWidth() {
+        return mZeroLineWidth;
+    }
+
+    /**
+     * Sets the width of the zero line in dp
+     *
+     * @param width
+     */
+    public void setZeroLineWidth(float width) {
+        this.mZeroLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    /**
+     * This is for normal (not horizontal) charts horizontal spacing.
+     *
+     * @param p
+     * @return
+     */
+    public float getRequiredWidthSpace(Paint p) {
+
+        p.setTextSize(mTextSize);
+
+        String label = getLongestLabel();
+        float width = (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f;
+
+        float minWidth = getMinWidth();
+        float maxWidth = getMaxWidth();
+
+        if (minWidth > 0.f)
+            minWidth = Utils.convertDpToPixel(minWidth);
+
+        if (maxWidth > 0.f && maxWidth != Float.POSITIVE_INFINITY)
+            maxWidth = Utils.convertDpToPixel(maxWidth);
+
+        width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width));
+
+        return width;
+    }
+
+    /**
+     * This is for HorizontalBarChart vertical spacing.
+     *
+     * @param p
+     * @return
+     */
+    public float getRequiredHeightSpace(Paint p) {
+
+        p.setTextSize(mTextSize);
+
+        String label = getLongestLabel();
+        return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f;
+    }
+
+    /**
+     * Returns true if this axis needs horizontal offset, false if no offset is needed.
+     *
+     * @return
+     */
+    public boolean needsOffset() {
+        if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition
+                .OUTSIDE_CHART)
+            return true;
+        else
+            return false;
+    }
+
+    /**
+     * Returns true if autoscale restriction for axis min value is enabled
+     */
+    @Deprecated
+    public boolean isUseAutoScaleMinRestriction( ) {
+        return mUseAutoScaleRestrictionMin;
+    }
+
+    /**
+     * Sets autoscale restriction for axis min value as enabled/disabled
+     */
+    @Deprecated
+    public void setUseAutoScaleMinRestriction( boolean isEnabled ) {
+        mUseAutoScaleRestrictionMin = isEnabled;
+    }
+
+    /**
+     * Returns true if autoscale restriction for axis max value is enabled
+     */
+    @Deprecated
+    public boolean isUseAutoScaleMaxRestriction() {
+        return mUseAutoScaleRestrictionMax;
+    }
+
+    /**
+     * Sets autoscale restriction for axis max value as enabled/disabled
+     */
+    @Deprecated
+    public void setUseAutoScaleMaxRestriction( boolean isEnabled ) {
+        mUseAutoScaleRestrictionMax = isEnabled;
+    }
+
+
+    @Override
+    public void calculate(float dataMin, float dataMax) {
+
+        float min = dataMin;
+        float max = dataMax;
+
+        // Make sure max is greater than min
+        // Discussion: https://github.com/danielgindi/Charts/pull/3650#discussion_r221409991
+        if (min > max)
+        {
+            if (mCustomAxisMax && mCustomAxisMin)
+            {
+                float t = min;
+                min = max;
+                max = t;
+            }
+            else if (mCustomAxisMax)
+            {
+                min = max < 0f ? max * 1.5f : max * 0.5f;
+            }
+            else if (mCustomAxisMin)
+            {
+                max = min < 0f ? min * 0.5f : min * 1.5f;
+            }
+        }
+
+        float range = Math.abs(max - min);
+
+        // in case all values are equal
+        if (range == 0f) {
+            max = max + 1f;
+            min = min - 1f;
+        }
+
+        // recalculate
+        range = Math.abs(max - min);
+
+        // calc extra spacing
+        this.mAxisMinimum = mCustomAxisMin ? this.mAxisMinimum : min - (range / 100f) * getSpaceBottom();
+        this.mAxisMaximum = mCustomAxisMax ? this.mAxisMaximum : max + (range / 100f) * getSpaceTop();
+
+        this.mAxisRange = Math.abs(this.mAxisMinimum - this.mAxisMaximum);
+    }
+}

+ 119 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java

@@ -0,0 +1,119 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+
+import java.util.List;
+
+/**
+ * Data object that represents all data for the BarChart.
+ *
+ * @author Philipp Jahoda
+ */
+public class BarData extends BarLineScatterCandleBubbleData<IBarDataSet> {
+
+    /**
+     * the width of the bars on the x-axis, in values (not pixels)
+     */
+    private float mBarWidth = 0.85f;
+
+    public BarData() {
+        super();
+    }
+
+    public BarData(IBarDataSet... dataSets) {
+        super(dataSets);
+    }
+
+    public BarData(List<IBarDataSet> dataSets) {
+        super(dataSets);
+    }
+
+    /**
+     * Sets the width each bar should have on the x-axis (in values, not pixels).
+     * Default 0.85f
+     *
+     * @param mBarWidth
+     */
+    public void setBarWidth(float mBarWidth) {
+        this.mBarWidth = mBarWidth;
+    }
+
+    public float getBarWidth() {
+        return mBarWidth;
+    }
+
+    /**
+     * Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries.
+     * Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified
+     * by the parameters.
+     * Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method.
+     *
+     * @param fromX      the starting point on the x-axis where the grouping should begin
+     * @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f
+     * @param barSpace   the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f
+     */
+    public void groupBars(float fromX, float groupSpace, float barSpace) {
+
+        int setCount = mDataSets.size();
+        if (setCount <= 1) {
+            throw new RuntimeException("BarData needs to hold at least 2 BarDataSets to allow grouping.");
+        }
+
+        IBarDataSet max = getMaxEntryCountSet();
+        int maxEntryCount = max.getEntryCount();
+
+        float groupSpaceWidthHalf = groupSpace / 2f;
+        float barSpaceHalf = barSpace / 2f;
+        float barWidthHalf = mBarWidth / 2f;
+
+        float interval = getGroupWidth(groupSpace, barSpace);
+
+        for (int i = 0; i < maxEntryCount; i++) {
+
+            float start = fromX;
+            fromX += groupSpaceWidthHalf;
+
+            for (IBarDataSet set : mDataSets) {
+
+                fromX += barSpaceHalf;
+                fromX += barWidthHalf;
+
+                if (i < set.getEntryCount()) {
+
+                    BarEntry entry = set.getEntryForIndex(i);
+
+                    if (entry != null) {
+                        entry.setX(fromX);
+                    }
+                }
+
+                fromX += barWidthHalf;
+                fromX += barSpaceHalf;
+            }
+
+            fromX += groupSpaceWidthHalf;
+            float end = fromX;
+            float innerInterval = end - start;
+            float diff = interval - innerInterval;
+
+            // correct rounding errors
+            if (diff > 0 || diff < 0) {
+                fromX += diff;
+            }
+        }
+
+        notifyDataChanged();
+    }
+
+    /**
+     * In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis.
+     *
+     * @param groupSpace
+     * @param barSpace
+     * @return
+     */
+    public float getGroupWidth(float groupSpace, float barSpace) {
+        return mDataSets.size() * (mBarWidth + barSpace) + groupSpace;
+    }
+}

+ 299 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java

@@ -0,0 +1,299 @@
+
+package com.github.mikephil.charting.data;
+
+import android.graphics.Color;
+
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+import com.github.mikephil.charting.utils.Fill;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BarDataSet extends BarLineScatterCandleBubbleDataSet<BarEntry> implements IBarDataSet {
+
+    /**
+     * the maximum number of bars that are stacked upon each other, this value
+     * is calculated from the Entries that are added to the DataSet
+     */
+    private int mStackSize = 1;
+
+    /**
+     * the color used for drawing the bar shadows
+     */
+    private int mBarShadowColor = Color.rgb(215, 215, 215);
+
+    private float mBarBorderWidth = 0.0f;
+
+    private int mBarBorderColor = Color.BLACK;
+
+    /**
+     * the alpha value used to draw the highlight indicator bar
+     */
+    private int mHighLightAlpha = 120;
+
+    /**
+     * the overall entry count, including counting each stack-value individually
+     */
+    private int mEntryCountStacks = 0;
+
+    /**
+     * array of labels used to describe the different values of the stacked bars
+     */
+    private String[] mStackLabels = new String[]{};
+
+    protected List<Fill> mFills = null;
+
+    public BarDataSet(List<BarEntry> yVals, String label) {
+        super(yVals, label);
+
+        mHighLightColor = Color.rgb(0, 0, 0);
+
+        calcStackSize(yVals);
+        calcEntryCountIncludingStacks(yVals);
+    }
+
+    @Override
+    public DataSet<BarEntry> copy() {
+        List<BarEntry> entries = new ArrayList<BarEntry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        BarDataSet copied = new BarDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(BarDataSet barDataSet) {
+        super.copy(barDataSet);
+        barDataSet.mStackSize = mStackSize;
+        barDataSet.mBarShadowColor = mBarShadowColor;
+        barDataSet.mBarBorderWidth = mBarBorderWidth;
+        barDataSet.mStackLabels = mStackLabels;
+        barDataSet.mHighLightAlpha = mHighLightAlpha;
+    }
+
+    @Override
+    public List<Fill> getFills() {
+        return mFills;
+    }
+
+    @Override
+    public Fill getFill(int index) {
+        return mFills.get(index % mFills.size());
+    }
+
+    /**
+     * This method is deprecated.
+     * Use getFills() instead.
+     */
+    @Deprecated
+    public List<Fill> getGradients() {
+        return mFills;
+    }
+
+    /**
+     * This method is deprecated.
+     * Use getFill(...) instead.
+     *
+     * @param index
+     */
+    @Deprecated
+    public Fill getGradient(int index) {
+        return getFill(index);
+    }
+
+    /**
+     * Sets the start and end color for gradient color, ONLY color that should be used for this DataSet.
+     *
+     * @param startColor
+     * @param endColor
+     */
+    public void setGradientColor(int startColor, int endColor) {
+        mFills.clear();
+        mFills.add(new Fill(startColor, endColor));
+    }
+
+    /**
+     * This method is deprecated.
+     * Use setFills(...) instead.
+     *
+     * @param gradientColors
+     */
+    @Deprecated
+    public void setGradientColors(List<Fill> gradientColors) {
+        this.mFills = gradientColors;
+    }
+
+    /**
+     * Sets the fills for the bars in this dataset.
+     *
+     * @param fills
+     */
+    public void setFills(List<Fill> fills) {
+        this.mFills = fills;
+    }
+
+    /**
+     * Calculates the total number of entries this DataSet represents, including
+     * stacks. All values belonging to a stack are calculated separately.
+     */
+    private void calcEntryCountIncludingStacks(List<BarEntry> yVals) {
+
+        mEntryCountStacks = 0;
+
+        for (int i = 0; i < yVals.size(); i++) {
+
+            float[] vals = yVals.get(i).getYVals();
+
+            if (vals == null)
+                mEntryCountStacks++;
+            else
+                mEntryCountStacks += vals.length;
+        }
+    }
+
+    /**
+     * calculates the maximum stacksize that occurs in the Entries array of this
+     * DataSet
+     */
+    private void calcStackSize(List<BarEntry> yVals) {
+
+        for (int i = 0; i < yVals.size(); i++) {
+
+            float[] vals = yVals.get(i).getYVals();
+
+            if (vals != null && vals.length > mStackSize)
+                mStackSize = vals.length;
+        }
+    }
+
+    @Override
+    protected void calcMinMax(BarEntry e) {
+
+        if (e != null && !Float.isNaN(e.getY())) {
+
+            if (e.getYVals() == null) {
+
+                if (e.getY() < mYMin)
+                    mYMin = e.getY();
+
+                if (e.getY() > mYMax)
+                    mYMax = e.getY();
+            } else {
+
+                if (-e.getNegativeSum() < mYMin)
+                    mYMin = -e.getNegativeSum();
+
+                if (e.getPositiveSum() > mYMax)
+                    mYMax = e.getPositiveSum();
+            }
+
+            calcMinMaxX(e);
+        }
+    }
+
+    @Override
+    public int getStackSize() {
+        return mStackSize;
+    }
+
+    @Override
+    public boolean isStacked() {
+        return mStackSize > 1 ? true : false;
+    }
+
+    /**
+     * returns the overall entry count, including counting each stack-value
+     * individually
+     *
+     * @return
+     */
+    public int getEntryCountStacks() {
+        return mEntryCountStacks;
+    }
+
+    /**
+     * Sets the color used for drawing the bar-shadows. The bar shadows is a
+     * surface behind the bar that indicates the maximum value. Don't for get to
+     * use getResources().getColor(...) to set this. Or Color.rgb(...).
+     *
+     * @param color
+     */
+    public void setBarShadowColor(int color) {
+        mBarShadowColor = color;
+    }
+
+    @Override
+    public int getBarShadowColor() {
+        return mBarShadowColor;
+    }
+
+    /**
+     * Sets the width used for drawing borders around the bars.
+     * If borderWidth == 0, no border will be drawn.
+     *
+     * @return
+     */
+    public void setBarBorderWidth(float width) {
+        mBarBorderWidth = width;
+    }
+
+    /**
+     * Returns the width used for drawing borders around the bars.
+     * If borderWidth == 0, no border will be drawn.
+     *
+     * @return
+     */
+    @Override
+    public float getBarBorderWidth() {
+        return mBarBorderWidth;
+    }
+
+    /**
+     * Sets the color drawing borders around the bars.
+     *
+     * @return
+     */
+    public void setBarBorderColor(int color) {
+        mBarBorderColor = color;
+    }
+
+    /**
+     * Returns the color drawing borders around the bars.
+     *
+     * @return
+     */
+    @Override
+    public int getBarBorderColor() {
+        return mBarBorderColor;
+    }
+
+    /**
+     * Set the alpha value (transparency) that is used for drawing the highlight
+     * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque)
+     *
+     * @param alpha
+     */
+    public void setHighLightAlpha(int alpha) {
+        mHighLightAlpha = alpha;
+    }
+
+    @Override
+    public int getHighLightAlpha() {
+        return mHighLightAlpha;
+    }
+
+    /**
+     * Sets labels for different values of bar-stacks, in case there are one.
+     *
+     * @param labels
+     */
+    public void setStackLabels(String[] labels) {
+        mStackLabels = labels;
+    }
+
+    @Override
+    public String[] getStackLabels() {
+        return mStackLabels;
+    }
+}

+ 310 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java

@@ -0,0 +1,310 @@
+package com.github.mikephil.charting.data;
+
+import android.annotation.SuppressLint;
+import android.graphics.drawable.Drawable;
+
+import com.github.mikephil.charting.highlight.Range;
+
+/**
+ * Entry class for the BarChart. (especially stacked bars)
+ *
+ * @author Philipp Jahoda
+ */
+@SuppressLint("ParcelCreator")
+public class BarEntry extends Entry {
+
+    /**
+     * the values the stacked barchart holds
+     */
+    private float[] mYVals;
+
+    /**
+     * the ranges for the individual stack values - automatically calculated
+     */
+    private Range[] mRanges;
+
+    /**
+     * the sum of all negative values this entry (if stacked) contains
+     */
+    private float mNegativeSum;
+
+    /**
+     * the sum of all positive values this entry (if stacked) contains
+     */
+    private float mPositiveSum;
+
+    /**
+     * Constructor for normal bars (not stacked).
+     *
+     * @param x
+     * @param y
+     */
+    public BarEntry(float x, float y) {
+        super(x, y);
+    }
+
+    /**
+     * Constructor for normal bars (not stacked).
+     *
+     * @param x
+     * @param y
+     * @param data - Spot for additional data this Entry represents.
+     */
+    public BarEntry(float x, float y, Object data) {
+        super(x, y, data);
+    }
+
+    /**
+     * Constructor for normal bars (not stacked).
+     *
+     * @param x
+     * @param y
+     * @param icon - icon image
+     */
+    public BarEntry(float x, float y, Drawable icon) {
+        super(x, y, icon);
+    }
+
+    /**
+     * Constructor for normal bars (not stacked).
+     *
+     * @param x
+     * @param y
+     * @param icon - icon image
+     * @param data - Spot for additional data this Entry represents.
+     */
+    public BarEntry(float x, float y, Drawable icon, Object data) {
+        super(x, y, icon, data);
+    }
+
+    /**
+     * Constructor for stacked bar entries. One data object for whole stack
+     *
+     * @param x
+     * @param vals - the stack values, use at least 2
+     */
+    public BarEntry(float x, float[] vals) {
+        super(x, calcSum(vals));
+
+        this.mYVals = vals;
+        calcPosNegSum();
+        calcRanges();
+    }
+
+    /**
+     * Constructor for stacked bar entries. One data object for whole stack
+     *
+     * @param x
+     * @param vals - the stack values, use at least 2
+     * @param data - Spot for additional data this Entry represents.
+     */
+    public BarEntry(float x, float[] vals, Object data) {
+        super(x, calcSum(vals), data);
+
+        this.mYVals = vals;
+        calcPosNegSum();
+        calcRanges();
+    }
+
+    /**
+     * Constructor for stacked bar entries. One data object for whole stack
+     *
+     * @param x
+     * @param vals - the stack values, use at least 2
+     * @param icon - icon image
+     */
+    public BarEntry(float x, float[] vals, Drawable icon) {
+        super(x, calcSum(vals), icon);
+
+        this.mYVals = vals;
+        calcPosNegSum();
+        calcRanges();
+    }
+
+    /**
+     * Constructor for stacked bar entries. One data object for whole stack
+     *
+     * @param x
+     * @param vals - the stack values, use at least 2
+     * @param icon - icon image
+     * @param data - Spot for additional data this Entry represents.
+     */
+    public BarEntry(float x, float[] vals, Drawable icon, Object data) {
+        super(x, calcSum(vals), icon, data);
+
+        this.mYVals = vals;
+        calcPosNegSum();
+        calcRanges();
+    }
+
+    /**
+     * Returns an exact copy of the BarEntry.
+     */
+    public BarEntry copy() {
+
+        BarEntry copied = new BarEntry(getX(), getY(), getData());
+        copied.setVals(mYVals);
+        return copied;
+    }
+
+    /**
+     * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use
+     * getY()).
+     *
+     * @return
+     */
+    public float[] getYVals() {
+        return mYVals;
+    }
+
+    /**
+     * Set the array of values this BarEntry should represent.
+     *
+     * @param vals
+     */
+    public void setVals(float[] vals) {
+        setY(calcSum(vals));
+        mYVals = vals;
+        calcPosNegSum();
+        calcRanges();
+    }
+
+    /**
+     * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values.
+     *
+     * @return
+     */
+    @Override
+    public float getY() {
+        return super.getY();
+    }
+
+    /**
+     * Returns the ranges of the individual stack-entries. Will return null if this entry is not stacked.
+     *
+     * @return
+     */
+    public Range[] getRanges() {
+        return mRanges;
+    }
+
+    /**
+     * Returns true if this BarEntry is stacked (has a values array), false if not.
+     *
+     * @return
+     */
+    public boolean isStacked() {
+        return mYVals != null;
+    }
+
+    /**
+     * Use `getSumBelow(stackIndex)` instead.
+     */
+    @Deprecated
+    public float getBelowSum(int stackIndex) {
+        return getSumBelow(stackIndex);
+    }
+
+    public float getSumBelow(int stackIndex) {
+
+        if (mYVals == null)
+            return 0;
+
+        float remainder = 0f;
+        int index = mYVals.length - 1;
+
+        while (index > stackIndex && index >= 0) {
+            remainder += mYVals[index];
+            index--;
+        }
+
+        return remainder;
+    }
+
+    /**
+     * Reuturns the sum of all positive values this entry (if stacked) contains.
+     *
+     * @return
+     */
+    public float getPositiveSum() {
+        return mPositiveSum;
+    }
+
+    /**
+     * Returns the sum of all negative values this entry (if stacked) contains. (this is a positive number)
+     *
+     * @return
+     */
+    public float getNegativeSum() {
+        return mNegativeSum;
+    }
+
+    private void calcPosNegSum() {
+
+        if (mYVals == null) {
+            mNegativeSum = 0;
+            mPositiveSum = 0;
+            return;
+        }
+
+        float sumNeg = 0f;
+        float sumPos = 0f;
+
+        for (float f : mYVals) {
+            if (f <= 0f)
+                sumNeg += Math.abs(f);
+            else
+                sumPos += f;
+        }
+
+        mNegativeSum = sumNeg;
+        mPositiveSum = sumPos;
+    }
+
+    /**
+     * Calculates the sum across all values of the given stack.
+     *
+     * @param vals
+     * @return
+     */
+    private static float calcSum(float[] vals) {
+
+        if (vals == null)
+            return 0f;
+
+        float sum = 0f;
+
+        for (float f : vals)
+            sum += f;
+
+        return sum;
+    }
+
+    protected void calcRanges() {
+
+        float[] values = getYVals();
+
+        if (values == null || values.length == 0)
+            return;
+
+        mRanges = new Range[values.length];
+
+        float negRemain = -getNegativeSum();
+        float posRemain = 0f;
+
+        for (int i = 0; i < mRanges.length; i++) {
+
+            float value = values[i];
+
+            if (value < 0) {
+                mRanges[i] = new Range(negRemain, negRemain - value);
+                negRemain -= value;
+            } else {
+                mRanges[i] = new Range(posRemain, posRemain + value);
+                posRemain += value;
+            }
+        }
+    }
+}
+
+

+ 27 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java

@@ -0,0 +1,27 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
+
+import java.util.List;
+
+/**
+ * Baseclass for all Line, Bar, Scatter, Candle and Bubble data.
+ * 
+ * @author Philipp Jahoda
+ */
+public abstract class BarLineScatterCandleBubbleData<T extends IBarLineScatterCandleBubbleDataSet<? extends Entry>>
+        extends ChartData<T> {
+    
+    public BarLineScatterCandleBubbleData() {
+        super();
+    }
+
+    public BarLineScatterCandleBubbleData(T... sets) {
+        super(sets);
+    }
+
+    public BarLineScatterCandleBubbleData(List<T> sets) {
+        super(sets);
+    }
+}

+ 48 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java

@@ -0,0 +1,48 @@
+
+package com.github.mikephil.charting.data;
+
+import android.graphics.Color;
+
+import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
+
+import java.util.List;
+
+/**
+ * Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart.
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class BarLineScatterCandleBubbleDataSet<T extends Entry>
+        extends DataSet<T>
+        implements IBarLineScatterCandleBubbleDataSet<T> {
+
+    /**
+     * default highlight color
+     */
+    protected int mHighLightColor = Color.rgb(255, 187, 115);
+
+    public BarLineScatterCandleBubbleDataSet(List<T> yVals, String label) {
+        super(yVals, label);
+    }
+
+    /**
+     * Sets the color that is used for drawing the highlight indicators. Dont
+     * forget to resolve the color using getResources().getColor(...) or
+     * Color.rgb(...).
+     *
+     * @param color
+     */
+    public void setHighLightColor(int color) {
+        mHighLightColor = color;
+    }
+
+    @Override
+    public int getHighLightColor() {
+        return mHighLightColor;
+    }
+
+    protected void copy(BarLineScatterCandleBubbleDataSet barLineScatterCandleBubbleDataSet) {
+        super.copy(barLineScatterCandleBubbleDataSet);
+        barLineScatterCandleBubbleDataSet.mHighLightColor = mHighLightColor;
+    }
+}

+ 506 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java

@@ -0,0 +1,506 @@
+package com.github.mikephil.charting.data;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Typeface;
+
+import com.github.mikephil.charting.components.Legend;
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.formatter.ValueFormatter;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Philipp Jahoda on 21/10/15.
+ * This is the base dataset of all DataSets. It's purpose is to implement critical methods
+ * provided by the IDataSet interface.
+ */
+public abstract class BaseDataSet<T extends Entry> implements IDataSet<T> {
+
+    /**
+     * List representing all colors that are used for this DataSet
+     */
+    protected List<Integer> mColors = null;
+
+    /**
+     * List representing all colors that are used for drawing the actual values for this DataSet
+     */
+    protected List<Integer> mValueColors = null;
+
+    /**
+     * label that describes the DataSet or the data the DataSet represents
+     */
+    private String mLabel = "DataSet";
+
+    /**
+     * this specifies which axis this DataSet should be plotted against
+     */
+    protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT;
+
+    /**
+     * if true, value highlightning is enabled
+     */
+    protected boolean mHighlightEnabled = true;
+
+    /**
+     * custom formatter that is used instead of the auto-formatter if set
+     */
+    protected transient ValueFormatter mValueFormatter;
+
+    /**
+     * the typeface used for the value text
+     */
+    protected Typeface mValueTypeface;
+
+    private Legend.LegendForm mForm = Legend.LegendForm.DEFAULT;
+    private float mFormSize = Float.NaN;
+    private float mFormLineWidth = Float.NaN;
+    private DashPathEffect mFormLineDashEffect = null;
+
+    /**
+     * if true, y-values are drawn on the chart
+     */
+    protected boolean mDrawValues = true;
+
+    /**
+     * if true, y-icons are drawn on the chart
+     */
+    protected boolean mDrawIcons = true;
+
+    /**
+     * the offset for drawing icons (in dp)
+     */
+    protected MPPointF mIconsOffset = new MPPointF();
+
+    /**
+     * the size of the value-text labels
+     */
+    protected float mValueTextSize = 17f;
+
+    /**
+     * flag that indicates if the DataSet is visible or not
+     */
+    protected boolean mVisible = true;
+
+    /**
+     * Default constructor.
+     */
+    public BaseDataSet() {
+        mColors = new ArrayList<Integer>();
+        mValueColors = new ArrayList<Integer>();
+
+        // default color
+        mColors.add(Color.rgb(140, 234, 255));
+        mValueColors.add(Color.BLACK);
+    }
+
+    /**
+     * Constructor with label.
+     *
+     * @param label
+     */
+    public BaseDataSet(String label) {
+        this();
+        this.mLabel = label;
+    }
+
+    /**
+     * Use this method to tell the data set that the underlying data has changed.
+     */
+    public void notifyDataSetChanged() {
+        calcMinMax();
+    }
+
+
+    /**
+     * ###### ###### COLOR GETTING RELATED METHODS ##### ######
+     */
+
+    @Override
+    public List<Integer> getColors() {
+        return mColors;
+    }
+
+    public List<Integer> getValueColors() {
+        return mValueColors;
+    }
+
+    @Override
+    public int getColor() {
+        return mColors.get(0);
+    }
+
+    @Override
+    public int getColor(int index) {
+        return mColors.get(index % mColors.size());
+    }
+
+    /**
+     * ###### ###### COLOR SETTING RELATED METHODS ##### ######
+     */
+
+    /**
+     * Sets the colors that should be used fore this DataSet. Colors are reused
+     * as soon as the number of Entries the DataSet represents is higher than
+     * the size of the colors array. If you are using colors from the resources,
+     * make sure that the colors are already prepared (by calling
+     * getResources().getColor(...)) before adding them to the DataSet.
+     *
+     * @param colors
+     */
+    public void setColors(List<Integer> colors) {
+        this.mColors = colors;
+    }
+
+    /**
+     * Sets the colors that should be used fore this DataSet. Colors are reused
+     * as soon as the number of Entries the DataSet represents is higher than
+     * the size of the colors array. If you are using colors from the resources,
+     * make sure that the colors are already prepared (by calling
+     * getResources().getColor(...)) before adding them to the DataSet.
+     *
+     * @param colors
+     */
+    public void setColors(int... colors) {
+        this.mColors = ColorTemplate.createColors(colors);
+    }
+
+    /**
+     * Sets the colors that should be used fore this DataSet. Colors are reused
+     * as soon as the number of Entries the DataSet represents is higher than
+     * the size of the colors array. You can use
+     * "new int[] { R.color.red, R.color.green, ... }" to provide colors for
+     * this method. Internally, the colors are resolved using
+     * getResources().getColor(...)
+     *
+     * @param colors
+     */
+    public void setColors(int[] colors, Context c) {
+
+        if (mColors == null) {
+            mColors = new ArrayList<>();
+        }
+
+        mColors.clear();
+
+        for (int color : colors) {
+            mColors.add(c.getResources().getColor(color));
+        }
+    }
+
+    /**
+     * Adds a new color to the colors array of the DataSet.
+     *
+     * @param color
+     */
+    public void addColor(int color) {
+        if (mColors == null)
+            mColors = new ArrayList<Integer>();
+        mColors.add(color);
+    }
+
+    /**
+     * Sets the one and ONLY color that should be used for this DataSet.
+     * Internally, this recreates the colors array and adds the specified color.
+     *
+     * @param color
+     */
+    public void setColor(int color) {
+        resetColors();
+        mColors.add(color);
+    }
+
+    /**
+     * Sets a color with a specific alpha value.
+     *
+     * @param color
+     * @param alpha from 0-255
+     */
+    public void setColor(int color, int alpha) {
+        setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)));
+    }
+
+    /**
+     * Sets colors with a specific alpha value.
+     *
+     * @param colors
+     * @param alpha
+     */
+    public void setColors(int[] colors, int alpha) {
+        resetColors();
+        for (int color : colors) {
+            addColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)));
+        }
+    }
+
+    /**
+     * Resets all colors of this DataSet and recreates the colors array.
+     */
+    public void resetColors() {
+        if (mColors == null) {
+            mColors = new ArrayList<Integer>();
+        }
+        mColors.clear();
+    }
+
+    /**
+     * ###### ###### OTHER STYLING RELATED METHODS ##### ######
+     */
+
+    @Override
+    public void setLabel(String label) {
+        mLabel = label;
+    }
+
+    @Override
+    public String getLabel() {
+        return mLabel;
+    }
+
+    @Override
+    public void setHighlightEnabled(boolean enabled) {
+        mHighlightEnabled = enabled;
+    }
+
+    @Override
+    public boolean isHighlightEnabled() {
+        return mHighlightEnabled;
+    }
+
+    @Override
+    public void setValueFormatter(ValueFormatter f) {
+
+        if (f == null)
+            return;
+        else
+            mValueFormatter = f;
+    }
+
+    @Override
+    public ValueFormatter getValueFormatter() {
+        if (needsFormatter())
+            return Utils.getDefaultValueFormatter();
+        return mValueFormatter;
+    }
+
+    @Override
+    public boolean needsFormatter() {
+        return mValueFormatter == null;
+    }
+
+    @Override
+    public void setValueTextColor(int color) {
+        mValueColors.clear();
+        mValueColors.add(color);
+    }
+
+    @Override
+    public void setValueTextColors(List<Integer> colors) {
+        mValueColors = colors;
+    }
+
+    @Override
+    public void setValueTypeface(Typeface tf) {
+        mValueTypeface = tf;
+    }
+
+    @Override
+    public void setValueTextSize(float size) {
+        mValueTextSize = Utils.convertDpToPixel(size);
+    }
+
+    @Override
+    public int getValueTextColor() {
+        return mValueColors.get(0);
+    }
+
+    @Override
+    public int getValueTextColor(int index) {
+        return mValueColors.get(index % mValueColors.size());
+    }
+
+    @Override
+    public Typeface getValueTypeface() {
+        return mValueTypeface;
+    }
+
+    @Override
+    public float getValueTextSize() {
+        return mValueTextSize;
+    }
+
+    public void setForm(Legend.LegendForm form) {
+        mForm = form;
+    }
+
+    @Override
+    public Legend.LegendForm getForm() {
+        return mForm;
+    }
+
+    public void setFormSize(float formSize) {
+        mFormSize = formSize;
+    }
+
+    @Override
+    public float getFormSize() {
+        return mFormSize;
+    }
+
+    public void setFormLineWidth(float formLineWidth) {
+        mFormLineWidth = formLineWidth;
+    }
+
+    @Override
+    public float getFormLineWidth() {
+        return mFormLineWidth;
+    }
+
+    public void setFormLineDashEffect(DashPathEffect dashPathEffect) {
+        mFormLineDashEffect = dashPathEffect;
+    }
+
+    @Override
+    public DashPathEffect getFormLineDashEffect() {
+        return mFormLineDashEffect;
+    }
+
+    @Override
+    public void setDrawValues(boolean enabled) {
+        this.mDrawValues = enabled;
+    }
+
+    @Override
+    public boolean isDrawValuesEnabled() {
+        return mDrawValues;
+    }
+
+    @Override
+    public void setDrawIcons(boolean enabled) {
+        mDrawIcons = enabled;
+    }
+
+    @Override
+    public boolean isDrawIconsEnabled() {
+        return mDrawIcons;
+    }
+
+    @Override
+    public void setIconsOffset(MPPointF offsetDp) {
+
+        mIconsOffset.x = offsetDp.x;
+        mIconsOffset.y = offsetDp.y;
+    }
+
+    @Override
+    public MPPointF getIconsOffset() {
+        return mIconsOffset;
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+        mVisible = visible;
+    }
+
+    @Override
+    public boolean isVisible() {
+        return mVisible;
+    }
+
+    @Override
+    public YAxis.AxisDependency getAxisDependency() {
+        return mAxisDependency;
+    }
+
+    @Override
+    public void setAxisDependency(YAxis.AxisDependency dependency) {
+        mAxisDependency = dependency;
+    }
+
+
+    /**
+     * ###### ###### DATA RELATED METHODS ###### ######
+     */
+
+    @Override
+    public int getIndexInEntries(int xIndex) {
+
+        for (int i = 0; i < getEntryCount(); i++) {
+            if (xIndex == getEntryForIndex(i).getX())
+                return i;
+        }
+
+        return -1;
+    }
+
+    @Override
+    public boolean removeFirst() {
+
+        if (getEntryCount() > 0) {
+
+            T entry = getEntryForIndex(0);
+            return removeEntry(entry);
+        } else
+            return false;
+    }
+
+    @Override
+    public boolean removeLast() {
+
+        if (getEntryCount() > 0) {
+
+            T e = getEntryForIndex(getEntryCount() - 1);
+            return removeEntry(e);
+        } else
+            return false;
+    }
+
+    @Override
+    public boolean removeEntryByXValue(float xValue) {
+
+        T e = getEntryForXValue(xValue, Float.NaN);
+        return removeEntry(e);
+    }
+
+    @Override
+    public boolean removeEntry(int index) {
+
+        T e = getEntryForIndex(index);
+        return removeEntry(e);
+    }
+
+    @Override
+    public boolean contains(T e) {
+
+        for (int i = 0; i < getEntryCount(); i++) {
+            if (getEntryForIndex(i).equals(e))
+                return true;
+        }
+
+        return false;
+    }
+
+    protected void copy(BaseDataSet baseDataSet) {
+        baseDataSet.mAxisDependency = mAxisDependency;
+        baseDataSet.mColors = mColors;
+        baseDataSet.mDrawIcons = mDrawIcons;
+        baseDataSet.mDrawValues = mDrawValues;
+        baseDataSet.mForm = mForm;
+        baseDataSet.mFormLineDashEffect = mFormLineDashEffect;
+        baseDataSet.mFormLineWidth = mFormLineWidth;
+        baseDataSet.mFormSize = mFormSize;
+        baseDataSet.mHighlightEnabled = mHighlightEnabled;
+        baseDataSet.mIconsOffset = mIconsOffset;
+        baseDataSet.mValueColors = mValueColors;
+        baseDataSet.mValueFormatter = mValueFormatter;
+        baseDataSet.mValueColors = mValueColors;
+        baseDataSet.mValueTextSize = mValueTextSize;
+        baseDataSet.mVisible = mVisible;
+    }
+}

+ 97 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java

@@ -0,0 +1,97 @@
+package com.github.mikephil.charting.data;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Created by Philipp Jahoda on 02/06/16.
+ */
+public abstract class BaseEntry {
+
+    /** the y value */
+    private float y = 0f;
+
+    /** optional spot for additional data this Entry represents */
+    private Object mData = null;
+
+    /** optional icon image */
+    private Drawable mIcon = null;
+
+    public BaseEntry() {
+
+    }
+
+    public BaseEntry(float y) {
+        this.y = y;
+    }
+
+    public BaseEntry(float y, Object data) {
+        this(y);
+        this.mData = data;
+    }
+
+    public BaseEntry(float y, Drawable icon) {
+        this(y);
+        this.mIcon = icon;
+    }
+
+    public BaseEntry(float y, Drawable icon, Object data) {
+        this(y);
+        this.mIcon = icon;
+        this.mData = data;
+    }
+
+    /**
+     * Returns the y value of this Entry.
+     *
+     * @return
+     */
+    public float getY() {
+        return y;
+    }
+
+    /**
+     * Sets the icon drawable
+     *
+     * @param icon
+     */
+    public void setIcon(Drawable icon) {
+        this.mIcon = icon;
+    }
+
+    /**
+     * Returns the icon of this Entry.
+     *
+     * @return
+     */
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Sets the y-value for the Entry.
+     *
+     * @param y
+     */
+    public void setY(float y) {
+        this.y = y;
+    }
+
+    /**
+     * Returns the data, additional information that this Entry represents, or
+     * null, if no data has been specified.
+     *
+     * @return
+     */
+    public Object getData() {
+        return mData;
+    }
+
+    /**
+     * Sets additional data this Entry should represent.
+     *
+     * @param data
+     */
+    public void setData(Object data) {
+        this.mData = data;
+    }
+}

+ 34 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java

@@ -0,0 +1,34 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
+
+import java.util.List;
+
+public class BubbleData extends BarLineScatterCandleBubbleData<IBubbleDataSet> {
+
+    public BubbleData() {
+        super();
+    }
+
+    public BubbleData(IBubbleDataSet... dataSets) {
+        super(dataSets);
+    }
+
+    public BubbleData(List<IBubbleDataSet> dataSets) {
+        super(dataSets);
+    }
+
+
+    /**
+     * Sets the width of the circle that surrounds the bubble when highlighted
+     * for all DataSet objects this data object contains, in dp.
+     * 
+     * @param width
+     */
+    public void setHighlightCircleWidth(float width) {
+        for (IBubbleDataSet set : mDataSets) {
+            set.setHighlightCircleWidth(width);
+        }
+    }
+}

+ 71 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java

@@ -0,0 +1,71 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet<BubbleEntry> implements IBubbleDataSet {
+
+    protected float mMaxSize;
+    protected boolean mNormalizeSize = true;
+
+    private float mHighlightCircleWidth = 2.5f;
+
+    public BubbleDataSet(List<BubbleEntry> yVals, String label) {
+        super(yVals, label);
+    }
+
+    @Override
+    public void setHighlightCircleWidth(float width) {
+        mHighlightCircleWidth = Utils.convertDpToPixel(width);
+    }
+
+    @Override
+    public float getHighlightCircleWidth() {
+        return mHighlightCircleWidth;
+    }
+
+    @Override
+    protected void calcMinMax(BubbleEntry e) {
+        super.calcMinMax(e);
+
+        final float size = e.getSize();
+
+        if (size > mMaxSize) {
+            mMaxSize = size;
+        }
+    }
+
+    @Override
+    public DataSet<BubbleEntry> copy() {
+        List<BubbleEntry> entries = new ArrayList<BubbleEntry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        BubbleDataSet copied = new BubbleDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(BubbleDataSet bubbleDataSet) {
+        bubbleDataSet.mHighlightCircleWidth = mHighlightCircleWidth;
+        bubbleDataSet.mNormalizeSize = mNormalizeSize;
+    }
+
+    @Override
+    public float getMaxSize() {
+        return mMaxSize;
+    }
+
+    @Override
+    public boolean isNormalizeSizeEnabled() {
+        return mNormalizeSize;
+    }
+
+    public void setNormalizeSizeEnabled(boolean normalizeSize) {
+        mNormalizeSize = normalizeSize;
+    }
+}

+ 91 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java

@@ -0,0 +1,91 @@
+
+package com.github.mikephil.charting.data;
+
+import android.annotation.SuppressLint;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble
+ * chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed under
+ * Apache License 2.0
+ *
+ * @author Philipp Jahoda
+ */
+@SuppressLint("ParcelCreator")
+public class BubbleEntry extends Entry {
+
+    /** size value */
+    private float mSize = 0f;
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis.
+     * @param y The value on the y-axis.
+     * @param size The size of the bubble.
+     */
+    public BubbleEntry(float x, float y, float size) {
+        super(x, y);
+        this.mSize = size;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis.
+     * @param y The value on the y-axis.
+     * @param size The size of the bubble.
+     * @param data Spot for additional data this Entry represents.
+     */
+    public BubbleEntry(float x, float y, float size, Object data) {
+        super(x, y, data);
+        this.mSize = size;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis.
+     * @param y The value on the y-axis.
+     * @param size The size of the bubble.
+     * @param icon Icon image
+     */
+    public BubbleEntry(float x, float y, float size, Drawable icon) {
+        super(x, y, icon);
+        this.mSize = size;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis.
+     * @param y The value on the y-axis.
+     * @param size The size of the bubble.
+     * @param icon Icon image
+     * @param data Spot for additional data this Entry represents.
+     */
+    public BubbleEntry(float x, float y, float size, Drawable icon, Object data) {
+        super(x, y, icon, data);
+        this.mSize = size;
+    }
+
+    public BubbleEntry copy() {
+
+        BubbleEntry c = new BubbleEntry(getX(), getY(), mSize, getData());
+        return c;
+    }
+
+    /**
+     * Returns the size of this entry (the size of the bubble).
+     *
+     * @return
+     */
+    public float getSize() {
+        return mSize;
+    }
+
+    public void setSize(float size) {
+        this.mSize = size;
+    }
+
+}

+ 21 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java

@@ -0,0 +1,21 @@
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CandleData extends BarLineScatterCandleBubbleData<ICandleDataSet> {
+
+    public CandleData() {
+        super();
+    }
+
+    public CandleData(List<ICandleDataSet> dataSets) {
+        super(dataSets);
+    }
+
+    public CandleData(ICandleDataSet... dataSets) {
+        super(dataSets);
+    }
+}

+ 295 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java

@@ -0,0 +1,295 @@
+
+package com.github.mikephil.charting.data;
+
+import android.graphics.Paint;
+
+import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DataSet for the CandleStickChart.
+ *
+ * @author Philipp Jahoda
+ */
+public class CandleDataSet extends LineScatterCandleRadarDataSet<CandleEntry> implements ICandleDataSet {
+
+    /**
+     * the width of the shadow of the candle
+     */
+    private float mShadowWidth = 3f;
+
+    /**
+     * should the candle bars show?
+     * when false, only "ticks" will show
+     * <p/>
+     * - default: true
+     */
+    private boolean mShowCandleBar = true;
+
+    /**
+     * the space between the candle entries, default 0.1f (10%)
+     */
+    private float mBarSpace = 0.1f;
+
+    /**
+     * use candle color for the shadow
+     */
+    private boolean mShadowColorSameAsCandle = false;
+
+    /**
+     * paint style when open < close
+     * increasing candlesticks are traditionally hollow
+     */
+    protected Paint.Style mIncreasingPaintStyle = Paint.Style.STROKE;
+
+    /**
+     * paint style when open > close
+     * descreasing candlesticks are traditionally filled
+     */
+    protected Paint.Style mDecreasingPaintStyle = Paint.Style.FILL;
+
+    /**
+     * color for open == close
+     */
+    protected int mNeutralColor = ColorTemplate.COLOR_SKIP;
+
+    /**
+     * color for open < close
+     */
+    protected int mIncreasingColor = ColorTemplate.COLOR_SKIP;
+
+    /**
+     * color for open > close
+     */
+    protected int mDecreasingColor = ColorTemplate.COLOR_SKIP;
+
+    /**
+     * shadow line color, set -1 for backward compatibility and uses default
+     * color
+     */
+    protected int mShadowColor = ColorTemplate.COLOR_SKIP;
+
+    public CandleDataSet(List<CandleEntry> yVals, String label) {
+        super(yVals, label);
+    }
+
+    @Override
+    public DataSet<CandleEntry> copy() {
+        List<CandleEntry> entries = new ArrayList<CandleEntry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        CandleDataSet copied = new CandleDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(CandleDataSet candleDataSet) {
+        super.copy(candleDataSet);
+        candleDataSet.mShadowWidth = mShadowWidth;
+        candleDataSet.mShowCandleBar = mShowCandleBar;
+        candleDataSet.mBarSpace = mBarSpace;
+        candleDataSet.mShadowColorSameAsCandle = mShadowColorSameAsCandle;
+        candleDataSet.mHighLightColor = mHighLightColor;
+        candleDataSet.mIncreasingPaintStyle = mIncreasingPaintStyle;
+        candleDataSet.mDecreasingPaintStyle = mDecreasingPaintStyle;
+        candleDataSet.mNeutralColor = mNeutralColor;
+        candleDataSet.mIncreasingColor = mIncreasingColor;
+        candleDataSet.mDecreasingColor = mDecreasingColor;
+        candleDataSet.mShadowColor = mShadowColor;
+    }
+
+    @Override
+    protected void calcMinMax(CandleEntry e) {
+
+        if (e.getLow() < mYMin)
+            mYMin = e.getLow();
+
+        if (e.getHigh() > mYMax)
+            mYMax = e.getHigh();
+
+        calcMinMaxX(e);
+    }
+
+    @Override
+    protected void calcMinMaxY(CandleEntry e) {
+
+        if (e.getHigh() < mYMin)
+            mYMin = e.getHigh();
+
+        if (e.getHigh() > mYMax)
+            mYMax = e.getHigh();
+
+        if (e.getLow() < mYMin)
+            mYMin = e.getLow();
+
+        if (e.getLow() > mYMax)
+            mYMax = e.getLow();
+    }
+
+    /**
+     * Sets the space that is left out on the left and right side of each
+     * candle, default 0.1f (10%), max 0.45f, min 0f
+     *
+     * @param space
+     */
+    public void setBarSpace(float space) {
+
+        if (space < 0f)
+            space = 0f;
+        if (space > 0.45f)
+            space = 0.45f;
+
+        mBarSpace = space;
+    }
+
+    @Override
+    public float getBarSpace() {
+        return mBarSpace;
+    }
+
+    /**
+     * Sets the width of the candle-shadow-line in pixels. Default 3f.
+     *
+     * @param width
+     */
+    public void setShadowWidth(float width) {
+        mShadowWidth = Utils.convertDpToPixel(width);
+    }
+
+    @Override
+    public float getShadowWidth() {
+        return mShadowWidth;
+    }
+
+    /**
+     * Sets whether the candle bars should show?
+     *
+     * @param showCandleBar
+     */
+    public void setShowCandleBar(boolean showCandleBar) {
+        mShowCandleBar = showCandleBar;
+    }
+
+    @Override
+    public boolean getShowCandleBar() {
+        return mShowCandleBar;
+    }
+
+    // TODO
+    /**
+     * It is necessary to implement ColorsList class that will encapsulate
+     * colors list functionality, because It's wrong to copy paste setColor,
+     * addColor, ... resetColors for each time when we want to add a coloring
+     * options for one of objects
+     *
+     * @author Mesrop
+     */
+
+    /** BELOW THIS COLOR HANDLING */
+
+    /**
+     * Sets the one and ONLY color that should be used for this DataSet when
+     * open == close.
+     *
+     * @param color
+     */
+    public void setNeutralColor(int color) {
+        mNeutralColor = color;
+    }
+
+    @Override
+    public int getNeutralColor() {
+        return mNeutralColor;
+    }
+
+    /**
+     * Sets the one and ONLY color that should be used for this DataSet when
+     * open <= close.
+     *
+     * @param color
+     */
+    public void setIncreasingColor(int color) {
+        mIncreasingColor = color;
+    }
+
+    @Override
+    public int getIncreasingColor() {
+        return mIncreasingColor;
+    }
+
+    /**
+     * Sets the one and ONLY color that should be used for this DataSet when
+     * open > close.
+     *
+     * @param color
+     */
+    public void setDecreasingColor(int color) {
+        mDecreasingColor = color;
+    }
+
+    @Override
+    public int getDecreasingColor() {
+        return mDecreasingColor;
+    }
+
+    @Override
+    public Paint.Style getIncreasingPaintStyle() {
+        return mIncreasingPaintStyle;
+    }
+
+    /**
+     * Sets paint style when open < close
+     *
+     * @param paintStyle
+     */
+    public void setIncreasingPaintStyle(Paint.Style paintStyle) {
+        this.mIncreasingPaintStyle = paintStyle;
+    }
+
+    @Override
+    public Paint.Style getDecreasingPaintStyle() {
+        return mDecreasingPaintStyle;
+    }
+
+    /**
+     * Sets paint style when open > close
+     *
+     * @param decreasingPaintStyle
+     */
+    public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) {
+        this.mDecreasingPaintStyle = decreasingPaintStyle;
+    }
+
+    @Override
+    public int getShadowColor() {
+        return mShadowColor;
+    }
+
+    /**
+     * Sets shadow color for all entries
+     *
+     * @param shadowColor
+     */
+    public void setShadowColor(int shadowColor) {
+        this.mShadowColor = shadowColor;
+    }
+
+    @Override
+    public boolean getShadowColorSameAsCandle() {
+        return mShadowColorSameAsCandle;
+    }
+
+    /**
+     * Sets shadow color to be the same color as the candle color
+     *
+     * @param shadowColorSameAsCandle
+     */
+    public void setShadowColorSameAsCandle(boolean shadowColorSameAsCandle) {
+        this.mShadowColorSameAsCandle = shadowColorSameAsCandle;
+    }
+}

+ 193 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java

@@ -0,0 +1,193 @@
+
+package com.github.mikephil.charting.data;
+
+import android.annotation.SuppressLint;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Subclass of Entry that holds all values for one entry in a CandleStickChart.
+ * 
+ * @author Philipp Jahoda
+ */
+@SuppressLint("ParcelCreator")
+public class CandleEntry extends Entry {
+
+    /** shadow-high value */
+    private float mShadowHigh = 0f;
+
+    /** shadow-low value */
+    private float mShadowLow = 0f;
+
+    /** close value */
+    private float mClose = 0f;
+
+    /** open value */
+    private float mOpen = 0f;
+
+    /**
+     * Constructor.
+     * 
+     * @param x The value on the x-axis
+     * @param shadowH The (shadow) high value
+     * @param shadowL The (shadow) low value
+     * @param open The open value
+     * @param close The close value
+     */
+    public CandleEntry(float x, float shadowH, float shadowL, float open, float close) {
+        super(x, (shadowH + shadowL) / 2f);
+
+        this.mShadowHigh = shadowH;
+        this.mShadowLow = shadowL;
+        this.mOpen = open;
+        this.mClose = close;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis
+     * @param shadowH The (shadow) high value
+     * @param shadowL The (shadow) low value
+     * @param open
+     * @param close
+     * @param data Spot for additional data this Entry represents
+     */
+    public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
+                       Object data) {
+        super(x, (shadowH + shadowL) / 2f, data);
+
+        this.mShadowHigh = shadowH;
+        this.mShadowLow = shadowL;
+        this.mOpen = open;
+        this.mClose = close;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis
+     * @param shadowH The (shadow) high value
+     * @param shadowL The (shadow) low value
+     * @param open
+     * @param close
+     * @param icon Icon image
+     */
+    public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
+                       Drawable icon) {
+        super(x, (shadowH + shadowL) / 2f, icon);
+
+        this.mShadowHigh = shadowH;
+        this.mShadowLow = shadowL;
+        this.mOpen = open;
+        this.mClose = close;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param x The value on the x-axis
+     * @param shadowH The (shadow) high value
+     * @param shadowL The (shadow) low value
+     * @param open
+     * @param close
+     * @param icon Icon image
+     * @param data Spot for additional data this Entry represents
+     */
+    public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
+                       Drawable icon, Object data) {
+        super(x, (shadowH + shadowL) / 2f, icon, data);
+
+        this.mShadowHigh = shadowH;
+        this.mShadowLow = shadowL;
+        this.mOpen = open;
+        this.mClose = close;
+    }
+
+    /**
+     * Returns the overall range (difference) between shadow-high and
+     * shadow-low.
+     * 
+     * @return
+     */
+    public float getShadowRange() {
+        return Math.abs(mShadowHigh - mShadowLow);
+    }
+
+    /**
+     * Returns the body size (difference between open and close).
+     * 
+     * @return
+     */
+    public float getBodyRange() {
+        return Math.abs(mOpen - mClose);
+    }
+
+    /**
+     * Returns the center value of the candle. (Middle value between high and
+     * low)
+     */
+    @Override
+    public float getY() {
+        return super.getY();
+    }
+
+    public CandleEntry copy() {
+
+        CandleEntry c = new CandleEntry(getX(), mShadowHigh, mShadowLow, mOpen,
+                mClose, getData());
+
+        return c;
+    }
+
+    /**
+     * Returns the upper shadows highest value.
+     * 
+     * @return
+     */
+    public float getHigh() {
+        return mShadowHigh;
+    }
+
+    public void setHigh(float mShadowHigh) {
+        this.mShadowHigh = mShadowHigh;
+    }
+
+    /**
+     * Returns the lower shadows lowest value.
+     * 
+     * @return
+     */
+    public float getLow() {
+        return mShadowLow;
+    }
+
+    public void setLow(float mShadowLow) {
+        this.mShadowLow = mShadowLow;
+    }
+
+    /**
+     * Returns the bodys close value.
+     * 
+     * @return
+     */
+    public float getClose() {
+        return mClose;
+    }
+
+    public void setClose(float mClose) {
+        this.mClose = mClose;
+    }
+
+    /**
+     * Returns the bodys open value.
+     * 
+     * @return
+     */
+    public float getOpen() {
+        return mOpen;
+    }
+
+    public void setOpen(float mOpen) {
+        this.mOpen = mOpen;
+    }
+}

+ 820 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java

@@ -0,0 +1,820 @@
+package com.github.mikephil.charting.data;
+
+import android.graphics.Typeface;
+import android.util.Log;
+
+import com.github.mikephil.charting.components.YAxis.AxisDependency;
+import com.github.mikephil.charting.formatter.ValueFormatter;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that holds all relevant data that represents the chart. That involves
+ * at least one (or more) DataSets, and an array of x-values.
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class ChartData<T extends IDataSet<? extends Entry>> {
+
+    /**
+     * maximum y-value in the value array across all axes
+     */
+    protected float mYMax = -Float.MAX_VALUE;
+
+    /**
+     * the minimum y-value in the value array across all axes
+     */
+    protected float mYMin = Float.MAX_VALUE;
+
+    /**
+     * maximum x-value in the value array
+     */
+    protected float mXMax = -Float.MAX_VALUE;
+
+    /**
+     * minimum x-value in the value array
+     */
+    protected float mXMin = Float.MAX_VALUE;
+
+
+    protected float mLeftAxisMax = -Float.MAX_VALUE;
+
+    protected float mLeftAxisMin = Float.MAX_VALUE;
+
+    protected float mRightAxisMax = -Float.MAX_VALUE;
+
+    protected float mRightAxisMin = Float.MAX_VALUE;
+
+    /**
+     * array that holds all DataSets the ChartData object represents
+     */
+    protected List<T> mDataSets;
+
+    /**
+     * Default constructor.
+     */
+    public ChartData() {
+        mDataSets = new ArrayList<T>();
+    }
+
+    /**
+     * Constructor taking single or multiple DataSet objects.
+     *
+     * @param dataSets
+     */
+    public ChartData(T... dataSets) {
+        mDataSets = arrayToList(dataSets);
+        notifyDataChanged();
+    }
+
+    /**
+     * Created because Arrays.asList(...) does not support modification.
+     *
+     * @param array
+     * @return
+     */
+    private List<T> arrayToList(T[] array) {
+
+        List<T> list = new ArrayList<>();
+
+        for (T set : array) {
+            list.add(set);
+        }
+
+        return list;
+    }
+
+    /**
+     * constructor for chart data
+     *
+     * @param sets the dataset array
+     */
+    public ChartData(List<T> sets) {
+        this.mDataSets = sets;
+        notifyDataChanged();
+    }
+
+    /**
+     * Call this method to let the ChartData know that the underlying data has
+     * changed. Calling this performs all necessary recalculations needed when
+     * the contained data has changed.
+     */
+    public void notifyDataChanged() {
+        calcMinMax();
+    }
+
+    /**
+     * Calc minimum and maximum y-values over all DataSets.
+     * Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax.
+     *
+     * @param fromX the x-value to start the calculation from
+     * @param toX   the x-value to which the calculation should be performed
+     */
+    public void calcMinMaxY(float fromX, float toX) {
+
+        for (T set : mDataSets) {
+            set.calcMinMaxY(fromX, toX);
+        }
+
+        // apply the new data
+        calcMinMax();
+    }
+
+    /**
+     * Calc minimum and maximum values (both x and y) over all DataSets.
+     */
+    protected void calcMinMax() {
+
+        if (mDataSets == null)
+            return;
+
+        mYMax = -Float.MAX_VALUE;
+        mYMin = Float.MAX_VALUE;
+        mXMax = -Float.MAX_VALUE;
+        mXMin = Float.MAX_VALUE;
+
+        for (T set : mDataSets) {
+            calcMinMax(set);
+        }
+
+        mLeftAxisMax = -Float.MAX_VALUE;
+        mLeftAxisMin = Float.MAX_VALUE;
+        mRightAxisMax = -Float.MAX_VALUE;
+        mRightAxisMin = Float.MAX_VALUE;
+
+        // left axis
+        T firstLeft = getFirstLeft(mDataSets);
+
+        if (firstLeft != null) {
+
+            mLeftAxisMax = firstLeft.getYMax();
+            mLeftAxisMin = firstLeft.getYMin();
+
+            for (T dataSet : mDataSets) {
+                if (dataSet.getAxisDependency() == AxisDependency.LEFT) {
+                    if (dataSet.getYMin() < mLeftAxisMin)
+                        mLeftAxisMin = dataSet.getYMin();
+
+                    if (dataSet.getYMax() > mLeftAxisMax)
+                        mLeftAxisMax = dataSet.getYMax();
+                }
+            }
+        }
+
+        // right axis
+        T firstRight = getFirstRight(mDataSets);
+
+        if (firstRight != null) {
+
+            mRightAxisMax = firstRight.getYMax();
+            mRightAxisMin = firstRight.getYMin();
+
+            for (T dataSet : mDataSets) {
+                if (dataSet.getAxisDependency() == AxisDependency.RIGHT) {
+                    if (dataSet.getYMin() < mRightAxisMin)
+                        mRightAxisMin = dataSet.getYMin();
+
+                    if (dataSet.getYMax() > mRightAxisMax)
+                        mRightAxisMax = dataSet.getYMax();
+                }
+            }
+        }
+    }
+
+    /** ONLY GETTERS AND SETTERS BELOW THIS */
+
+    /**
+     * returns the number of LineDataSets this object contains
+     *
+     * @return
+     */
+    public int getDataSetCount() {
+        if (mDataSets == null)
+            return 0;
+        return mDataSets.size();
+    }
+
+    /**
+     * Returns the smallest y-value the data object contains.
+     *
+     * @return
+     */
+    public float getYMin() {
+        return mYMin;
+    }
+
+    /**
+     * Returns the minimum y-value for the specified axis.
+     *
+     * @param axis
+     * @return
+     */
+    public float getYMin(AxisDependency axis) {
+        if (axis == AxisDependency.LEFT) {
+
+            if (mLeftAxisMin == Float.MAX_VALUE) {
+                return mRightAxisMin;
+            } else
+                return mLeftAxisMin;
+        } else {
+            if (mRightAxisMin == Float.MAX_VALUE) {
+                return mLeftAxisMin;
+            } else
+                return mRightAxisMin;
+        }
+    }
+
+    /**
+     * Returns the greatest y-value the data object contains.
+     *
+     * @return
+     */
+    public float getYMax() {
+        return mYMax;
+    }
+
+    /**
+     * Returns the maximum y-value for the specified axis.
+     *
+     * @param axis
+     * @return
+     */
+    public float getYMax(AxisDependency axis) {
+        if (axis == AxisDependency.LEFT) {
+
+            if (mLeftAxisMax == -Float.MAX_VALUE) {
+                return mRightAxisMax;
+            } else
+                return mLeftAxisMax;
+        } else {
+            if (mRightAxisMax == -Float.MAX_VALUE) {
+                return mLeftAxisMax;
+            } else
+                return mRightAxisMax;
+        }
+    }
+
+    /**
+     * Returns the minimum x-value this data object contains.
+     *
+     * @return
+     */
+    public float getXMin() {
+        return mXMin;
+    }
+
+    /**
+     * Returns the maximum x-value this data object contains.
+     *
+     * @return
+     */
+    public float getXMax() {
+        return mXMax;
+    }
+
+    /**
+     * Returns all DataSet objects this ChartData object holds.
+     *
+     * @return
+     */
+    public List<T> getDataSets() {
+        return mDataSets;
+    }
+
+    /**
+     * Retrieve the index of a DataSet with a specific label from the ChartData.
+     * Search can be case sensitive or not. IMPORTANT: This method does
+     * calculations at runtime, do not over-use in performance critical
+     * situations.
+     *
+     * @param dataSets   the DataSet array to search
+     * @param label
+     * @param ignorecase if true, the search is not case-sensitive
+     * @return
+     */
+    protected int getDataSetIndexByLabel(List<T> dataSets, String label,
+                                         boolean ignorecase) {
+
+        if (ignorecase) {
+            for (int i = 0; i < dataSets.size(); i++)
+                if (label.equalsIgnoreCase(dataSets.get(i).getLabel()))
+                    return i;
+        } else {
+            for (int i = 0; i < dataSets.size(); i++)
+                if (label.equals(dataSets.get(i).getLabel()))
+                    return i;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Returns the labels of all DataSets as a string array.
+     *
+     * @return
+     */
+    public String[] getDataSetLabels() {
+
+        String[] types = new String[mDataSets.size()];
+
+        for (int i = 0; i < mDataSets.size(); i++) {
+            types[i] = mDataSets.get(i).getLabel();
+        }
+
+        return types;
+    }
+
+    /**
+     * Get the Entry for a corresponding highlight object
+     *
+     * @param highlight
+     * @return the entry that is highlighted
+     */
+    public Entry getEntryForHighlight(Highlight highlight) {
+        if (highlight.getDataSetIndex() >= mDataSets.size())
+            return null;
+        else {
+            return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY());
+        }
+    }
+
+    /**
+     * Returns the DataSet object with the given label. Search can be case
+     * sensitive or not. IMPORTANT: This method does calculations at runtime.
+     * Use with care in performance critical situations.
+     *
+     * @param label
+     * @param ignorecase
+     * @return
+     */
+    public T getDataSetByLabel(String label, boolean ignorecase) {
+
+        int index = getDataSetIndexByLabel(mDataSets, label, ignorecase);
+
+        if (index < 0 || index >= mDataSets.size())
+            return null;
+        else
+            return mDataSets.get(index);
+    }
+
+    public T getDataSetByIndex(int index) {
+
+        if (mDataSets == null || index < 0 || index >= mDataSets.size())
+            return null;
+
+        return mDataSets.get(index);
+    }
+
+    /**
+     * Adds a DataSet dynamically.
+     *
+     * @param d
+     */
+    public void addDataSet(T d) {
+
+        if (d == null)
+            return;
+
+        calcMinMax(d);
+
+        mDataSets.add(d);
+    }
+
+    /**
+     * Removes the given DataSet from this data object. Also recalculates all
+     * minimum and maximum values. Returns true if a DataSet was removed, false
+     * if no DataSet could be removed.
+     *
+     * @param d
+     */
+    public boolean removeDataSet(T d) {
+
+        if (d == null)
+            return false;
+
+        boolean removed = mDataSets.remove(d);
+
+        // if a DataSet was removed
+        if (removed) {
+            notifyDataChanged();
+        }
+
+        return removed;
+    }
+
+    /**
+     * Removes the DataSet at the given index in the DataSet array from the data
+     * object. Also recalculates all minimum and maximum values. Returns true if
+     * a DataSet was removed, false if no DataSet could be removed.
+     *
+     * @param index
+     */
+    public boolean removeDataSet(int index) {
+
+        if (index >= mDataSets.size() || index < 0)
+            return false;
+
+        T set = mDataSets.get(index);
+        return removeDataSet(set);
+    }
+
+    /**
+     * Adds an Entry to the DataSet at the specified index.
+     * Entries are added to the end of the list.
+     *
+     * @param e
+     * @param dataSetIndex
+     */
+    public void addEntry(Entry e, int dataSetIndex) {
+
+        if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) {
+
+            IDataSet set = mDataSets.get(dataSetIndex);
+            // add the entry to the dataset
+            if (!set.addEntry(e))
+                return;
+
+            calcMinMax(e, set.getAxisDependency());
+
+        } else {
+            Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low.");
+        }
+    }
+
+    /**
+     * Adjusts the current minimum and maximum values based on the provided Entry object.
+     *
+     * @param e
+     * @param axis
+     */
+    protected void calcMinMax(Entry e, AxisDependency axis) {
+
+        if (mYMax < e.getY())
+            mYMax = e.getY();
+        if (mYMin > e.getY())
+            mYMin = e.getY();
+
+        if (mXMax < e.getX())
+            mXMax = e.getX();
+        if (mXMin > e.getX())
+            mXMin = e.getX();
+
+        if (axis == AxisDependency.LEFT) {
+
+            if (mLeftAxisMax < e.getY())
+                mLeftAxisMax = e.getY();
+            if (mLeftAxisMin > e.getY())
+                mLeftAxisMin = e.getY();
+        } else {
+            if (mRightAxisMax < e.getY())
+                mRightAxisMax = e.getY();
+            if (mRightAxisMin > e.getY())
+                mRightAxisMin = e.getY();
+        }
+    }
+
+    /**
+     * Adjusts the minimum and maximum values based on the given DataSet.
+     *
+     * @param d
+     */
+    protected void calcMinMax(T d) {
+
+        if (mYMax < d.getYMax())
+            mYMax = d.getYMax();
+        if (mYMin > d.getYMin())
+            mYMin = d.getYMin();
+
+        if (mXMax < d.getXMax())
+            mXMax = d.getXMax();
+        if (mXMin > d.getXMin())
+            mXMin = d.getXMin();
+
+        if (d.getAxisDependency() == AxisDependency.LEFT) {
+
+            if (mLeftAxisMax < d.getYMax())
+                mLeftAxisMax = d.getYMax();
+            if (mLeftAxisMin > d.getYMin())
+                mLeftAxisMin = d.getYMin();
+        } else {
+            if (mRightAxisMax < d.getYMax())
+                mRightAxisMax = d.getYMax();
+            if (mRightAxisMin > d.getYMin())
+                mRightAxisMin = d.getYMin();
+        }
+    }
+
+    /**
+     * Removes the given Entry object from the DataSet at the specified index.
+     *
+     * @param e
+     * @param dataSetIndex
+     */
+    public boolean removeEntry(Entry e, int dataSetIndex) {
+
+        // entry null, outofbounds
+        if (e == null || dataSetIndex >= mDataSets.size())
+            return false;
+
+        IDataSet set = mDataSets.get(dataSetIndex);
+
+        if (set != null) {
+            // remove the entry from the dataset
+            boolean removed = set.removeEntry(e);
+
+            if (removed) {
+                notifyDataChanged();
+            }
+
+            return removed;
+        } else
+            return false;
+    }
+
+    /**
+     * Removes the Entry object closest to the given DataSet at the
+     * specified index. Returns true if an Entry was removed, false if no Entry
+     * was found that meets the specified requirements.
+     *
+     * @param xValue
+     * @param dataSetIndex
+     * @return
+     */
+    public boolean removeEntry(float xValue, int dataSetIndex) {
+
+        if (dataSetIndex >= mDataSets.size())
+            return false;
+
+        IDataSet dataSet = mDataSets.get(dataSetIndex);
+        Entry e = dataSet.getEntryForXValue(xValue, Float.NaN);
+
+        if (e == null)
+            return false;
+
+        return removeEntry(e, dataSetIndex);
+    }
+
+    /**
+     * Returns the DataSet that contains the provided Entry, or null, if no
+     * DataSet contains this Entry.
+     *
+     * @param e
+     * @return
+     */
+    public T getDataSetForEntry(Entry e) {
+
+        if (e == null)
+            return null;
+
+        for (int i = 0; i < mDataSets.size(); i++) {
+
+            T set = mDataSets.get(i);
+
+            for (int j = 0; j < set.getEntryCount(); j++) {
+                if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY())))
+                    return set;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns all colors used across all DataSet objects this object
+     * represents.
+     *
+     * @return
+     */
+    public int[] getColors() {
+
+        if (mDataSets == null)
+            return null;
+
+        int clrcnt = 0;
+
+        for (int i = 0; i < mDataSets.size(); i++) {
+            clrcnt += mDataSets.get(i).getColors().size();
+        }
+
+        int[] colors = new int[clrcnt];
+        int cnt = 0;
+
+        for (int i = 0; i < mDataSets.size(); i++) {
+
+            List<Integer> clrs = mDataSets.get(i).getColors();
+
+            for (Integer clr : clrs) {
+                colors[cnt] = clr;
+                cnt++;
+            }
+        }
+
+        return colors;
+    }
+
+    /**
+     * Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist.
+     *
+     * @param dataSet
+     * @return
+     */
+    public int getIndexOfDataSet(T dataSet) {
+        return mDataSets.indexOf(dataSet);
+    }
+
+    /**
+     * Returns the first DataSet from the datasets-array that has it's dependency on the left axis.
+     * Returns null if no DataSet with left dependency could be found.
+     *
+     * @return
+     */
+    protected T getFirstLeft(List<T> sets) {
+        for (T dataSet : sets) {
+            if (dataSet.getAxisDependency() == AxisDependency.LEFT)
+                return dataSet;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the first DataSet from the datasets-array that has it's dependency on the right axis.
+     * Returns null if no DataSet with right dependency could be found.
+     *
+     * @return
+     */
+    public T getFirstRight(List<T> sets) {
+        for (T dataSet : sets) {
+            if (dataSet.getAxisDependency() == AxisDependency.RIGHT)
+                return dataSet;
+        }
+        return null;
+    }
+
+    /**
+     * Sets a custom IValueFormatter for all DataSets this data object contains.
+     *
+     * @param f
+     */
+    public void setValueFormatter(ValueFormatter f) {
+        if (f == null)
+            return;
+        else {
+            for (IDataSet set : mDataSets) {
+                set.setValueFormatter(f);
+            }
+        }
+    }
+
+    /**
+     * Sets the color of the value-text (color in which the value-labels are
+     * drawn) for all DataSets this data object contains.
+     *
+     * @param color
+     */
+    public void setValueTextColor(int color) {
+        for (IDataSet set : mDataSets) {
+            set.setValueTextColor(color);
+        }
+    }
+
+    /**
+     * Sets the same list of value-colors for all DataSets this
+     * data object contains.
+     *
+     * @param colors
+     */
+    public void setValueTextColors(List<Integer> colors) {
+        for (IDataSet set : mDataSets) {
+            set.setValueTextColors(colors);
+        }
+    }
+
+    /**
+     * Sets the Typeface for all value-labels for all DataSets this data object
+     * contains.
+     *
+     * @param tf
+     */
+    public void setValueTypeface(Typeface tf) {
+        for (IDataSet set : mDataSets) {
+            set.setValueTypeface(tf);
+        }
+    }
+
+    /**
+     * Sets the size (in dp) of the value-text for all DataSets this data object
+     * contains.
+     *
+     * @param size
+     */
+    public void setValueTextSize(float size) {
+        for (IDataSet set : mDataSets) {
+            set.setValueTextSize(size);
+        }
+    }
+
+    /**
+     * Enables / disables drawing values (value-text) for all DataSets this data
+     * object contains.
+     *
+     * @param enabled
+     */
+    public void setDrawValues(boolean enabled) {
+        for (IDataSet set : mDataSets) {
+            set.setDrawValues(enabled);
+        }
+    }
+
+    /**
+     * Enables / disables highlighting values for all DataSets this data object
+     * contains. If set to true, this means that values can
+     * be highlighted programmatically or by touch gesture.
+     */
+    public void setHighlightEnabled(boolean enabled) {
+        for (IDataSet set : mDataSets) {
+            set.setHighlightEnabled(enabled);
+        }
+    }
+
+    /**
+     * Returns true if highlighting of all underlying values is enabled, false
+     * if not.
+     *
+     * @return
+     */
+    public boolean isHighlightEnabled() {
+        for (IDataSet set : mDataSets) {
+            if (!set.isHighlightEnabled())
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Clears this data object from all DataSets and removes all Entries. Don't
+     * forget to invalidate the chart after this.
+     */
+    public void clearValues() {
+        if (mDataSets != null) {
+            mDataSets.clear();
+        }
+        notifyDataChanged();
+    }
+
+    /**
+     * Checks if this data object contains the specified DataSet. Returns true
+     * if so, false if not.
+     *
+     * @param dataSet
+     * @return
+     */
+    public boolean contains(T dataSet) {
+
+        for (T set : mDataSets) {
+            if (set.equals(dataSet))
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the total entry count across all DataSet objects this data object contains.
+     *
+     * @return
+     */
+    public int getEntryCount() {
+
+        int count = 0;
+
+        for (T set : mDataSets) {
+            count += set.getEntryCount();
+        }
+
+        return count;
+    }
+
+    /**
+     * Returns the DataSet object with the maximum number of entries or null if there are no DataSets.
+     *
+     * @return
+     */
+    public T getMaxEntryCountSet() {
+
+        if (mDataSets == null || mDataSets.isEmpty())
+            return null;
+
+        T max = mDataSets.get(0);
+
+        for (T set : mDataSets) {
+
+            if (set.getEntryCount() > max.getEntryCount())
+                max = set;
+        }
+
+        return max;
+    }
+}

+ 272 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java

@@ -0,0 +1,272 @@
+
+package com.github.mikephil.charting.data;
+
+import android.util.Log;
+
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Data object that allows the combination of Line-, Bar-, Scatter-, Bubble- and
+ * CandleData. Used in the CombinedChart class.
+ *
+ * @author Philipp Jahoda
+ */
+public class CombinedData extends BarLineScatterCandleBubbleData<IBarLineScatterCandleBubbleDataSet<? extends Entry>> {
+
+    private LineData mLineData;
+    private BarData mBarData;
+    private ScatterData mScatterData;
+    private CandleData mCandleData;
+    private BubbleData mBubbleData;
+
+    public CombinedData() {
+        super();
+    }
+
+    public void setData(LineData data) {
+        mLineData = data;
+        notifyDataChanged();
+    }
+
+    public void setData(BarData data) {
+        mBarData = data;
+        notifyDataChanged();
+    }
+
+    public void setData(ScatterData data) {
+        mScatterData = data;
+        notifyDataChanged();
+    }
+
+    public void setData(CandleData data) {
+        mCandleData = data;
+        notifyDataChanged();
+    }
+
+    public void setData(BubbleData data) {
+        mBubbleData = data;
+        notifyDataChanged();
+    }
+
+    @Override
+    public void calcMinMax() {
+
+        if(mDataSets == null){
+            mDataSets = new ArrayList<>();
+        }
+        mDataSets.clear();
+
+        mYMax = -Float.MAX_VALUE;
+        mYMin = Float.MAX_VALUE;
+        mXMax = -Float.MAX_VALUE;
+        mXMin = Float.MAX_VALUE;
+
+        mLeftAxisMax = -Float.MAX_VALUE;
+        mLeftAxisMin = Float.MAX_VALUE;
+        mRightAxisMax = -Float.MAX_VALUE;
+        mRightAxisMin = Float.MAX_VALUE;
+
+        List<BarLineScatterCandleBubbleData> allData = getAllData();
+
+        for (ChartData data : allData) {
+
+            data.calcMinMax();
+
+            List<IBarLineScatterCandleBubbleDataSet<? extends Entry>> sets = data.getDataSets();
+            mDataSets.addAll(sets);
+
+            if (data.getYMax() > mYMax)
+                mYMax = data.getYMax();
+
+            if (data.getYMin() < mYMin)
+                mYMin = data.getYMin();
+
+            if (data.getXMax() > mXMax)
+                mXMax = data.getXMax();
+
+            if (data.getXMin() < mXMin)
+                mXMin = data.getXMin();
+
+            for (IBarLineScatterCandleBubbleDataSet<? extends Entry> dataset : sets) {
+                if (dataset.getAxisDependency() == YAxis.AxisDependency.LEFT)  {
+                    if (dataset.getYMax() > mLeftAxisMax) {
+                        mLeftAxisMax = dataset.getYMax();
+                    }
+
+                    if (dataset.getYMin() < mLeftAxisMin) {
+                        mLeftAxisMin = dataset.getYMin();
+                    }
+                }
+                else {
+                    if (dataset.getYMax() > mRightAxisMax) {
+                        mRightAxisMax = dataset.getYMax();
+                    }
+
+                    if (dataset.getYMin() < mRightAxisMin) {
+                        mRightAxisMin = dataset.getYMin();
+                    }
+                }
+            }
+        }
+    }
+
+    public BubbleData getBubbleData() {
+        return mBubbleData;
+    }
+
+    public LineData getLineData() {
+        return mLineData;
+    }
+
+    public BarData getBarData() {
+        return mBarData;
+    }
+
+    public ScatterData getScatterData() {
+        return mScatterData;
+    }
+
+    public CandleData getCandleData() {
+        return mCandleData;
+    }
+
+    /**
+     * Returns all data objects in row: line-bar-scatter-candle-bubble if not null.
+     *
+     * @return
+     */
+    public List<BarLineScatterCandleBubbleData> getAllData() {
+
+        List<BarLineScatterCandleBubbleData> data = new ArrayList<BarLineScatterCandleBubbleData>();
+        if (mLineData != null)
+            data.add(mLineData);
+        if (mBarData != null)
+            data.add(mBarData);
+        if (mScatterData != null)
+            data.add(mScatterData);
+        if (mCandleData != null)
+            data.add(mCandleData);
+        if (mBubbleData != null)
+            data.add(mBubbleData);
+
+        return data;
+    }
+
+    public BarLineScatterCandleBubbleData getDataByIndex(int index) {
+        return getAllData().get(index);
+    }
+
+    @Override
+    public void notifyDataChanged() {
+        if (mLineData != null)
+            mLineData.notifyDataChanged();
+        if (mBarData != null)
+            mBarData.notifyDataChanged();
+        if (mCandleData != null)
+            mCandleData.notifyDataChanged();
+        if (mScatterData != null)
+            mScatterData.notifyDataChanged();
+        if (mBubbleData != null)
+            mBubbleData.notifyDataChanged();
+
+        calcMinMax(); // recalculate everything
+    }
+
+    /**
+     * Get the Entry for a corresponding highlight object
+     *
+     * @param highlight
+     * @return the entry that is highlighted
+     */
+    @Override
+    public Entry getEntryForHighlight(Highlight highlight) {
+
+        if (highlight.getDataIndex() >= getAllData().size())
+            return null;
+
+        ChartData data = getDataByIndex(highlight.getDataIndex());
+
+        if (highlight.getDataSetIndex() >= data.getDataSetCount())
+            return null;
+
+        // The value of the highlighted entry could be NaN -
+        //   if we are not interested in highlighting a specific value.
+
+        List<Entry> entries = data.getDataSetByIndex(highlight.getDataSetIndex())
+                .getEntriesForXValue(highlight.getX());
+        for (Entry entry : entries)
+            if (entry.getY() == highlight.getY() ||
+                    Float.isNaN(highlight.getY()))
+                return entry;
+
+        return null;
+    }
+
+    /**
+     * Get dataset for highlight
+     *
+     * @param highlight current highlight
+     * @return dataset related to highlight
+     */
+    public IBarLineScatterCandleBubbleDataSet<? extends Entry> getDataSetByHighlight(Highlight highlight) {
+        if (highlight.getDataIndex() >= getAllData().size())
+            return null;
+
+        BarLineScatterCandleBubbleData data = getDataByIndex(highlight.getDataIndex());
+
+        if (highlight.getDataSetIndex() >= data.getDataSetCount())
+            return null;
+
+        return (IBarLineScatterCandleBubbleDataSet<? extends Entry>)
+                data.getDataSets().get(highlight.getDataSetIndex());
+    }
+
+    public int getDataIndex(ChartData data) {
+        return getAllData().indexOf(data);
+    }
+
+    @Override
+    public boolean removeDataSet(IBarLineScatterCandleBubbleDataSet<? extends Entry> d) {
+
+        List<BarLineScatterCandleBubbleData> datas = getAllData();
+
+        boolean success = false;
+
+        for (ChartData data : datas) {
+
+            success = data.removeDataSet(d);
+
+            if (success) {
+                break;
+            }
+        }
+
+        return success;
+    }
+
+    @Deprecated
+    @Override
+    public boolean removeDataSet(int index) {
+        Log.e("MPAndroidChart", "removeDataSet(int index) not supported for CombinedData");
+        return false;
+    }
+
+    @Deprecated
+    @Override
+    public boolean removeEntry(Entry e, int dataSetIndex) {
+        Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData");
+        return false;
+    }
+
+    @Deprecated
+    @Override
+    public boolean removeEntry(float xValue, int dataSetIndex) {
+        Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData");
+        return false;
+    }
+}

+ 456 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java

@@ -0,0 +1,456 @@
+
+package com.github.mikephil.charting.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The DataSet class represents one group or type of entries (Entry) in the
+ * Chart that belong together. It is designed to logically separate different
+ * groups of values inside the Chart (e.g. the values for a specific line in the
+ * LineChart, or the values of a specific group of bars in the BarChart).
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
+
+    /**
+     * the entries that this DataSet represents / holds together
+     */
+    protected List<T> mEntries;
+
+    /**
+     * maximum y-value in the value array
+     */
+    protected float mYMax = -Float.MAX_VALUE;
+
+    /**
+     * minimum y-value in the value array
+     */
+    protected float mYMin = Float.MAX_VALUE;
+
+    /**
+     * maximum x-value in the value array
+     */
+    protected float mXMax = -Float.MAX_VALUE;
+
+    /**
+     * minimum x-value in the value array
+     */
+    protected float mXMin = Float.MAX_VALUE;
+
+
+    /**
+     * Creates a new DataSet object with the given values (entries) it represents. Also, a
+     * label that describes the DataSet can be specified. The label can also be
+     * used to retrieve the DataSet from a ChartData object.
+     *
+     * @param entries
+     * @param label
+     */
+    public DataSet(List<T> entries, String label) {
+        super(label);
+        this.mEntries = entries;
+
+        if (mEntries == null)
+            mEntries = new ArrayList<T>();
+
+        calcMinMax();
+    }
+
+    @Override
+    public void calcMinMax() {
+
+        mYMax = -Float.MAX_VALUE;
+        mYMin = Float.MAX_VALUE;
+        mXMax = -Float.MAX_VALUE;
+        mXMin = Float.MAX_VALUE;
+
+        if (mEntries == null || mEntries.isEmpty())
+            return;
+
+        for (T e : mEntries) {
+            calcMinMax(e);
+        }
+    }
+
+    @Override
+    public void calcMinMaxY(float fromX, float toX) {
+        mYMax = -Float.MAX_VALUE;
+        mYMin = Float.MAX_VALUE;
+        
+        if (mEntries == null || mEntries.isEmpty())
+            return;
+
+        int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN);
+        int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP);
+
+        if (indexTo < indexFrom) return;
+
+        for (int i = indexFrom; i <= indexTo; i++) {
+
+            // only recalculate y
+            calcMinMaxY(mEntries.get(i));
+        }
+    }
+
+    /**
+     * Updates the min and max x and y value of this DataSet based on the given Entry.
+     *
+     * @param e
+     */
+    protected void calcMinMax(T e) {
+
+        if (e == null)
+            return;
+
+        calcMinMaxX(e);
+
+        calcMinMaxY(e);
+    }
+
+    protected void calcMinMaxX(T e) {
+
+        if (e.getX() < mXMin)
+            mXMin = e.getX();
+
+        if (e.getX() > mXMax)
+            mXMax = e.getX();
+    }
+
+    protected void calcMinMaxY(T e) {
+
+        if (e.getY() < mYMin)
+            mYMin = e.getY();
+
+        if (e.getY() > mYMax)
+            mYMax = e.getY();
+    }
+
+    @Override
+    public int getEntryCount() {
+        return mEntries.size();
+    }
+
+    /**
+     * This method is deprecated.
+     * Use getEntries() instead.
+     *
+     * @return
+     */
+    @Deprecated
+    public List<T> getValues() {
+        return mEntries;
+    }
+
+    /**
+     * Returns the array of entries that this DataSet represents.
+     *
+     * @return
+     */
+    public List<T> getEntries() {
+        return mEntries;
+    }
+
+    /**
+     * This method is deprecated.
+     * Use setEntries(...) instead.
+     *
+     * @param values
+     */
+    @Deprecated
+    public void setValues(List<T> values) {
+        setEntries(values);
+    }
+
+    /**
+     * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged()
+     *
+     * @return
+     */
+    public void setEntries(List<T> entries) {
+        mEntries = entries;
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Provides an exact copy of the DataSet this method is used on.
+     *
+     * @return
+     */
+    public abstract DataSet<T> copy();
+
+    /**
+     *
+     * @param dataSet
+     */
+    protected void copy(DataSet dataSet) {
+        super.copy(dataSet);
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(toSimpleString());
+        for (int i = 0; i < mEntries.size(); i++) {
+            buffer.append(mEntries.get(i).toString() + " ");
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns a simple string representation of the DataSet with the type and
+     * the number of Entries.
+     *
+     * @return
+     */
+    public String toSimpleString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mEntries.size() +
+                "\n");
+        return buffer.toString();
+    }
+
+    @Override
+    public float getYMin() {
+        return mYMin;
+    }
+
+    @Override
+    public float getYMax() {
+        return mYMax;
+    }
+
+    @Override
+    public float getXMin() {
+        return mXMin;
+    }
+
+    @Override
+    public float getXMax() {
+        return mXMax;
+    }
+
+    @Override
+    public void addEntryOrdered(T e) {
+
+        if (e == null)
+            return;
+
+        if (mEntries == null) {
+            mEntries = new ArrayList<T>();
+        }
+
+        calcMinMax(e);
+
+        if (mEntries.size() > 0 && mEntries.get(mEntries.size() - 1).getX() > e.getX()) {
+            int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP);
+            mEntries.add(closestIndex, e);
+        } else {
+            mEntries.add(e);
+        }
+    }
+
+    @Override
+    public void clear() {
+        mEntries.clear();
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public boolean addEntry(T e) {
+
+        if (e == null)
+            return false;
+
+        List<T> values = getEntries();
+        if (values == null) {
+            values = new ArrayList<>();
+        }
+
+        calcMinMax(e);
+
+        // add the entry
+        return values.add(e);
+    }
+
+    @Override
+    public boolean removeEntry(T e) {
+
+        if (e == null)
+            return false;
+
+        if (mEntries == null)
+            return false;
+
+        // remove the entry
+        boolean removed = mEntries.remove(e);
+
+        if (removed) {
+            calcMinMax();
+        }
+
+        return removed;
+    }
+
+    @Override
+    public int getEntryIndex(Entry e) {
+        return mEntries.indexOf(e);
+    }
+
+    @Override
+    public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) {
+
+        int index = getEntryIndex(xValue, closestToY, rounding);
+        if (index > -1)
+            return mEntries.get(index);
+        return null;
+    }
+
+    @Override
+    public T getEntryForXValue(float xValue, float closestToY) {
+        return getEntryForXValue(xValue, closestToY, Rounding.CLOSEST);
+    }
+
+    @Override
+    public T getEntryForIndex(int index) {
+        return mEntries.get(index);
+    }
+
+    @Override
+    public int getEntryIndex(float xValue, float closestToY, Rounding rounding) {
+
+        if (mEntries == null || mEntries.isEmpty())
+            return -1;
+
+        int low = 0;
+        int high = mEntries.size() - 1;
+        int closest = high;
+
+        while (low < high) {
+            int m = (low + high) / 2;
+
+            final float d1 = mEntries.get(m).getX() - xValue,
+                    d2 = mEntries.get(m + 1).getX() - xValue,
+                    ad1 = Math.abs(d1), ad2 = Math.abs(d2);
+
+            if (ad2 < ad1) {
+                // [m + 1] is closer to xValue
+                // Search in an higher place
+                low = m + 1;
+            } else if (ad1 < ad2) {
+                // [m] is closer to xValue
+                // Search in a lower place
+                high = m;
+            } else {
+                // We have multiple sequential x-value with same distance
+
+                if (d1 >= 0.0) {
+                    // Search in a lower place
+                    high = m;
+                } else if (d1 < 0.0) {
+                    // Search in an higher place
+                    low = m + 1;
+                }
+            }
+
+            closest = high;
+        }
+
+        if (closest != -1) {
+            float closestXValue = mEntries.get(closest).getX();
+            if (rounding == Rounding.UP) {
+                // If rounding up, and found x-value is lower than specified x, and we can go upper...
+                if (closestXValue < xValue && closest < mEntries.size() - 1) {
+                    ++closest;
+                }
+            } else if (rounding == Rounding.DOWN) {
+                // If rounding down, and found x-value is upper than specified x, and we can go lower...
+                if (closestXValue > xValue && closest > 0) {
+                    --closest;
+                }
+            }
+
+            // Search by closest to y-value
+            if (!Float.isNaN(closestToY)) {
+                while (closest > 0 && mEntries.get(closest - 1).getX() == closestXValue)
+                    closest -= 1;
+
+                float closestYValue = mEntries.get(closest).getY();
+                int closestYIndex = closest;
+
+                while (true) {
+                    closest += 1;
+                    if (closest >= mEntries.size())
+                        break;
+
+                    final Entry value = mEntries.get(closest);
+
+                    if (value.getX() != closestXValue)
+                        break;
+
+                    if (Math.abs(value.getY() - closestToY) <= Math.abs(closestYValue - closestToY)) {
+                        closestYValue = closestToY;
+                        closestYIndex = closest;
+                    }
+                }
+
+                closest = closestYIndex;
+            }
+        }
+
+        return closest;
+    }
+
+    @Override
+    public List<T> getEntriesForXValue(float xValue) {
+
+        List<T> entries = new ArrayList<T>();
+
+        int low = 0;
+        int high = mEntries.size() - 1;
+
+        while (low <= high) {
+            int m = (high + low) / 2;
+            T entry = mEntries.get(m);
+
+            // if we have a match
+            if (xValue == entry.getX()) {
+                while (m > 0 && mEntries.get(m - 1).getX() == xValue)
+                    m--;
+
+                high = mEntries.size();
+
+                // loop over all "equal" entries
+                for (; m < high; m++) {
+                    entry = mEntries.get(m);
+                    if (entry.getX() == xValue) {
+                        entries.add(entry);
+                    } else {
+                        break;
+                    }
+                }
+
+                break;
+            } else {
+                if (xValue > entry.getX())
+                    low = m + 1;
+                else
+                    high = m - 1;
+            }
+        }
+
+        return entries;
+    }
+
+    /**
+     * Determines how to round DataSet index values for
+     * {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()}
+     * when an exact x-index is not found.
+     */
+    public enum Rounding {
+        UP,
+        DOWN,
+        CLOSEST,
+    }
+}

+ 173 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java

@@ -0,0 +1,173 @@
+
+package com.github.mikephil.charting.data;
+
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.ParcelFormatException;
+import android.os.Parcelable;
+
+import com.github.mikephil.charting.utils.Utils;
+
+/**
+ * Class representing one entry in the chart. Might contain multiple values.
+ * Might only contain a single value depending on the used constructor.
+ * 
+ * @author Philipp Jahoda
+ */
+public class Entry extends BaseEntry implements Parcelable {
+
+    /** the x value */
+    private float x = 0f;
+
+    public Entry() {
+
+    }
+
+    /**
+     * A Entry represents one single entry in the chart.
+     *
+     * @param x the x value
+     * @param y the y value (the actual value of the entry)
+     */
+    public Entry(float x, float y) {
+        super(y);
+        this.x = x;
+    }
+
+    /**
+     * A Entry represents one single entry in the chart.
+     *
+     * @param x the x value
+     * @param y the y value (the actual value of the entry)
+     * @param data Spot for additional data this Entry represents.
+     */
+    public Entry(float x, float y, Object data) {
+        super(y, data);
+        this.x = x;
+    }
+
+    /**
+     * A Entry represents one single entry in the chart.
+     *
+     * @param x the x value
+     * @param y the y value (the actual value of the entry)
+     * @param icon icon image
+     */
+    public Entry(float x, float y, Drawable icon) {
+        super(y, icon);
+        this.x = x;
+    }
+
+    /**
+     * A Entry represents one single entry in the chart.
+     *
+     * @param x the x value
+     * @param y the y value (the actual value of the entry)
+     * @param icon icon image
+     * @param data Spot for additional data this Entry represents.
+     */
+    public Entry(float x, float y, Drawable icon, Object data) {
+        super(y, icon, data);
+        this.x = x;
+    }
+
+    /**
+     * Returns the x-value of this Entry object.
+     * 
+     * @return
+     */
+    public float getX() {
+        return x;
+    }
+
+    /**
+     * Sets the x-value of this Entry object.
+     * 
+     * @param x
+     */
+    public void setX(float x) {
+        this.x = x;
+    }
+
+    /**
+     * returns an exact copy of the entry
+     * 
+     * @return
+     */
+    public Entry copy() {
+        Entry e = new Entry(x, getY(), getData());
+        return e;
+    }
+
+    /**
+     * Compares value, xIndex and data of the entries. Returns true if entries
+     * are equal in those points, false if not. Does not check by hash-code like
+     * it's done by the "equals" method.
+     * 
+     * @param e
+     * @return
+     */
+    public boolean equalTo(Entry e) {
+
+        if (e == null)
+            return false;
+
+        if (e.getData() != this.getData())
+            return false;
+
+        if (Math.abs(e.x - this.x) > Utils.FLOAT_EPSILON)
+            return false;
+
+        if (Math.abs(e.getY() - this.getY()) > Utils.FLOAT_EPSILON)
+            return false;
+
+        return true;
+    }
+
+    /**
+     * returns a string representation of the entry containing x-index and value
+     */
+    @Override
+    public String toString() {
+        return "Entry, x: " + x + " y: " + getY();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(this.x);
+        dest.writeFloat(this.getY());
+        if (getData() != null) {
+            if (getData() instanceof Parcelable) {
+                dest.writeInt(1);
+                dest.writeParcelable((Parcelable) this.getData(), flags);
+            } else {
+                throw new ParcelFormatException("Cannot parcel an Entry with non-parcelable data");
+            }
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    protected Entry(Parcel in) {
+        this.x = in.readFloat();
+        this.setY(in.readFloat());
+        if (in.readInt() == 1) {
+            this.setData(in.readParcelable(Object.class.getClassLoader()));
+        }
+    }
+
+    public static final Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator<Entry>() {
+        public Entry createFromParcel(Parcel source) {
+            return new Entry(source);
+        }
+
+        public Entry[] newArray(int size) {
+            return new Entry[size];
+        }
+    };
+}

+ 27 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java

@@ -0,0 +1,27 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Data object that encapsulates all data associated with a LineChart.
+ * 
+ * @author Philipp Jahoda
+ */
+public class LineData extends BarLineScatterCandleBubbleData<ILineDataSet> {
+
+    public LineData() {
+        super();
+    }
+
+    public LineData(ILineDataSet... dataSets) {
+        super(dataSets);
+    }
+
+    public LineData(List<ILineDataSet> dataSets) {
+        super(dataSets);
+    }
+}

+ 417 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java

@@ -0,0 +1,417 @@
+
+package com.github.mikephil.charting.data;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.util.Log;
+
+import com.github.mikephil.charting.formatter.DefaultFillFormatter;
+import com.github.mikephil.charting.formatter.IFillFormatter;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet {
+
+    /**
+     * Drawing mode for this line dataset
+     **/
+    private LineDataSet.Mode mMode = Mode.LINEAR;
+
+    /**
+     * List representing all colors that are used for the circles
+     */
+    private List<Integer> mCircleColors = null;
+
+    /**
+     * the color of the inner circles
+     */
+    private int mCircleHoleColor = Color.WHITE;
+
+    /**
+     * the radius of the circle-shaped value indicators
+     */
+    private float mCircleRadius = 8f;
+
+    /**
+     * the hole radius of the circle-shaped value indicators
+     */
+    private float mCircleHoleRadius = 4f;
+
+    /**
+     * sets the intensity of the cubic lines
+     */
+    private float mCubicIntensity = 0.2f;
+
+    /**
+     * the path effect of this DataSet that makes dashed lines possible
+     */
+    private DashPathEffect mDashPathEffect = null;
+
+    /**
+     * formatter for customizing the position of the fill-line
+     */
+    private IFillFormatter mFillFormatter = new DefaultFillFormatter();
+
+    /**
+     * if true, drawing circles is enabled
+     */
+    private boolean mDrawCircles = true;
+
+    private boolean mDrawCircleHole = true;
+
+
+    public LineDataSet(List<Entry> yVals, String label) {
+        super(yVals, label);
+
+        // mCircleRadius = Utils.convertDpToPixel(4f);
+        // mLineWidth = Utils.convertDpToPixel(1f);
+
+        if (mCircleColors == null) {
+            mCircleColors = new ArrayList<Integer>();
+        }
+        mCircleColors.clear();
+
+        // default colors
+        // mColors.add(Color.rgb(192, 255, 140));
+        // mColors.add(Color.rgb(255, 247, 140));
+        mCircleColors.add(Color.rgb(140, 234, 255));
+    }
+
+    @Override
+    public DataSet<Entry> copy() {
+        List<Entry> entries = new ArrayList<Entry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        LineDataSet copied = new LineDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(LineDataSet lineDataSet) {
+        super.copy(lineDataSet);
+        lineDataSet.mCircleColors = mCircleColors;
+        lineDataSet.mCircleHoleColor = mCircleHoleColor;
+        lineDataSet.mCircleHoleRadius = mCircleHoleRadius;
+        lineDataSet.mCircleRadius = mCircleRadius;
+        lineDataSet.mCubicIntensity = mCubicIntensity;
+        lineDataSet.mDashPathEffect = mDashPathEffect;
+        lineDataSet.mDrawCircleHole = mDrawCircleHole;
+        lineDataSet.mDrawCircles = mDrawCircleHole;
+        lineDataSet.mFillFormatter = mFillFormatter;
+        lineDataSet.mMode = mMode;
+    }
+
+    /**
+     * Returns the drawing mode for this line dataset
+     *
+     * @return
+     */
+    @Override
+    public LineDataSet.Mode getMode() {
+        return mMode;
+    }
+
+    /**
+     * Returns the drawing mode for this LineDataSet
+     *
+     * @return
+     */
+    public void setMode(LineDataSet.Mode mode) {
+        mMode = mode;
+    }
+
+    /**
+     * Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic,
+     * Min = 0.05f = low cubic effect, Default: 0.2f
+     *
+     * @param intensity
+     */
+    public void setCubicIntensity(float intensity) {
+
+        if (intensity > 1f)
+            intensity = 1f;
+        if (intensity < 0.05f)
+            intensity = 0.05f;
+
+        mCubicIntensity = intensity;
+    }
+
+    @Override
+    public float getCubicIntensity() {
+        return mCubicIntensity;
+    }
+
+
+    /**
+     * Sets the radius of the drawn circles.
+     * Default radius = 4f, Min = 1f
+     *
+     * @param radius
+     */
+    public void setCircleRadius(float radius) {
+
+        if (radius >= 1f) {
+            mCircleRadius = Utils.convertDpToPixel(radius);
+        } else {
+            Log.e("LineDataSet", "Circle radius cannot be < 1");
+        }
+    }
+
+    @Override
+    public float getCircleRadius() {
+        return mCircleRadius;
+    }
+
+    /**
+     * Sets the hole radius of the drawn circles.
+     * Default radius = 2f, Min = 0.5f
+     *
+     * @param holeRadius
+     */
+    public void setCircleHoleRadius(float holeRadius) {
+
+        if (holeRadius >= 0.5f) {
+            mCircleHoleRadius = Utils.convertDpToPixel(holeRadius);
+        } else {
+            Log.e("LineDataSet", "Circle radius cannot be < 0.5");
+        }
+    }
+
+    @Override
+    public float getCircleHoleRadius() {
+        return mCircleHoleRadius;
+    }
+
+    /**
+     * sets the size (radius) of the circle shpaed value indicators,
+     * default size = 4f
+     * <p/>
+     * This method is deprecated because of unclarity. Use setCircleRadius instead.
+     *
+     * @param size
+     */
+    @Deprecated
+    public void setCircleSize(float size) {
+        setCircleRadius(size);
+    }
+
+    /**
+     * This function is deprecated because of unclarity. Use getCircleRadius instead.
+     */
+    @Deprecated
+    public float getCircleSize() {
+        return getCircleRadius();
+    }
+
+    /**
+     * Enables the line to be drawn in dashed mode, e.g. like this
+     * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
+     * Keep in mind that hardware acceleration boosts performance.
+     *
+     * @param lineLength  the length of the line pieces
+     * @param spaceLength the length of space in between the pieces
+     * @param phase       offset, in degrees (normally, use 0)
+     */
+    public void enableDashedLine(float lineLength, float spaceLength, float phase) {
+        mDashPathEffect = new DashPathEffect(new float[]{
+                lineLength, spaceLength
+        }, phase);
+    }
+
+    /**
+     * Disables the line to be drawn in dashed mode.
+     */
+    public void disableDashedLine() {
+        mDashPathEffect = null;
+    }
+
+    @Override
+    public boolean isDashedLineEnabled() {
+        return mDashPathEffect == null ? false : true;
+    }
+
+    @Override
+    public DashPathEffect getDashPathEffect() {
+        return mDashPathEffect;
+    }
+
+    /**
+     * set this to true to enable the drawing of circle indicators for this
+     * DataSet, default true
+     *
+     * @param enabled
+     */
+    public void setDrawCircles(boolean enabled) {
+        this.mDrawCircles = enabled;
+    }
+
+    @Override
+    public boolean isDrawCirclesEnabled() {
+        return mDrawCircles;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isDrawCubicEnabled() {
+        return mMode == Mode.CUBIC_BEZIER;
+    }
+
+    @Deprecated
+    @Override
+    public boolean isDrawSteppedEnabled() {
+        return mMode == Mode.STEPPED;
+    }
+
+    /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */
+
+    /**
+     * returns all colors specified for the circles
+     *
+     * @return
+     */
+    public List<Integer> getCircleColors() {
+        return mCircleColors;
+    }
+
+    @Override
+    public int getCircleColor(int index) {
+        return mCircleColors.get(index);
+    }
+
+    @Override
+    public int getCircleColorCount() {
+        return mCircleColors.size();
+    }
+
+    /**
+     * Sets the colors that should be used for the circles of this DataSet.
+     * Colors are reused as soon as the number of Entries the DataSet represents
+     * is higher than the size of the colors array. Make sure that the colors
+     * are already prepared (by calling getResources().getColor(...)) before
+     * adding them to the DataSet.
+     *
+     * @param colors
+     */
+    public void setCircleColors(List<Integer> colors) {
+        mCircleColors = colors;
+    }
+
+    /**
+     * Sets the colors that should be used for the circles of this DataSet.
+     * Colors are reused as soon as the number of Entries the DataSet represents
+     * is higher than the size of the colors array. Make sure that the colors
+     * are already prepared (by calling getResources().getColor(...)) before
+     * adding them to the DataSet.
+     *
+     * @param colors
+     */
+    public void setCircleColors(int... colors) {
+        this.mCircleColors = ColorTemplate.createColors(colors);
+    }
+
+    /**
+     * ets the colors that should be used for the circles of this DataSet.
+     * Colors are reused as soon as the number of Entries the DataSet represents
+     * is higher than the size of the colors array. You can use
+     * "new String[] { R.color.red, R.color.green, ... }" to provide colors for
+     * this method. Internally, the colors are resolved using
+     * getResources().getColor(...)
+     *
+     * @param colors
+     */
+    public void setCircleColors(int[] colors, Context c) {
+
+        List<Integer> clrs = mCircleColors;
+        if (clrs == null) {
+            clrs = new ArrayList<>();
+        }
+        clrs.clear();
+
+        for (int color : colors) {
+            clrs.add(c.getResources().getColor(color));
+        }
+
+        mCircleColors = clrs;
+    }
+
+    /**
+     * Sets the one and ONLY color that should be used for this DataSet.
+     * Internally, this recreates the colors array and adds the specified color.
+     *
+     * @param color
+     */
+    public void setCircleColor(int color) {
+        resetCircleColors();
+        mCircleColors.add(color);
+    }
+
+    /**
+     * resets the circle-colors array and creates a new one
+     */
+    public void resetCircleColors() {
+        if (mCircleColors == null) {
+            mCircleColors = new ArrayList<Integer>();
+        }
+        mCircleColors.clear();
+    }
+
+    /**
+     * Sets the color of the inner circle of the line-circles.
+     *
+     * @param color
+     */
+    public void setCircleHoleColor(int color) {
+        mCircleHoleColor = color;
+    }
+
+    @Override
+    public int getCircleHoleColor() {
+        return mCircleHoleColor;
+    }
+
+    /**
+     * Set this to true to allow drawing a hole in each data circle.
+     *
+     * @param enabled
+     */
+    public void setDrawCircleHole(boolean enabled) {
+        mDrawCircleHole = enabled;
+    }
+
+    @Override
+    public boolean isDrawCircleHoleEnabled() {
+        return mDrawCircleHole;
+    }
+
+    /**
+     * Sets a custom IFillFormatter to the chart that handles the position of the
+     * filled-line for each DataSet. Set this to null to use the default logic.
+     *
+     * @param formatter
+     */
+    public void setFillFormatter(IFillFormatter formatter) {
+
+        if (formatter == null)
+            mFillFormatter = new DefaultFillFormatter();
+        else
+            mFillFormatter = formatter;
+    }
+
+    @Override
+    public IFillFormatter getFillFormatter() {
+        return mFillFormatter;
+    }
+
+    public enum Mode {
+        LINEAR,
+        STEPPED,
+        CUBIC_BEZIER,
+        HORIZONTAL_BEZIER
+    }
+}

+ 135 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java

@@ -0,0 +1,135 @@
+
+package com.github.mikephil.charting.data;
+
+import android.annotation.TargetApi;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+
+import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.List;
+
+/**
+ * Base dataset for line and radar DataSets.
+ *
+ * @author Philipp Jahoda
+ */
+public abstract class LineRadarDataSet<T extends Entry> extends LineScatterCandleRadarDataSet<T> implements ILineRadarDataSet<T> {
+
+    // TODO: Move to using `Fill` class
+    /**
+     * the color that is used for filling the line surface
+     */
+    private int mFillColor = Color.rgb(140, 234, 255);
+
+    /**
+     * the drawable to be used for filling the line surface
+     */
+    protected Drawable mFillDrawable;
+
+    /**
+     * transparency used for filling line surface
+     */
+    private int mFillAlpha = 85;
+
+    /**
+     * the width of the drawn data lines
+     */
+    private float mLineWidth = 2.5f;
+
+    /**
+     * if true, the data will also be drawn filled
+     */
+    private boolean mDrawFilled = false;
+
+
+    public LineRadarDataSet(List<T> yVals, String label) {
+        super(yVals, label);
+    }
+
+    @Override
+    public int getFillColor() {
+        return mFillColor;
+    }
+
+    /**
+     * Sets the color that is used for filling the area below the line.
+     * Resets an eventually set "fillDrawable".
+     *
+     * @param color
+     */
+    public void setFillColor(int color) {
+        mFillColor = color;
+        mFillDrawable = null;
+    }
+
+    @Override
+    public Drawable getFillDrawable() {
+        return mFillDrawable;
+    }
+
+    /**
+     * Sets the drawable to be used to fill the area below the line.
+     *
+     * @param drawable
+     */
+    @TargetApi(18)
+    public void setFillDrawable(Drawable drawable) {
+        this.mFillDrawable = drawable;
+    }
+
+    @Override
+    public int getFillAlpha() {
+        return mFillAlpha;
+    }
+
+    /**
+     * sets the alpha value (transparency) that is used for filling the line
+     * surface (0-255), default: 85
+     *
+     * @param alpha
+     */
+    public void setFillAlpha(int alpha) {
+        mFillAlpha = alpha;
+    }
+
+    /**
+     * set the line width of the chart (min = 0.2f, max = 10f); default 1f NOTE:
+     * thinner line == better performance, thicker line == worse performance
+     *
+     * @param width
+     */
+    public void setLineWidth(float width) {
+
+        if (width < 0.0f)
+            width = 0.0f;
+        if (width > 10.0f)
+            width = 10.0f;
+        mLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    @Override
+    public float getLineWidth() {
+        return mLineWidth;
+    }
+
+    @Override
+    public void setDrawFilled(boolean filled) {
+        mDrawFilled = filled;
+    }
+
+    @Override
+    public boolean isDrawFilledEnabled() {
+        return mDrawFilled;
+    }
+
+    protected void copy(LineRadarDataSet lineRadarDataSet) {
+        super.copy(lineRadarDataSet);
+        lineRadarDataSet.mDrawFilled = mDrawFilled;
+        lineRadarDataSet.mFillAlpha = mFillAlpha;
+        lineRadarDataSet.mFillColor = mFillColor;
+        lineRadarDataSet.mFillDrawable = mFillDrawable;
+        lineRadarDataSet.mLineWidth = mLineWidth;
+    }
+}

+ 120 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java

@@ -0,0 +1,120 @@
+package com.github.mikephil.charting.data;
+
+import android.graphics.DashPathEffect;
+
+import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.List;
+
+/**
+ * Created by Philipp Jahoda on 11/07/15.
+ */
+public abstract class LineScatterCandleRadarDataSet<T extends Entry> extends BarLineScatterCandleBubbleDataSet<T> implements ILineScatterCandleRadarDataSet<T> {
+
+    protected boolean mDrawVerticalHighlightIndicator = true;
+    protected boolean mDrawHorizontalHighlightIndicator = true;
+
+    /** the width of the highlight indicator lines */
+    protected float mHighlightLineWidth = 0.5f;
+
+    /** the path effect for dashed highlight-lines */
+    protected DashPathEffect mHighlightDashPathEffect = null;
+
+
+    public LineScatterCandleRadarDataSet(List<T> yVals, String label) {
+        super(yVals, label);
+        mHighlightLineWidth = Utils.convertDpToPixel(0.5f);
+    }
+
+    /**
+     * Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn.
+     * @param enabled
+     */
+    public void setDrawHorizontalHighlightIndicator(boolean enabled) {
+        this.mDrawHorizontalHighlightIndicator = enabled;
+    }
+
+    /**
+     * Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn.
+     * @param enabled
+     */
+    public void setDrawVerticalHighlightIndicator(boolean enabled) {
+        this.mDrawVerticalHighlightIndicator = enabled;
+    }
+
+    /**
+     * Enables / disables both vertical and horizontal highlight-indicators.
+     * @param enabled
+     */
+    public void setDrawHighlightIndicators(boolean enabled) {
+        setDrawVerticalHighlightIndicator(enabled);
+        setDrawHorizontalHighlightIndicator(enabled);
+    }
+
+    @Override
+    public boolean isVerticalHighlightIndicatorEnabled() {
+        return mDrawVerticalHighlightIndicator;
+    }
+
+    @Override
+    public boolean isHorizontalHighlightIndicatorEnabled() {
+        return mDrawHorizontalHighlightIndicator;
+    }
+
+    /**
+     * Sets the width of the highlight line in dp.
+     * @param width
+     */
+    public void setHighlightLineWidth(float width) {
+        mHighlightLineWidth = Utils.convertDpToPixel(width);
+    }
+
+    @Override
+    public float getHighlightLineWidth() {
+        return mHighlightLineWidth;
+    }
+
+    /**
+     * Enables the highlight-line to be drawn in dashed mode, e.g. like this "- - - - - -"
+     *
+     * @param lineLength the length of the line pieces
+     * @param spaceLength the length of space inbetween the line-pieces
+     * @param phase offset, in degrees (normally, use 0)
+     */
+    public void enableDashedHighlightLine(float lineLength, float spaceLength, float phase) {
+        mHighlightDashPathEffect = new DashPathEffect(new float[] {
+                lineLength, spaceLength
+        }, phase);
+    }
+
+    /**
+     * Disables the highlight-line to be drawn in dashed mode.
+     */
+    public void disableDashedHighlightLine() {
+        mHighlightDashPathEffect = null;
+    }
+
+    /**
+     * Returns true if the dashed-line effect is enabled for highlight lines, false if not.
+     * Default: disabled
+     *
+     * @return
+     */
+    public boolean isDashedHighlightLineEnabled() {
+        return mHighlightDashPathEffect == null ? false : true;
+    }
+
+    @Override
+    public DashPathEffect getDashPathEffectHighlight() {
+        return mHighlightDashPathEffect;
+    }
+
+    protected void copy(LineScatterCandleRadarDataSet lineScatterCandleRadarDataSet) {
+        super.copy(lineScatterCandleRadarDataSet);
+        lineScatterCandleRadarDataSet.mDrawHorizontalHighlightIndicator = mDrawHorizontalHighlightIndicator;
+        lineScatterCandleRadarDataSet.mDrawVerticalHighlightIndicator = mDrawVerticalHighlightIndicator;
+        lineScatterCandleRadarDataSet.mHighlightLineWidth = mHighlightLineWidth;
+        lineScatterCandleRadarDataSet.mHighlightDashPathEffect = mHighlightDashPathEffect;
+    }
+}

+ 100 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java

@@ -0,0 +1,100 @@
+
+package com.github.mikephil.charting.data;
+
+import android.util.Log;
+
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A PieData object can only represent one DataSet. Unlike all other charts, the
+ * legend labels of the PieChart are created from the x-values array, and not
+ * from the DataSet labels. Each PieData object can only represent one
+ * PieDataSet (multiple PieDataSets inside a single PieChart are not possible).
+ *
+ * @author Philipp Jahoda
+ */
+public class PieData extends ChartData<IPieDataSet> {
+
+    public PieData() {
+        super();
+    }
+
+    public PieData(IPieDataSet dataSet) {
+        super(dataSet);
+    }
+
+    /**
+     * Sets the PieDataSet this data object should represent.
+     *
+     * @param dataSet
+     */
+    public void setDataSet(IPieDataSet dataSet) {
+        mDataSets.clear();
+        mDataSets.add(dataSet);
+        notifyDataChanged();
+    }
+
+    /**
+     * Returns the DataSet this PieData object represents. A PieData object can
+     * only contain one DataSet.
+     *
+     * @return
+     */
+    public IPieDataSet getDataSet() {
+        return mDataSets.get(0);
+    }
+
+    @Override
+    public List<IPieDataSet> getDataSets() {
+        List<IPieDataSet> dataSets = super.getDataSets();
+
+        if (dataSets.size() < 1) {
+            Log.e("MPAndroidChart",
+                    "Found multiple data sets while pie chart only allows one");
+        }
+
+        return dataSets;
+    }
+
+    /**
+     * The PieData object can only have one DataSet. Use getDataSet() method instead.
+     *
+     * @param index
+     * @return
+     */
+    @Override
+    public IPieDataSet getDataSetByIndex(int index) {
+        return index == 0 ? getDataSet() : null;
+    }
+
+    @Override
+    public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) {
+        return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0)
+                : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null;
+    }
+
+    @Override
+    public Entry getEntryForHighlight(Highlight highlight) {
+        return getDataSet().getEntryForIndex((int) highlight.getX());
+    }
+
+    /**
+     * Returns the sum of all values in this PieData object.
+     *
+     * @return
+     */
+    public float getYValueSum() {
+
+        float sum = 0;
+
+        for (int i = 0; i < getDataSet().getEntryCount(); i++)
+            sum += getDataSet().getEntryForIndex(i).getY();
+
+
+        return sum;
+    }
+}

+ 263 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java

@@ -0,0 +1,263 @@
+
+package com.github.mikephil.charting.data;
+
+
+import android.support.annotation.Nullable;
+
+import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
+
+    /**
+     * the space in pixels between the chart-slices, default 0f
+     */
+    private float mSliceSpace = 0f;
+    private boolean mAutomaticallyDisableSliceSpacing;
+
+    /**
+     * indicates the selection distance of a pie slice
+     */
+    private float mShift = 18f;
+
+    private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE;
+    private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE;
+    private int mValueLineColor = 0xff000000;
+    private boolean mUseValueColorForLine = false;
+    private float mValueLineWidth = 1.0f;
+    private float mValueLinePart1OffsetPercentage = 75.f;
+    private float mValueLinePart1Length = 0.3f;
+    private float mValueLinePart2Length = 0.4f;
+    private boolean mValueLineVariableLength = true;
+    private Integer mHighlightColor = null;
+
+    public PieDataSet(List<PieEntry> yVals, String label) {
+        super(yVals, label);
+//        mShift = Utils.convertDpToPixel(12f);
+    }
+
+    @Override
+    public DataSet<PieEntry> copy() {
+        List<PieEntry> entries = new ArrayList<>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        PieDataSet copied = new PieDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(PieDataSet pieDataSet) {
+        super.copy(pieDataSet);
+    }
+
+    @Override
+    protected void calcMinMax(PieEntry e) {
+
+        if (e == null)
+            return;
+
+        calcMinMaxY(e);
+    }
+
+    /**
+     * Sets the space that is left out between the piechart-slices in dp.
+     * Default: 0 --> no space, maximum 20f
+     *
+     * @param spaceDp
+     */
+    public void setSliceSpace(float spaceDp) {
+
+        if (spaceDp > 20)
+            spaceDp = 20f;
+        if (spaceDp < 0)
+            spaceDp = 0f;
+
+        mSliceSpace = Utils.convertDpToPixel(spaceDp);
+    }
+
+    @Override
+    public float getSliceSpace() {
+        return mSliceSpace;
+    }
+
+    /**
+     * When enabled, slice spacing will be 0.0 when the smallest value is going to be
+     * smaller than the slice spacing itself.
+     *
+     * @param autoDisable
+     */
+    public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) {
+        mAutomaticallyDisableSliceSpacing = autoDisable;
+    }
+
+    /**
+     * When enabled, slice spacing will be 0.0 when the smallest value is going to be
+     * smaller than the slice spacing itself.
+     *
+     * @return
+     */
+    @Override
+    public boolean isAutomaticallyDisableSliceSpacingEnabled() {
+        return mAutomaticallyDisableSliceSpacing;
+    }
+
+    /**
+     * sets the distance the highlighted piechart-slice of this DataSet is
+     * "shifted" away from the center of the chart, default 12f
+     *
+     * @param shift
+     */
+    public void setSelectionShift(float shift) {
+        mShift = Utils.convertDpToPixel(shift);
+    }
+
+    @Override
+    public float getSelectionShift() {
+        return mShift;
+    }
+
+    @Override
+    public ValuePosition getXValuePosition() {
+        return mXValuePosition;
+    }
+
+    public void setXValuePosition(ValuePosition xValuePosition) {
+        this.mXValuePosition = xValuePosition;
+    }
+
+    @Override
+    public ValuePosition getYValuePosition() {
+        return mYValuePosition;
+    }
+
+    public void setYValuePosition(ValuePosition yValuePosition) {
+        this.mYValuePosition = yValuePosition;
+    }
+
+    /**
+     * This method is deprecated.
+     * Use isUseValueColorForLineEnabled() instead.
+     */
+    @Deprecated
+    public boolean isUsingSliceColorAsValueLineColor() {
+        return isUseValueColorForLineEnabled();
+    }
+
+    /**
+     * This method is deprecated.
+     * Use setUseValueColorForLine(...) instead.
+     *
+     * @param enabled
+     */
+    @Deprecated
+    public void setUsingSliceColorAsValueLineColor(boolean enabled) {
+        setUseValueColorForLine(enabled);
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, indicates line color
+     */
+    @Override
+    public int getValueLineColor() {
+        return mValueLineColor;
+    }
+
+    public void setValueLineColor(int valueLineColor) {
+        this.mValueLineColor = valueLineColor;
+    }
+
+    @Override
+    public boolean isUseValueColorForLineEnabled()
+    {
+        return mUseValueColorForLine;
+    }
+
+    public void setUseValueColorForLine(boolean enabled)
+    {
+        mUseValueColorForLine = enabled;
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, indicates line width
+     */
+    @Override
+    public float getValueLineWidth() {
+        return mValueLineWidth;
+    }
+
+    public void setValueLineWidth(float valueLineWidth) {
+        this.mValueLineWidth = valueLineWidth;
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size
+     */
+    @Override
+    public float getValueLinePart1OffsetPercentage() {
+        return mValueLinePart1OffsetPercentage;
+    }
+
+    public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) {
+        this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage;
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, indicates length of first half of the line
+     */
+    @Override
+    public float getValueLinePart1Length() {
+        return mValueLinePart1Length;
+    }
+
+    public void setValueLinePart1Length(float valueLinePart1Length) {
+        this.mValueLinePart1Length = valueLinePart1Length;
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, indicates length of second half of the line
+     */
+    @Override
+    public float getValueLinePart2Length() {
+        return mValueLinePart2Length;
+    }
+
+    public void setValueLinePart2Length(float valueLinePart2Length) {
+        this.mValueLinePart2Length = valueLinePart2Length;
+    }
+
+    /**
+     * When valuePosition is OutsideSlice, this allows variable line length
+     */
+    @Override
+    public boolean isValueLineVariableLength() {
+        return mValueLineVariableLength;
+    }
+
+    public void setValueLineVariableLength(boolean valueLineVariableLength) {
+        this.mValueLineVariableLength = valueLineVariableLength;
+    }
+
+    /** Gets the color for the highlighted sector */
+    @Override
+    @Nullable
+    public Integer getHighlightColor()
+    {
+        return mHighlightColor;
+    }
+
+    /** Sets the color for the highlighted sector (null for using entry color) */
+    public void setHighlightColor(@Nullable Integer color)
+    {
+        this.mHighlightColor = color;
+    }
+
+
+    public enum ValuePosition {
+        INSIDE_SLICE,
+        OUTSIDE_SLICE
+    }
+}

+ 86 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java

@@ -0,0 +1,86 @@
+package com.github.mikephil.charting.data;
+
+import android.annotation.SuppressLint;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+/**
+ * @author Philipp Jahoda
+ */
+@SuppressLint("ParcelCreator")
+public class PieEntry extends Entry {
+
+    private String label;
+
+    public PieEntry(float value) {
+        super(0f, value);
+    }
+
+    public PieEntry(float value, Object data) {
+        super(0f, value, data);
+    }
+
+    public PieEntry(float value, Drawable icon) {
+        super(0f, value, icon);
+    }
+
+    public PieEntry(float value, Drawable icon, Object data) {
+        super(0f, value, icon, data);
+    }
+
+    public PieEntry(float value, String label) {
+        super(0f, value);
+        this.label = label;
+    }
+
+    public PieEntry(float value, String label, Object data) {
+        super(0f, value, data);
+        this.label = label;
+    }
+
+    public PieEntry(float value, String label, Drawable icon) {
+        super(0f, value, icon);
+        this.label = label;
+    }
+
+    public PieEntry(float value, String label, Drawable icon, Object data) {
+        super(0f, value, icon, data);
+        this.label = label;
+    }
+
+    /**
+     * This is the same as getY(). Returns the value of the PieEntry.
+     *
+     * @return
+     */
+    public float getValue() {
+        return getY();
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    @Deprecated
+    @Override
+    public void setX(float x) {
+        super.setX(x);
+        Log.i("DEPRECATED", "Pie entries do not have x values");
+    }
+
+    @Deprecated
+    @Override
+    public float getX() {
+        Log.i("DEPRECATED", "Pie entries do not have x values");
+        return super.getX();
+    }
+
+    public PieEntry copy() {
+        PieEntry e = new PieEntry(getY(), label, getData());
+        return e;
+    }
+}

+ 58 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java

@@ -0,0 +1,58 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.highlight.Highlight;
+import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Data container for the RadarChart.
+ *
+ * @author Philipp Jahoda
+ */
+public class RadarData extends ChartData<IRadarDataSet> {
+
+    private List<String> mLabels;
+
+    public RadarData() {
+        super();
+    }
+
+    public RadarData(List<IRadarDataSet> dataSets) {
+        super(dataSets);
+    }
+
+    public RadarData(IRadarDataSet... dataSets) {
+        super(dataSets);
+    }
+
+    /**
+     * Sets the labels that should be drawn around the RadarChart at the end of each web line.
+     *
+     * @param labels
+     */
+    public void setLabels(List<String> labels) {
+        this.mLabels = labels;
+    }
+
+    /**
+     * Sets the labels that should be drawn around the RadarChart at the end of each web line.
+     *
+     * @param labels
+     */
+    public void setLabels(String... labels) {
+        this.mLabels = Arrays.asList(labels);
+    }
+
+    public List<String> getLabels() {
+        return mLabels;
+    }
+
+    @Override
+    public Entry getEntryForHighlight(Highlight highlight) {
+        return getDataSetByIndex(highlight.getDataSetIndex()).getEntryForIndex((int) highlight.getX());
+    }
+}

+ 122 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java

@@ -0,0 +1,122 @@
+
+package com.github.mikephil.charting.data;
+
+import android.graphics.Color;
+
+import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet;
+import com.github.mikephil.charting.utils.ColorTemplate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RadarDataSet extends LineRadarDataSet<RadarEntry> implements IRadarDataSet {
+
+    /// flag indicating whether highlight circle should be drawn or not
+    protected boolean mDrawHighlightCircleEnabled = false;
+
+    protected int mHighlightCircleFillColor = Color.WHITE;
+
+    /// The stroke color for highlight circle.
+    /// If Utils.COLOR_NONE, the color of the dataset is taken.
+    protected int mHighlightCircleStrokeColor = ColorTemplate.COLOR_NONE;
+
+    protected int mHighlightCircleStrokeAlpha = (int) (0.3 * 255);
+    protected float mHighlightCircleInnerRadius = 3.0f;
+    protected float mHighlightCircleOuterRadius = 4.0f;
+    protected float mHighlightCircleStrokeWidth = 2.0f;
+
+    public RadarDataSet(List<RadarEntry> yVals, String label) {
+        super(yVals, label);
+    }
+
+    /// Returns true if highlight circle should be drawn, false if not
+    @Override
+    public boolean isDrawHighlightCircleEnabled() {
+        return mDrawHighlightCircleEnabled;
+    }
+
+    /// Sets whether highlight circle should be drawn or not
+    @Override
+    public void setDrawHighlightCircleEnabled(boolean enabled) {
+        mDrawHighlightCircleEnabled = enabled;
+    }
+
+    @Override
+    public int getHighlightCircleFillColor() {
+        return mHighlightCircleFillColor;
+    }
+
+    public void setHighlightCircleFillColor(int color) {
+        mHighlightCircleFillColor = color;
+    }
+
+    /// Returns the stroke color for highlight circle.
+    /// If Utils.COLOR_NONE, the color of the dataset is taken.
+    @Override
+    public int getHighlightCircleStrokeColor() {
+        return mHighlightCircleStrokeColor;
+    }
+
+    /// Sets the stroke color for highlight circle.
+    /// Set to Utils.COLOR_NONE in order to use the color of the dataset;
+    public void setHighlightCircleStrokeColor(int color) {
+        mHighlightCircleStrokeColor = color;
+    }
+
+    @Override
+    public int getHighlightCircleStrokeAlpha() {
+        return mHighlightCircleStrokeAlpha;
+    }
+
+    public void setHighlightCircleStrokeAlpha(int alpha) {
+        mHighlightCircleStrokeAlpha = alpha;
+    }
+
+    @Override
+    public float getHighlightCircleInnerRadius() {
+        return mHighlightCircleInnerRadius;
+    }
+
+    public void setHighlightCircleInnerRadius(float radius) {
+        mHighlightCircleInnerRadius = radius;
+    }
+
+    @Override
+    public float getHighlightCircleOuterRadius() {
+        return mHighlightCircleOuterRadius;
+    }
+
+    public void setHighlightCircleOuterRadius(float radius) {
+        mHighlightCircleOuterRadius = radius;
+    }
+
+    @Override
+    public float getHighlightCircleStrokeWidth() {
+        return mHighlightCircleStrokeWidth;
+    }
+
+    public void setHighlightCircleStrokeWidth(float strokeWidth) {
+        mHighlightCircleStrokeWidth = strokeWidth;
+    }
+
+    @Override
+    public DataSet<RadarEntry> copy() {
+        List<RadarEntry> entries = new ArrayList<RadarEntry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        RadarDataSet copied = new RadarDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(RadarDataSet radarDataSet) {
+        super.copy(radarDataSet);
+        radarDataSet.mDrawHighlightCircleEnabled = mDrawHighlightCircleEnabled;
+        radarDataSet.mHighlightCircleFillColor = mHighlightCircleFillColor;
+        radarDataSet.mHighlightCircleInnerRadius = mHighlightCircleInnerRadius;
+        radarDataSet.mHighlightCircleStrokeAlpha = mHighlightCircleStrokeAlpha;
+        radarDataSet.mHighlightCircleStrokeColor = mHighlightCircleStrokeColor;
+        radarDataSet.mHighlightCircleStrokeWidth = mHighlightCircleStrokeWidth;
+    }
+}

+ 44 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java

@@ -0,0 +1,44 @@
+package com.github.mikephil.charting.data;
+
+import android.annotation.SuppressLint;
+
+/**
+ * Created by philipp on 13/06/16.
+ */
+@SuppressLint("ParcelCreator")
+public class RadarEntry extends Entry {
+
+    public RadarEntry(float value) {
+        super(0f, value);
+    }
+
+    public RadarEntry(float value, Object data) {
+        super(0f, value, data);
+    }
+
+    /**
+     * This is the same as getY(). Returns the value of the RadarEntry.
+     *
+     * @return
+     */
+    public float getValue() {
+        return getY();
+    }
+
+    public RadarEntry copy() {
+        RadarEntry e = new RadarEntry(getY(), getData());
+        return e;
+    }
+
+    @Deprecated
+    @Override
+    public void setX(float x) {
+        super.setX(x);
+    }
+
+    @Deprecated
+    @Override
+    public float getX() {
+        return super.getX();
+    }
+}

+ 40 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java

@@ -0,0 +1,40 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet;
+
+import java.util.List;
+
+public class ScatterData extends BarLineScatterCandleBubbleData<IScatterDataSet> {
+
+    public ScatterData() {
+        super();
+    }
+
+    public ScatterData(List<IScatterDataSet> dataSets) {
+        super(dataSets);
+    }
+
+    public ScatterData(IScatterDataSet... dataSets) {
+        super(dataSets);
+    }
+
+    /**
+     * Returns the maximum shape-size across all DataSets.
+     *
+     * @return
+     */
+    public float getGreatestShapeSize() {
+
+        float max = 0f;
+
+        for (IScatterDataSet set : mDataSets) {
+            float size = set.getScatterShapeSize();
+
+            if (size > max)
+                max = size;
+        }
+
+        return max;
+    }
+}

+ 157 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java

@@ -0,0 +1,157 @@
+
+package com.github.mikephil.charting.data;
+
+import com.github.mikephil.charting.charts.ScatterChart;
+import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet;
+import com.github.mikephil.charting.renderer.scatter.ChevronDownShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.IShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer;
+import com.github.mikephil.charting.renderer.scatter.XShapeRenderer;
+import com.github.mikephil.charting.utils.ColorTemplate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ScatterDataSet extends LineScatterCandleRadarDataSet<Entry> implements IScatterDataSet {
+
+    /**
+     * the size the scattershape will have, in density pixels
+     */
+    private float mShapeSize = 15f;
+
+    /**
+     * Renderer responsible for rendering this DataSet, default: square
+     */
+    protected IShapeRenderer mShapeRenderer = new SquareShapeRenderer();
+
+    /**
+     * The radius of the hole in the shape (applies to Square, Circle and Triangle)
+     * - default: 0.0
+     */
+    private float mScatterShapeHoleRadius = 0f;
+
+    /**
+     * Color for the hole in the shape.
+     * Setting to `ColorTemplate.COLOR_NONE` will behave as transparent.
+     * - default: ColorTemplate.COLOR_NONE
+     */
+    private int mScatterShapeHoleColor = ColorTemplate.COLOR_NONE;
+
+    public ScatterDataSet(List<Entry> yVals, String label) {
+        super(yVals, label);
+    }
+
+    @Override
+    public DataSet<Entry> copy() {
+        List<Entry> entries = new ArrayList<Entry>();
+        for (int i = 0; i < mEntries.size(); i++) {
+            entries.add(mEntries.get(i).copy());
+        }
+        ScatterDataSet copied = new ScatterDataSet(entries, getLabel());
+        copy(copied);
+        return copied;
+    }
+
+    protected void copy(ScatterDataSet scatterDataSet) {
+        super.copy(scatterDataSet);
+        scatterDataSet.mShapeSize = mShapeSize;
+        scatterDataSet.mShapeRenderer = mShapeRenderer;
+        scatterDataSet.mScatterShapeHoleRadius = mScatterShapeHoleRadius;
+        scatterDataSet.mScatterShapeHoleColor = mScatterShapeHoleColor;
+    }
+
+    /**
+     * Sets the size in density pixels the drawn scattershape will have. This
+     * only applies for non custom shapes.
+     *
+     * @param size
+     */
+    public void setScatterShapeSize(float size) {
+        mShapeSize = size;
+    }
+
+    @Override
+    public float getScatterShapeSize() {
+        return mShapeSize;
+    }
+
+    /**
+     * Sets the ScatterShape this DataSet should be drawn with. This will search for an available IShapeRenderer and set this
+     * renderer for the DataSet.
+     *
+     * @param shape
+     */
+    public void setScatterShape(ScatterChart.ScatterShape shape) {
+        mShapeRenderer = getRendererForShape(shape);
+    }
+
+    /**
+     * Sets a new IShapeRenderer responsible for drawing this DataSet.
+     * This can also be used to set a custom IShapeRenderer aside from the default ones.
+     *
+     * @param shapeRenderer
+     */
+    public void setShapeRenderer(IShapeRenderer shapeRenderer) {
+        mShapeRenderer = shapeRenderer;
+    }
+
+    @Override
+    public IShapeRenderer getShapeRenderer() {
+        return mShapeRenderer;
+    }
+
+    /**
+     * Sets the radius of the hole in the shape (applies to Square, Circle and Triangle)
+     * Set this to <= 0 to remove holes.
+     *
+     * @param holeRadius
+     */
+    public void setScatterShapeHoleRadius(float holeRadius) {
+        mScatterShapeHoleRadius = holeRadius;
+    }
+
+    @Override
+    public float getScatterShapeHoleRadius() {
+        return mScatterShapeHoleRadius;
+    }
+
+    /**
+     * Sets the color for the hole in the shape.
+     *
+     * @param holeColor
+     */
+    public void setScatterShapeHoleColor(int holeColor) {
+        mScatterShapeHoleColor = holeColor;
+    }
+
+    @Override
+    public int getScatterShapeHoleColor() {
+        return mScatterShapeHoleColor;
+    }
+
+    public static IShapeRenderer getRendererForShape(ScatterChart.ScatterShape shape) {
+
+        switch (shape) {
+            case SQUARE:
+                return new SquareShapeRenderer();
+            case CIRCLE:
+                return new CircleShapeRenderer();
+            case TRIANGLE:
+                return new TriangleShapeRenderer();
+            case CROSS:
+                return new CrossShapeRenderer();
+            case X:
+                return new XShapeRenderer();
+            case CHEVRON_UP:
+                return new ChevronUpShapeRenderer();
+            case CHEVRON_DOWN:
+                return new ChevronDownShapeRenderer();
+        }
+
+        return null;
+    }
+}

+ 102 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java

@@ -0,0 +1,102 @@
+
+package com.github.mikephil.charting.data.filter;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import java.util.Arrays;
+
+/**
+ * Implemented according to Wiki-Pseudocode {@link}
+ * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm
+ *
+ * @author Philipp Baldauf & Phliipp Jahoda
+ */
+public class Approximator {
+
+    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+    public float[] reduceWithDouglasPeucker(float[] points, float tolerance) {
+
+        int greatestIndex = 0;
+        float greatestDistance = 0f;
+
+        Line line = new Line(points[0], points[1], points[points.length - 2], points[points.length - 1]);
+
+        for (int i = 2; i < points.length - 2; i += 2) {
+
+            float distance = line.distance(points[i], points[i + 1]);
+
+            if (distance > greatestDistance) {
+                greatestDistance = distance;
+                greatestIndex = i;
+            }
+        }
+
+        if (greatestDistance > tolerance) {
+
+            float[] reduced1 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, 0, greatestIndex + 2), tolerance);
+            float[] reduced2 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, greatestIndex, points.length),
+                    tolerance);
+
+            float[] result1 = reduced1;
+            float[] result2 = Arrays.copyOfRange(reduced2, 2, reduced2.length);
+
+            return concat(result1, result2);
+        } else {
+            return line.getPoints();
+        }
+    }
+
+    /**
+     * Combine arrays.
+     *
+     * @param arrays
+     * @return
+     */
+    float[] concat(float[]... arrays) {
+        int length = 0;
+        for (float[] array : arrays) {
+            length += array.length;
+        }
+        float[] result = new float[length];
+        int pos = 0;
+        for (float[] array : arrays) {
+            for (float element : array) {
+                result[pos] = element;
+                pos++;
+            }
+        }
+        return result;
+    }
+
+    private class Line {
+
+        private float[] points;
+
+        private float sxey;
+        private float exsy;
+
+        private float dx;
+        private float dy;
+
+        private float length;
+
+        public Line(float x1, float y1, float x2, float y2) {
+            dx = x1 - x2;
+            dy = y1 - y2;
+            sxey = x1 * y2;
+            exsy = x2 * y1;
+            length = (float) Math.sqrt(dx * dx + dy * dy);
+
+            points = new float[]{x1, y1, x2, y2};
+        }
+
+        public float distance(float x, float y) {
+            return Math.abs(dy * x - dx * y + sxey - exsy) / length;
+        }
+
+        public float[] getPoints() {
+            return points;
+        }
+    }
+}

+ 146 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java

@@ -0,0 +1,146 @@
+
+package com.github.mikephil.charting.data.filter;
+
+import java.util.ArrayList;
+
+/**
+ * Implemented according to modified Douglas Peucker {@link}
+ * http://psimpl.sourceforge.net/douglas-peucker.html
+ */
+public class ApproximatorN
+{
+    public float[] reduceWithDouglasPeucker(float[] points, float resultCount) {
+
+        int pointCount = points.length / 2;
+
+        // if a shape has 2 or less points it cannot be reduced
+        if (resultCount <= 2 || resultCount >= pointCount)
+            return points;
+
+        boolean[] keep = new boolean[pointCount];
+
+        // first and last always stay
+        keep[0] = true;
+        keep[pointCount - 1] = true;
+
+        int currentStoredPoints = 2;
+
+        ArrayList<Line> queue = new ArrayList<>();
+        Line line = new Line(0, pointCount - 1, points);
+        queue.add(line);
+
+        do {
+            line = queue.remove(queue.size() - 1);
+
+            // store the key
+            keep[line.index] = true;
+
+            // check point count tolerance
+            currentStoredPoints += 1;
+
+            if (currentStoredPoints == resultCount)
+                break;
+
+            // split the polyline at the key and recurse
+            Line left = new Line(line.start, line.index, points);
+            if (left.index > 0) {
+                int insertionIndex = insertionIndex(left, queue);
+                queue.add(insertionIndex, left);
+            }
+
+            Line right = new Line(line.index, line.end, points);
+            if (right.index > 0) {
+                int insertionIndex = insertionIndex(right, queue);
+                queue.add(insertionIndex, right);
+            }
+        } while (queue.isEmpty());
+
+        float[] reducedEntries = new float[currentStoredPoints * 2];
+
+        for (int i = 0, i2 = 0, r2 = 0; i < currentStoredPoints; i++, r2 += 2) {
+            if (keep[i]) {
+                reducedEntries[i2++] = points[r2];
+                reducedEntries[i2++] = points[r2 + 1];
+            }
+        }
+
+        return reducedEntries;
+    }
+
+    private static float distanceToLine(
+            float ptX, float ptY, float[]
+            fromLinePoint1, float[] fromLinePoint2) {
+        float dx = fromLinePoint2[0] - fromLinePoint1[0];
+        float dy = fromLinePoint2[1] - fromLinePoint1[1];
+
+        float dividend = Math.abs(
+                dy * ptX -
+                        dx * ptY -
+                        fromLinePoint1[0] * fromLinePoint2[1] +
+                        fromLinePoint2[0] * fromLinePoint1[1]);
+        double divisor = Math.sqrt(dx * dx + dy * dy);
+
+        return (float)(dividend / divisor);
+    }
+
+    private static class Line {
+        int start;
+        int end;
+
+        float distance = 0;
+        int index = 0;
+
+        Line(int start, int end, float[] points) {
+            this.start = start;
+            this.end = end;
+
+            float[] startPoint = new float[]{points[start * 2], points[start * 2 + 1]};
+            float[] endPoint = new float[]{points[end * 2], points[end * 2 + 1]};
+
+            if (end <= start + 1) return;
+
+            for (int i = start + 1, i2 = i * 2; i < end; i++, i2 += 2) {
+                float distance = distanceToLine(
+                        points[i2], points[i2 + 1],
+                        startPoint, endPoint);
+
+                if (distance > this.distance) {
+                    this.index = i;
+                    this.distance = distance;
+                }
+            }
+        }
+
+        boolean equals(final Line rhs) {
+            return (start == rhs.start) && (end == rhs.end) && index == rhs.index;
+        }
+
+        boolean lessThan(final Line rhs) {
+            return distance < rhs.distance;
+        }
+    }
+
+    private static int insertionIndex(Line line, ArrayList<Line> queue) {
+        int min = 0;
+        int max = queue.size();
+
+        while (!queue.isEmpty()) {
+            int midIndex = min + (max - min) / 2;
+            Line midLine = queue.get(midIndex);
+
+            if (midLine.equals(line)) {
+                return midIndex;
+            }
+            else if (line.lessThan(midLine)) {
+                // perform search in left half
+                max = midIndex;
+            }
+            else {
+                // perform search in right half
+                min = midIndex + 1;
+            }
+        }
+
+        return min;
+    }
+}

+ 14 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java

@@ -0,0 +1,14 @@
+package com.github.mikephil.charting.exception;
+
+public class DrawingDataSetNotCreatedException extends RuntimeException {
+
+	/**
+     * 
+     */
+    private static final long serialVersionUID = 1L;
+
+    public DrawingDataSetNotCreatedException() {
+		super("Have to create a new drawing set first. Call ChartData's createNewDrawingDataSet() method");
+	}
+
+}

+ 24 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java

@@ -0,0 +1,24 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.data.DataSet;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+
+/**
+ * Interface that can be used to return a customized color instead of setting
+ * colors via the setColor(...) method of the DataSet.
+ *
+ * @author Philipp Jahoda
+ */
+public interface ColorFormatter {
+
+    /**
+     * Returns the color to be used for the given Entry at the given index (in the entries array)
+     *
+     * @param index index in the entries array
+     * @param e     the entry to color
+     * @param set   the DataSet the entry belongs to
+     * @return
+     */
+    int getColor(int index, Entry e, IDataSet set);
+}

+ 54 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java

@@ -0,0 +1,54 @@
+package com.github.mikephil.charting.formatter;
+
+import java.text.DecimalFormat;
+
+/**
+ * Created by philipp on 02/06/16.
+ */
+public class DefaultAxisValueFormatter extends ValueFormatter
+{
+
+    /**
+     * decimalformat for formatting
+     */
+    protected DecimalFormat mFormat;
+
+    /**
+     * the number of decimal digits this formatter uses
+     */
+    protected int digits;
+
+    /**
+     * Constructor that specifies to how many digits the value should be
+     * formatted.
+     *
+     * @param digits
+     */
+    public DefaultAxisValueFormatter(int digits) {
+        this.digits = digits;
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < digits; i++) {
+            if (i == 0)
+                b.append(".");
+            b.append("0");
+        }
+
+        mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
+    }
+
+    @Override
+    public String getFormattedValue(float value) {
+        // avoid memory allocations here (for performance)
+        return mFormat.format(value);
+    }
+
+    /**
+     * Returns the number of decimal digits this formatter uses or -1, if unspecified.
+     *
+     * @return
+     */
+    public int getDecimalDigits() {
+        return digits;
+    }
+}

+ 45 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java

@@ -0,0 +1,45 @@
+package com.github.mikephil.charting.formatter;
+
+
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+
+/**
+ * Default formatter that calculates the position of the filled line.
+ *
+ * @author Philipp Jahoda
+ */
+public class DefaultFillFormatter implements IFillFormatter
+{
+
+    @Override
+    public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
+
+        float fillMin = 0f;
+        float chartMaxY = dataProvider.getYChartMax();
+        float chartMinY = dataProvider.getYChartMin();
+
+        LineData data = dataProvider.getLineData();
+
+        if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) {
+            fillMin = 0f;
+        } else {
+
+            float max, min;
+
+            if (data.getYMax() > 0)
+                max = 0f;
+            else
+                max = chartMaxY;
+            if (data.getYMin() < 0)
+                min = 0f;
+            else
+                min = chartMinY;
+
+            fillMin = dataSet.getYMin() >= 0 ? min : max;
+        }
+
+        return fillMin;
+    }
+}

+ 67 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java

@@ -0,0 +1,67 @@
+package com.github.mikephil.charting.formatter;
+
+import java.text.DecimalFormat;
+
+/**
+ * Default formatter used for formatting values inside the chart. Uses a DecimalFormat with
+ * pre-calculated number of digits (depending on max and min value).
+ *
+ * @author Philipp Jahoda
+ */
+public class DefaultValueFormatter extends ValueFormatter
+{
+
+    /**
+     * DecimalFormat for formatting
+     */
+    protected DecimalFormat mFormat;
+
+    protected int mDecimalDigits;
+
+    /**
+     * Constructor that specifies to how many digits the value should be
+     * formatted.
+     *
+     * @param digits
+     */
+    public DefaultValueFormatter(int digits) {
+        setup(digits);
+    }
+
+    /**
+     * Sets up the formatter with a given number of decimal digits.
+     *
+     * @param digits
+     */
+    public void setup(int digits) {
+
+        this.mDecimalDigits = digits;
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < digits; i++) {
+            if (i == 0)
+                b.append(".");
+            b.append("0");
+        }
+
+        mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
+    }
+
+    @Override
+    public String getFormattedValue(float value) {
+
+        // put more logic here ...
+        // avoid memory allocations here (for performance reasons)
+
+        return mFormat.format(value);
+    }
+
+    /**
+     * Returns the number of decimal digits this formatter uses.
+     *
+     * @return
+     */
+    public int getDecimalDigits() {
+        return mDecimalDigits;
+    }
+}

+ 29 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java

@@ -0,0 +1,29 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.components.AxisBase;
+
+/**
+ * Created by Philipp Jahoda on 20/09/15.
+ * Custom formatter interface that allows formatting of
+ * axis labels before they are being drawn.
+ *
+ * @deprecated Extend {@link ValueFormatter} instead
+ */
+@Deprecated
+public interface IAxisValueFormatter
+{
+
+    /**
+     * Called when a value from an axis is to be formatted
+     * before being drawn. For performance reasons, avoid excessive calculations
+     * and memory allocations inside this method.
+     *
+     * @param value the value to be formatted
+     * @param axis  the axis the value belongs to
+     * @return
+     *
+     * @deprecated Extend {@link ValueFormatter} and use {@link ValueFormatter#getAxisLabel(float, AxisBase)}
+     */
+    @Deprecated
+    String getFormattedValue(float value, AxisBase axis);
+}

+ 24 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java

@@ -0,0 +1,24 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
+
+/**
+ * Interface for providing a custom logic to where the filling line of a LineDataSet
+ * should end. This of course only works if setFillEnabled(...) is set to true.
+ * 
+ * @author Philipp Jahoda
+ */
+public interface IFillFormatter
+{
+
+    /**
+     * Returns the vertical (y-axis) position where the filled-line of the
+     * LineDataSet should end.
+     * 
+     * @param dataSet the ILineDataSet that is currently drawn
+     * @param dataProvider
+     * @return
+     */
+    float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider);
+}

+ 31 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java

@@ -0,0 +1,31 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.utils.ViewPortHandler;
+
+/**
+ * Interface to format all values before they are drawn as labels.
+ *
+ * @author Philipp Jahoda
+ * @deprecated Extend {@link ValueFormatter} instead
+ */
+@Deprecated
+public interface IValueFormatter
+{
+
+    /**
+     * Called when a value (from labels inside the chart) is formatted
+     * before being drawn. For performance reasons, avoid excessive calculations
+     * and memory allocations inside this method.
+     *
+     * @param value           the value to be formatted
+     * @param entry           the entry the value belongs to - in e.g. BarChart, this is of class BarEntry
+     * @param dataSetIndex    the index of the DataSet the entry in focus belongs to
+     * @param viewPortHandler provides information about the current chart state (scale, translation, ...)
+     * @return the formatted label ready for being drawn
+     *
+     * @deprecated Extend {@link ValueFormatter} and override an appropriate method
+     */
+    @Deprecated
+    String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler);
+}

+ 63 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java

@@ -0,0 +1,63 @@
+package com.github.mikephil.charting.formatter;
+
+import java.util.Collection;
+
+/**
+ * This formatter is used for passing an array of x-axis labels, on whole x steps.
+ */
+public class IndexAxisValueFormatter extends ValueFormatter
+{
+    protected String[] mValues = new String[] {};
+    protected int mValueCount = 0;
+
+    /**
+     * An empty constructor.
+     * Use `setValues` to set the axis labels.
+     */
+    public IndexAxisValueFormatter() {
+    }
+
+    /**
+     * Constructor that specifies axis labels.
+     *
+     * @param values The values string array
+     */
+    public IndexAxisValueFormatter(String[] values) {
+        if (values != null)
+            setValues(values);
+    }
+
+    /**
+     * Constructor that specifies axis labels.
+     *
+     * @param values The values string array
+     */
+    public IndexAxisValueFormatter(Collection<String> values) {
+        if (values != null)
+            setValues(values.toArray(new String[values.size()]));
+    }
+
+    @Override
+    public String getFormattedValue(float value) {
+        int index = Math.round(value);
+
+        if (index < 0 || index >= mValueCount || index != (int)value)
+            return "";
+
+        return mValues[index];
+    }
+
+    public String[] getValues()
+    {
+        return mValues;
+    }
+
+    public void setValues(String[] values)
+    {
+        if (values == null)
+            values = new String[] {};
+
+        this.mValues = values;
+        this.mValueCount = values.length;
+    }
+}

+ 91 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java

@@ -0,0 +1,91 @@
+package com.github.mikephil.charting.formatter;
+
+import java.text.DecimalFormat;
+
+/**
+ * Predefined value-formatter that formats large numbers in a pretty way.
+ * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k;
+ * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 =
+ * 10m; 1000000000 = 1b; Special thanks to Roman Gromov
+ * (https://github.com/romangromov) for this piece of code.
+ *
+ * @author Philipp Jahoda
+ * @author Oleksandr Tyshkovets <olexandr.tyshkovets@gmail.com>
+ */
+public class LargeValueFormatter extends ValueFormatter
+{
+
+    private String[] mSuffix = new String[]{
+            "", "k", "m", "b", "t"
+    };
+    private int mMaxLength = 5;
+    private DecimalFormat mFormat;
+    private String mText = "";
+
+    public LargeValueFormatter() {
+        mFormat = new DecimalFormat("###E00");
+    }
+
+    /**
+     * Creates a formatter that appends a specified text to the result string
+     *
+     * @param appendix a text that will be appended
+     */
+    public LargeValueFormatter(String appendix) {
+        this();
+        mText = appendix;
+    }
+
+    @Override
+    public String getFormattedValue(float value) {
+        return makePretty(value) + mText;
+    }
+
+    /**
+     * Set an appendix text to be added at the end of the formatted value.
+     *
+     * @param appendix
+     */
+    public void setAppendix(String appendix) {
+        this.mText = appendix;
+    }
+
+    /**
+     * Set custom suffix to be appended after the values.
+     * Default suffix: ["", "k", "m", "b", "t"]
+     *
+     * @param suffix new suffix
+     */
+    public void setSuffix(String[] suffix) {
+        this.mSuffix = suffix;
+    }
+
+    public void setMaxLength(int maxLength) {
+        this.mMaxLength = maxLength;
+    }
+
+    /**
+     * Formats each number properly. Special thanks to Roman Gromov
+     * (https://github.com/romangromov) for this piece of code.
+     */
+    private String makePretty(double number) {
+
+        String r = mFormat.format(number);
+
+        int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1));
+        int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2));
+        int combined = Integer.valueOf(numericValue2 + "" + numericValue1);
+
+        r = r.replaceAll("E[0-9][0-9]", mSuffix[combined / 3]);
+
+        while (r.length() > mMaxLength || r.matches("[0-9]+\\.[a-z]")) {
+            r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1);
+        }
+
+        return r;
+    }
+
+    public int getDecimalDigits() {
+        return 0;
+    }
+}

+ 54 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java

@@ -0,0 +1,54 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.charts.PieChart;
+import com.github.mikephil.charting.data.PieEntry;
+
+import java.text.DecimalFormat;
+
+/**
+ * This IValueFormatter is just for convenience and simply puts a "%" sign after
+ * each value. (Recommended for PieChart)
+ *
+ * @author Philipp Jahoda
+ */
+public class PercentFormatter extends ValueFormatter
+{
+
+    public DecimalFormat mFormat;
+    private PieChart pieChart;
+    private boolean percentSignSeparated;
+
+    public PercentFormatter() {
+        mFormat = new DecimalFormat("###,###,##0.0");
+        percentSignSeparated = true;
+    }
+
+    // Can be used to remove percent signs if the chart isn't in percent mode
+    public PercentFormatter(PieChart pieChart) {
+        this();
+        this.pieChart = pieChart;
+    }
+
+    // Can be used to remove percent signs if the chart isn't in percent mode
+    public PercentFormatter(PieChart pieChart, boolean percentSignSeparated) {
+        this(pieChart);
+        this.percentSignSeparated = percentSignSeparated;
+    }
+
+    @Override
+    public String getFormattedValue(float value) {
+        return mFormat.format(value) + (percentSignSeparated ? " %" : "%");
+    }
+
+    @Override
+    public String getPieLabel(float value, PieEntry pieEntry) {
+        if (pieChart != null && pieChart.isUsePercentValuesEnabled()) {
+            // Converted to percent
+            return getFormattedValue(value);
+        } else {
+            // raw value, skip percent sign
+            return mFormat.format(value);
+        }
+    }
+
+}

+ 71 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java

@@ -0,0 +1,71 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.data.BarEntry;
+
+import java.text.DecimalFormat;
+
+/**
+ * Created by Philipp Jahoda on 28/01/16.
+ * <p/>
+ * A formatter specifically for stacked BarChart that allows to specify whether the all stack values
+ * or just the top value should be drawn.
+ */
+public class StackedValueFormatter extends ValueFormatter
+{
+
+    /**
+     * if true, all stack values of the stacked bar entry are drawn, else only top
+     */
+    private boolean mDrawWholeStack;
+
+    /**
+     * a string that should be appended behind the value
+     */
+    private String mSuffix;
+
+    private DecimalFormat mFormat;
+
+    /**
+     * Constructor.
+     *
+     * @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top
+     * @param suffix         a string that should be appended behind the value
+     * @param decimals       the number of decimal digits to use
+     */
+    public StackedValueFormatter(boolean drawWholeStack, String suffix, int decimals) {
+        this.mDrawWholeStack = drawWholeStack;
+        this.mSuffix = suffix;
+
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < decimals; i++) {
+            if (i == 0)
+                b.append(".");
+            b.append("0");
+        }
+
+        this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
+    }
+
+    @Override
+    public String getBarStackedLabel(float value, BarEntry entry) {
+        if (!mDrawWholeStack) {
+
+            float[] vals = entry.getYVals();
+
+            if (vals != null) {
+
+                // find out if we are on top of the stack
+                if (vals[vals.length - 1] == value) {
+
+                    // return the "sum" across all stack values
+                    return mFormat.format(entry.getY()) + mSuffix;
+                } else {
+                    return ""; // return empty
+                }
+            }
+        }
+
+        // return the "proposed" value
+        return mFormat.format(value) + mSuffix;
+    }
+}

+ 137 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ValueFormatter.java

@@ -0,0 +1,137 @@
+package com.github.mikephil.charting.formatter;
+
+import com.github.mikephil.charting.components.AxisBase;
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.data.BubbleEntry;
+import com.github.mikephil.charting.data.CandleEntry;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.PieEntry;
+import com.github.mikephil.charting.data.RadarEntry;
+import com.github.mikephil.charting.utils.ViewPortHandler;
+
+/**
+ * Class to format all values before they are drawn as labels.
+ */
+public abstract class ValueFormatter implements IAxisValueFormatter, IValueFormatter{
+
+    /**
+     * <b>DO NOT USE</b>, only for backwards compatibility and will be removed in future versions.
+     *
+     * @param value the value to be formatted
+     * @param axis  the axis the value belongs to
+     * @return formatted string label
+     */
+    @Override
+    @Deprecated
+    public String getFormattedValue(float value, AxisBase axis) {
+        return getFormattedValue(value);
+    }
+
+    /**
+     * <b>DO NOT USE</b>, only for backwards compatibility and will be removed in future versions.
+     * @param value           the value to be formatted
+     * @param entry           the entry the value belongs to - in e.g. BarChart, this is of class BarEntry
+     * @param dataSetIndex    the index of the DataSet the entry in focus belongs to
+     * @param viewPortHandler provides information about the current chart state (scale, translation, ...)
+     * @return formatted string label
+     */
+    @Override
+    @Deprecated
+    public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
+        return getFormattedValue(value);
+    }
+
+    /**
+     * Called when drawing any label, used to change numbers into formatted strings.
+     *
+     * @param value float to be formatted
+     * @return formatted string label
+     */
+    public String getFormattedValue(float value) {
+        return String.valueOf(value);
+    }
+
+    /**
+     * Used to draw axis labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param value float to be formatted
+     * @param axis  axis being labeled
+     * @return formatted string label
+     */
+    public String getAxisLabel(float value, AxisBase axis) {
+        return getFormattedValue(value);
+    }
+
+    /**
+     * Used to draw bar labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param barEntry bar being labeled
+     * @return formatted string label
+     */
+    public String getBarLabel(BarEntry barEntry) {
+        return getFormattedValue(barEntry.getY());
+    }
+
+    /**
+     * Used to draw stacked bar labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param value        current value to be formatted
+     * @param stackedEntry stacked entry being labeled, contains all Y values
+     * @return formatted string label
+     */
+    public String getBarStackedLabel(float value, BarEntry stackedEntry) {
+        return getFormattedValue(value);
+    }
+
+    /**
+     * Used to draw line and scatter labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param entry point being labeled, contains X value
+     * @return formatted string label
+     */
+    public String getPointLabel(Entry entry) {
+        return getFormattedValue(entry.getY());
+    }
+
+    /**
+     * Used to draw pie value labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param value    float to be formatted, may have been converted to percentage
+     * @param pieEntry slice being labeled, contains original, non-percentage Y value
+     * @return formatted string label
+     */
+    public String getPieLabel(float value, PieEntry pieEntry) {
+        return getFormattedValue(value);
+    }
+
+    /**
+     * Used to draw radar value labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param radarEntry entry being labeled
+     * @return formatted string label
+     */
+    public String getRadarLabel(RadarEntry radarEntry) {
+        return getFormattedValue(radarEntry.getY());
+    }
+
+    /**
+     * Used to draw bubble size labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param bubbleEntry bubble being labeled, also contains X and Y values
+     * @return formatted string label
+     */
+    public String getBubbleLabel(BubbleEntry bubbleEntry) {
+        return getFormattedValue(bubbleEntry.getSize());
+    }
+
+    /**
+     * Used to draw high labels, calls {@link #getFormattedValue(float)} by default.
+     *
+     * @param candleEntry candlestick being labeled
+     * @return formatted string label
+     */
+    public String getCandleLabel(CandleEntry candleEntry) {
+        return getFormattedValue(candleEntry.getHigh());
+    }
+
+}

+ 163 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java

@@ -0,0 +1,163 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BarEntry;
+import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
+import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+import com.github.mikephil.charting.utils.MPPointD;
+
+/**
+ * Created by Philipp Jahoda on 22/07/15.
+ */
+public class BarHighlighter extends ChartHighlighter<BarDataProvider> {
+
+    public BarHighlighter(BarDataProvider chart) {
+        super(chart);
+    }
+
+    @Override
+    public Highlight getHighlight(float x, float y) {
+        Highlight high = super.getHighlight(x, y);
+
+        if(high == null) {
+            return null;
+        }
+
+        MPPointD pos = getValsForTouch(x, y);
+
+        BarData barData = mChart.getBarData();
+
+        IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
+        if (set.isStacked()) {
+
+            return getStackedHighlight(high,
+                    set,
+                    (float) pos.x,
+                    (float) pos.y);
+        }
+
+        MPPointD.recycleInstance(pos);
+
+        return high;
+    }
+
+    /**
+     * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been
+     * selected.
+     *
+     * @param high the Highlight to work with looking for stacked values
+     * @param set
+     * @param xVal
+     * @param yVal
+     * @return
+     */
+    public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) {
+
+        BarEntry entry = set.getEntryForXValue(xVal, yVal);
+
+        if (entry == null)
+            return null;
+
+        // not stacked
+        if (entry.getYVals() == null) {
+            return high;
+        } else {
+            Range[] ranges = entry.getRanges();
+
+            if (ranges.length > 0) {
+                int stackIndex = getClosestStackIndex(ranges, yVal);
+
+                MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(high.getX(), ranges[stackIndex].to);
+
+                Highlight stackedHigh = new Highlight(
+                        entry.getX(),
+                        entry.getY(),
+                        (float) pixels.x,
+                        (float) pixels.y,
+                        high.getDataSetIndex(),
+                        stackIndex,
+                        high.getAxis()
+                );
+
+                MPPointD.recycleInstance(pixels);
+
+                return stackedHigh;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the index of the closest value inside the values array / ranges (stacked barchart) to the value
+     * given as
+     * a parameter.
+     *
+     * @param ranges
+     * @param value
+     * @return
+     */
+    protected int getClosestStackIndex(Range[] ranges, float value) {
+
+        if (ranges == null || ranges.length == 0)
+            return 0;
+
+        int stackIndex = 0;
+
+        for (Range range : ranges) {
+            if (range.contains(value))
+                return stackIndex;
+            else
+                stackIndex++;
+        }
+
+        int length = Math.max(ranges.length - 1, 0);
+
+        return (value > ranges[length].to) ? length : 0;
+    }
+
+//    /**
+//     * Splits up the stack-values of the given bar-entry into Range objects.
+//     *
+//     * @param entry
+//     * @return
+//     */
+//    protected Range[] getRanges(BarEntry entry) {
+//
+//        float[] values = entry.getYVals();
+//
+//        if (values == null || values.length == 0)
+//            return new Range[0];
+//
+//        Range[] ranges = new Range[values.length];
+//
+//        float negRemain = -entry.getNegativeSum();
+//        float posRemain = 0f;
+//
+//        for (int i = 0; i < ranges.length; i++) {
+//
+//            float value = values[i];
+//
+//            if (value < 0) {
+//                ranges[i] = new Range(negRemain, negRemain + Math.abs(value));
+//                negRemain += Math.abs(value);
+//            } else {
+//                ranges[i] = new Range(posRemain, posRemain + value);
+//                posRemain += value;
+//            }
+//        }
+//
+//        return ranges;
+//    }
+
+    @Override
+    protected float getDistance(float x1, float y1, float x2, float y2) {
+        return Math.abs(x1 - x2);
+    }
+
+    @Override
+    protected BarLineScatterCandleBubbleData getData() {
+        return mChart.getBarData();
+    }
+}

+ 246 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java

@@ -0,0 +1,246 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
+import com.github.mikephil.charting.data.DataSet;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.utils.MPPointD;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Philipp Jahoda on 21/07/15.
+ */
+public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider> implements IHighlighter
+{
+
+    /**
+     * instance of the data-provider
+     */
+    protected T mChart;
+
+    /**
+     * buffer for storing previously highlighted values
+     */
+    protected List<Highlight> mHighlightBuffer = new ArrayList<Highlight>();
+
+    public ChartHighlighter(T chart) {
+        this.mChart = chart;
+    }
+
+    @Override
+    public Highlight getHighlight(float x, float y) {
+
+        MPPointD pos = getValsForTouch(x, y);
+        float xVal = (float) pos.x;
+        MPPointD.recycleInstance(pos);
+
+        Highlight high = getHighlightForX(xVal, x, y);
+        return high;
+    }
+
+    /**
+     * Returns a recyclable MPPointD instance.
+     * Returns the corresponding xPos for a given touch-position in pixels.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    protected MPPointD getValsForTouch(float x, float y) {
+
+        // take any transformer to determine the x-axis value
+        MPPointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y);
+        return pos;
+    }
+
+    /**
+     * Returns the corresponding Highlight for a given xVal and x- and y-touch position in pixels.
+     *
+     * @param xVal
+     * @param x
+     * @param y
+     * @return
+     */
+    protected Highlight getHighlightForX(float xVal, float x, float y) {
+
+        List<Highlight> closestValues = getHighlightsAtXValue(xVal, x, y);
+
+        if(closestValues.isEmpty()) {
+            return null;
+        }
+
+        float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT);
+        float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT);
+
+        YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT;
+
+        Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance());
+
+        return detail;
+    }
+
+    /**
+     * Returns the minimum distance from a touch value (in pixels) to the
+     * closest value (in pixels) that is displayed in the chart.
+     *
+     * @param closestValues
+     * @param pos
+     * @param axis
+     * @return
+     */
+    protected float getMinimumDistance(List<Highlight> closestValues, float pos, YAxis.AxisDependency axis) {
+
+        float distance = Float.MAX_VALUE;
+
+        for (int i = 0; i < closestValues.size(); i++) {
+
+            Highlight high = closestValues.get(i);
+
+            if (high.getAxis() == axis) {
+
+                float tempDistance = Math.abs(getHighlightPos(high) - pos);
+                if (tempDistance < distance) {
+                    distance = tempDistance;
+                }
+            }
+        }
+
+        return distance;
+    }
+
+    protected float getHighlightPos(Highlight h) {
+        return h.getYPx();
+    }
+
+    /**
+     * Returns a list of Highlight objects representing the entries closest to the given xVal.
+     * The returned list contains two objects per DataSet (closest rounding up, closest rounding down).
+     *
+     * @param xVal the transformed x-value of the x-touch position
+     * @param x    touch position
+     * @param y    touch position
+     * @return
+     */
+    protected List<Highlight> getHighlightsAtXValue(float xVal, float x, float y) {
+
+        mHighlightBuffer.clear();
+
+        BarLineScatterCandleBubbleData data = getData();
+
+        if (data == null)
+            return mHighlightBuffer;
+
+        for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) {
+
+            IDataSet dataSet = data.getDataSetByIndex(i);
+
+            // don't include DataSets that cannot be highlighted
+            if (!dataSet.isHighlightEnabled())
+                continue;
+
+            mHighlightBuffer.addAll(buildHighlights(dataSet, i, xVal, DataSet.Rounding.CLOSEST));
+        }
+
+        return mHighlightBuffer;
+    }
+
+    /**
+     * An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex.
+     *
+     * @param set
+     * @param dataSetIndex
+     * @param xVal
+     * @param rounding
+     * @return
+     */
+    protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
+
+        ArrayList<Highlight> highlights = new ArrayList<>();
+
+        //noinspection unchecked
+        List<Entry> entries = set.getEntriesForXValue(xVal);
+        if (entries.size() == 0) {
+            // Try to find closest x-value and take all entries for that x-value
+            final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
+            if (closest != null)
+            {
+                //noinspection unchecked
+                entries = set.getEntriesForXValue(closest.getX());
+            }
+        }
+
+        if (entries.size() == 0)
+            return highlights;
+
+        for (Entry e : entries) {
+            MPPointD pixels = mChart.getTransformer(
+                    set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
+
+            highlights.add(new Highlight(
+                    e.getX(), e.getY(),
+                    (float) pixels.x, (float) pixels.y,
+                    dataSetIndex, set.getAxisDependency()));
+        }
+
+        return highlights;
+    }
+
+    /**
+     * Returns the Highlight of the DataSet that contains the closest value on the
+     * y-axis.
+     *
+     * @param closestValues        contains two Highlight objects per DataSet closest to the selected x-position (determined by
+     *                             rounding up an down)
+     * @param x
+     * @param y
+     * @param axis                 the closest axis
+     * @param minSelectionDistance
+     * @return
+     */
+    public Highlight getClosestHighlightByPixel(List<Highlight> closestValues, float x, float y,
+                                                YAxis.AxisDependency axis, float minSelectionDistance) {
+
+        Highlight closest = null;
+        float distance = minSelectionDistance;
+
+        for (int i = 0; i < closestValues.size(); i++) {
+
+            Highlight high = closestValues.get(i);
+
+            if (axis == null || high.getAxis() == axis) {
+
+                float cDistance = getDistance(x, y, high.getXPx(), high.getYPx());
+
+                if (cDistance < distance) {
+                    closest = high;
+                    distance = cDistance;
+                }
+            }
+        }
+
+        return closest;
+    }
+
+    /**
+     * Calculates the distance between the two given points.
+     *
+     * @param x1
+     * @param y1
+     * @param x2
+     * @param y2
+     * @return
+     */
+    protected float getDistance(float x1, float y1, float x2, float y2) {
+        //return Math.abs(y1 - y2);
+        //return Math.abs(x1 - x2);
+        return (float) Math.hypot(x1 - x2, y1 - y2);
+    }
+
+    protected BarLineScatterCandleBubbleData getData() {
+        return mChart.getData();
+    }
+}

+ 93 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java

@@ -0,0 +1,93 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
+import com.github.mikephil.charting.data.ChartData;
+import com.github.mikephil.charting.data.DataSet;
+import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
+import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+
+import java.util.List;
+
+/**
+ * Created by Philipp Jahoda on 12/09/15.
+ */
+public class CombinedHighlighter extends ChartHighlighter<CombinedDataProvider> implements IHighlighter
+{
+
+    /**
+     * bar highlighter for supporting stacked highlighting
+     */
+    protected BarHighlighter barHighlighter;
+
+    public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) {
+        super(chart);
+
+        // if there is BarData, create a BarHighlighter
+        barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart);
+    }
+
+    @Override
+    protected List<Highlight> getHighlightsAtXValue(float xVal, float x, float y) {
+
+        mHighlightBuffer.clear();
+
+        List<BarLineScatterCandleBubbleData> dataObjects = mChart.getCombinedData().getAllData();
+
+        for (int i = 0; i < dataObjects.size(); i++) {
+
+            ChartData dataObject = dataObjects.get(i);
+
+            // in case of BarData, let the BarHighlighter take over
+            if (barHighlighter != null && dataObject instanceof BarData) {
+                Highlight high = barHighlighter.getHighlight(x, y);
+
+                if (high != null) {
+                    high.setDataIndex(i);
+                    mHighlightBuffer.add(high);
+                }
+            } else {
+
+                for (int j = 0, dataSetCount = dataObject.getDataSetCount(); j < dataSetCount; j++) {
+
+                    IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j);
+
+                    // don't include datasets that cannot be highlighted
+                    if (!dataSet.isHighlightEnabled())
+                        continue;
+
+                    List<Highlight> highs = buildHighlights(dataSet, j, xVal, DataSet.Rounding.CLOSEST);
+                    for (Highlight high : highs)
+                    {
+                        high.setDataIndex(i);
+                        mHighlightBuffer.add(high);
+                    }
+                }
+            }
+        }
+
+        return mHighlightBuffer;
+    }
+
+//    protected Highlight getClosest(float x, float y, Highlight... highs) {
+//
+//        Highlight closest = null;
+//        float minDistance = Float.MAX_VALUE;
+//
+//        for (Highlight high : highs) {
+//
+//            if (high == null)
+//                continue;
+//
+//            float tempDistance = getDistance(x, y, high.getXPx(), high.getYPx());
+//
+//            if (tempDistance < minDistance) {
+//                minDistance = tempDistance;
+//                closest = high;
+//            }
+//        }
+//
+//        return closest;
+//    }
+}

+ 243 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java

@@ -0,0 +1,243 @@
+
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.components.YAxis;
+
+/**
+ * Contains information needed to determine the highlighted value.
+ *
+ * @author Philipp Jahoda
+ */
+public class Highlight {
+
+    /**
+     * the x-value of the highlighted value
+     */
+    private float mX = Float.NaN;
+
+    /**
+     * the y-value of the highlighted value
+     */
+    private float mY = Float.NaN;
+
+    /**
+     * the x-pixel of the highlight
+     */
+    private float mXPx;
+
+    /**
+     * the y-pixel of the highlight
+     */
+    private float mYPx;
+
+    /**
+     * the index of the data object - in case it refers to more than one
+     */
+    private int mDataIndex = -1;
+
+    /**
+     * the index of the dataset the highlighted value is in
+     */
+    private int mDataSetIndex;
+
+    /**
+     * index which value of a stacked bar entry is highlighted, default -1
+     */
+    private int mStackIndex = -1;
+
+    /**
+     * the axis the highlighted value belongs to
+     */
+    private YAxis.AxisDependency axis;
+
+    /**
+     * the x-position (pixels) on which this highlight object was last drawn
+     */
+    private float mDrawX;
+
+    /**
+     * the y-position (pixels) on which this highlight object was last drawn
+     */
+    private float mDrawY;
+
+    public Highlight(float x, float y, int dataSetIndex, int dataIndex) {
+        this.mX = x;
+        this.mY = y;
+        this.mDataSetIndex = dataSetIndex;
+        this.mDataIndex = dataIndex;
+    }
+
+    public Highlight(float x, float y, int dataSetIndex) {
+        this.mX = x;
+        this.mY = y;
+        this.mDataSetIndex = dataSetIndex;
+        this.mDataIndex = -1;
+    }
+
+    public Highlight(float x, int dataSetIndex, int stackIndex) {
+        this(x, Float.NaN, dataSetIndex);
+        this.mStackIndex = stackIndex;
+    }
+
+    /**
+     * constructor
+     *
+     * @param x            the x-value of the highlighted value
+     * @param y            the y-value of the highlighted value
+     * @param dataSetIndex the index of the DataSet the highlighted value belongs to
+     */
+    public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) {
+        this.mX = x;
+        this.mY = y;
+        this.mXPx = xPx;
+        this.mYPx = yPx;
+        this.mDataSetIndex = dataSetIndex;
+        this.axis = axis;
+    }
+
+    /**
+     * Constructor, only used for stacked-barchart.
+     *
+     * @param x            the index of the highlighted value on the x-axis
+     * @param y            the y-value of the highlighted value
+     * @param dataSetIndex the index of the DataSet the highlighted value belongs to
+     * @param stackIndex   references which value of a stacked-bar entry has been
+     *                     selected
+     */
+    public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, YAxis.AxisDependency axis) {
+        this(x, y, xPx, yPx, dataSetIndex, axis);
+        this.mStackIndex = stackIndex;
+    }
+
+    /**
+     * returns the x-value of the highlighted value
+     *
+     * @return
+     */
+    public float getX() {
+        return mX;
+    }
+
+    /**
+     * returns the y-value of the highlighted value
+     *
+     * @return
+     */
+    public float getY() {
+        return mY;
+    }
+
+    /**
+     * returns the x-position of the highlight in pixels
+     */
+    public float getXPx() {
+        return mXPx;
+    }
+
+    /**
+     * returns the y-position of the highlight in pixels
+     */
+    public float getYPx() {
+        return mYPx;
+    }
+
+    /**
+     * the index of the data object - in case it refers to more than one
+     *
+     * @return
+     */
+    public int getDataIndex() {
+        return mDataIndex;
+    }
+
+    public void setDataIndex(int mDataIndex) {
+        this.mDataIndex = mDataIndex;
+    }
+
+    /**
+     * returns the index of the DataSet the highlighted value is in
+     *
+     * @return
+     */
+    public int getDataSetIndex() {
+        return mDataSetIndex;
+    }
+
+    /**
+     * Only needed if a stacked-barchart entry was highlighted. References the
+     * selected value within the stacked-entry.
+     *
+     * @return
+     */
+    public int getStackIndex() {
+        return mStackIndex;
+    }
+
+    public boolean isStacked() {
+        return mStackIndex >= 0;
+    }
+
+    /**
+     * Returns the axis the highlighted value belongs to.
+     *
+     * @return
+     */
+    public YAxis.AxisDependency getAxis() {
+        return axis;
+    }
+
+    /**
+     * Sets the x- and y-position (pixels) where this highlight was last drawn.
+     *
+     * @param x
+     * @param y
+     */
+    public void setDraw(float x, float y) {
+        this.mDrawX = x;
+        this.mDrawY = y;
+    }
+
+    /**
+     * Returns the x-position in pixels where this highlight object was last drawn.
+     *
+     * @return
+     */
+    public float getDrawX() {
+        return mDrawX;
+    }
+
+    /**
+     * Returns the y-position in pixels where this highlight object was last drawn.
+     *
+     * @return
+     */
+    public float getDrawY() {
+        return mDrawY;
+    }
+
+    /**
+     * Returns true if this highlight object is equal to the other (compares
+     * xIndex and dataSetIndex)
+     *
+     * @param h
+     * @return
+     */
+    public boolean equalTo(Highlight h) {
+
+        if (h == null)
+            return false;
+        else {
+            if (this.mDataSetIndex == h.mDataSetIndex && this.mX == h.mX
+                    && this.mStackIndex == h.mStackIndex && this.mDataIndex == h.mDataIndex)
+                return true;
+            else
+                return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Highlight, x: " + mX + ", y: " + mY + ", dataSetIndex: " + mDataSetIndex
+                + ", stackIndex (only stacked barentry): " + mStackIndex;
+    }
+}

+ 85 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java

@@ -0,0 +1,85 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.data.BarData;
+import com.github.mikephil.charting.data.DataSet;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
+import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.utils.MPPointD;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Philipp Jahoda on 22/07/15.
+ */
+public class HorizontalBarHighlighter extends BarHighlighter {
+
+	public HorizontalBarHighlighter(BarDataProvider chart) {
+		super(chart);
+	}
+
+	@Override
+	public Highlight getHighlight(float x, float y) {
+
+		BarData barData = mChart.getBarData();
+
+		MPPointD pos = getValsForTouch(y, x);
+
+		Highlight high = getHighlightForX((float) pos.y, y, x);
+		if (high == null)
+			return null;
+
+		IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
+		if (set.isStacked()) {
+
+			return getStackedHighlight(high,
+					set,
+					(float) pos.y,
+					(float) pos.x);
+		}
+
+		MPPointD.recycleInstance(pos);
+
+		return high;
+	}
+
+	@Override
+	protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
+
+		ArrayList<Highlight> highlights = new ArrayList<>();
+
+		//noinspection unchecked
+		List<Entry> entries = set.getEntriesForXValue(xVal);
+		if (entries.size() == 0) {
+			// Try to find closest x-value and take all entries for that x-value
+			final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
+			if (closest != null)
+			{
+				//noinspection unchecked
+				entries = set.getEntriesForXValue(closest.getX());
+			}
+		}
+
+		if (entries.size() == 0)
+			return highlights;
+
+		for (Entry e : entries) {
+			MPPointD pixels = mChart.getTransformer(
+					set.getAxisDependency()).getPixelForValues(e.getY(), e.getX());
+
+			highlights.add(new Highlight(
+					e.getX(), e.getY(),
+					(float) pixels.x, (float) pixels.y,
+					dataSetIndex, set.getAxisDependency()));
+		}
+
+		return highlights;
+	}
+
+	@Override
+	protected float getDistance(float x1, float y1, float x2, float y2) {
+		return Math.abs(y1 - y2);
+	}
+}

+ 17 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java

@@ -0,0 +1,17 @@
+package com.github.mikephil.charting.highlight;
+
+/**
+ * Created by philipp on 10/06/16.
+ */
+public interface IHighlighter
+{
+
+    /**
+     * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels.
+     *
+     * @param x
+     * @param y
+     * @return
+     */
+    Highlight getHighlight(float x, float y);
+}

+ 25 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java

@@ -0,0 +1,25 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.charts.PieChart;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
+
+/**
+ * Created by philipp on 12/06/16.
+ */
+public class PieHighlighter extends PieRadarHighlighter<PieChart> {
+
+    public PieHighlighter(PieChart chart) {
+        super(chart);
+    }
+
+    @Override
+    protected Highlight getClosestHighlight(int index, float x, float y) {
+
+        IPieDataSet set = mChart.getData().getDataSet();
+
+        final Entry entry = set.getEntryForIndex(index);
+
+        return new Highlight(index, entry.getY(), x, y, 0, set.getAxisDependency());
+    }
+}

+ 66 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java

@@ -0,0 +1,66 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.charts.PieChart;
+import com.github.mikephil.charting.charts.PieRadarChartBase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by philipp on 12/06/16.
+ */
+public abstract class PieRadarHighlighter<T extends PieRadarChartBase> implements IHighlighter
+{
+
+    protected T mChart;
+
+    /**
+     * buffer for storing previously highlighted values
+     */
+    protected List<Highlight> mHighlightBuffer = new ArrayList<Highlight>();
+
+    public PieRadarHighlighter(T chart) {
+        this.mChart = chart;
+    }
+
+    @Override
+    public Highlight getHighlight(float x, float y) {
+
+        float touchDistanceToCenter = mChart.distanceToCenter(x, y);
+
+        // check if a slice was touched
+        if (touchDistanceToCenter > mChart.getRadius()) {
+
+            // if no slice was touched, highlight nothing
+            return null;
+
+        } else {
+
+            float angle = mChart.getAngleForPoint(x, y);
+
+            if (mChart instanceof PieChart) {
+                angle /= mChart.getAnimator().getPhaseY();
+            }
+
+            int index = mChart.getIndexForAngle(angle);
+
+            // check if the index could be found
+            if (index < 0 || index >= mChart.getData().getMaxEntryCountSet().getEntryCount()) {
+                return null;
+
+            } else {
+                return getClosestHighlight(index, x, y);
+            }
+        }
+    }
+
+    /**
+     * Returns the closest Highlight object of the given objects based on the touch position inside the chart.
+     *
+     * @param index
+     * @param x
+     * @param y
+     * @return
+     */
+    protected abstract Highlight getClosestHighlight(int index, float x, float y);
+}

+ 79 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java

@@ -0,0 +1,79 @@
+package com.github.mikephil.charting.highlight;
+
+import com.github.mikephil.charting.charts.RadarChart;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.interfaces.datasets.IDataSet;
+import com.github.mikephil.charting.utils.MPPointF;
+import com.github.mikephil.charting.utils.Utils;
+
+import java.util.List;
+
+/**
+ * Created by philipp on 12/06/16.
+ */
+public class RadarHighlighter extends PieRadarHighlighter<RadarChart> {
+
+    public RadarHighlighter(RadarChart chart) {
+        super(chart);
+    }
+
+    @Override
+    protected Highlight getClosestHighlight(int index, float x, float y) {
+
+        List<Highlight> highlights = getHighlightsAtIndex(index);
+
+        float distanceToCenter = mChart.distanceToCenter(x, y) / mChart.getFactor();
+
+        Highlight closest = null;
+        float distance = Float.MAX_VALUE;
+
+        for (int i = 0; i < highlights.size(); i++) {
+
+            Highlight high = highlights.get(i);
+
+            float cdistance = Math.abs(high.getY() - distanceToCenter);
+            if (cdistance < distance) {
+                closest = high;
+                distance = cdistance;
+            }
+        }
+
+        return closest;
+    }
+    /**
+     * Returns an array of Highlight objects for the given index. The Highlight
+     * objects give information about the value at the selected index and the
+     * DataSet it belongs to. INFORMATION: This method does calculations at
+     * runtime. Do not over-use in performance critical situations.
+     *
+     * @param index
+     * @return
+     */
+    protected List<Highlight> getHighlightsAtIndex(int index) {
+
+        mHighlightBuffer.clear();
+
+        float phaseX = mChart.getAnimator().getPhaseX();
+        float phaseY = mChart.getAnimator().getPhaseY();
+        float sliceangle = mChart.getSliceAngle();
+        float factor = mChart.getFactor();
+
+        MPPointF pOut = MPPointF.getInstance(0,0);
+        for (int i = 0; i < mChart.getData().getDataSetCount(); i++) {
+
+            IDataSet<?> dataSet = mChart.getData().getDataSetByIndex(i);
+
+            final Entry entry = dataSet.getEntryForIndex(index);
+
+            float y = (entry.getY() - mChart.getYChartMin());
+
+            Utils.getPosition(
+                    mChart.getCenterOffsets(), y * factor * phaseY,
+                    sliceangle * index * phaseX + mChart.getRotationAngle(), pOut);
+
+            mHighlightBuffer.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency()));
+        }
+
+        return mHighlightBuffer;
+    }
+}

+ 38 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java

@@ -0,0 +1,38 @@
+package com.github.mikephil.charting.highlight;
+
+/**
+ * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g.
+ * stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25).
+ */
+public final class Range {
+
+	public float from;
+	public float to;
+
+	public Range(float from, float to) {
+		this.from = from;
+		this.to = to;
+	}
+
+	/**
+	 * Returns true if this range contains (if the value is in between) the given value, false if not.
+	 * 
+	 * @param value
+	 * @return
+	 */
+	public boolean contains(float value) {
+
+		if (value > from && value <= to)
+			return true;
+		else
+			return false;
+	}
+
+	public boolean isLarger(float value) {
+		return value > to;
+	}
+
+	public boolean isSmaller(float value) {
+		return value < from;
+	}
+}

+ 11 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java

@@ -0,0 +1,11 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import com.github.mikephil.charting.data.BarData;
+
+public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider {
+
+    BarData getBarData();
+    boolean isDrawBarShadowEnabled();
+    boolean isDrawValueAboveBarEnabled();
+    boolean isHighlightFullBarEnabled();
+}

+ 16 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java

@@ -0,0 +1,16 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import com.github.mikephil.charting.components.YAxis.AxisDependency;
+import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
+import com.github.mikephil.charting.utils.Transformer;
+
+public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface {
+
+    Transformer getTransformer(AxisDependency axis);
+    boolean isInverted(AxisDependency axis);
+    
+    float getLowestVisibleX();
+    float getHighestVisibleX();
+
+    BarLineScatterCandleBubbleData getData();
+}

+ 8 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java

@@ -0,0 +1,8 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import com.github.mikephil.charting.data.BubbleData;
+
+public interface BubbleDataProvider extends BarLineScatterCandleBubbleDataProvider {
+
+    BubbleData getBubbleData();
+}

+ 8 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java

@@ -0,0 +1,8 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import com.github.mikephil.charting.data.CandleData;
+
+public interface CandleDataProvider extends BarLineScatterCandleBubbleDataProvider {
+
+    CandleData getCandleData();
+}

+ 69 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java

@@ -0,0 +1,69 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import android.graphics.RectF;
+
+import com.github.mikephil.charting.data.ChartData;
+import com.github.mikephil.charting.formatter.ValueFormatter;
+import com.github.mikephil.charting.utils.MPPointF;
+
+/**
+ * Interface that provides everything there is to know about the dimensions,
+ * bounds, and range of the chart.
+ *
+ * @author Philipp Jahoda
+ */
+public interface ChartInterface {
+
+    /**
+     * Returns the minimum x value of the chart, regardless of zoom or translation.
+     *
+     * @return
+     */
+    float getXChartMin();
+
+    /**
+     * Returns the maximum x value of the chart, regardless of zoom or translation.
+     *
+     * @return
+     */
+    float getXChartMax();
+
+    float getXRange();
+
+    /**
+     * Returns the minimum y value of the chart, regardless of zoom or translation.
+     *
+     * @return
+     */
+    float getYChartMin();
+
+    /**
+     * Returns the maximum y value of the chart, regardless of zoom or translation.
+     *
+     * @return
+     */
+    float getYChartMax();
+
+    /**
+     * Returns the maximum distance in scren dp a touch can be away from an entry to cause it to get highlighted.
+     *
+     * @return
+     */
+    float getMaxHighlightDistance();
+
+    int getWidth();
+
+    int getHeight();
+
+    MPPointF getCenterOfView();
+
+    MPPointF getCenterOffsets();
+
+    RectF getContentRect();
+
+    ValueFormatter getDefaultValueFormatter();
+
+    ChartData getData();
+
+    int getMaxVisibleCount();
+}

+ 11 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java

@@ -0,0 +1,11 @@
+package com.github.mikephil.charting.interfaces.dataprovider;
+
+import com.github.mikephil.charting.data.CombinedData;
+
+/**
+ * Created by philipp on 11/06/16.
+ */
+public interface CombinedDataProvider extends LineDataProvider, BarDataProvider, BubbleDataProvider, CandleDataProvider, ScatterDataProvider {
+
+    CombinedData getCombinedData();
+}

+ 0 - 0
MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java


Some files were not shown because too many files changed in this diff