diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..0a213d6792 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: mpandroidchart +open_collective: philippjahoda +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..b75838b907 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,37 @@ + + +**Summary** + + +**Expected Behavior** + + +**Possible Solution** + + + +**Device (please complete the following information):** + - Device: [e.g. Google Pixel] + - Android Version [e.g. 7.0] + - Library Version (e.g. 3.1.0-alpha) + +**Additional Context** + diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md new file mode 100644 index 0000000000..3a5938eee9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -0,0 +1,44 @@ +--- +name: Bugs +about: Create a bug report to help us improve + +--- + + + +**Summary** + + +**Expected Behavior** + + +**Possible Solution** + + + +**Device (please complete the following information):** + - Device: [e.g. Google Pixel] + - Android Version [e.g. 7.0] + - Library Version (e.g. 3.1.0-alpha) + +**Additional Context** + + +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 0000000000..54b30c983b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,34 @@ +--- +name: Feature Request +about: Suggest an idea for this project + +--- + + + +**Is your feature request related to a problem? Please describe.** + + +**Describe the solution you'd like** + + +**Describe alternatives you've considered** + + +**Additional context** + +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. diff --git a/.github/ISSUE_TEMPLATE/Support_help.md b/.github/ISSUE_TEMPLATE/Support_help.md new file mode 100644 index 0000000000..70a086d3d7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_help.md @@ -0,0 +1,27 @@ +--- +name: Support +about: I need help! + +--- + +# *STOP RIGHT THERE!* + +Issues are ***NOT*** for getting help, only for reporting bugs and feature requests. + +Search open and closed issues to see if your question already has an answer. However, **do not create a new issue.** + +Instead, do the following: + +1. Download the [Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) and check out the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample). 90% of the time there is an example that does exactly what you are trying to do. + +1. Look at the [Wiki](https://github.com/PhilJay/MPAndroidChart/wiki) for the official documentation for MPAndroidChart. You can also browse the [javadoc](https://jitpack.io/com/github/philjay/mpandroidchart/v3.1.0-alpha/javadoc/) for a more detailed tutorial of the API. + +1. Go to [StackOverflow](https://stackoverflow.com/questions/tagged/mpandroidchart) and ask your questions there. The community will be much more helpful and willing to offer guidance. + + +### You have been warned! + +From now on, any issues asking for help will get closed with a link to this file. + +### Additional Context +ADD A REWARD using [Speed](speedoss.com) to SOLVE this issue QUICKLY and SUPPORT this project. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..f65a30b178 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,15 @@ +## PR Checklist: +- [ ] I have tested this extensively and it does not break any existing behavior. +- [ ] I have added/updated examples and tests for any new behavior. +- [ ] If this is a significant change, an issue has already been created where the problem / solution was discussed: [N/A, or add link to issue here] + + + +## PR Description + + + + + diff --git a/.gitignore b/.gitignore index 2152318d78..feed37b27d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,12 @@ # generated files bin/ gen/ +generated/ +docs/ +finalOutput/ +projectFilesBackup/ + +build.xml # Local configuration file (sdk path, etc) local.properties @@ -37,3 +43,5 @@ build/ # maven target/ + +.DS_Store diff --git a/.idea/runConfigurations/MPChartExample.xml b/.idea/runConfigurations/MPChartExample.xml new file mode 100644 index 0000000000..e6bcf50331 --- /dev/null +++ b/.idea/runConfigurations/MPChartExample.xml @@ -0,0 +1,52 @@ + + + + + \ No newline at end of file diff --git a/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index 9383f623c6..0000000000 --- a/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -build.family.org.gradle.tooling.model.eclipse.HierarchicalEclipseProject=;MPChartExample;MPChartLib; -org.springsource.ide.eclipse.gradle.rootprojectloc= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..98cf0ab89b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,66 @@ +> ### Notice +> *Before you continue, this is the* **ANDROID** *library. If you have an* **iOS** *device, please go here instead*: +> +> – https://github.com/danielgindi/Charts +> +> They might tell you to come back here, if they do, listen to them and ignore this notice. + +# How to contribute + +Bug-fixes and features often come from users of the MPAndroidChart library and improve it greatly. We want to keep it as easy as possible to contribute changes that improve the experience for users all around the world. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +## Creating Issues + +There are two main issue templates, one for bugs and another for feature requests. Please use them! You're issue will be much easier to understand, and bugs easier to fix, if you follow the templates. If your issue doesn't fit into those, just use the generic template. + +Search existing [issues] to see if your bug has already been reported or if a feature request already exists. Don't forget to remove `is:open` so you see all the issues! If you find that one already exists, use reactions to show how much you care! + +## Making Pull Requests + +Careful! If you fail to follow these guidlines, you're pull request may be closed, *even if it's really awesome*. + + 0. **Search** open [pull requests] AND existing [issues] to make sure what you want to do isn't already being worked on or already has an open pull request. + 1. **Fork** the repository + 1. **Create** a new branch based on `master`, and name it according to your changes + 1. **Add** your commits, they MUST follow the [Commit Style](#commit-style) below + 1. **Test** your changes by actually running the example app, or create a new example + 1. **Create** a pull request, following the auto-generated template + 1. ??? + 1. Profit :money_with_wings: + +You are encouraged to use [GitHub Desktop] to inspect your code changes before committing them. It can reveal small changes that might have gone unnoticed, and would be requested for removal before merging. + +Check out [#3975](https://github.com/PhilJay/MPAndroidChart/pull/3975) for an example of a good-made-better pull request. + +## Commit Style + + * **Make commits of logical units** + Don't load your commits with tons of changes, this makes it hard to follow what is happening. However, if you have done a lot of work, and there are commits and merges all over the place, squash them down into fewer commits. + + * **Conform to the code style** + It's easy, just look around! + + * **Write good commit messages** + You may prefer [Tim Pope's style], you might like the [commitizen-friendly] way. Regardless of the color you pick, you MUST stay within the lines! + ``` +The commit title CANNOT exceed 50 characters + +The body of the message comes after an empty new line, and describes the +changes more thoroughly. If the change is obvious and self-explanatory +from the title, you can omit the body. You should describe all changes +if many were made, or maybe some trickery that only code wizards can +understand. + +Be polite and wrap your lines to 72 characters, but if you prefer going +to 100 characters then I guess we can't stop you. +``` + +## Final Notes + +Thanks for reading the contributing file! Have some cake! :cake: + +[issues]: https://github.com/PhilJay/MPAndroidChart/issues +[pull requests]: https://github.com/PhilJay/MPAndroidChart/pulls +[GitHub Desktop]: https://desktop.github.com/ +[Tim Pope's style]: https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html +[commitizen-friendly]: https://github.com/commitizen/cz-cli diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..c1551a9dce --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + Copyright 2020 Philipp Jahoda + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this software 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. diff --git a/MPChartExample/.gitignore b/MPChartExample/.gitignore index 796b96d1c4..67e07b8fea 100644 --- a/MPChartExample/.gitignore +++ b/MPChartExample/.gitignore @@ -1 +1,2 @@ /build +/release diff --git a/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index be975eb2db..0000000000 --- a/MPChartExample/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartExample/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml deleted file mode 100644 index 6895b904fd..0000000000 --- a/MPChartExample/AndroidManifest.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index 300386b1f4..2d607e9991 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,59 +1,26 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion '23.0.2' + compileSdkVersion 28 defaultConfig { - minSdkVersion 8 - targetSdkVersion 23 - versionCode 49 - versionName '2.2.3' - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } + applicationId "com.xxmassdeveloper.mpchartexample" + minSdkVersion 16 + targetSdkVersion 28 + versionCode 57 + versionName '3.1.0' + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { - release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - - lintOptions { - abortOnError false - } -} - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -repositories { - maven { url "https://jitpack.io" } - maven { // this is for realm-db - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' - } } dependencies { - //compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':MPChartLib') // remove this if you only imported the example project - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'io.realm:realm-android:0.87.5' // dependency for realm-database API (http://realm.io) - //compile 'com.github.PhilJay:MPAndroidChart:v2.2.0' + implementation "androidx.appcompat:appcompat:1.0.2" + implementation 'com.google.android.material:material:1.0.0' + implementation project(':MPChartLib') } diff --git a/MPChartExample/libs/android-support-v4.jar b/MPChartExample/libs/android-support-v4.jar deleted file mode 100644 index c31cede47e..0000000000 Binary files a/MPChartExample/libs/android-support-v4.jar and /dev/null differ diff --git a/MPChartExample/proguard-project.txt b/MPChartExample/proguard-project.txt deleted file mode 100644 index f2fe1559a2..0000000000 --- a/MPChartExample/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/MPChartExample/proguard-rules.pro b/MPChartExample/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/MPChartExample/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/MPChartExample/project.properties b/MPChartExample/project.properties deleted file mode 100644 index b32d807be6..0000000000 --- a/MPChartExample/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 -android.library.reference.1=../MPChartLib diff --git a/MPChartExample/res/drawable-nodpi/marker.png b/MPChartExample/res/drawable-nodpi/marker.png deleted file mode 100644 index 616cdd7b31..0000000000 Binary files a/MPChartExample/res/drawable-nodpi/marker.png and /dev/null differ diff --git a/MPChartExample/res/drawable/new_background.xml b/MPChartExample/res/drawable/new_background.xml deleted file mode 100644 index c2d3b9cfc5..0000000000 --- a/MPChartExample/res/drawable/new_background.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml b/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml deleted file mode 100644 index 6d7a7763df..0000000000 --- a/MPChartExample/res/layout/activity_bubblechart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_candlechart_noseekbar.xml b/MPChartExample/res/layout/activity_candlechart_noseekbar.xml deleted file mode 100644 index 8e5d4bc26e..0000000000 --- a/MPChartExample/res/layout/activity_candlechart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml b/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml deleted file mode 100644 index a48307987e..0000000000 --- a/MPChartExample/res/layout/activity_horizontalbarchart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml b/MPChartExample/res/layout/activity_radarchart_noseekbar.xml deleted file mode 100644 index 1de38d5660..0000000000 --- a/MPChartExample/res/layout/activity_radarchart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_realm_wiki.xml b/MPChartExample/res/layout/activity_realm_wiki.xml deleted file mode 100644 index d4e27933cf..0000000000 --- a/MPChartExample/res/layout/activity_realm_wiki.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml b/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml deleted file mode 100644 index 548a0c7a66..0000000000 --- a/MPChartExample/res/layout/activity_scatterchart_noseekbar.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/bubble.xml b/MPChartExample/res/menu/bubble.xml deleted file mode 100644 index a25afd9861..0000000000 --- a/MPChartExample/res/menu/bubble.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/candle.xml b/MPChartExample/res/menu/candle.xml deleted file mode 100644 index cdf1c4e4dd..0000000000 --- a/MPChartExample/res/menu/candle.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/combined.xml b/MPChartExample/res/menu/combined.xml deleted file mode 100644 index 9b034d7f4f..0000000000 --- a/MPChartExample/res/menu/combined.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/dynamical.xml b/MPChartExample/res/menu/dynamical.xml deleted file mode 100644 index c43a3a0ae6..0000000000 --- a/MPChartExample/res/menu/dynamical.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/line.xml b/MPChartExample/res/menu/line.xml deleted file mode 100644 index 42ca211c06..0000000000 --- a/MPChartExample/res/menu/line.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/pie.xml b/MPChartExample/res/menu/pie.xml deleted file mode 100644 index fd315839a5..0000000000 --- a/MPChartExample/res/menu/pie.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/radar.xml b/MPChartExample/res/menu/radar.xml deleted file mode 100644 index 8dbcf05e9c..0000000000 --- a/MPChartExample/res/menu/radar.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/realtime.xml b/MPChartExample/res/menu/realtime.xml deleted file mode 100644 index a4b2d22a78..0000000000 --- a/MPChartExample/res/menu/realtime.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/values-sw600dp/dimens.xml b/MPChartExample/res/values-sw600dp/dimens.xml deleted file mode 100644 index 44f01db75f..0000000000 --- a/MPChartExample/res/values-sw600dp/dimens.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/MPChartExample/res/values-sw720dp-land/dimens.xml b/MPChartExample/res/values-sw720dp-land/dimens.xml deleted file mode 100644 index 61e3fa8fbc..0000000000 --- a/MPChartExample/res/values-sw720dp-land/dimens.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - 128dp - - diff --git a/MPChartExample/res/values-v11/styles.xml b/MPChartExample/res/values-v11/styles.xml deleted file mode 100644 index 3c02242ad0..0000000000 --- a/MPChartExample/res/values-v11/styles.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/values-v14/styles.xml b/MPChartExample/res/values-v14/styles.xml deleted file mode 100644 index a91fd0372b..0000000000 --- a/MPChartExample/res/values-v14/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/MPChartExample/res/values/dimens.xml b/MPChartExample/res/values/dimens.xml deleted file mode 100644 index 55c1e5908c..0000000000 --- a/MPChartExample/res/values/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - 16dp - 16dp - - diff --git a/MPChartExample/res/values/strings.xml b/MPChartExample/res/values/strings.xml deleted file mode 100644 index 7f59af64bb..0000000000 --- a/MPChartExample/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - MPAndroidChart Example - Settings - Hello world! - - diff --git a/MPChartExample/res/values/styles.xml b/MPChartExample/res/values/styles.xml deleted file mode 100644 index 6ce89c7ba4..0000000000 --- a/MPChartExample/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java deleted file mode 100644 index cabd72e9dd..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ /dev/null @@ -1,206 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class AnotherBarActivity extends DemoBase implements OnSeekBarChangeListener { - - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (BarChart) findViewById(R.id.chart1); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - mChart.setDrawGridBackground(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(0); - xAxis.setDrawGridLines(false); - - mChart.getAxisLeft().setDrawGridLines(false); - - // setting data - mSeekBarX.setProgress(10); - mSeekBarY.setProgress(100); - - // add a nice and smooth animation - mChart.animateY(2500); - - mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val1 = (float) (Math.random() * mult) + mult / 3; - yVals1.add(new BarEntry((int) val1, i)); - } - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add((int) yVals1.get(i).getVal() + ""); - } - - BarDataSet set1 = new BarDataSet(yVals1, "Data Set"); - set1.setColors(ColorTemplate.VORDIPLOM_COLORS); - set1.setDrawValues(false); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java deleted file mode 100644 index 99d3d027fd..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ /dev/null @@ -1,274 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.annotation.SuppressLint; -import android.graphics.PointF; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class BarChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - protected BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface mTf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mChart = (BarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); - xAxis.setDrawGridLines(false); - xAxis.setSpaceBetweenLabels(2); - - YAxisValueFormatter custom = new MyYAxisValueFormatter(); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(8, false); - leftAxis.setValueFormatter(custom); - leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); - leftAxis.setSpaceTop(15f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(8, false); - rightAxis.setValueFormatter(custom); - rightAxis.setSpaceTop(15f); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); - l.setForm(LegendForm.SQUARE); - l.setFormSize(9f); - l.setTextSize(11f); - l.setXEntrySpace(4f); - // l.setExtra(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", - // "def", "ghj", "ikl", "mno" }); - // l.setCustom(ColorTemplate.VORDIPLOM_COLORS, new String[] { "abc", - // "def", "ghj", "ikl", "mno" }); - - setData(12, 50); - - // setting data - mSeekBarY.setProgress(50); - mSeekBarX.setProgress(12); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - // mChart.setDrawLegend(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult); - yVals1.add(new BarEntry(val, i)); - } - - BarDataSet set1 = new BarDataSet(yVals1, "DataSet"); - set1.setBarSpacePercent(35f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); - - mChart.setData(data); - } - - @SuppressLint("NewApi") - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - if (e == null) - return; - - RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, AxisDependency.LEFT); - - Log.i("bounds", bounds.toString()); - Log.i("position", position.toString()); - - Log.i("x-index", - "low: " + mChart.getLowestVisibleXIndex() + ", high: " - + mChart.getHighestVisibleXIndex()); - } - - public void onNothingSelected() { - }; -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java deleted file mode 100644 index 4f869ecc3a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ /dev/null @@ -1,254 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.LargeValueFormatter; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class BarChartActivityMultiDataset extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (BarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDescription(""); - -// mChart.setDrawBorders(true); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - - mChart.setDrawGridBackground(false); - - // create a custom MarkerView (extend MarkerView) and specify the layout - // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // define an offset to change the original position of the marker - // (optional) - // mv.setOffsets(-mv.getMeasuredWidth() / 2, -mv.getMeasuredHeight()); - - // set the marker to the chart - mChart.setMarkerView(mv); - - mSeekBarX.setProgress(10); - mSeekBarY.setProgress(100); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART_INSIDE); - l.setTypeface(tf); - l.setYOffset(0f); - l.setYEntrySpace(0f); - l.setTextSize(8f); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - leftAxis.setValueFormatter(new LargeValueFormatter()); - leftAxis.setDrawGridLines(false); - leftAxis.setSpaceTop(30f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - mChart.getAxisRight().setEnabled(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IBarDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() * 3)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i+1990) + ""); - } - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - - float mult = mSeekBarY.getProgress() * 1000f; - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals1.add(new BarEntry(val, i)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals2.add(new BarEntry(val, i)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mult) + 3; - yVals3.add(new BarEntry(val, i)); - } - - // create 3 datasets with different types - BarDataSet set1 = new BarDataSet(yVals1, "Company A"); - // set1.setColors(ColorTemplate.createColors(getApplicationContext(), - // ColorTemplate.FRESH_COLORS)); - set1.setColor(Color.rgb(104, 241, 175)); - BarDataSet set2 = new BarDataSet(yVals2, "Company B"); - set2.setColor(Color.rgb(164, 228, 251)); - BarDataSet set3 = new BarDataSet(yVals3, "Company C"); - set3.setColor(Color.rgb(242, 247, 158)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - dataSets.add(set2); - dataSets.add(set3); - - BarData data = new BarData(xVals, dataSets); -// data.setValueFormatter(new LargeValueFormatter()); - - // add space between the dataset groups in percent of bar-width - data.setGroupSpace(80f); - data.setValueTypeface(tf); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + dataSetIndex); - } - - @Override - public void onNothingSelected() { - Log.i("Activity", "Nothing selected."); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java deleted file mode 100644 index 62d9b43096..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ /dev/null @@ -1,228 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.FileUtils; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class BarChartActivitySinus extends DemoBase implements OnSeekBarChangeListener { - - protected BarChart mChart; - private SeekBar mSeekBarX; - private TextView tvX; - - private Typeface mTf; - - private List mSinusData; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart_sinus); - - mSinusData = FileUtils.loadBarEntriesFromAssets(getAssets(),"othersine.txt"); - - tvX = (TextView) findViewById(R.id.tvValueCount); - - mSeekBarX = (SeekBar) findViewById(R.id.seekbarValues); - - mChart = (BarChart) findViewById(R.id.chart1); - - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); - - // mChart.setDrawXLabels(false); - - mChart.setDrawGridBackground(false); - // mChart.setDrawYLabels(false); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); - xAxis.setDrawGridLines(false); - xAxis.setEnabled(false); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(6, false); - leftAxis.setAxisMinValue(-2.5f); - leftAxis.setAxisMaxValue(2.5f); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(6, false); - rightAxis.setAxisMinValue(-2.5f); - rightAxis.setAxisMaxValue(2.5f); - - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarX.setProgress(150); // set data - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); - l.setForm(LegendForm.SQUARE); - l.setFormSize(9f); - l.setTextSize(11f); - l.setXEntrySpace(4f); - - mChart.animateXY(2000, 2000); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IBarDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(1500); - break; - } - case R.id.animateY: { - mChart.animateY(1500); - break; - } - case R.id.animateXY: { - - mChart.animateXY(2000, 2000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress())); - - setData(mSeekBarX.getProgress()); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count) { - - ArrayList xVals = new ArrayList(); - - ArrayList entries = new ArrayList(); - - for (int i = 0; i < count; i++) { - xVals.add(i+""); - entries.add(mSinusData.get(i)); - } - - BarDataSet set = new BarDataSet(entries, "Sinus Function"); - set.setBarSpacePercent(40f); - set.setColor(Color.rgb(240, 120, 124)); - - BarData data = new BarData(xVals, set); - data.setValueTextSize(10f); - data.setValueTypeface(mTf); - data.setDrawValues(false); - - mChart.setData(data); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java deleted file mode 100644 index 26f8e6fceb..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java +++ /dev/null @@ -1,154 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.ViewPortHandler; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -public class BarChartPositiveNegative extends DemoBase { - - protected BarChart mChart; - private Typeface mTf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart_noseekbar); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - mChart = (BarChart) findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); - mChart.setExtraTopOffset(-30f); - mChart.setExtraBottomOffset(10f); - mChart.setExtraLeftOffset(70f); - mChart.setExtraRightOffset(70f); - - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); - - mChart.setDescription(""); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); - xAxis.setDrawGridLines(false); - xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(2); - xAxis.setTextColor(Color.LTGRAY); - xAxis.setTextSize(13f); - - YAxis left = mChart.getAxisLeft(); - left.setDrawLabels(false); - left.setStartAtZero(false); - left.setSpaceTop(25f); - left.setSpaceBottom(25f); - left.setDrawAxisLine(false); - left.setDrawGridLines(false); - left.setDrawZeroLine(true); // draw a zero line - left.setZeroLineColor(Color.GRAY); - left.setZeroLineWidth(0.7f); - mChart.getAxisRight().setEnabled(false); - mChart.getLegend().setEnabled(false); - - // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT - List data = new ArrayList<>(); - data.add(new Data(0, -224.1f, "12-29")); - data.add(new Data(1, 238.5f, "12-30")); - data.add(new Data(2, 1280.1f, "12-31")); - data.add(new Data(3, -442.3f, "01-01")); - data.add(new Data(4, -2280.1f, "01-02")); - - setData(data); - } - - private void setData(List dataList) { - - ArrayList values = new ArrayList(); - String[] dates = new String[dataList.size()]; - List colors = new ArrayList(); - - int green = Color.rgb(110, 190, 102); - int red = Color.rgb(211, 74, 88); - - for (int i = 0; i < dataList.size(); i++) { - - Data d = dataList.get(i); - BarEntry entry = new BarEntry(d.yValue, d.xIndex); - values.add(entry); - - dates[i] = dataList.get(i).xAxisValue; - - // specific colors - if (d.yValue >= 0) - colors.add(red); - else - colors.add(green); - } - - BarDataSet set = new BarDataSet(values, "Values"); - set.setBarSpacePercent(40f); - set.setColors(colors); - set.setValueTextColors(colors); - - BarData data = new BarData(dates, set); - data.setValueTextSize(13f); - data.setValueTypeface(mTf); - data.setValueFormatter(new ValueFormatter()); - - mChart.setData(data); - mChart.invalidate(); - } - - /** - * Demo class representing data. - */ - private class Data { - - public String xAxisValue; - public float yValue; - public int xIndex; - - public Data(int xIndex, float yValue, String xAxisValue) { - this.xAxisValue = xAxisValue; - this.yValue = yValue; - this.xIndex = xIndex; - } - } - - private class ValueFormatter implements com.github.mikephil.charting.formatter.ValueFormatter { - - private DecimalFormat mFormat; - - public ValueFormatter() { - mFormat = new DecimalFormat("######.0"); - } - - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(value); - } - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java deleted file mode 100644 index 03d97ed812..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ /dev/null @@ -1,248 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.charts.BubbleChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.BubbleDataSet; -import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class BubbleChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private BubbleChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_bubblechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (BubbleChart) findViewById(R.id.chart1); - mChart.setDescription(""); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setOnChartValueSelectedListener(this); - - mChart.setDrawGridBackground(false); - - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - mChart.setMaxVisibleValueCount(200); - mChart.setPinchZoom(true); - - mSeekBarX.setProgress(5); - mSeekBarY.setProgress(50); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); - - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); - yl.setSpaceTop(30f); - yl.setSpaceBottom(30f); - yl.setDrawZeroLine(false); - - mChart.getAxisRight().setEnabled(false); - - XAxis xl = mChart.getXAxis(); - xl.setPosition(XAxis.XAxisPosition.BOTTOM); - xl.setTypeface(tf); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bubble, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int count = mSeekBarX.getProgress() + 1; - int range = mSeekBarY.getProgress(); - - tvX.setText("" + count); - tvY.setText("" + range); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals1.add(new BubbleEntry(i, val, size)); - } - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals2.add(new BubbleEntry(i, val, size)); - } - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * range); - float size = (float) (Math.random() * range); - - yVals3.add(new BubbleEntry(i, val, size)); - } - - // create a dataset and give it a type - BubbleDataSet set1 = new BubbleDataSet(yVals1, "DS 1"); - set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); - set1.setDrawValues(true); - BubbleDataSet set2 = new BubbleDataSet(yVals2, "DS 2"); - set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); - set2.setDrawValues(true); - BubbleDataSet set3 = new BubbleDataSet(yVals3, "DS 3"); - set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); - set3.setDrawValues(true); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - dataSets.add(set2); - dataSets.add(set3); - - // create a data object with the datasets - BubbleData data = new BubbleData(xVals, dataSets); - data.setValueTypeface(tf); - data.setValueTextSize(8f); - data.setValueTextColor(Color.WHITE); - data.setHighlightCircleWidth(1.5f); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java deleted file mode 100644 index 36211e175c..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ /dev/null @@ -1,221 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.CandleStickChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.CandleDataSet; -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class CandleStickChartActivity extends DemoBase implements OnSeekBarChangeListener { - - private CandleStickChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_candlechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (CandleStickChart) findViewById(R.id.chart1); - mChart.setBackgroundColor(Color.WHITE); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setSpaceBetweenLabels(2); - xAxis.setDrawGridLines(false); - - YAxis leftAxis = mChart.getAxisLeft(); -// leftAxis.setEnabled(false); - leftAxis.setLabelCount(7, false); - leftAxis.setDrawGridLines(false); - leftAxis.setDrawAxisLine(false); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setEnabled(false); -// rightAxis.setStartAtZero(false); - - // setting data - mSeekBarX.setProgress(40); - mSeekBarY.setProgress(100); - - mChart.getLegend().setEnabled(false); - - // Legend l = mChart.getLegend(); - // l.setPosition(LegendPosition.BELOW_CHART_CENTER); - // l.setFormSize(8f); - // l.setFormToTextSpace(4f); - // l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.candle, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleMakeShadowSameColorAsCandle: { - for (ICandleDataSet set : mChart.getData().getDataSets()) { - //TODO: set.setShadowColorSameAsCandle(!set.getShadowColorSameAsCandle()); - } - - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int prog = (mSeekBarX.getProgress() + 1); - - tvX.setText("" + prog); - tvY.setText("" + (mSeekBarY.getProgress())); - - mChart.resetTracking(); - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < prog; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val = (float) (Math.random() * 40) + mult; - - float high = (float) (Math.random() * 9) + 8f; - float low = (float) (Math.random() * 9) + 8f; - - float open = (float) (Math.random() * 6) + 1f; - float close = (float) (Math.random() * 6) + 1f; - - boolean even = i % 2 == 0; - - yVals1.add(new CandleEntry(i, val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close)); - } - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < prog; i++) { - xVals.add("" + (1990 + i)); - } - - CandleDataSet set1 = new CandleDataSet(yVals1, "Data Set"); - set1.setAxisDependency(AxisDependency.LEFT); -// set1.setColor(Color.rgb(80, 80, 80)); - set1.setShadowColor(Color.DKGRAY); - set1.setShadowWidth(0.7f); - set1.setDecreasingColor(Color.RED); - set1.setDecreasingPaintStyle(Paint.Style.FILL); - set1.setIncreasingColor(Color.rgb(122, 242, 84)); - set1.setIncreasingPaintStyle(Paint.Style.STROKE); - set1.setNeutralColor(Color.BLUE); - //set1.setHighlightLineWidth(1f); - - CandleData data = new CandleData(xVals, set1); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java deleted file mode 100644 index 1cc5a5d9ab..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java +++ /dev/null @@ -1,223 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.CombinedChart; -import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.BubbleDataSet; -import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.CandleDataSet; -import com.github.mikephil.charting.data.CandleEntry; -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.LineDataSet; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class CombinedChartActivity extends DemoBase { - - private CombinedChart mChart; - private final int itemcount = 12; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_combined); - - mChart = (CombinedChart) findViewById(R.id.chart1); - mChart.setDescription(""); - mChart.setBackgroundColor(Color.WHITE); - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - - // draw bars behind lines - mChart.setDrawOrder(new DrawOrder[] { - DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER - }); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setDrawGridLines(false); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setDrawGridLines(false); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTH_SIDED); - - CombinedData data = new CombinedData(mMonths); - - data.setData(generateLineData()); - data.setData(generateBarData()); -// data.setData(generateBubbleData()); -// data.setData(generateScatterData()); -// data.setData(generateCandleData()); - - mChart.setData(data); - mChart.invalidate(); - } - - private LineData generateLineData() { - - LineData d = new LineData(); - - ArrayList entries = new ArrayList(); - - for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(15, 10), index)); - - LineDataSet set = new LineDataSet(entries, "Line DataSet"); - set.setColor(Color.rgb(240, 238, 70)); - set.setLineWidth(2.5f); - set.setCircleColor(Color.rgb(240, 238, 70)); - set.setCircleRadius(5f); - set.setFillColor(Color.rgb(240, 238, 70)); - set.setDrawCubic(true); - set.setDrawValues(true); - set.setValueTextSize(10f); - set.setValueTextColor(Color.rgb(240, 238, 70)); - - set.setAxisDependency(YAxis.AxisDependency.LEFT); - - d.addDataSet(set); - - return d; - } - - private BarData generateBarData() { - - BarData d = new BarData(); - - ArrayList entries = new ArrayList(); - - for (int index = 0; index < itemcount; index++) - entries.add(new BarEntry(getRandom(15, 30), index)); - - BarDataSet set = new BarDataSet(entries, "Bar DataSet"); - set.setColor(Color.rgb(60, 220, 78)); - set.setValueTextColor(Color.rgb(60, 220, 78)); - set.setValueTextSize(10f); - d.addDataSet(set); - - set.setAxisDependency(YAxis.AxisDependency.LEFT); - - return d; - } - - protected ScatterData generateScatterData() { - - ScatterData d = new ScatterData(); - - ArrayList entries = new ArrayList(); - - for (int index = 0; index < itemcount; index++) - entries.add(new Entry(getRandom(20, 15), index)); - - ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); - set.setColor(Color.GREEN); - set.setScatterShapeSize(7.5f); - set.setDrawValues(false); - set.setValueTextSize(10f); - d.addDataSet(set); - - return d; - } - - protected CandleData generateCandleData() { - - CandleData d = new CandleData(); - - ArrayList entries = new ArrayList(); - - for (int index = 0; index < itemcount; index++) - entries.add(new CandleEntry(index, 20f, 10f, 13f, 17f)); - - CandleDataSet set = new CandleDataSet(entries, "Candle DataSet"); - set.setColor(Color.rgb(80, 80, 80)); - set.setBarSpace(0.3f); - set.setValueTextSize(10f); - set.setDrawValues(false); - d.addDataSet(set); - - return d; - } - - protected BubbleData generateBubbleData() { - - BubbleData bd = new BubbleData(); - - ArrayList entries = new ArrayList(); - - for (int index = 0; index < itemcount; index++) { - float rnd = getRandom(20, 30); - entries.add(new BubbleEntry(index, rnd, rnd)); - } - - BubbleDataSet set = new BubbleDataSet(entries, "Bubble DataSet"); - set.setColors(ColorTemplate.VORDIPLOM_COLORS); - set.setValueTextSize(10f); - set.setValueTextColor(Color.WHITE); - set.setHighlightCircleWidth(1.5f); - set.setDrawValues(true); - bd.addDataSet(set); - - return bd; - } - - private float getRandom(float range, float startsfrom) { - return (float) (Math.random() * range) + startsfrom; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.combined, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.actionToggleLineValues: { - for (IDataSet set : mChart.getData().getDataSets()) { - if (set instanceof LineDataSet) - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleBarValues: { - for (IDataSet set : mChart.getData().getDataSets()) { - if (set instanceof BarDataSet) - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - } - return true; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java deleted file mode 100644 index 31f951a379..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ /dev/null @@ -1,287 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.formatter.FillFormatter; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class CubicLineChartActivity extends DemoBase implements OnSeekBarChangeListener { - - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setViewPortOffsets(0, 20, 0, 0); - mChart.setBackgroundColor(Color.rgb(104, 241, 175)); - - // no description text - mChart.setDescription(""); - - // enable touch gestures - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis x = mChart.getXAxis(); - x.setEnabled(false); - - YAxis y = mChart.getAxisLeft(); - y.setTypeface(tf); - y.setLabelCount(6, false); - y.setTextColor(Color.WHITE); - y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); - y.setDrawGridLines(false); - y.setAxisLineColor(Color.WHITE); - - mChart.getAxisRight().setEnabled(false); - - // add data - setData(45, 100); - - mChart.getLegend().setEnabled(false); - - mChart.animateXY(2000, 2000); - - // dont forget to refresh the drawing - mChart.invalidate(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.line, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleFilled: { - - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - - // redraw - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((1990 +i) + ""); - } - - ArrayList vals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 20;// + (float) - // ((mult * - // 0.1) / 10); - vals1.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(vals1, "DataSet 1"); - set1.setDrawCubic(true); - set1.setCubicIntensity(0.2f); - //set1.setDrawFilled(true); - set1.setDrawCircles(false); - set1.setLineWidth(1.8f); - set1.setCircleRadius(4f); - set1.setCircleColor(Color.WHITE); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setColor(Color.WHITE); - set1.setFillColor(Color.WHITE); - set1.setFillAlpha(100); - set1.setDrawHorizontalHighlightIndicator(false); - set1.setFillFormatter(new FillFormatter() { - @Override - public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { - return -10; - } - }); - - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - data.setValueTypeface(tf); - data.setValueTextSize(9f); - data.setDrawValues(false); - - // set data - mChart.setData(data); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java deleted file mode 100644 index 0c19b2032d..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java +++ /dev/null @@ -1,221 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class DynamicalAddingActivity extends DemoBase implements OnChartValueSelectedListener { - - private LineChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart_noseekbar); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - mChart.setDescription(""); - - // add an empty data object - mChart.setData(new LineData()); -// mChart.getXAxis().setDrawLabels(false); -// mChart.getXAxis().setDrawGridLines(false); - - mChart.invalidate(); - } - - int[] mColors = ColorTemplate.VORDIPLOM_COLORS; - - private void addEntry() { - - LineData data = mChart.getData(); - - if(data != null) { - - ILineDataSet set = data.getDataSetByIndex(0); - // set.addEntry(...); // can be called as well - - if (set == null) { - set = createSet(); - data.addDataSet(set); - } - - // add a new x-value first - data.addXValue(set.getEntryCount() + ""); - - // choose a random dataSet - int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); - - data.addEntry(new Entry((float) (Math.random() * 10) + 50f, set.getEntryCount()), randomDataSetIndex); - - // let the chart know it's data has changed - mChart.notifyDataSetChanged(); - - mChart.setVisibleXRangeMaximum(6); - mChart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); -// -// // this automatically refreshes the chart (calls invalidate()) - mChart.moveViewTo(data.getXValCount()-7, 50f, AxisDependency.LEFT); - } - } - - private void removeLastEntry() { - - LineData data = mChart.getData(); - - if(data != null) { - - ILineDataSet set = data.getDataSetByIndex(0); - - if (set != null) { - - Entry e = set.getEntryForXIndex(set.getEntryCount() - 1); - - data.removeEntry(e, 0); - // or remove by index - // mData.removeEntry(xIndex, dataSetIndex); - - mChart.notifyDataSetChanged(); - mChart.invalidate(); - } - } - } - - private void addDataSet() { - - LineData data = mChart.getData(); - - if(data != null) { - - int count = (data.getDataSetCount() + 1); - - // create 10 y-vals - ArrayList yVals = new ArrayList(); - - if(data.getXValCount() == 0) { - // add 10 x-entries - for (int i = 0; i < 10; i++) { - data.addXValue("" + (i+1)); - } - } - - for (int i = 0; i < data.getXValCount(); i++) { - yVals.add(new Entry((float) (Math.random() * 50f) + 50f * count, i)); - } - - LineDataSet set = new LineDataSet(yVals, "DataSet " + count); - set.setLineWidth(2.5f); - set.setCircleRadius(4.5f); - - int color = mColors[count % mColors.length]; - - set.setColor(color); - set.setCircleColor(color); - set.setHighLightColor(color); - set.setValueTextSize(10f); - set.setValueTextColor(color); - - data.addDataSet(set); - mChart.notifyDataSetChanged(); - mChart.invalidate(); - } - } - - private void removeDataSet() { - - LineData data = mChart.getData(); - - if(data != null) { - - data.removeDataSet(data.getDataSetByIndex(data.getDataSetCount() - 1)); - - mChart.notifyDataSetChanged(); - mChart.invalidate(); - } - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); - } - - @Override - public void onNothingSelected() { - - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.dynamical, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionAddEntry: - addEntry(); - Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionRemoveEntry: - removeLastEntry(); - Toast.makeText(this, "Entry removed!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionAddDataSet: - addDataSet(); - Toast.makeText(this, "DataSet added!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionRemoveDataSet: - removeDataSet(); - Toast.makeText(this, "DataSet removed!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionAddEmptyLineData: - mChart.setData(new LineData()); - mChart.invalidate(); - Toast.makeText(this, "Empty data added!", Toast.LENGTH_SHORT).show(); - break; - case R.id.actionClear: - mChart.clear(); - Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); - break; - } - - return true; - } - - private LineDataSet createSet() { - - LineDataSet set = new LineDataSet(null, "DataSet 1"); - set.setLineWidth(2.5f); - set.setCircleRadius(4.5f); - set.setColor(Color.rgb(240, 99, 99)); - set.setCircleColor(Color.rgb(240, 99, 99)); - set.setHighLightColor(Color.rgb(190, 190, 190)); - set.setAxisDependency(AxisDependency.LEFT); - set.setValueTextSize(10f); - - return set; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java deleted file mode 100644 index 3027188cf5..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ /dev/null @@ -1,267 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.annotation.SuppressLint; -import android.graphics.PointF; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.highlight.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - protected HorizontalBarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_horizontalbarchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mChart = (HorizontalBarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - // mChart.setHighlightEnabled(false); - - mChart.setDrawBarShadow(false); - - mChart.setDrawValueAboveBar(true); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - // draw shadows for each bar that show the maximum value - // mChart.setDrawBarShadow(true); - - // mChart.setDrawXLabels(false); - - mChart.setDrawGridBackground(false); - - // mChart.setDrawYLabels(false); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis xl = mChart.getXAxis(); - xl.setPosition(XAxisPosition.BOTTOM); - xl.setTypeface(tf); - xl.setDrawAxisLine(true); - xl.setDrawGridLines(true); - xl.setGridLineWidth(0.3f); - - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); - yl.setDrawAxisLine(true); - yl.setDrawGridLines(true); - yl.setGridLineWidth(0.3f); - yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) -// yl.setInverted(true); - - YAxis yr = mChart.getAxisRight(); - yr.setTypeface(tf); - yr.setDrawAxisLine(true); - yr.setDrawGridLines(false); - yr.setAxisMinValue(0f); // this replaces setStartAtZero(true) -// yr.setInverted(true); - - setData(12, 50); - mChart.animateY(2500); - - // setting data - mSeekBarY.setProgress(50); - mSeekBarX.setProgress(12); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); - l.setFormSize(8f); - l.setXEntrySpace(4f); - - // mChart.setDrawLegend(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (IBarDataSet iSet : sets) { - - IBarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList yVals1 = new ArrayList(); - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - yVals1.add(new BarEntry((float) (Math.random() * range), i)); - } - - BarDataSet set1 = new BarDataSet(yVals1, "DataSet 1"); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - data.setValueTextSize(10f); - data.setValueTypeface(tf); - - mChart.setData(data); - } - - @SuppressLint("NewApi") - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - if (e == null) - return; - - RectF bounds = mChart.getBarBounds((BarEntry) e); - PointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(dataSetIndex) - .getAxisDependency()); - - Log.i("bounds", bounds.toString()); - Log.i("position", position.toString()); - } - - public void onNothingSelected() { - }; -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java deleted file mode 100644 index 0c331f1738..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ /dev/null @@ -1,411 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.ChartTouchListener; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListener, - OnChartGestureListener, OnChartValueSelectedListener { - - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartGestureListener(this); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable touch gestures - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - // mChart.setScaleXEnabled(true); - // mChart.setScaleYEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); - - // set an alternative background color - // mChart.setBackgroundColor(Color.GRAY); - - // create a custom MarkerView (extend MarkerView) and specify the layout - // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); - - // x-axis limit line - LimitLine llXAxis = new LimitLine(10f, "Index 10"); - llXAxis.setLineWidth(4f); - llXAxis.enableDashedLine(10f, 10f, 0f); - llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); - llXAxis.setTextSize(10f); - - XAxis xAxis = mChart.getXAxis(); - //xAxis.setValueFormatter(new MyCustomXAxisValueFormatter()); - //xAxis.addLimitLine(llXAxis); // add x-axis limit line - - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - LimitLine ll1 = new LimitLine(130f, "Upper Limit"); - ll1.setLineWidth(4f); - ll1.enableDashedLine(10f, 10f, 0f); - ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP); - ll1.setTextSize(10f); - ll1.setTypeface(tf); - - LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); - ll2.setLineWidth(4f); - ll2.enableDashedLine(10f, 10f, 0f); - ll2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); - ll2.setTextSize(10f); - ll2.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines - leftAxis.addLimitLine(ll1); - leftAxis.addLimitLine(ll2); - leftAxis.setAxisMaxValue(220f); - leftAxis.setAxisMinValue(-50f); - //leftAxis.setYOffset(20f); - leftAxis.enableGridDashedLine(10f, 10f, 0f); - leftAxis.setDrawZeroLine(false); - - // limit lines are drawn behind data (and not on top) - leftAxis.setDrawLimitLinesBehindData(true); - - mChart.getAxisRight().setEnabled(false); - - //mChart.getViewPortHandler().setMaximumScaleY(2f); - //mChart.getViewPortHandler().setMaximumScaleX(2f); - - // add data - setData(45, 100); - -// mChart.setVisibleXRange(20); -// mChart.setVisibleYRange(20f, AxisDependency.LEFT); -// mChart.centerViewTo(20, 50, AxisDependency.LEFT); - - mChart.animateX(2500, Easing.EasingOption.EaseInOutQuart); -// mChart.invalidate(); - - // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); - - // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); - l.setForm(LegendForm.LINE); - - // // dont forget to refresh the drawing - // mChart.invalidate(); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.line, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleFilled: { - - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000, Easing.EasingOption.EaseInCubic); - break; - } - case R.id.animateXY: { - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - - // redraw - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - // set1.setFillAlpha(110); - // set1.setFillColor(Color.RED); - - // set the line to be drawn like this "- - - - - -" - set1.enableDashedLine(10f, 5f, 0f); - set1.enableDashedHighlightLine(10f, 5f, 0f); - set1.setColor(Color.BLACK); - set1.setCircleColor(Color.BLACK); - set1.setLineWidth(1f); - set1.setCircleRadius(3f); - set1.setDrawCircleHole(false); - set1.setValueTextSize(9f); - Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); - set1.setFillDrawable(drawable); - set1.setDrawFilled(true); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - - // set data - mChart.setData(data); - } - - @Override - public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); - } - - @Override - public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { - Log.i("Gesture", "END, lastGesture: " + lastPerformedGesture); - - // un-highlight values after the gesture is finished and no single-tap - if(lastPerformedGesture != ChartTouchListener.ChartGesture.SINGLE_TAP) - mChart.highlightValues(null); // or highlightTouch(null) for callback to onNothingSelected(...) - } - - @Override - public void onChartLongPressed(MotionEvent me) { - Log.i("LongPress", "Chart longpressed."); - } - - @Override - public void onChartDoubleTapped(MotionEvent me) { - Log.i("DoubleTap", "Chart double-tapped."); - } - - @Override - public void onChartSingleTapped(MotionEvent me) { - Log.i("SingleTap", "Chart single-tapped."); - } - - @Override - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - Log.i("Fling", "Chart flinged. VeloX: " + velocityX + ", VeloY: " + velocityY); - } - - @Override - public void onChartScale(MotionEvent me, float scaleX, float scaleY) { - Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); - } - - @Override - public void onChartTranslate(MotionEvent me, float dX, float dY) { - Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("Entry selected", e.toString()); - Log.i("LOWHIGH", "low: " + mChart.getLowestVisibleXIndex() + ", high: " + mChart.getHighestVisibleXIndex()); - Log.i("MIN MAX", "xmin: " + mChart.getXChartMin() + ", xmax: " + mChart.getXChartMax() + ", ymin: " + mChart.getYChartMin() + ", ymax: " + mChart.getYChartMax()); - } - - @Override - public void onNothingSelected() { - Log.i("Nothing selected", "Nothing selected."); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java deleted file mode 100644 index 7c4236f60c..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ /dev/null @@ -1,351 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendForm; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable touch gestures - mChart.setTouchEnabled(true); - - mChart.setDragDecelerationFrictionCoef(0.9f); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setDrawGridBackground(false); - mChart.setHighlightPerDragEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); - - // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); - - // add data - setData(20, 30); - - mChart.animateX(2500); - - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); - - // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); - l.setForm(LegendForm.LINE); - l.setTypeface(tf); - l.setTextSize(11f); - l.setTextColor(Color.WHITE); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); -// l.setYOffset(11f); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); - xAxis.setTextSize(12f); - xAxis.setTextColor(Color.WHITE); - xAxis.setDrawGridLines(false); - xAxis.setDrawAxisLine(false); - xAxis.setSpaceBetweenLabels(1); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - leftAxis.setTextColor(ColorTemplate.getHoloBlue()); - leftAxis.setAxisMaxValue(200f); - leftAxis.setAxisMinValue(0f); - leftAxis.setDrawGridLines(true); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(tf); - rightAxis.setTextColor(Color.RED); - rightAxis.setAxisMaxValue(900); - rightAxis.setAxisMinValue(-200); - rightAxis.setDrawGridLines(false); - rightAxis.setDrawZeroLine(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.line, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleFilled: { - - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - mChart.animateXY(3000, 3000); - break; - } - - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); - - // redraw - mChart.invalidate(); - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = range / 2f; - float val = (float) (Math.random() * mult) + 50;// + (float) - // ((mult * - // 0.1) / 10); - yVals1.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals1, "DataSet 1"); - set1.setAxisDependency(AxisDependency.LEFT); - set1.setColor(ColorTemplate.getHoloBlue()); - set1.setCircleColor(Color.WHITE); - set1.setLineWidth(2f); - set1.setCircleRadius(3f); - set1.setFillAlpha(65); - set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setDrawCircleHole(false); - //set1.setFillFormatter(new MyFillFormatter(0f)); -// set1.setDrawHorizontalHighlightIndicator(false); -// set1.setVisible(false); -// set1.setCircleHoleColor(Color.WHITE); - - ArrayList yVals2 = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = range; - float val = (float) (Math.random() * mult) + 450;// + (float) - // ((mult * - // 0.1) / 10); - yVals2.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set2 = new LineDataSet(yVals2, "DataSet 2"); - set2.setAxisDependency(AxisDependency.RIGHT); - set2.setColor(Color.RED); - set2.setCircleColor(Color.WHITE); - set2.setLineWidth(2f); - set2.setCircleRadius(3f); - set2.setFillAlpha(65); - set2.setFillColor(Color.RED); - set2.setDrawCircleHole(false); - set2.setHighLightColor(Color.rgb(244, 117, 117)); - //set2.setFillFormatter(new MyFillFormatter(900f)); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set2); - dataSets.add(set1); // add the datasets - - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(9f); - - // set data - mChart.setData(data); - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("Entry selected", e.toString()); - - mChart.centerViewToAnimated(e.getXIndex(), e.getVal(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 500); - //mChart.zoomAndCenterAnimated(2.5f, 2.5f, e.getXIndex(), e.getVal(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); - //mChart.zoomAndCenterAnimated(1.8f, 1.8f, e.getXIndex(), e.getVal(), mChart.getData().getDataSetByIndex(dataSetIndex).getAxisDependency(), 1000); - } - - @Override - public void onNothingSelected() { - Log.i("Nothing selected", "Nothing selected."); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java deleted file mode 100644 index 05e25fae64..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java +++ /dev/null @@ -1,250 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class MultiLineChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - mChart.setDrawGridBackground(false); - mChart.setDescription(""); - mChart.setDrawBorders(false); - - mChart.getAxisLeft().setDrawAxisLine(false); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getAxisRight().setDrawAxisLine(false); - mChart.getAxisRight().setDrawGridLines(false); - mChart.getXAxis().setDrawAxisLine(false); - mChart.getXAxis().setDrawGridLines(false); - - // enable touch gestures - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mSeekBarX.setProgress(20); - mSeekBarY.setProgress(100); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.line, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleFilled: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - List sets = mChart.getData() - .getDataSets(); - - for (ILineDataSet iSet : sets) { - - LineDataSet set = (LineDataSet) iSet; - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - } - return true; - } - - private int[] mColors = new int[] { - ColorTemplate.VORDIPLOM_COLORS[0], - ColorTemplate.VORDIPLOM_COLORS[1], - ColorTemplate.VORDIPLOM_COLORS[2] - }; - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - mChart.resetTracking(); - - tvX.setText("" + (mSeekBarX.getProgress())); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - xVals.add((i) + ""); - } - - ArrayList dataSets = new ArrayList(); - - for (int z = 0; z < 3; z++) { - - ArrayList values = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - double val = (Math.random() * mSeekBarY.getProgress()) + 3; - values.add(new Entry((float) val, i)); - } - - LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); - d.setLineWidth(2.5f); - d.setCircleRadius(4f); - - int color = mColors[z % mColors.length]; - d.setColor(color); - d.setCircleColor(color); - dataSets.add(d); - } - - // make the first DataSet dashed - ((LineDataSet) dataSets.get(0)).enableDashedLine(10, 10, 0); - ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); - ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); - - LineData data = new LineData(xVals, dataSets); - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java deleted file mode 100644 index 9a195cb4b4..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ /dev/null @@ -1,130 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class PerformanceLineChart extends DemoBase implements OnSeekBarChangeListener { - - private LineChart mChart; - private SeekBar mSeekBarValues; - private TextView mTvCount; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_performance_linechart); - - mTvCount = (TextView) findViewById(R.id.tvValueCount); - mSeekBarValues = (SeekBar) findViewById(R.id.seekbarValues); - mTvCount.setText("500"); - - mSeekBarValues.setProgress(500); - - mSeekBarValues.setOnSeekBarChangeListener(this); - - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setDrawGridBackground(false); - - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable touch gestures - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getAxisRight().setEnabled(false); - mChart.getXAxis().setDrawGridLines(true); - mChart.getXAxis().setDrawAxisLine(false); - - // dont forget to refresh the drawing - mChart.invalidate(); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - int count = mSeekBarValues.getProgress() + 1000; - mTvCount.setText("" + count); - - mChart.resetTracking(); - - setData(count, 500f); - - // redraw - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i) + ""); - } - - ArrayList yVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - - set1.setColor(Color.BLACK); - set1.setLineWidth(0.5f); - set1.setDrawValues(false); - set1.setDrawCircles(false); - set1.setDrawCubic(false); - set1.setDrawFilled(false); - - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - - // set data - mChart.setData(data); - - // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); - l.setEnabled(false); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java deleted file mode 100644 index bab7dcb525..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ /dev/null @@ -1,278 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.text.style.RelativeSizeSpan; -import android.text.style.StyleSpan; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.formatter.PercentFormatter; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class PieChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private PieChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_piechart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - - mSeekBarY.setProgress(10); - - mSeekBarX.setOnSeekBarChangeListener(this); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (PieChart) findViewById(R.id.chart1); - mChart.setUsePercentValues(true); - mChart.setDescription(""); - mChart.setExtraOffsets(5, 10, 5, 5); - - mChart.setDragDecelerationFrictionCoef(0.95f); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); - mChart.setCenterText(generateCenterSpannableText()); - - mChart.setDrawHoleEnabled(true); - mChart.setHoleColor(Color.WHITE); - - mChart.setTransparentCircleColor(Color.WHITE); - mChart.setTransparentCircleAlpha(110); - - mChart.setHoleRadius(58f); - mChart.setTransparentCircleRadius(61f); - - mChart.setDrawCenterText(true); - - mChart.setRotationAngle(0); - // enable rotation of the chart by touch - mChart.setRotationEnabled(true); - mChart.setHighlightPerTapEnabled(true); - - // mChart.setUnit(" €"); - // mChart.setDrawUnitsInChart(true); - - // add a selection listener - mChart.setOnChartValueSelectedListener(this); - - setData(3, 100); - - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad); - // mChart.spin(2000, 0, 360); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setXEntrySpace(7f); - l.setYEntrySpace(0f); - l.setYOffset(0f); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.pie, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHole: { - if (mChart.isDrawHoleEnabled()) - mChart.setDrawHoleEnabled(false); - else - mChart.setDrawHoleEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionDrawCenter: { - if (mChart.isDrawCenterTextEnabled()) - mChart.setDrawCenterText(false); - else - mChart.setDrawCenterText(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleXVals: { - - mChart.setDrawSliceText(!mChart.isDrawSliceTextEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.actionTogglePercent: - mChart.setUsePercentValues(!mChart.isUsePercentValuesEnabled()); - mChart.invalidate(); - break; - case R.id.animateX: { - mChart.animateX(1400); - break; - } - case R.id.animateY: { - mChart.animateY(1400); - break; - } - case R.id.animateXY: { - mChart.animateXY(1400, 1400); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - setData(mSeekBarX.getProgress(), mSeekBarY.getProgress()); - } - - private void setData(int count, float range) { - - float mult = range; - - ArrayList yVals1 = new ArrayList(); - - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. - for (int i = 0; i < count + 1; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 5, i)); - } - - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count + 1; i++) - xVals.add(mParties[i % mParties.length]); - - PieDataSet dataSet = new PieDataSet(yVals1, "Election Results"); - dataSet.setSliceSpace(3f); - dataSet.setSelectionShift(5f); - - // add a lot of colors - - ArrayList colors = new ArrayList(); - - for (int c : ColorTemplate.VORDIPLOM_COLORS) - colors.add(c); - - for (int c : ColorTemplate.JOYFUL_COLORS) - colors.add(c); - - for (int c : ColorTemplate.COLORFUL_COLORS) - colors.add(c); - - for (int c : ColorTemplate.LIBERTY_COLORS) - colors.add(c); - - for (int c : ColorTemplate.PASTEL_COLORS) - colors.add(c); - - colors.add(ColorTemplate.getHoloBlue()); - - dataSet.setColors(colors); - //dataSet.setSelectionShift(0f); - - PieData data = new PieData(xVals, dataSet); - data.setValueFormatter(new PercentFormatter()); - data.setValueTextSize(11f); - data.setValueTextColor(Color.WHITE); - data.setValueTypeface(tf); - mChart.setData(data); - - // undo all highlights - mChart.highlightValues(null); - - mChart.invalidate(); - } - - private SpannableString generateCenterSpannableText() { - - SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); - s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); - s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); - s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); - s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); - s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); - s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); - return s; - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - if (e == null) - return; - Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); - } - - @Override - public void onNothingSelected() { - Log.i("PieChart", "nothing selected"); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java deleted file mode 100644 index dbbdc695c8..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ /dev/null @@ -1,222 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Typeface; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.Toast; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.RadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class RadarChartActivitry extends DemoBase { - - private RadarChart mChart; - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart); - - mChart = (RadarChart) findViewById(R.id.chart1); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setDescription(""); - - mChart.setWebLineWidth(1.5f); - mChart.setWebLineWidthInner(0.75f); - mChart.setWebAlpha(100); - - // create a custom MarkerView (extend MarkerView) and specify the layout - // to use for it - MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); - - // set the marker to the chart - mChart.setMarkerView(mv); - - setData(); - - mChart.animateXY( - 1400, 1400, - Easing.EasingOption.EaseInOutQuad, - Easing.EasingOption.EaseInOutQuad); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); - xAxis.setTextSize(9f); - - YAxis yAxis = mChart.getYAxis(); - yAxis.setTypeface(tf); - yAxis.setLabelCount(5, false); - yAxis.setTextSize(9f); - yAxis.setAxisMinValue(0f); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); - l.setXEntrySpace(7f); - l.setYEntrySpace(5f); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.radar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - for (IDataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionToggleRotate: { - if (mChart.isRotationEnabled()) - mChart.setRotationEnabled(false); - else - mChart.setRotationEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (IRadarDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - case R.id.actionToggleXLabels: { - mChart.getXAxis().setEnabled(!mChart.getXAxis().isEnabled()); - mChart.notifyDataSetChanged(); - mChart.invalidate(); - break; - } - case R.id.actionToggleYLabels: { - - mChart.getYAxis().setEnabled(!mChart.getYAxis().isEnabled()); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(1400); - break; - } - case R.id.animateY: { - mChart.animateY(1400); - break; - } - case R.id.animateXY: { - mChart.animateXY(1400, 1400); - break; - } - case R.id.actionToggleSpin: { - mChart.spin(2000, mChart.getRotationAngle(), mChart.getRotationAngle() + 360, Easing.EasingOption.EaseInCubic); - break; - } - } - return true; - } - - private String[] mParties = new String[]{ - "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", - "Party I" - }; - - public void setData() { - - float mult = 150; - int cnt = 9; - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - - // IMPORTANT: In a PieChart, no values (Entry) should have the same - // xIndex (even if from different DataSets), since no values can be - // drawn above each other. - for (int i = 0; i < cnt; i++) { - yVals1.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); - } - - for (int i = 0; i < cnt; i++) { - yVals2.add(new Entry((float) (Math.random() * mult) + mult / 2, i)); - } - - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < cnt; i++) - xVals.add(mParties[i % mParties.length]); - - RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1"); - set1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); - set1.setFillColor(ColorTemplate.VORDIPLOM_COLORS[0]); - set1.setDrawFilled(true); - set1.setLineWidth(2f); - - RadarDataSet set2 = new RadarDataSet(yVals2, "Set 2"); - set2.setColor(ColorTemplate.VORDIPLOM_COLORS[4]); - set2.setFillColor(ColorTemplate.VORDIPLOM_COLORS[4]); - set2.setDrawFilled(true); - set2.setLineWidth(2f); - - ArrayList sets = new ArrayList(); - sets.add(set1); - sets.add(set2); - - RadarData data = new RadarData(xVals, sets); - data.setValueTypeface(tf); - data.setValueTextSize(8f); - data.setDrawValues(false); - - mChart.setData(data); - - mChart.invalidate(); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java deleted file mode 100644 index e716f7ff47..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ /dev/null @@ -1,246 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; - -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeListener, - OnChartValueSelectedListener { - - private ScatterChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - private Typeface tf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_scatterchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (ScatterChart) findViewById(R.id.chart1); - mChart.setDescription(""); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setOnChartValueSelectedListener(this); - - mChart.setDrawGridBackground(false); - - mChart.setTouchEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - mChart.setMaxVisibleValueCount(200); - mChart.setPinchZoom(true); - - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setTypeface(tf); - - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); - yl.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - mChart.getAxisRight().setEnabled(false); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); - xl.setDrawGridLines(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.scatter, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (IScatterDataSet iSet : sets) { - - ScatterDataSet set = (ScatterDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add((i) + ""); - } - - ArrayList yVals1 = new ArrayList(); - ArrayList yVals2 = new ArrayList(); - ArrayList yVals3 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals1.add(new Entry(val, i)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals2.add(new Entry(val, i)); - } - - for (int i = 0; i < mSeekBarX.getProgress(); i++) { - float val = (float) (Math.random() * mSeekBarY.getProgress()) + 3; - yVals3.add(new Entry(val, i)); - } - - // create a dataset and give it a type - ScatterDataSet set1 = new ScatterDataSet(yVals1, "DS 1"); - set1.setScatterShape(ScatterShape.SQUARE); - set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); - ScatterDataSet set2 = new ScatterDataSet(yVals2, "DS 2"); - set2.setScatterShape(ScatterShape.CIRCLE); - set2.setScatterShapeHoleColor(Color.WHITE); - set2.setScatterShapeHoleRadius(5f); - set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); - ScatterDataSet set3 = new ScatterDataSet(yVals3, "DS 3"); - set3.setScatterShape(ScatterShape.CROSS); - set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); - - set1.setScatterShapeSize(8f); - set2.setScatterShapeSize(8f); - set3.setScatterShapeSize(8f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets - dataSets.add(set2); - dataSets.add(set3); - - // create a data object with the datasets - ScatterData data = new ScatterData(xVals, dataSets); - data.setValueTypeface(tf); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java deleted file mode 100644 index 61244bad2e..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java +++ /dev/null @@ -1,72 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class ScrollViewActivity extends DemoBase { - - private BarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_scrollview); - - mChart = (BarChart) findViewById(R.id.chart1); - - mChart.setDescription(""); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - mChart.setDrawGridBackground(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setLabelsToSkip(0); - xAxis.setDrawGridLines(false); - - mChart.getAxisLeft().setDrawGridLines(false); - - mChart.getLegend().setEnabled(false); - - setData(10); - } - - private void setData(int count) { - - ArrayList yVals = new ArrayList(); - ArrayList xVals = new ArrayList(); - - for (int i = 0; i < count; i++) { - float val = (float) (Math.random() * count) + 15; - yVals.add(new BarEntry((int) val, i)); - xVals.add((int) val + ""); - } - - BarDataSet set = new BarDataSet(yVals, "Data Set"); - set.setColors(ColorTemplate.VORDIPLOM_COLORS); - set.setDrawValues(false); - - BarData data = new BarData(xVals, set); - - mChart.setData(data); - mChart.invalidate(); - mChart.animateY(800); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java deleted file mode 100644 index 77c6d1f23f..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ /dev/null @@ -1,253 +0,0 @@ -package com.xxmassdeveloper.mpchartexample; - -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; -import android.widget.TextView; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; -import java.util.List; - -public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - - private BarChart mChart; - private SeekBar mSeekBarX, mSeekBarY; - private TextView tvX, tvY; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart); - - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); - - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarX.setOnSeekBarChangeListener(this); - - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - - mChart = (BarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - - mChart.setDescription(""); - - // if more than 60 entries are displayed in the chart, no values will be - // drawn - mChart.setMaxVisibleValueCount(60); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - - mChart.setDrawValueAboveBar(false); - - // change the position of the y-labels - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setValueFormatter(new MyYAxisValueFormatter()); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); - - XAxis xLabels = mChart.getXAxis(); - xLabels.setPosition(XAxisPosition.TOP); - - // mChart.setDrawXLabels(false); - // mChart.setDrawYLabels(false); - - // setting data - mSeekBarX.setProgress(12); - mSeekBarY.setProgress(100); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); - l.setFormSize(8f); - l.setFormToTextSpace(4f); - l.setXEntrySpace(6f); - - // mChart.setDrawLegend(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (IBarDataSet iSet : sets) { - - BarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT).show(); - break; - } - } - return true; - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - xVals.add(mMonths[i % mMonths.length]); - } - - ArrayList yVals1 = new ArrayList(); - - for (int i = 0; i < mSeekBarX.getProgress() + 1; i++) { - float mult = (mSeekBarY.getProgress() + 1); - float val1 = (float) (Math.random() * mult) + mult / 3; - float val2 = (float) (Math.random() * mult) + mult / 3; - float val3 = (float) (Math.random() * mult) + mult / 3; - - yVals1.add(new BarEntry(new float[] { val1, val2, val3 }, i)); - } - - BarDataSet set1 = new BarDataSet(yVals1, "Statistics Vienna 2014"); - set1.setColors(getColors()); - set1.setStackLabels(new String[] { "Births", "Divorces", "Marriages" }); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); - - BarData data = new BarData(xVals, dataSets); - data.setValueFormatter(new MyValueFormatter()); - - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - BarEntry entry = (BarEntry) e; - - if (entry.getVals() != null) - Log.i("VAL SELECTED", "Value: " + entry.getVals()[h.getStackIndex()]); - else - Log.i("VAL SELECTED", "Value: " + entry.getVal()); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - - } - - private int[] getColors() { - - int stacksize = 3; - - // have as many colors as stack-values per entry - int[] colors = new int[stacksize]; - - for (int i = 0; i < stacksize; i++) { - colors[i] = ColorTemplate.VORDIPLOM_COLORS[i]; - } - - return colors; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java deleted file mode 100644 index d25ee39f2b..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java +++ /dev/null @@ -1,225 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -import android.graphics.Color; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.WindowManager; -import android.widget.Toast; - -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.formatter.ValueFormatter; -import com.github.mikephil.charting.utils.ViewPortHandler; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -public class StackedBarActivityNegative extends DemoBase implements - OnChartValueSelectedListener { - - private HorizontalBarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_age_distribution); - - setTitle("Age Distribution Austria"); - - mChart = (HorizontalBarChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - mChart.setDescription(""); - - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - mChart.setDrawValueAboveBar(true); - - mChart.getAxisLeft().setEnabled(false); - mChart.getAxisRight().setAxisMaxValue(25f); - mChart.getAxisRight().setAxisMinValue(-25f); - mChart.getAxisRight().setDrawGridLines(false); - mChart.getAxisRight().setDrawZeroLine(true); - mChart.getAxisRight().setLabelCount(7, false); - mChart.getAxisRight().setValueFormatter(new CustomFormatter()); - mChart.getAxisRight().setTextSize(9f); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxisPosition.BOTH_SIDED); - xAxis.setDrawGridLines(false); - xAxis.setDrawAxisLine(false); - xAxis.setTextSize(9f); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_RIGHT); - l.setFormSize(8f); - l.setFormToTextSpace(4f); - l.setXEntrySpace(6f); - - // IMPORTANT: When using negative values in stacked bars, always make sure the negative values are in the array first - ArrayList yValues = new ArrayList(); - yValues.add(new BarEntry(new float[]{ -10, 10 }, 0)); - yValues.add(new BarEntry(new float[]{ -12, 13 }, 1)); - yValues.add(new BarEntry(new float[]{ -15, 15 }, 2)); - yValues.add(new BarEntry(new float[]{ -17, 17 }, 3)); - yValues.add(new BarEntry(new float[]{ -19, 20 }, 4)); - yValues.add(new BarEntry(new float[]{ -19, 19 }, 5)); - yValues.add(new BarEntry(new float[]{ -16, 16 }, 6)); - yValues.add(new BarEntry(new float[]{ -13, 14 }, 7)); - yValues.add(new BarEntry(new float[]{ -10, 11 }, 8)); - yValues.add(new BarEntry(new float[]{ -5, 6 }, 9)); - yValues.add(new BarEntry(new float[]{ -1, 2 }, 10)); - - BarDataSet set = new BarDataSet(yValues, "Age Distribution"); - set.setValueFormatter(new CustomFormatter()); - set.setValueTextSize(7f); - set.setAxisDependency(YAxis.AxisDependency.RIGHT); - set.setBarSpacePercent(40f); - set.setColors(new int[] {Color.rgb(67,67,72), Color.rgb(124,181,236)}); - set.setStackLabels(new String[]{ - "Men", "Women" - }); - - String []xVals = new String[]{"0-10", "10-20", "20-30", "30-40", "40-50", "50-60", "60-70", "70-80", "80-90", "90-100", "100+"}; - - BarData data = new BarData(xVals, set); - mChart.setData(data); - mChart.invalidate(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.bar, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionToggleValues: { - List sets = mChart.getData() - .getDataSets(); - - for (IBarDataSet iSet : sets) { - - BarDataSet set = (BarDataSet) iSet; - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); - } - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.animateX: { - mChart.animateX(3000); - break; - } - case R.id.animateY: { - mChart.animateY(3000); - break; - } - case R.id.animateXY: { - - mChart.animateXY(3000, 3000); - break; - } - case R.id.actionSave: { - if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - break; - } - } - return true; - } - - @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - - BarEntry entry = (BarEntry) e; - Log.i("VAL SELECTED", - "Value: " + Math.abs(entry.getVals()[h.getStackIndex()])); - } - - @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - Log.i("NOTING SELECTED", ""); - } - - private class CustomFormatter implements ValueFormatter, YAxisValueFormatter { - - private DecimalFormat mFormat; - - public CustomFormatter() { - mFormat = new DecimalFormat("###"); - } - - // data - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - return mFormat.format(Math.abs(value)) + "m"; - } - - // YAxis - @Override - public String getFormattedValue(float value, YAxis yAxis) { - return mFormat.format(Math.abs(value)) + "m"; - } - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java deleted file mode 100644 index bdf918b1b7..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.utils.ViewPortHandler; -import com.github.mikephil.charting.formatter.XAxisValueFormatter; - -/** - * Created by Philipp Jahoda on 14/09/15. - */ -public class MyCustomXAxisValueFormatter implements XAxisValueFormatter { - - public MyCustomXAxisValueFormatter() { - // maybe do something here or provide parameters in constructor - } - - @Override - public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { - - //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); - - // e.g. adjust the x-axis values depending on scale / zoom level - if (viewPortHandler.getScaleX() > 5) - return "4"; - else if (viewPortHandler.getScaleX() > 3) - return "3"; - else if (viewPortHandler.getScaleX() > 1) - return "2"; - else - return original; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java deleted file mode 100644 index e874a57ab6..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyEasingFunction.java +++ /dev/null @@ -1,18 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.animation.EasingFunction; - -/** - * Example of a custom made animation EasingFunction. - * - * @author Philipp Jahoda - */ -public class MyEasingFunction implements EasingFunction { - - @Override - public float getInterpolation(float input) { - // do awesome stuff here, this is just linear easing - return input; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java deleted file mode 100644 index cae82be1d9..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyYAxisValueFormatter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; - -import java.text.DecimalFormat; - -public class MyYAxisValueFormatter implements YAxisValueFormatter { - - private DecimalFormat mFormat; - - public MyYAxisValueFormatter() { - mFormat = new DecimalFormat("###,###,###,##0.0"); - } - - @Override - public String getFormattedValue(float value, YAxis yAxis) { - return mFormat.format(value) + " $"; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java deleted file mode 100644 index 58767447fe..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmDemoData.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - - -import io.realm.RealmList; -import io.realm.RealmObject; - -/** - * Demo class that encapsulates data stored in realm.io database. - * This class represents data suitable for all chart-types. - */ -public class RealmDemoData extends RealmObject { - - private float value; - - private float open, close, high, low; - - private float bubbleSize; - - private RealmList stackValues; - - private int xIndex; - - private String xValue; - - private String someStringField; - - // ofc there could me more fields here... - - public RealmDemoData() { - - } - - public RealmDemoData(float value, int xIndex, String xValue) { - this.value = value; - this.xIndex = xIndex; - this.xValue = xValue; - } - - /** - * Constructor for stacked bars. - * - * @param stackValues - * @param xIndex - * @param xValue - */ - public RealmDemoData(float[] stackValues, int xIndex, String xValue) { - this.xIndex = xIndex; - this.xValue = xValue; - this.stackValues = new RealmList(); - - for (float val : stackValues) { - this.stackValues.add(new RealmFloat(val)); - } - } - - /** - * Constructor for candles. - * - * @param high - * @param low - * @param open - * @param close - * @param xIndex - */ - public RealmDemoData(float high, float low, float open, float close, int xIndex, String xValue) { - this.value = (high + low) / 2f; - this.high = high; - this.low = low; - this.open = open; - this.close = close; - this.xIndex = xIndex; - this.xValue = xValue; - } - - /** - * Constructor for bubbles. - * - * @param value - * @param xIndex - * @param bubbleSize - * @param xValue - */ - public RealmDemoData(float value, int xIndex, float bubbleSize, String xValue) { - this.value = value; - this.xIndex = xIndex; - this.bubbleSize = bubbleSize; - this.xValue = xValue; - } - - public float getValue() { - return value; - } - - public void setValue(float value) { - this.value = value; - } - - public RealmList getStackValues() { - return stackValues; - } - - public void setStackValues(RealmList stackValues) { - this.stackValues = stackValues; - } - - public int getxIndex() { - return xIndex; - } - - public void setxIndex(int xIndex) { - this.xIndex = xIndex; - } - - public String getxValue() { - return xValue; - } - - public void setxValue(String xValue) { - this.xValue = xValue; - } - - public float getOpen() { - return open; - } - - public void setOpen(float open) { - this.open = open; - } - - public float getClose() { - return close; - } - - public void setClose(float close) { - this.close = close; - } - - public float getHigh() { - return high; - } - - public void setHigh(float high) { - this.high = high; - } - - public float getLow() { - return low; - } - - public void setLow(float low) { - this.low = low; - } - - public float getBubbleSize() { - return bubbleSize; - } - - public void setBubbleSize(float bubbleSize) { - this.bubbleSize = bubbleSize; - } - - public String getSomeStringField() { - return someStringField; - } - - public void setSomeStringField(String someStringField) { - this.someStringField = someStringField; - } -} \ No newline at end of file diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java deleted file mode 100644 index 15b027b3ff..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/RealmFloat.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import io.realm.RealmObject; - -/** - * Created by Philipp Jahoda on 09/11/15. - */ -public class RealmFloat extends RealmObject { - - private float floatValue; - - public RealmFloat() { - - } - - public RealmFloat(float floatValue) { - this.floatValue = floatValue; - } - - public float getFloatValue() { - return floatValue; - } - - public void setFloatValue(float value) { - this.floatValue = value; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java deleted file mode 100644 index 418a856f5f..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.fragments; -import android.graphics.Typeface; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.xxmassdeveloper.mpchartexample.R; - - -public class SineCosineFragment extends SimpleFragment { - - public static Fragment newInstance() { - return new SineCosineFragment(); - } - - private LineChart mChart; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.frag_simple_line, container, false); - - mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); - - mChart.setDrawGridBackground(false); - - mChart.setData(generateLineData()); - mChart.animateX(3000); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); - l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - leftAxis.setAxisMaxValue(1.2f); - leftAxis.setAxisMinValue(-1.2f); - - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setEnabled(false); - - return v; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java deleted file mode 100644 index 97bb230ec9..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.notimportant; - -/** - * Created by Philipp Jahoda on 07/12/15. - */ -public class ContentItem { - - String name; - String desc; - boolean isNew = false; - - public ContentItem(String n, String d) { - name = n; - desc = d; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java deleted file mode 100644 index 774aff1f75..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java +++ /dev/null @@ -1,31 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample.notimportant; - -import android.support.v4.app.FragmentActivity; - -import com.xxmassdeveloper.mpchartexample.R; - -/** - * Baseclass of all Activities of the Demo Application. - * - * @author Philipp Jahoda - */ -public abstract class DemoBase extends FragmentActivity { - - protected String[] mMonths = new String[] { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" - }; - - protected String[] mParties = new String[] { - "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", - "Party I", "Party J", "Party K", "Party L", "Party M", "Party N", "Party O", "Party P", - "Party Q", "Party R", "Party S", "Party T", "Party U", "Party V", "Party W", "Party X", - "Party Y", "Party Z" - }; - - @Override - public void onBackPressed() { - super.onBackPressed(); - overridePendingTransition(R.anim.move_left_in_activity, R.anim.move_right_out_activity); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java deleted file mode 100644 index 8395ce2720..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.notimportant; - -import android.content.Context; -import android.graphics.Typeface; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import com.xxmassdeveloper.mpchartexample.R; - -import java.util.List; - -/** - * Created by Philipp Jahoda on 07/12/15. - */ -public class MyAdapter extends ArrayAdapter { - - private Typeface mTypeFaceLight; - private Typeface mTypeFaceRegular; - - public MyAdapter(Context context, List objects) { - super(context, 0, objects); - - mTypeFaceLight = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); - mTypeFaceRegular = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - ContentItem c = getItem(position); - - ViewHolder holder = null; - - if (convertView == null) { - - holder = new ViewHolder(); - - convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); - holder.tvName = (TextView) convertView.findViewById(R.id.tvName); - holder.tvDesc = (TextView) convertView.findViewById(R.id.tvDesc); - holder.tvNew = (TextView) convertView.findViewById(R.id.tvNew); - - convertView.setTag(holder); - - } else { - holder = (ViewHolder) convertView.getTag(); - } - - holder.tvNew.setTypeface(mTypeFaceRegular); - holder.tvName.setTypeface(mTypeFaceLight); - holder.tvDesc.setTypeface(mTypeFaceLight); - - holder.tvName.setText(c.name); - holder.tvDesc.setText(c.desc); - - if(c.isNew) - holder.tvNew.setVisibility(View.VISIBLE); - else - holder.tvNew.setVisibility(View.GONE); - - return convertView; - } - - private class ViewHolder { - - TextView tvName, tvDesc; - TextView tvNew; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java deleted file mode 100644 index 09bcc59e1e..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmBaseActivity.java +++ /dev/null @@ -1,210 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; - -import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.charts.Chart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.DefaultYAxisValueFormatter; -import com.github.mikephil.charting.formatter.PercentFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyYAxisValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -/** - * Created by Philipp Jahoda on 05/11/15. - */ -public abstract class RealmBaseActivity extends DemoBase { - - protected Realm mRealm; - - protected Typeface mTf; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setTitle("Realm.io Examples"); - } - - protected void setup(Chart chart) { - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - // no description text - chart.setDescription(""); - chart.setNoDataTextDescription("You need to provide data for the chart."); - - // enable touch gestures - chart.setTouchEnabled(true); - - if (chart instanceof BarLineChartBase) { - - BarLineChartBase mChart = (BarLineChartBase) chart; - - mChart.setDrawGridBackground(false); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(false); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.removeAllLimitLines(); // reset all limit lines to avoid overlapping lines - leftAxis.setTypeface(mTf); - leftAxis.setTextSize(8f); - leftAxis.setTextColor(Color.DKGRAY); - leftAxis.setValueFormatter(new PercentFormatter()); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(mTf); - xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); - xAxis.setTextSize(8f); - xAxis.setTextColor(Color.DKGRAY); - - mChart.getAxisRight().setEnabled(false); - } - } - - protected void styleData(ChartData data) { - data.setValueTypeface(mTf); - data.setValueTextSize(8f); - data.setValueTextColor(Color.DKGRAY); - data.setValueFormatter(new PercentFormatter()); - } - - @Override - protected void onResume() { - super.onResume(); - - RealmConfiguration config = new RealmConfiguration.Builder(this) - .name("myrealm.realm") - .build(); - - Realm.deleteRealm(config); - - Realm.setDefaultConfiguration(config); - - mRealm = Realm.getInstance(config); - } - - @Override - protected void onPause() { - super.onPause(); - mRealm.close(); - } - - protected void writeToDB(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.clear(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 40f + (float) (Math.random() * 60f); - - RealmDemoData d = new RealmDemoData(value, i, "" + i); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBStack(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.clear(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float val1 = 34f + (float) (Math.random() * 12.0f); - float val2 = 34f + (float) (Math.random() * 12.0f); - float[] stack = new float[]{val1, val2, 100 - val1 - val2}; - - RealmDemoData d = new RealmDemoData(stack, i, "" + i); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBCandle(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.clear(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float mult = 50; - float val = (float) (Math.random() * 40) + mult; - - float high = (float) (Math.random() * 9) + 8f; - float low = (float) (Math.random() * 9) + 8f; - - float open = (float) (Math.random() * 6) + 1f; - float close = (float) (Math.random() * 6) + 1f; - - boolean even = i % 2 == 0; - - RealmDemoData d = new RealmDemoData(val + high, val - low, even ? val + open : val - open, - even ? val - close : val + close, i, i + ""); - - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBBubble(int objectCount) { - - mRealm.beginTransaction(); - - mRealm.clear(RealmDemoData.class); - - for (int i = 0; i < objectCount; i++) { - - float value = 30f + (float) (Math.random() * 100.0); - float size = 15f + (float) (Math.random() * 20.0); - - RealmDemoData d = new RealmDemoData(value, i, size, "" + i); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } - - protected void writeToDBPie() { - - mRealm.beginTransaction(); - - mRealm.clear(RealmDemoData.class); - - float value1 = 15f + (float) (Math.random() * 8f); - float value2 = 15f + (float) (Math.random() * 8f); - float value3 = 15f + (float) (Math.random() * 8f); - float value4 = 15f + (float) (Math.random() * 8f); - float value5 = 100f - value1 - value2 - value3 - value4; - - float[] values = new float[] { value1, value2, value3, value4, value5 }; - String[] xValues = new String[]{ "iOS", "Android", "WP 10", "BlackBerry", "Other"}; - - for (int i = 0; i < values.length; i++) { - RealmDemoData d = new RealmDemoData(values[i], i, xValues[i]); - mRealm.copyToRealm(d); - } - - mRealm.commitTransaction(); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java deleted file mode 100644 index 4387035191..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBar.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBar extends RealmBaseActivity { - - private BarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_barchart_noseekbar); - - mChart = (BarChart) findViewById(R.id.chart1); - setup(mChart); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(20); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "value", "xIndex"); // stacked entries - set.setColors(new int[] {ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); - set.setLabel("Realm BarDataSet"); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xValue", dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java deleted file mode 100644 index b98ca0b746..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityBubble.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BubbleChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBubbleData; -import com.github.mikephil.charting.data.realm.implementation.RealmBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityBubble extends RealmBaseActivity { - - private BubbleChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_bubblechart_noseekbar); - - mChart = (BubbleChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getXAxis().setDrawGridLines(false); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBBubble(10); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - RealmBubbleDataSet set = new RealmBubbleDataSet(result, "value", "xIndex", "bubbleSize"); - set.setLabel("Realm BubbleDataSet"); - set.setColors(ColorTemplate.COLORFUL_COLORS, 110); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmBubbleData data = new RealmBubbleData(result, "xValue", dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java deleted file mode 100644 index 70e324d4dd..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityCandle.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Paint; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.CandleStickChart; -import com.github.mikephil.charting.data.realm.implementation.RealmCandleData; -import com.github.mikephil.charting.data.realm.implementation.RealmCandleDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityCandle extends RealmBaseActivity { - - private CandleStickChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_candlechart_noseekbar); - - mChart = (CandleStickChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBCandle(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - RealmCandleDataSet set = new RealmCandleDataSet(result, "high", "low", "open", "close", "xIndex"); - set.setLabel("Realm Realm CandleDataSet"); - set.setShadowColor(Color.DKGRAY); - set.setShadowWidth(0.7f); - set.setDecreasingColor(Color.RED); - set.setDecreasingPaintStyle(Paint.Style.FILL); - set.setIncreasingColor(Color.rgb(122, 242, 84)); - set.setIncreasingPaintStyle(Paint.Style.STROKE); - set.setNeutralColor(Color.BLUE); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmCandleData data = new RealmCandleData(result, "xValue", dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java deleted file mode 100644 index 7a2ef39c18..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityHorizontalBar.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityHorizontalBar extends RealmBaseActivity { - - private HorizontalBarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_horizontalbarchart_noseekbar); - - mChart = (HorizontalBarChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMinValue(0f); - mChart.setDrawValueAboveBar(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBStack(50); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex", "floatValue"); // stacked entries - set.setColors(new int[]{ColorTemplate.rgb("#8BC34A"), ColorTemplate.rgb("#FFC107"), ColorTemplate.rgb("#9E9E9E")}); - set.setLabel("Mobile OS distribution"); - set.setStackLabels(new String[]{"iOS", "Android", "Other"}); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmBarData data = new RealmBarData(result, "xValue", dataSets); - styleData(data); - data.setValueTextColor(Color.WHITE); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java deleted file mode 100644 index 057662d057..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityLine.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.realm.implementation.RealmLineData; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityLine extends RealmBaseActivity { - - private LineChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_linechart_noseekbar); - - mChart = (LineChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setAxisMaxValue(150f); - mChart.getAxisLeft().setAxisMinValue(0f); - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(40); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - RealmLineDataSet set = new RealmLineDataSet(result, "value", "xIndex"); - set.setDrawCubic(false); - set.setLabel("Realm LineDataSet"); - set.setDrawCircleHole(false); - set.setColor(ColorTemplate.rgb("#FF5722")); - set.setCircleColor(ColorTemplate.rgb("#FF5722")); - set.setLineWidth(1.8f); - set.setCircleSize(3.6f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmLineData data = new RealmLineData(result, "xValue", dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java deleted file mode 100644 index de6767d145..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityPie.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.os.Bundle; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.text.style.RelativeSizeSpan; -import android.text.style.StyleSpan; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.realm.implementation.RealmPieData; -import com.github.mikephil.charting.data.realm.implementation.RealmPieDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityPie extends RealmBaseActivity { - - private PieChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_piechart_noseekbar); - - mChart = (PieChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.setCenterText(generateCenterSpannableText()); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDBPie(); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmPieDataSet set = new RealmPieDataSet(result, "value", "xIndex"); // stacked entries - set.setColors(ColorTemplate.VORDIPLOM_COLORS); - set.setLabel("Example market share"); - set.setSliceSpace(2); - - // create a data object with the dataset list - RealmPieData data = new RealmPieData(result, "xValue", set); - styleData(data); - data.setValueTextColor(Color.WHITE); - data.setValueTextSize(12f); - - // set data - mChart.setData(data); - mChart.animateY(1400); - } - - private SpannableString generateCenterSpannableText() { - - SpannableString s = new SpannableString("Realm.io\nmobile database"); - s.setSpan(new ForegroundColorSpan(Color.rgb(240, 115, 126)), 0, 8, 0); - s.setSpan(new RelativeSizeSpan(2.2f), 0, 8, 0); - s.setSpan(new StyleSpan(Typeface.ITALIC), 9, s.length(), 0); - s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 9, s.length(), 0); - s.setSpan(new RelativeSizeSpan(0.85f), 9, s.length(), 0); - return s; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java deleted file mode 100644 index 7aae4ae377..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityRadar.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.realm.implementation.RealmRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityRadar extends RealmBaseActivity { - - private RadarChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_radarchart_noseekbar); - - mChart = (RadarChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getYAxis().setEnabled(false); - mChart.setWebAlpha(180); - mChart.setWebColorInner(Color.DKGRAY); - mChart.setWebColor(Color.GRAY); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(7); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - //RealmBarDataSet set = new RealmBarDataSet(result, "stackValues", "xIndex"); // normal entries - RealmRadarDataSet set = new RealmRadarDataSet(result, "value", "xIndex"); // stacked entries - set.setLabel("Realm RadarDataSet"); - set.setDrawFilled(true); - set.setColor(ColorTemplate.rgb("#009688")); - set.setFillColor(ColorTemplate.rgb("#009688")); - set.setFillAlpha(130); - set.setLineWidth(2f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RadarData data = new RadarData(new String[] {"2013", "2014", "2015", "2016", "2017", "2018", "2019"}, dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java deleted file mode 100644 index b28bf137a8..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmDatabaseActivityScatter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.data.realm.implementation.RealmScatterData; -import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.custom.RealmDemoData; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmDatabaseActivityScatter extends RealmBaseActivity { - - private ScatterChart mChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_scatterchart_noseekbar); - - mChart = (ScatterChart) findViewById(R.id.chart1); - setup(mChart); - - mChart.getAxisLeft().setDrawGridLines(false); - mChart.getXAxis().setDrawGridLines(false); - mChart.setPinchZoom(true); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - // write some demo-data into the realm.io database - writeToDB(45); - - // add data to the chart - setData(); - } - - private void setData() { - - RealmResults result = mRealm.allObjects(RealmDemoData.class); - - RealmScatterDataSet set = new RealmScatterDataSet(result, "value", "xIndex"); - set.setLabel("Realm ScatterDataSet"); - set.setScatterShapeSize(9f); - set.setColor(ColorTemplate.rgb("#CDDC39")); - set.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - - ArrayList dataSets = new ArrayList(); - dataSets.add(set); // add the dataset - - // create a data object with the dataset list - RealmScatterData data = new RealmScatterData(result, "xValue", dataSets); - styleData(data); - - // set data - mChart.setData(data); - mChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java deleted file mode 100644 index fc1ca357e2..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmMainActivity.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.widget.AdapterView; -import android.widget.ListView; - -import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.notimportant.ContentItem; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; -import com.xxmassdeveloper.mpchartexample.notimportant.MyAdapter; - -import java.util.ArrayList; - -/** - * Created by Philipp Jahoda on 07/12/15. - */ -public class RealmMainActivity extends DemoBase implements AdapterView.OnItemClickListener { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_main); - - setTitle("Realm.io Examples"); - - ArrayList objects = new ArrayList(); - - objects.add(new ContentItem("Line Chart", "Creating a LineChart with Realm.io database")); - objects.add(new ContentItem("Bar Chart", - "Creating a BarChart with Realm.io database")); - objects.add(new ContentItem("Horizontal Bar Chart", - "Creating a HorizontalBarChart with Realm.io database")); - objects.add(new ContentItem("Scatter Chart", - "Creating a ScatterChart with Realm.io database")); - objects.add(new ContentItem("Candle Stick Chart", "Creating a CandleStickChart with Realm.io database")); - objects.add(new ContentItem("Bubble Chart", "Creating a BubbleChart with Realm.io database")); - objects.add(new ContentItem("Pie Chart", "Creating a PieChart with Realm.io database")); - objects.add(new ContentItem("Radar Chart", "Creating a RadarChart with Realm.io database")); - objects.add(new ContentItem("Realm Wiki", "This is the code related to the wiki entry about realm.io on the MPAndroidChart github page.")); - - MyAdapter adapter = new MyAdapter(this, objects); - - ListView lv = (ListView) findViewById(R.id.listView1); - lv.setAdapter(adapter); - - lv.setOnItemClickListener(this); - } - - @Override - public void onItemClick(AdapterView av, View v, int pos, long arg3) { - - Intent i; - - switch (pos) { - case 0: - i = new Intent(this, RealmDatabaseActivityLine.class); - startActivity(i); - break; - case 1: - i = new Intent(this, RealmDatabaseActivityBar.class); - startActivity(i); - break; - case 2: - i = new Intent(this, RealmDatabaseActivityHorizontalBar.class); - startActivity(i); - break; - case 3: - i = new Intent(this, RealmDatabaseActivityScatter.class); - startActivity(i); - break; - case 4: - i = new Intent(this, RealmDatabaseActivityCandle.class); - startActivity(i); - break; - case 5: - i = new Intent(this, RealmDatabaseActivityBubble.class); - startActivity(i); - break; - case 6: - i = new Intent(this, RealmDatabaseActivityPie.class); - startActivity(i); - break; - case 7: - i = new Intent(this, RealmDatabaseActivityRadar.class); - startActivity(i); - break; - case 8: - i = new Intent(this, RealmWikiExample.class); - startActivity(i); - break; - } - - overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.realm, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("https://realm.io")); - startActivity(i); - - return super.onOptionsItemSelected(item); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java deleted file mode 100644 index 7066d8167b..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/RealmWikiExample.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - -import android.os.Bundle; -import android.view.WindowManager; - -import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.realm.implementation.RealmBarData; -import com.github.mikephil.charting.data.realm.implementation.RealmBarDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmLineData; -import com.github.mikephil.charting.data.realm.implementation.RealmLineDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.R; - -import java.util.ArrayList; - -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 18/12/15. - */ -public class RealmWikiExample extends RealmBaseActivity { - - private LineChart lineChart; - private BarChart barChart; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_realm_wiki); - - lineChart = (LineChart) findViewById(R.id.lineChart); - barChart = (BarChart) findViewById(R.id.barChart); - setup(lineChart); - setup(barChart); - - lineChart.setExtraBottomOffset(5f); - barChart.setExtraBottomOffset(5f); - - lineChart.getAxisLeft().setDrawGridLines(false); - lineChart.getXAxis().setDrawGridLines(false); - barChart.getAxisLeft().setDrawGridLines(false); - barChart.getXAxis().setDrawGridLines(false); - } - - @Override - protected void onResume() { - super.onResume(); // setup realm - - mRealm.beginTransaction(); - - // write some demo-data into the realm.io database - Score score1 = new Score(100f, 0, "Peter"); - mRealm.copyToRealm(score1); - Score score2 = new Score(110f, 1, "Lisa"); - mRealm.copyToRealm(score2); - Score score3 = new Score(130f, 2, "Dennis"); - mRealm.copyToRealm(score3); - Score score4 = new Score(70f, 3, "Luke"); - mRealm.copyToRealm(score4); - Score score5 = new Score(80f, 4, "Sarah"); - mRealm.copyToRealm(score5); - - mRealm.commitTransaction(); - - // add data to the chart - setData(); - } - - private void setData() { - - // LINE-CHART - RealmResults results = mRealm.allObjects(Score.class); - - RealmLineDataSet lineDataSet = new RealmLineDataSet(results, "totalScore", "scoreNr"); - lineDataSet.setDrawCubic(false); - lineDataSet.setLabel("Realm LineDataSet"); - lineDataSet.setDrawCircleHole(false); - lineDataSet.setColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setCircleColor(ColorTemplate.rgb("#FF5722")); - lineDataSet.setLineWidth(1.8f); - lineDataSet.setCircleSize(3.6f); - - ArrayList dataSets = new ArrayList(); - dataSets.add(lineDataSet); - - RealmLineData lineData = new RealmLineData(results, "playerName", dataSets); - styleData(lineData); - - // set data - lineChart.setData(lineData); - lineChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - - - // BAR-CHART - RealmBarDataSet barDataSet = new RealmBarDataSet(results, "totalScore", "scoreNr"); - barDataSet.setColors(new int[]{ColorTemplate.rgb("#FF5722"), ColorTemplate.rgb("#03A9F4")}); - barDataSet.setLabel("Realm BarDataSet"); - - ArrayList barDataSets = new ArrayList(); - barDataSets.add(barDataSet); - - RealmBarData barData = new RealmBarData(results, "playerName", barDataSets); - styleData(barData); - - barChart.setData(barData); - barChart.animateY(1400, Easing.EasingOption.EaseInOutQuart); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java deleted file mode 100644 index cf3afcf46d..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/realm/Score.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.realm; - - -import io.realm.RealmObject; - -/** - * our data object - */ -public class Score extends RealmObject { - - private float totalScore; - - private int scoreNr; - - private String playerName; - - public Score() { - } - - public Score(float totalScore, int scoreNr, String playerName) { - this.scoreNr = scoreNr; - this.playerName = playerName; - this.totalScore = totalScore; - } - - // all getters and setters... - - public float getTotalScore() { - return totalScore; - } - - public void setTotalScore(float totalScore) { - this.totalScore = totalScore; - } - - public int getScoreNr() { - return scoreNr; - } - - public void setScoreNr(int scoreNr) { - this.scoreNr = scoreNr; - } - - public String getPlayerName() { - return playerName; - } - - public void setPlayerName(String playerName) { - this.playerName = playerName; - } -} \ No newline at end of file diff --git a/MPChartExample/src/main/AndroidManifest.xml b/MPChartExample/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..99334e601a --- /dev/null +++ b/MPChartExample/src/main/AndroidManifest.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/assets/OpenSans-Bold.ttf b/MPChartExample/src/main/assets/OpenSans-Bold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Bold.ttf rename to MPChartExample/src/main/assets/OpenSans-Bold.ttf diff --git a/MPChartExample/assets/OpenSans-BoldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-BoldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-BoldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-BoldItalic.ttf diff --git a/MPChartExample/assets/OpenSans-ExtraBold.ttf b/MPChartExample/src/main/assets/OpenSans-ExtraBold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-ExtraBold.ttf rename to MPChartExample/src/main/assets/OpenSans-ExtraBold.ttf diff --git a/MPChartExample/assets/OpenSans-ExtraBoldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-ExtraBoldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-ExtraBoldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-ExtraBoldItalic.ttf diff --git a/MPChartExample/assets/OpenSans-Italic.ttf b/MPChartExample/src/main/assets/OpenSans-Italic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Italic.ttf rename to MPChartExample/src/main/assets/OpenSans-Italic.ttf diff --git a/MPChartExample/assets/OpenSans-Light.ttf b/MPChartExample/src/main/assets/OpenSans-Light.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Light.ttf rename to MPChartExample/src/main/assets/OpenSans-Light.ttf diff --git a/MPChartExample/assets/OpenSans-LightItalic.ttf b/MPChartExample/src/main/assets/OpenSans-LightItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-LightItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-LightItalic.ttf diff --git a/MPChartExample/assets/OpenSans-Regular.ttf b/MPChartExample/src/main/assets/OpenSans-Regular.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Regular.ttf rename to MPChartExample/src/main/assets/OpenSans-Regular.ttf diff --git a/MPChartExample/assets/OpenSans-Semibold.ttf b/MPChartExample/src/main/assets/OpenSans-Semibold.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-Semibold.ttf rename to MPChartExample/src/main/assets/OpenSans-Semibold.ttf diff --git a/MPChartExample/assets/OpenSans-SemiboldItalic.ttf b/MPChartExample/src/main/assets/OpenSans-SemiboldItalic.ttf similarity index 100% rename from MPChartExample/assets/OpenSans-SemiboldItalic.ttf rename to MPChartExample/src/main/assets/OpenSans-SemiboldItalic.ttf diff --git a/MPChartExample/assets/cosine.txt b/MPChartExample/src/main/assets/cosine.txt similarity index 100% rename from MPChartExample/assets/cosine.txt rename to MPChartExample/src/main/assets/cosine.txt diff --git a/MPChartExample/assets/hugecosine.txt b/MPChartExample/src/main/assets/hugecosine.txt similarity index 100% rename from MPChartExample/assets/hugecosine.txt rename to MPChartExample/src/main/assets/hugecosine.txt diff --git a/MPChartExample/assets/hugesine.txt b/MPChartExample/src/main/assets/hugesine.txt similarity index 100% rename from MPChartExample/assets/hugesine.txt rename to MPChartExample/src/main/assets/hugesine.txt diff --git a/MPChartExample/assets/n.txt b/MPChartExample/src/main/assets/n.txt similarity index 100% rename from MPChartExample/assets/n.txt rename to MPChartExample/src/main/assets/n.txt diff --git a/MPChartExample/assets/nlogn.txt b/MPChartExample/src/main/assets/nlogn.txt similarity index 100% rename from MPChartExample/assets/nlogn.txt rename to MPChartExample/src/main/assets/nlogn.txt diff --git a/MPChartExample/assets/othersine.txt b/MPChartExample/src/main/assets/othersine.txt similarity index 100% rename from MPChartExample/assets/othersine.txt rename to MPChartExample/src/main/assets/othersine.txt diff --git a/MPChartExample/assets/sine.txt b/MPChartExample/src/main/assets/sine.txt similarity index 100% rename from MPChartExample/assets/sine.txt rename to MPChartExample/src/main/assets/sine.txt diff --git a/MPChartExample/assets/square.txt b/MPChartExample/src/main/assets/square.txt similarity index 100% rename from MPChartExample/assets/square.txt rename to MPChartExample/src/main/assets/square.txt diff --git a/MPChartExample/assets/stacked_bars.txt b/MPChartExample/src/main/assets/stacked_bars.txt similarity index 100% rename from MPChartExample/assets/stacked_bars.txt rename to MPChartExample/src/main/assets/stacked_bars.txt diff --git a/MPChartExample/assets/three.txt b/MPChartExample/src/main/assets/three.txt similarity index 100% rename from MPChartExample/assets/three.txt rename to MPChartExample/src/main/assets/three.txt diff --git a/MPChartExample/ic_launcher-web.png b/MPChartExample/src/main/ic_launcher-web.png similarity index 100% rename from MPChartExample/ic_launcher-web.png rename to MPChartExample/src/main/ic_launcher-web.png diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java new file mode 100644 index 0000000000..69b5a96c42 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java @@ -0,0 +1,214 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class AnotherBarActivity extends DemoBase implements OnSeekBarChangeListener { + + private BarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart); + + setTitle("AnotherBarActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawBarShadow(false); + chart.setDrawGridBackground(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setDrawGridLines(false); + + chart.getAxisLeft().setDrawGridLines(false); + + // setting data + seekBarX.setProgress(10); + seekBarY.setProgress(100); + + // add a nice and smooth animation + chart.animateY(1500); + + chart.getLegend().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float multi = (seekBarY.getProgress() + 1); + float val = (float) (Math.random() * multi) + multi / 3; + values.add(new BarEntry(i, val)); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "Data Set"); + set1.setColors(ColorTemplate.VORDIPLOM_COLORS); + set1.setDrawValues(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + chart.setData(data); + chart.setFitBars(true); + } + + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + menu.removeItem(R.id.actionToggleIcons); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + /* + case R.id.actionToggleIcons: { break; } + */ + case R.id.actionToggleHighlight: { + + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "AnotherBarActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java new file mode 100644 index 0000000000..0d83e3444a --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivity.java @@ -0,0 +1,327 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.RectF; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendForm; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.Fill; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.custom.DayAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.XYMarkerView; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class BarChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private BarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart); + + setTitle("BarChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawGridBackground(false); + // chart.setDrawYLabels(false); + + IAxisValueFormatter xAxisFormatter = new DayAxisValueFormatter(chart); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setTypeface(tfLight); + xAxis.setDrawGridLines(false); + xAxis.setGranularity(1f); // only intervals of 1 day + xAxis.setLabelCount(7); + xAxis.setValueFormatter(xAxisFormatter); + + IAxisValueFormatter custom = new MyAxisValueFormatter(); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); + leftAxis.setLabelCount(8, false); + leftAxis.setValueFormatter(custom); + leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); + leftAxis.setSpaceTop(15f); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setDrawGridLines(false); + rightAxis.setTypeface(tfLight); + rightAxis.setLabelCount(8, false); + rightAxis.setValueFormatter(custom); + rightAxis.setSpaceTop(15f); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setForm(LegendForm.SQUARE); + l.setFormSize(9f); + l.setTextSize(11f); + l.setXEntrySpace(4f); + + XYMarkerView mv = new XYMarkerView(this, xAxisFormatter); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart + + // setting data + seekBarY.setProgress(50); + seekBarX.setProgress(12); + + // chart.setDrawLegend(false); + } + + private void setData(int count, float range) { + + float start = 1f; + + ArrayList values = new ArrayList<>(); + + for (int i = (int) start; i < start + count; i++) { + float val = (float) (Math.random() * (range + 1)); + + if (Math.random() * 100 < 25) { + values.add(new BarEntry(i, val, getResources().getDrawable(R.drawable.star))); + } else { + values.add(new BarEntry(i, val)); + } + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + + } else { + set1 = new BarDataSet(values, "The year 2017"); + + set1.setDrawIcons(false); + + int startColor1 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor2 = ContextCompat.getColor(this, android.R.color.holo_blue_light); + int startColor3 = ContextCompat.getColor(this, android.R.color.holo_orange_light); + int startColor4 = ContextCompat.getColor(this, android.R.color.holo_green_light); + int startColor5 = ContextCompat.getColor(this, android.R.color.holo_red_light); + int endColor1 = ContextCompat.getColor(this, android.R.color.holo_blue_dark); + int endColor2 = ContextCompat.getColor(this, android.R.color.holo_purple); + int endColor3 = ContextCompat.getColor(this, android.R.color.holo_green_dark); + int endColor4 = ContextCompat.getColor(this, android.R.color.holo_red_dark); + int endColor5 = ContextCompat.getColor(this, android.R.color.holo_orange_dark); + + List gradientFills = new ArrayList<>(); + gradientFills.add(new Fill(startColor1, endColor1)); + gradientFills.add(new Fill(startColor2, endColor2)); + gradientFills.add(new Fill(startColor3, endColor3)); + gradientFills.add(new Fill(startColor4, endColor4)); + gradientFills.add(new Fill(startColor5, endColor5)); + + set1.setFills(gradientFills); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(0.9f); + + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "BarChartActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + private final RectF onValueSelectedRectF = new RectF(); + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + + RectF bounds = onValueSelectedRectF; + chart.getBarBounds((BarEntry) e, bounds); + MPPointF position = chart.getPosition(e, AxisDependency.LEFT); + + Log.i("bounds", bounds.toString()); + Log.i("position", position.toString()); + + Log.i("x-index", + "low: " + chart.getLowestVisibleX() + ", high: " + + chart.getHighestVisibleX()); + + MPPointF.recycleInstance(position); + } + + @Override + public void onNothingSelected() { } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java new file mode 100644 index 0000000000..075af0edbc --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java @@ -0,0 +1,289 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.LargeValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.Locale; + +public class BarChartActivityMultiDataset extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private BarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart); + + setTitle("BarChartActivityMultiDataset"); + + tvX = findViewById(R.id.tvXMax); + tvX.setTextSize(10); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setMax(50); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.getDescription().setEnabled(false); + +// chart.setDrawBorders(true); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawBarShadow(false); + + chart.setDrawGridBackground(false); + + // create a custom MarkerView (extend MarkerView) and specify the layout + // to use for it + MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart + + seekBarX.setProgress(10); + seekBarY.setProgress(100); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(true); + l.setTypeface(tfLight); + l.setYOffset(0f); + l.setXOffset(10f); + l.setYEntrySpace(0f); + l.setTextSize(8f); + + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); + xAxis.setGranularity(1f); + xAxis.setCenterAxisLabels(true); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return String.valueOf((int) value); + } + }); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); + leftAxis.setValueFormatter(new LargeValueFormatter()); + leftAxis.setDrawGridLines(false); + leftAxis.setSpaceTop(35f); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + chart.getAxisRight().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + float groupSpace = 0.08f; + float barSpace = 0.03f; // x4 DataSet + float barWidth = 0.2f; // x4 DataSet + // (0.2 + 0.03) * 4 + 0.08 = 1.00 -> interval per "group" + + int groupCount = seekBarX.getProgress() + 1; + int startYear = 1980; + int endYear = startYear + groupCount; + + tvX.setText(String.format(Locale.ENGLISH, "%d-%d", startYear, endYear)); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + ArrayList values4 = new ArrayList<>(); + + float randomMultiplier = seekBarY.getProgress() * 100000f; + + for (int i = startYear; i < endYear; i++) { + values1.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values2.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values3.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + values4.add(new BarEntry(i, (float) (Math.random() * randomMultiplier))); + } + + BarDataSet set1, set2, set3, set4; + + if (chart.getData() != null && chart.getData().getDataSetCount() > 0) { + + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set2 = (BarDataSet) chart.getData().getDataSetByIndex(1); + set3 = (BarDataSet) chart.getData().getDataSetByIndex(2); + set4 = (BarDataSet) chart.getData().getDataSetByIndex(3); + set1.setValues(values1); + set2.setValues(values2); + set3.setValues(values3); + set4.setValues(values4); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + + } else { + // create 4 DataSets + set1 = new BarDataSet(values1, "Company A"); + set1.setColor(Color.rgb(104, 241, 175)); + set2 = new BarDataSet(values2, "Company B"); + set2.setColor(Color.rgb(164, 228, 251)); + set3 = new BarDataSet(values3, "Company C"); + set3.setColor(Color.rgb(242, 247, 158)); + set4 = new BarDataSet(values4, "Company D"); + set4.setColor(Color.rgb(255, 102, 0)); + + BarData data = new BarData(set1, set2, set3, set4); + data.setValueFormatter(new LargeValueFormatter()); + data.setValueTypeface(tfLight); + + chart.setData(data); + } + + // specify the width each bar should have + chart.getBarData().setBarWidth(barWidth); + + // restrict the x-axis range + chart.getXAxis().setAxisMinimum(startYear); + + // barData.getGroupWith(...) is a helper that calculates the width each group needs based on the provided parameters + chart.getXAxis().setAxisMaximum(startYear + chart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount); + chart.groupBars(startYear, groupSpace, barSpace); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IBarDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "BarChartActivityMultiDataset"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("Activity", "Selected: " + e.toString() + ", dataSet: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() { + Log.i("Activity", "Nothing selected."); + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java new file mode 100644 index 0000000000..95443e8214 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java @@ -0,0 +1,239 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendForm; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.FileUtils; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class BarChartActivitySinus extends DemoBase implements OnSeekBarChangeListener { + + private BarChart chart; + private SeekBar seekBarX; + private TextView tvX; + + private List data; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_sinus); + + setTitle("BarChartActivitySinus"); + + data = FileUtils.loadBarEntriesFromAssets(getAssets(), "othersine.txt"); + + tvX = findViewById(R.id.tvValueCount); + + seekBarX = findViewById(R.id.seekbarValues); + + chart = findViewById(R.id.chart1); + + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + // draw shadows for each bar that show the maximum value + // chart.setDrawBarShadow(true); + + // chart.setDrawXLabels(false); + + chart.setDrawGridBackground(false); + // chart.setDrawYLabels(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setEnabled(false); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); + leftAxis.setLabelCount(6, false); + leftAxis.setAxisMinimum(-2.5f); + leftAxis.setAxisMaximum(2.5f); + leftAxis.setGranularityEnabled(true); + leftAxis.setGranularity(0.1f); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setDrawGridLines(false); + rightAxis.setTypeface(tfLight); + rightAxis.setLabelCount(6, false); + rightAxis.setAxisMinimum(-2.5f); + rightAxis.setAxisMaximum(2.5f); + rightAxis.setGranularity(0.1f); + + seekBarX.setOnSeekBarChangeListener(this); + seekBarX.setProgress(150); // set data + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setForm(LegendForm.SQUARE); + l.setFormSize(9f); + l.setTextSize(11f); + l.setXEntrySpace(4f); + + chart.animateXY(1500, 1500); + } + + private void setData(int count) { + + ArrayList entries = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + entries.add(data.get(i)); + } + + BarDataSet set; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set = (BarDataSet) chart.getData().getDataSetByIndex(0); + set.setValues(entries); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set = new BarDataSet(entries, "Sinus Function"); + set.setColor(Color.rgb(240, 120, 124)); + } + + BarData data = new BarData(set); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setDrawValues(false); + data.setBarWidth(0.8f); + + chart.setData(data); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IBarDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + + setData(seekBarX.getProgress()); + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "BarChartActivitySinus"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java new file mode 100644 index 0000000000..4fec7dd6ab --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java @@ -0,0 +1,200 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class BarChartPositiveNegative extends DemoBase { + + private BarChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart_noseekbar); + + setTitle("BarChartPositiveNegative"); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + chart.setExtraTopOffset(-30f); + chart.setExtraBottomOffset(10f); + chart.setExtraLeftOffset(70f); + chart.setExtraRightOffset(70f); + + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawGridBackground(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setTypeface(tfRegular); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + xAxis.setTextColor(Color.LTGRAY); + xAxis.setTextSize(13f); + xAxis.setLabelCount(5); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(1f); + + YAxis left = chart.getAxisLeft(); + left.setDrawLabels(false); + left.setSpaceTop(25f); + left.setSpaceBottom(25f); + left.setDrawAxisLine(false); + left.setDrawGridLines(false); + left.setDrawZeroLine(true); // draw a zero line + left.setZeroLineColor(Color.GRAY); + left.setZeroLineWidth(0.7f); + chart.getAxisRight().setEnabled(false); + chart.getLegend().setEnabled(false); + + // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT + final List data = new ArrayList<>(); + data.add(new Data(0f, -224.1f, "12-29")); + data.add(new Data(1f, 238.5f, "12-30")); + data.add(new Data(2f, 1280.1f, "12-31")); + data.add(new Data(3f, -442.3f, "01-01")); + data.add(new Data(4f, -2280.1f, "01-02")); + + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return data.get(Math.min(Math.max((int) value, 0), data.size()-1)).xAxisValue; + } + }); + + setData(data); + } + + private void setData(List dataList) { + + ArrayList values = new ArrayList<>(); + List colors = new ArrayList<>(); + + int green = Color.rgb(110, 190, 102); + int red = Color.rgb(211, 74, 88); + + for (int i = 0; i < dataList.size(); i++) { + + Data d = dataList.get(i); + BarEntry entry = new BarEntry(d.xValue, d.yValue); + values.add(entry); + + // specific colors + if (d.yValue >= 0) + colors.add(red); + else + colors.add(green); + } + + BarDataSet set; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set = (BarDataSet) chart.getData().getDataSetByIndex(0); + set.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set = new BarDataSet(values, "Values"); + set.setColors(colors); + set.setValueTextColors(colors); + + BarData data = new BarData(set); + data.setValueTextSize(13f); + data.setValueTypeface(tfRegular); + data.setValueFormatter(new ValueFormatter()); + data.setBarWidth(0.8f); + + chart.setData(data); + chart.invalidate(); + } + } + + /** + * Demo class representing data. + */ + private class Data { + + final String xAxisValue; + final float yValue; + final float xValue; + + Data(float xValue, float yValue, String xAxisValue) { + this.xAxisValue = xAxisValue; + this.yValue = yValue; + this.xValue = xValue; + } + } + + private class ValueFormatter implements IValueFormatter + { + + private final DecimalFormat mFormat; + + ValueFormatter() { + mFormat = new DecimalFormat("######.0"); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartPositiveNegative.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java new file mode 100644 index 0000000000..6288dda2be --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java @@ -0,0 +1,248 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BubbleChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.BubbleDataSet; +import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class BubbleChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private BubbleChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_bubblechart); + + setTitle("BubbleChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); + + chart.setOnChartValueSelectedListener(this); + + chart.setDrawGridBackground(false); + + chart.setTouchEnabled(true); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + + chart.setMaxVisibleValueCount(200); + chart.setPinchZoom(true); + + seekBarX.setProgress(10); + seekBarY.setProgress(50); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setTypeface(tfLight); + + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); + yl.setSpaceTop(30f); + yl.setSpaceBottom(30f); + yl.setDrawZeroLine(false); + + chart.getAxisRight().setEnabled(false); + + XAxis xl = chart.getXAxis(); + xl.setPosition(XAxis.XAxisPosition.BOTTOM); + xl.setTypeface(tfLight); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + int count = seekBarX.getProgress(); + int range = seekBarY.getProgress(); + + tvX.setText(String.valueOf(count)); + tvY.setText(String.valueOf(range)); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + values1.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range), getResources().getDrawable(R.drawable.star))); + values2.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range), getResources().getDrawable(R.drawable.star))); + values3.add(new BubbleEntry(i, (float) (Math.random() * range), (float) (Math.random() * range))); + } + + // create a dataset and give it a type + BubbleDataSet set1 = new BubbleDataSet(values1, "DS 1"); + set1.setDrawIcons(false); + set1.setColor(ColorTemplate.COLORFUL_COLORS[0], 130); + set1.setDrawValues(true); + + BubbleDataSet set2 = new BubbleDataSet(values2, "DS 2"); + set2.setDrawIcons(false); + set2.setIconsOffset(new MPPointF(0, 15)); + set2.setColor(ColorTemplate.COLORFUL_COLORS[1], 130); + set2.setDrawValues(true); + + BubbleDataSet set3 = new BubbleDataSet(values3, "DS 3"); + set3.setColor(ColorTemplate.COLORFUL_COLORS[2], 130); + set3.setDrawValues(true); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + dataSets.add(set2); + dataSets.add(set3); + + // create a data object with the data sets + BubbleData data = new BubbleData(dataSets); + data.setDrawValues(false); + data.setValueTypeface(tfLight); + data.setValueTextSize(8f); + data.setValueTextColor(Color.WHITE); + data.setHighlightCircleWidth(1.5f); + + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bubble, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "BubbleChartActivity"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() {} + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java new file mode 100644 index 0000000000..ecf7167a62 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java @@ -0,0 +1,238 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.Paint; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.CandleStickChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.CandleDataSet; +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class CandleStickChartActivity extends DemoBase implements OnSeekBarChangeListener { + + private CandleStickChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_candlechart); + + setTitle("CandleStickChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawGridBackground(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setDrawGridLines(false); + + YAxis leftAxis = chart.getAxisLeft(); +// leftAxis.setEnabled(false); + leftAxis.setLabelCount(7, false); + leftAxis.setDrawGridLines(false); + leftAxis.setDrawAxisLine(false); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); +// rightAxis.setStartAtZero(false); + + // setting data + seekBarX.setProgress(40); + seekBarY.setProgress(100); + + chart.getLegend().setEnabled(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + progress = (seekBarX.getProgress()); + + tvX.setText(String.valueOf(progress)); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + chart.resetTracking(); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < progress; i++) { + float multi = (seekBarY.getProgress() + 1); + float val = (float) (Math.random() * 40) + multi; + + float high = (float) (Math.random() * 9) + 8f; + float low = (float) (Math.random() * 9) + 8f; + + float open = (float) (Math.random() * 6) + 1f; + float close = (float) (Math.random() * 6) + 1f; + + boolean even = i % 2 == 0; + + values.add(new CandleEntry( + i, val + high, + val - low, + even ? val + open : val - open, + even ? val - close : val + close, + getResources().getDrawable(R.drawable.star) + )); + } + + CandleDataSet set1 = new CandleDataSet(values, "Data Set"); + + set1.setDrawIcons(false); + set1.setAxisDependency(AxisDependency.LEFT); +// set1.setColor(Color.rgb(80, 80, 80)); + set1.setShadowColor(Color.DKGRAY); + set1.setShadowWidth(0.7f); + set1.setDecreasingColor(Color.RED); + set1.setDecreasingPaintStyle(Paint.Style.FILL); + set1.setIncreasingColor(Color.rgb(122, 242, 84)); + set1.setIncreasingPaintStyle(Paint.Style.STROKE); + set1.setNeutralColor(Color.BLUE); + //set1.setHighlightLineWidth(1f); + + CandleData data = new CandleData(set1); + + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.candle, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleMakeShadowSameColorAsCandle: { + for (ICandleDataSet set : chart.getData().getDataSets()) { + ((CandleDataSet) set).setShadowColorSameAsCandle(!set.getShadowColorSameAsCandle()); + } + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "CandleStickChartActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java new file mode 100644 index 0000000000..0308b9a891 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java @@ -0,0 +1,282 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.CombinedChart; +import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.BubbleData; +import com.github.mikephil.charting.data.BubbleDataSet; +import com.github.mikephil.charting.data.BubbleEntry; +import com.github.mikephil.charting.data.CandleData; +import com.github.mikephil.charting.data.CandleDataSet; +import com.github.mikephil.charting.data.CandleEntry; +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.LineDataSet; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class CombinedChartActivity extends DemoBase { + + private CombinedChart chart; + private final int count = 12; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_combined); + + setTitle("CombinedChartActivity"); + + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); + chart.setBackgroundColor(Color.WHITE); + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + chart.setHighlightFullBarEnabled(false); + + // draw bars behind lines + chart.setDrawOrder(new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.CANDLE, DrawOrder.LINE, DrawOrder.SCATTER + }); + + Legend l = chart.getLegend(); + l.setWordWrapEnabled(true); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setDrawGridLines(false); + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setDrawGridLines(false); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTH_SIDED); + xAxis.setAxisMinimum(0f); + xAxis.setGranularity(1f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + @Override + public String getFormattedValue(float value, AxisBase axis) { + return months[(int) value % months.length]; + } + }); + + CombinedData data = new CombinedData(); + + data.setData(generateLineData()); + data.setData(generateBarData()); + data.setData(generateBubbleData()); + data.setData(generateScatterData()); + data.setData(generateCandleData()); + data.setValueTypeface(tfLight); + + xAxis.setAxisMaximum(data.getXMax() + 0.25f); + + chart.setData(data); + chart.invalidate(); + } + + private LineData generateLineData() { + + LineData d = new LineData(); + + ArrayList entries = new ArrayList<>(); + + for (int index = 0; index < count; index++) + entries.add(new Entry(index + 0.5f, getRandom(15, 5))); + + LineDataSet set = new LineDataSet(entries, "Line DataSet"); + set.setColor(Color.rgb(240, 238, 70)); + set.setLineWidth(2.5f); + set.setCircleColor(Color.rgb(240, 238, 70)); + set.setCircleRadius(5f); + set.setFillColor(Color.rgb(240, 238, 70)); + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set.setDrawValues(true); + set.setValueTextSize(10f); + set.setValueTextColor(Color.rgb(240, 238, 70)); + + set.setAxisDependency(YAxis.AxisDependency.LEFT); + d.addDataSet(set); + + return d; + } + + private BarData generateBarData() { + + ArrayList entries1 = new ArrayList<>(); + ArrayList entries2 = new ArrayList<>(); + + for (int index = 0; index < count; index++) { + entries1.add(new BarEntry(0, getRandom(25, 25))); + + // stacked + entries2.add(new BarEntry(0, new float[]{getRandom(13, 12), getRandom(13, 12)})); + } + + BarDataSet set1 = new BarDataSet(entries1, "Bar 1"); + set1.setColor(Color.rgb(60, 220, 78)); + set1.setValueTextColor(Color.rgb(60, 220, 78)); + set1.setValueTextSize(10f); + set1.setAxisDependency(YAxis.AxisDependency.LEFT); + + BarDataSet set2 = new BarDataSet(entries2, ""); + set2.setStackLabels(new String[]{"Stack 1", "Stack 2"}); + set2.setColors(Color.rgb(61, 165, 255), Color.rgb(23, 197, 255)); + set2.setValueTextColor(Color.rgb(61, 165, 255)); + set2.setValueTextSize(10f); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); + + float groupSpace = 0.06f; + float barSpace = 0.02f; // x2 dataset + float barWidth = 0.45f; // x2 dataset + // (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" + + BarData d = new BarData(set1, set2); + d.setBarWidth(barWidth); + + // make this BarData object grouped + d.groupBars(0, groupSpace, barSpace); // start at x = 0 + + return d; + } + + private ScatterData generateScatterData() { + + ScatterData d = new ScatterData(); + + ArrayList entries = new ArrayList<>(); + + for (float index = 0; index < count; index += 0.5f) + entries.add(new Entry(index + 0.25f, getRandom(10, 55))); + + ScatterDataSet set = new ScatterDataSet(entries, "Scatter DataSet"); + set.setColors(ColorTemplate.MATERIAL_COLORS); + set.setScatterShapeSize(7.5f); + set.setDrawValues(false); + set.setValueTextSize(10f); + d.addDataSet(set); + + return d; + } + + private CandleData generateCandleData() { + + CandleData d = new CandleData(); + + ArrayList entries = new ArrayList<>(); + + for (int index = 0; index < count; index += 2) + entries.add(new CandleEntry(index + 1f, 90, 70, 85, 75f)); + + CandleDataSet set = new CandleDataSet(entries, "Candle DataSet"); + set.setDecreasingColor(Color.rgb(142, 150, 175)); + set.setShadowColor(Color.DKGRAY); + set.setBarSpace(0.3f); + set.setValueTextSize(10f); + set.setDrawValues(false); + d.addDataSet(set); + + return d; + } + + private BubbleData generateBubbleData() { + + BubbleData bd = new BubbleData(); + + ArrayList entries = new ArrayList<>(); + + for (int index = 0; index < count; index++) { + float y = getRandom(10, 105); + float size = getRandom(100, 105); + entries.add(new BubbleEntry(index + 0.5f, y, size)); + } + + BubbleDataSet set = new BubbleDataSet(entries, "Bubble DataSet"); + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setValueTextSize(10f); + set.setValueTextColor(Color.WHITE); + set.setHighlightCircleWidth(1.5f); + set.setDrawValues(true); + bd.addDataSet(set); + + return bd; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.combined, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleLineValues: { + for (IDataSet set : chart.getData().getDataSets()) { + if (set instanceof LineDataSet) + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleBarValues: { + for (IDataSet set : chart.getData().getDataSets()) { + if (set instanceof BarDataSet) + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionRemoveDataSet: { + int rnd = (int) getRandom(chart.getData().getDataSetCount(), 0); + chart.getData().removeDataSet(chart.getData().getDataSetByIndex(rnd)); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + chart.invalidate(); + break; + } + } + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java new file mode 100644 index 0000000000..996e088f43 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java @@ -0,0 +1,320 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class CubicLineChartActivity extends DemoBase implements OnSeekBarChangeListener { + + private LineChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart); + + setTitle("CubicLineChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + chart = findViewById(R.id.chart1); + chart.setViewPortOffsets(0, 0, 0, 0); + chart.setBackgroundColor(Color.rgb(104, 241, 175)); + + // no description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawGridBackground(false); + chart.setMaxHighlightDistance(300); + + XAxis x = chart.getXAxis(); + x.setEnabled(false); + + YAxis y = chart.getAxisLeft(); + y.setTypeface(tfLight); + y.setLabelCount(6, false); + y.setTextColor(Color.WHITE); + y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + y.setDrawGridLines(false); + y.setAxisLineColor(Color.WHITE); + + chart.getAxisRight().setEnabled(false); + + // add data + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + // lower max, as cubic runs significantly slower than linear + seekBarX.setMax(700); + + seekBarX.setProgress(45); + seekBarY.setProgress(100); + + chart.getLegend().setEnabled(false); + + chart.animateXY(2000, 2000); + + // don't forget to refresh the drawing + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * (range + 1)) + 20; + values.add(new Entry(i, val)); + } + + LineDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values, "DataSet 1"); + + set1.setMode(LineDataSet.Mode.CUBIC_BEZIER); + set1.setCubicIntensity(0.2f); + set1.setDrawFilled(true); + set1.setDrawCircles(false); + set1.setLineWidth(1.8f); + set1.setCircleRadius(4f); + set1.setCircleColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setColor(Color.WHITE); + set1.setFillColor(Color.WHITE); + set1.setFillAlpha(100); + set1.setDrawHorizontalHighlightIndicator(false); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return chart.getAxisLeft().getAxisMinimum(); + } + }); + + // create a data object with the data sets + LineData data = new LineData(set1); + data.setValueTypeface(tfLight); + data.setValueTextSize(9f); + data.setDrawValues(false); + + // set data + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + + // redraw + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "CubicLineChartActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java similarity index 61% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index d12b3ecbd7..7a32e5329c 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java @@ -1,8 +1,10 @@ - +// TODO: Finish and add to main activity list package com.xxmassdeveloper.mpchartexample; -import android.graphics.Typeface; +import android.Manifest; +import android.content.pm.PackageManager; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -16,10 +18,10 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.listener.OnDrawListener; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; @@ -28,13 +30,13 @@ /** * This Activity demonstrates drawing into the Chart with the finger. Both line, * bar and scatter charts can be used for drawing. - * + * * @author Philipp Jahoda */ public class DrawChartActivity extends DemoBase implements OnChartValueSelectedListener, OnDrawListener { - private LineChart mChart; + private LineChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,56 +45,49 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_draw_chart); - mChart = (LineChart) findViewById(R.id.chart1); + setTitle("DrawChartActivity"); + + chart = findViewById(R.id.chart1); // listener for selecting and drawing - mChart.setOnChartValueSelectedListener(this); - mChart.setOnDrawListener(this); + chart.setOnChartValueSelectedListener(this); + chart.setOnDrawListener(this); - // if disabled, drawn datasets with the finger will not be automatically + // if disabled, drawn data sets with the finger will not be automatically // finished - // mChart.setAutoFinish(true); - mChart.setDrawGridBackground(false); + // chart.setAutoFinish(true); + chart.setDrawGridBackground(false); // add dummy-data to the chart initWithDummyData(); - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfRegular); xl.setAvoidFirstLastClipping(true); - YAxis yl = mChart.getAxisLeft(); - yl.setTypeface(tf); + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfRegular); - mChart.getLegend().setEnabled(false); + chart.getLegend().setEnabled(false); - // mChart.setYRange(-40f, 40f, true); + // chart.setYRange(-40f, 40f, true); // call this to reset the changed y-range - // mChart.resetYRange(true); + // chart.resetYRange(true); } private void initWithDummyData() { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < 24; i++) { - xVals.add((i) + ":00"); - } - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); // create a dataset and give it a type (0) - LineDataSet set1 = new LineDataSet(yVals, "DataSet"); + LineDataSet set1 = new LineDataSet(values, "DataSet"); set1.setLineWidth(3f); set1.setCircleRadius(5f); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets + // create a data object with the data sets + LineData data = new LineData(set1); - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); - - mChart.setData(data); + chart.setData(data); } @Override @@ -106,7 +101,7 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -115,33 +110,36 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionSave: { - // mChart.saveToGallery("title"+System.currentTimeMillis()); - mChart.saveToPath("title" + System.currentTimeMillis(), ""); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -149,10 +147,15 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + protected void saveToGallery() { + saveToGallery(chart, "DrawChartActivity"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override @@ -171,7 +174,7 @@ public void onDrawFinished(DataSet dataSet) { Log.i(Chart.LOG_TAG, "DataSet drawn. " + dataSet.toSimpleString()); // prepare the legend again - mChart.getLegendRenderer().computeLegend(mChart.getData()); + chart.getLegendRenderer().computeLegend(chart.getData()); } @Override diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java new file mode 100644 index 0000000000..84de449283 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java @@ -0,0 +1,242 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class DynamicalAddingActivity extends DemoBase implements OnChartValueSelectedListener { + + private LineChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_noseekbar); + + setTitle("DynamicalAddingActivity"); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); + chart.setNoDataText("No chart data available. Use the menu to add entries and data sets!"); + +// chart.getXAxis().setDrawLabels(false); +// chart.getXAxis().setDrawGridLines(false); + + chart.invalidate(); + } + + private final int[] colors = ColorTemplate.VORDIPLOM_COLORS; + + private void addEntry() { + + LineData data = chart.getData(); + + if (data == null) { + data = new LineData(); + chart.setData(data); + } + + ILineDataSet set = data.getDataSetByIndex(0); + // set.addEntry(...); // can be called as well + + if (set == null) { + set = createSet(); + data.addDataSet(set); + } + + // choose a random dataSet + int randomDataSetIndex = (int) (Math.random() * data.getDataSetCount()); + ILineDataSet randomSet = data.getDataSetByIndex(randomDataSetIndex); + float value = (float) (Math.random() * 50) + 50f * (randomDataSetIndex + 1); + + data.addEntry(new Entry(randomSet.getEntryCount(), value), randomDataSetIndex); + data.notifyDataChanged(); + + // let the chart know it's data has changed + chart.notifyDataSetChanged(); + + chart.setVisibleXRangeMaximum(6); + //chart.setVisibleYRangeMaximum(15, AxisDependency.LEFT); +// +// // this automatically refreshes the chart (calls invalidate()) + chart.moveViewTo(data.getEntryCount() - 7, 50f, AxisDependency.LEFT); + + } + + private void removeLastEntry() { + + LineData data = chart.getData(); + + if (data != null) { + + ILineDataSet set = data.getDataSetByIndex(0); + + if (set != null) { + + Entry e = set.getEntryForXValue(set.getEntryCount() - 1, Float.NaN); + + data.removeEntry(e, 0); + // or remove by index + // mData.removeEntryByXValue(xIndex, dataSetIndex); + data.notifyDataChanged(); + chart.notifyDataSetChanged(); + chart.invalidate(); + } + } + } + + private void addDataSet() { + + LineData data = chart.getData(); + + if (data == null) { + chart.setData(new LineData()); + } else { + int count = (data.getDataSetCount() + 1); + int amount = data.getDataSetByIndex(0).getEntryCount(); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < amount; i++) { + values.add(new Entry(i, (float) (Math.random() * 50f) + 50f * count)); + } + + LineDataSet set = new LineDataSet(values, "DataSet " + count); + set.setLineWidth(2.5f); + set.setCircleRadius(4.5f); + + int color = colors[count % colors.length]; + + set.setColor(color); + set.setCircleColor(color); + set.setHighLightColor(color); + set.setValueTextSize(10f); + set.setValueTextColor(color); + + data.addDataSet(set); + data.notifyDataChanged(); + chart.notifyDataSetChanged(); + chart.invalidate(); + } + } + + private void removeDataSet() { + + LineData data = chart.getData(); + + if (data != null) { + + data.removeDataSet(data.getDataSetByIndex(data.getDataSetCount() - 1)); + + chart.notifyDataSetChanged(); + chart.invalidate(); + } + } + + private LineDataSet createSet() { + + LineDataSet set = new LineDataSet(null, "DataSet 1"); + set.setLineWidth(2.5f); + set.setCircleRadius(4.5f); + set.setColor(Color.rgb(240, 99, 99)); + set.setCircleColor(Color.rgb(240, 99, 99)); + set.setHighLightColor(Color.rgb(190, 190, 190)); + set.setAxisDependency(AxisDependency.LEFT); + set.setValueTextSize(10f); + + return set; + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show(); + } + + @Override + public void onNothingSelected() {} + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.dynamical, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.java")); + startActivity(i); + break; + } + case R.id.actionAddEntry: { + addEntry(); + Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionRemoveEntry: { + removeLastEntry(); + Toast.makeText(this, "Entry removed!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionAddDataSet: { + addDataSet(); + Toast.makeText(this, "DataSet added!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionRemoveDataSet: { + removeDataSet(); + Toast.makeText(this, "DataSet removed!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionClear: { + chart.clear(); + Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "DynamicalAddingActivity"); + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java new file mode 100644 index 0000000000..e821a04969 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java @@ -0,0 +1,189 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +/** + * This works by inverting the background and desired "fill" color. First, we draw the fill color + * that we want between the lines as the actual background of the chart. Then, we fill the area + * above the highest line and the area under the lowest line with the desired background color. + * + * This method makes it look like we filled the area between the lines, but really we are filling + * the area OUTSIDE the lines! + */ +@SuppressWarnings("SameParameterValue") +public class FilledLineActivity extends DemoBase { + + private LineChart chart; + private final int fillColor = Color.argb(150, 51, 181, 229); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_noseekbar); + + setTitle("FilledLineActivity"); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + chart.setGridBackgroundColor(fillColor); + chart.setDrawGridBackground(true); + + chart.setDrawBorders(true); + + // no description text + chart.getDescription().setEnabled(false); + + // if disabled, scaling can be done on x- and y-axis separately + chart.setPinchZoom(false); + + Legend l = chart.getLegend(); + l.setEnabled(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setEnabled(false); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setAxisMaximum(900f); + leftAxis.setAxisMinimum(-250f); + leftAxis.setDrawAxisLine(false); + leftAxis.setDrawZeroLine(false); + leftAxis.setDrawGridLines(false); + + chart.getAxisRight().setEnabled(false); + + // add data + setData(100, 60); + + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList values1 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 50; + values1.add(new Entry(i, val)); + } + + ArrayList values2 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 450; + values2.add(new Entry(i, val)); + } + + LineDataSet set1, set2; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) chart.getData().getDataSetByIndex(1); + set1.setValues(values1); + set2.setValues(values2); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values1, "DataSet 1"); + + set1.setAxisDependency(YAxis.AxisDependency.LEFT); + set1.setColor(Color.rgb(255, 241, 46)); + set1.setDrawCircles(false); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(255); + set1.setDrawFilled(true); + set1.setFillColor(Color.WHITE); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + // change the return value here to better understand the effect + // return 0; + return chart.getAxisLeft().getAxisMinimum(); + } + }); + + // create a dataset and give it a type + set2 = new LineDataSet(values2, "DataSet 2"); + set2.setAxisDependency(YAxis.AxisDependency.LEFT); + set2.setColor(Color.rgb(255, 241, 46)); + set2.setDrawCircles(false); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(255); + set2.setDrawFilled(true); + set2.setFillColor(Color.WHITE); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + set2.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + // change the return value here to better understand the effect + // return 600; + return chart.getAxisLeft().getAxisMaximum(); + } + }); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + dataSets.add(set2); + + // create a data object with the data sets + LineData data = new LineData(dataSets); + data.setDrawValues(false); + + // set data + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/FilledLineActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java new file mode 100644 index 0000000000..ed2adcc960 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java @@ -0,0 +1,168 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.graphics.Color; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.util.DisplayMetrics; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.RelativeLayout; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +@SuppressWarnings("SameParameterValue") +public class HalfPieChartActivity extends DemoBase { + + private PieChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart_half); + + setTitle("HalfPieChartActivity"); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.WHITE); + + moveOffScreen(); + + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); + + chart.setCenterTextTypeface(tfLight); + chart.setCenterText(generateCenterSpannableText()); + + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); + + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); + + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); + + chart.setDrawCenterText(true); + + chart.setRotationEnabled(false); + chart.setHighlightPerTapEnabled(true); + + chart.setMaxAngle(180f); // HALF CHART + chart.setRotationAngle(180f); + chart.setCenterTextOffset(0, -20); + + setData(4, 100); + + chart.animateY(1400, Easing.EaseInOutQuad); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setXEntrySpace(7f); + l.setYEntrySpace(0f); + l.setYOffset(0f); + + // entry label styling + chart.setEntryLabelColor(Color.WHITE); + chart.setEntryLabelTypeface(tfRegular); + chart.setEntryLabelTextSize(12f); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + values.add(new PieEntry((float) ((Math.random() * range) + range / 5), parties[i % parties.length])); + } + + PieDataSet dataSet = new PieDataSet(values, "Election Results"); + dataSet.setSliceSpace(3f); + dataSet.setSelectionShift(5f); + + dataSet.setColors(ColorTemplate.MATERIAL_COLORS); + //dataSet.setSelectionShift(0f); + + PieData data = new PieData(dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.WHITE); + data.setValueTypeface(tfLight); + chart.setData(data); + + chart.invalidate(); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + private void moveOffScreen() { + + DisplayMetrics displayMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); + + int height = displayMetrics.heightPixels; + + int offset = (int)(height * 0.65); /* percent to move */ + + RelativeLayout.LayoutParams rlParams = + (RelativeLayout.LayoutParams) chart.getLayoutParams(); + rlParams.setMargins(0, 0, 0, -offset); + chart.setLayoutParams(rlParams); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HalfPieChartActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java new file mode 100644 index 0000000000..27f7f29627 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java @@ -0,0 +1,291 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.RectF; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +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.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class HorizontalBarChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private HorizontalBarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_horizontalbarchart); + + setTitle("HorizontalBarChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + // chart.setHighlightEnabled(false); + + chart.setDrawBarShadow(false); + + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + // draw shadows for each bar that show the maximum value + // chart.setDrawBarShadow(true); + + chart.setDrawGridBackground(false); + + XAxis xl = chart.getXAxis(); + xl.setPosition(XAxisPosition.BOTTOM); + xl.setTypeface(tfLight); + xl.setDrawAxisLine(true); + xl.setDrawGridLines(false); + xl.setGranularity(10f); + + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); + yl.setDrawAxisLine(true); + yl.setDrawGridLines(true); + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) +// yl.setInverted(true); + + YAxis yr = chart.getAxisRight(); + yr.setTypeface(tfLight); + yr.setDrawAxisLine(true); + yr.setDrawGridLines(false); + yr.setAxisMinimum(0f); // this replaces setStartAtZero(true) +// yr.setInverted(true); + + chart.setFitBars(true); + chart.animateY(2500); + + // setting data + seekBarY.setProgress(50); + seekBarX.setProgress(12); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setXEntrySpace(4f); + } + + private void setData(int count, float range) { + + float barWidth = 9f; + float spaceForBar = 10f; + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range); + values.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(barWidth); + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + iSet.setDrawValues(!iSet.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + iSet.setDrawIcons(!iSet.isDrawIconsEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.setFitBars(true); + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "HorizontalBarChartActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + private final RectF mOnValueSelectedRectF = new RectF(); + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + + RectF bounds = mOnValueSelectedRectF; + chart.getBarBounds((BarEntry) e, bounds); + + MPPointF position = chart.getPosition(e, chart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency()); + + Log.i("bounds", bounds.toString()); + Log.i("position", position.toString()); + + MPPointF.recycleInstance(position); + } + + @Override + public void onNothingSelected() {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java new file mode 100644 index 0000000000..86d578cc43 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/HorizontalBarNegativeChartActivity.java @@ -0,0 +1,292 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.RectF; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +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.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class HorizontalBarNegativeChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private HorizontalBarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_horizontalbarchart); + + setTitle("HorizontalBarChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + // chart.setHighlightEnabled(false); + + chart.setDrawBarShadow(false); + + chart.setDrawValueAboveBar(true); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(60); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + // draw shadows for each bar that show the maximum value + // chart.setDrawBarShadow(true); + + chart.setDrawGridBackground(false); + + XAxis xl = chart.getXAxis(); + xl.setPosition(XAxisPosition.BOTTOM); + xl.setTypeface(tfLight); + xl.setDrawAxisLine(true); + xl.setDrawGridLines(false); + xl.setGranularity(10f); + + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); + yl.setDrawAxisLine(true); + yl.setDrawGridLines(true); +// yl.setInverted(true); + + YAxis yr = chart.getAxisRight(); + yr.setTypeface(tfLight); + yr.setDrawAxisLine(true); + yr.setDrawGridLines(false); +// yr.setInverted(true); + + chart.setFitBars(true); + chart.animateY(2500); + + // setting data + seekBarY.setProgress(50); + seekBarX.setProgress(12); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setXEntrySpace(4f); + } + + private void setData(int count, float range) { + + float barWidth = 9f; + float spaceForBar = 10f; + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range - range / 2); + values.add(new BarEntry(i * spaceForBar, val, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueTextSize(10f); + data.setValueTypeface(tfLight); + data.setBarWidth(barWidth); + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + iSet.setDrawValues(!iSet.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + iSet.setDrawIcons(!iSet.isDrawIconsEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + chart.setFitBars(true); + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "HorizontalBarChartActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + private final RectF mOnValueSelectedRectF = new RectF(); + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + + RectF bounds = mOnValueSelectedRectF; + chart.getBarBounds((BarEntry) e, bounds); + + MPPointF position = chart.getPosition(e, chart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency()); + + Log.i("bounds", bounds.toString()); + Log.i("position", position.toString()); + + MPPointF.recycleInstance(position); + } + + @Override + public void onNothingSelected() {} +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java similarity index 52% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java index 2f92912e60..08983710f2 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -1,7 +1,12 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -9,7 +14,6 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -import android.widget.Toast; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Legend; @@ -19,22 +23,22 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.EntryXComparator; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class InvertedLineChartActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { - private LineChart mChart; - private SeekBar mSeekBarX, mSeekBarY; + private LineChart chart; + private SeekBar seekBarX, seekBarY; private TextView tvX, tvY; @Override @@ -44,73 +48,98 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_linechart); - tvX = (TextView) findViewById(R.id.tvXMax); - tvY = (TextView) findViewById(R.id.tvYMax); + setTitle("InvertedLineChartActivity"); - mSeekBarX = (SeekBar) findViewById(R.id.seekBar1); - mSeekBarY = (SeekBar) findViewById(R.id.seekBar2); + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); - mSeekBarX.setProgress(45); - mSeekBarY.setProgress(100); + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); - mSeekBarY.setOnSeekBarChangeListener(this); - mSeekBarX.setOnSeekBarChangeListener(this); + seekBarY.setOnSeekBarChangeListener(this); + seekBarX.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); - mChart.setDrawGridBackground(false); - // no description text - mChart.setDescription(""); + chart.getDescription().setEnabled(false); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + chart.setPinchZoom(true); // set an alternative background color - // mChart.setBackgroundColor(Color.GRAY); + // chart.setBackgroundColor(Color.GRAY); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart - // set the marker to the chart - mChart.setMarkerView(mv); - - XAxis xl = mChart.getXAxis(); + XAxis xl = chart.getXAxis(); xl.setAvoidFirstLastClipping(true); - - YAxis leftAxis = mChart.getAxisLeft(); + xl.setAxisMinimum(0f); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setInverted(true); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - YAxis rightAxis = mChart.getAxisRight(); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); // add data - setData(25, 50); + seekBarX.setProgress(25); + seekBarY.setProgress(50); // // restrain the maximum scale-out factor - // mChart.setScaleMinima(3f, 3f); + // chart.setScaleMinima(3f, 3f); // // // center the view to a specific position inside the chart - // mChart.centerViewPort(10, 50); + // chart.centerViewPort(10, 50); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - // dont forget to refresh the drawing - mChart.invalidate(); + // don't forget to refresh the drawing + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList entries = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float xVal = (float) (Math.random() * range); + float yVal = (float) (Math.random() * range); + entries.add(new Entry(xVal, yVal)); + } + + // sort by x-value + Collections.sort(entries, new EntryXComparator()); + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(entries, "DataSet 1"); + + set1.setLineWidth(1.5f); + set1.setCircleRadius(4f); + + // create a data object with the data sets + LineData data = new LineData(set1); + + // set data + chart.setData(data); } @Override @@ -123,8 +152,14 @@ public boolean onCreateOptionsMenu(Menu menu) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java")); + startActivity(i); + break; + } case R.id.actionToggleValues: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -133,19 +168,19 @@ public boolean onOptionsItemSelected(MenuItem item) { set.setDrawValues(!set.isDrawValuesEnabled()); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if(mChart.getData() != null) { - mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled()); - mChart.invalidate(); + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); } break; } case R.id.actionToggleFilled: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -156,11 +191,11 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawFilled(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleCircles: { - List sets = mChart.getData() + List sets = chart.getData() .getDataSets(); for (ILineDataSet iSet : sets) { @@ -171,45 +206,42 @@ public boolean onOptionsItemSelected(MenuItem item) { else set.setDrawCircles(true); } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.animateX: { - mChart.animateX(3000); + chart.animateX(2000); break; } case R.id.animateY: { - mChart.animateY(3000); + chart.animateY(2000); break; } case R.id.animateXY: { - mChart.animateXY(3000, 3000); + chart.animateXY(2000, 2000); break; } case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); else - mChart.setPinchZoom(true); + chart.setPinchZoom(true); - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleAutoScaleMinMax: { - mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled()); - mChart.notifyDataSetChanged(); + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); break; } case R.id.actionSave: { - if (mChart.saveToPath("title" + System.currentTimeMillis(), "")) { - Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", - Toast.LENGTH_SHORT).show(); - } else - Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) - .show(); - - // mChart.saveToGallery("title"+System.currentTimeMillis()) + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } break; } } @@ -219,67 +251,33 @@ public boolean onOptionsItemSelected(MenuItem item) { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - tvX.setText("" + (mSeekBarX.getProgress() + 1)); - tvY.setText("" + (mSeekBarY.getProgress())); + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); - setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress()); + setData(seekBarX.getProgress(), seekBarY.getProgress()); // redraw - mChart.invalidate(); + chart.invalidate(); } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { - Log.i("VAL SELECTED", - "Value: " + e.getVal() + ", xIndex: " + e.getXIndex() - + ", DataSet index: " + dataSetIndex); + protected void saveToGallery() { + saveToGallery(chart, "InvertedLineChartActivity"); } @Override - public void onNothingSelected() { - // TODO Auto-generated method stub - + public void onValueSelected(Entry e, Highlight h) { + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); } @Override - public void onStartTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } + public void onNothingSelected() {} @Override - public void onStopTrackingTouch(SeekBar seekBar) { - // TODO Auto-generated method stub - - } - - private void setData(int count, float range) { - - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add((i % 30) + "/" + (i % 12) + "/14"); - } - - ArrayList yVals = new ArrayList(); + public void onStartTrackingTouch(SeekBar seekBar) {} - for (int i = 0; i < count; i++) { - float mult = (range + 1); - float val = (float) (Math.random() * mult) + 3;// + (float) - // ((mult * - // 0.1) / 10); - yVals.add(new Entry(val, i)); - } - - // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); - - set1.setLineWidth(1.5f); - set1.setCircleRadius(4f); - - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - - // set data - mChart.setData(data); - } + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java new file mode 100644 index 0000000000..dd43b056eb --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java @@ -0,0 +1,454 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendForm; +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.Utils; +import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Example of a heavily customized {@link LineChart} with limit lines, custom line shapes, etc. + * + * @since 1.7.4 + * @version 3.1.0 + */ +public class LineChartActivity1 extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private LineChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart); + + setTitle("LineChartActivity1"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setMax(180); + seekBarY.setOnSeekBarChangeListener(this); + + + { // // Chart Style // // + chart = findViewById(R.id.chart1); + + // background color + chart.setBackgroundColor(Color.WHITE); + + // disable description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + // set listeners + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); + + // create marker to display box when values are selected + MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); + + // Set the marker to the chart + mv.setChartView(chart); + chart.setMarker(mv); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + // chart.setScaleXEnabled(true); + // chart.setScaleYEnabled(true); + + // force pinch zoom along both axis + chart.setPinchZoom(true); + } + + XAxis xAxis; + { // // X-Axis Style // // + xAxis = chart.getXAxis(); + + // vertical grid lines + xAxis.enableGridDashedLine(10f, 10f, 0f); + } + + YAxis yAxis; + { // // Y-Axis Style // // + yAxis = chart.getAxisLeft(); + + // disable dual axis (only use LEFT axis) + chart.getAxisRight().setEnabled(false); + + // horizontal grid lines + yAxis.enableGridDashedLine(10f, 10f, 0f); + + // axis range + yAxis.setAxisMaximum(200f); + yAxis.setAxisMinimum(-50f); + } + + + { // // Create Limit Lines // // + LimitLine llXAxis = new LimitLine(9f, "Index 10"); + llXAxis.setLineWidth(4f); + llXAxis.enableDashedLine(10f, 10f, 0f); + llXAxis.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + llXAxis.setTextSize(10f); + llXAxis.setTypeface(tfRegular); + + LimitLine ll1 = new LimitLine(150f, "Upper Limit"); + ll1.setLineWidth(4f); + ll1.enableDashedLine(10f, 10f, 0f); + ll1.setLabelPosition(LimitLabelPosition.RIGHT_TOP); + ll1.setTextSize(10f); + ll1.setTypeface(tfRegular); + + LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); + ll2.setLineWidth(4f); + ll2.enableDashedLine(10f, 10f, 0f); + ll2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM); + ll2.setTextSize(10f); + ll2.setTypeface(tfRegular); + + // draw limit lines behind data instead of on top + yAxis.setDrawLimitLinesBehindData(true); + xAxis.setDrawLimitLinesBehindData(true); + + // add limit lines + yAxis.addLimitLine(ll1); + yAxis.addLimitLine(ll2); + //xAxis.addLimitLine(llXAxis); + } + + // add data + seekBarX.setProgress(45); + seekBarY.setProgress(180); + setData(45, 180); + + // draw points over time + chart.animateX(1500); + + // get the legend (only possible after setting data) + Legend l = chart.getLegend(); + + // draw legend entries as lines + l.setForm(LegendForm.LINE); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + + float val = (float) (Math.random() * range) - 30; + values.add(new Entry(i, val, getResources().getDrawable(R.drawable.star))); + } + + LineDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + set1.notifyDataSetChanged(); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values, "DataSet 1"); + + set1.setDrawIcons(false); + + // draw dashed line + set1.enableDashedLine(10f, 5f, 0f); + + // black lines and points + set1.setColor(Color.BLACK); + set1.setCircleColor(Color.BLACK); + + // line thickness and point size + set1.setLineWidth(1f); + set1.setCircleRadius(3f); + + // draw points as solid circles + set1.setDrawCircleHole(false); + + // customize legend entry + set1.setFormLineWidth(1f); + set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f)); + set1.setFormSize(15.f); + + // text size of values + set1.setValueTextSize(9f); + + // draw selection line as dashed + set1.enableDashedHighlightLine(10f, 5f, 0f); + + // set the filled area + set1.setDrawFilled(true); + set1.setFillFormatter(new IFillFormatter() { + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + return chart.getAxisLeft().getAxisMinimum(); + } + }); + + // set color of filled area + if (Utils.getSDKInt() >= 18) { + // drawables only supported on api level 18 and above + Drawable drawable = ContextCompat.getDrawable(this, R.drawable.fade_red); + set1.setFillDrawable(drawable); + } else { + set1.setFillColor(Color.BLACK); + } + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + + // create a data object with the data sets + LineData data = new LineData(dataSets); + + // set data + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000, Easing.EaseInCubic); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + + // redraw + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "LineChartActivity1"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("Entry selected", e.toString()); + Log.i("LOW HIGH", "low: " + chart.getLowestVisibleX() + ", high: " + chart.getHighestVisibleX()); + Log.i("MIN MAX", "xMin: " + chart.getXChartMin() + ", xMax: " + chart.getXChartMax() + ", yMin: " + chart.getYChartMin() + ", yMax: " + chart.getYChartMax()); + } + + @Override + public void onNothingSelected() { + Log.i("Nothing selected", "Nothing selected."); + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java new file mode 100644 index 0000000000..6b9cbb5f22 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java @@ -0,0 +1,409 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.Legend.LegendForm; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Example of a dual axis {@link LineChart} with multiple data sets. + * + * @since 1.7.4 + * @version 3.1.0 + */ +public class LineChartActivity2 extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private LineChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart); + + setTitle("LineChartActivity2"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + + // no description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + chart.setDragDecelerationFrictionCoef(0.9f); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); + chart.setHighlightPerDragEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + chart.setPinchZoom(true); + + // set an alternative background color + chart.setBackgroundColor(Color.LTGRAY); + + // add data + seekBarX.setProgress(20); + seekBarY.setProgress(30); + + chart.animateX(1500); + + // get the legend (only possible after setting data) + Legend l = chart.getLegend(); + + // modify the legend ... + l.setForm(LegendForm.LINE); + l.setTypeface(tfLight); + l.setTextSize(11f); + l.setTextColor(Color.WHITE); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); +// l.setYOffset(11f); + + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); + xAxis.setTextSize(11f); + xAxis.setTextColor(Color.WHITE); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); + leftAxis.setTextColor(ColorTemplate.getHoloBlue()); + leftAxis.setAxisMaximum(200f); + leftAxis.setAxisMinimum(0f); + leftAxis.setDrawGridLines(true); + leftAxis.setGranularityEnabled(true); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setTypeface(tfLight); + rightAxis.setTextColor(Color.RED); + rightAxis.setAxisMaximum(900); + rightAxis.setAxisMinimum(-200); + rightAxis.setDrawGridLines(false); + rightAxis.setDrawZeroLine(false); + rightAxis.setGranularityEnabled(false); + } + + private void setData(int count, float range) { + + ArrayList values1 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * (range / 2f)) + 50; + values1.add(new Entry(i, val)); + } + + ArrayList values2 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 450; + values2.add(new Entry(i, val)); + } + + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * range) + 500; + values3.add(new Entry(i, val)); + } + + LineDataSet set1, set2, set3; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (LineDataSet) chart.getData().getDataSetByIndex(0); + set2 = (LineDataSet) chart.getData().getDataSetByIndex(1); + set3 = (LineDataSet) chart.getData().getDataSetByIndex(2); + set1.setValues(values1); + set2.setValues(values2); + set3.setValues(values3); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + // create a dataset and give it a type + set1 = new LineDataSet(values1, "DataSet 1"); + + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setCircleColor(Color.WHITE); + set1.setLineWidth(2f); + set1.setCircleRadius(3f); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + //set1.setFillFormatter(new MyFillFormatter(0f)); + //set1.setDrawHorizontalHighlightIndicator(false); + //set1.setVisible(false); + //set1.setCircleHoleColor(Color.WHITE); + + // create a dataset and give it a type + set2 = new LineDataSet(values2, "DataSet 2"); + set2.setAxisDependency(AxisDependency.RIGHT); + set2.setColor(Color.RED); + set2.setCircleColor(Color.WHITE); + set2.setLineWidth(2f); + set2.setCircleRadius(3f); + set2.setFillAlpha(65); + set2.setFillColor(Color.RED); + set2.setDrawCircleHole(false); + set2.setHighLightColor(Color.rgb(244, 117, 117)); + //set2.setFillFormatter(new MyFillFormatter(900f)); + + set3 = new LineDataSet(values3, "DataSet 3"); + set3.setAxisDependency(AxisDependency.RIGHT); + set3.setColor(Color.YELLOW); + set3.setCircleColor(Color.WHITE); + set3.setLineWidth(2f); + set3.setCircleRadius(3f); + set3.setFillAlpha(65); + set3.setFillColor(ColorTemplate.colorWithAlpha(Color.YELLOW, 200)); + set3.setDrawCircleHole(false); + set3.setHighLightColor(Color.rgb(244, 117, 117)); + + // create a data object with the data sets + LineData data = new LineData(set1, set2, set3); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + chart.setData(data); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + + // redraw + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "LineChartActivity2"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("Entry selected", e.toString()); + + chart.centerViewToAnimated(e.getX(), e.getY(), chart.getData().getDataSetByIndex(h.getDataSetIndex()) + .getAxisDependency(), 500); + //chart.zoomAndCenterAnimated(2.5f, 2.5f, e.getX(), e.getY(), chart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); + //chart.zoomAndCenterAnimated(1.8f, 1.8f, e.getX(), e.getY(), chart.getData().getDataSetByIndex(dataSetIndex) + // .getAxisDependency(), 1000); + } + + @Override + public void onNothingSelected() { + Log.i("Nothing selected", "Nothing selected."); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java similarity index 55% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java index 5d1e212d78..6a12e8f95d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java @@ -1,9 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.content.Intent; import android.graphics.Color; import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.github.mikephil.charting.charts.LineChart; @@ -11,15 +15,14 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +@SuppressWarnings("SameParameterValue") public class LineChartActivityColored extends DemoBase { - private LineChart[] mCharts = new LineChart[4]; - private Typeface mTf; + private final LineChart[] charts = new LineChart[4]; @Override protected void onCreate(Bundle savedInstanceState) { @@ -28,35 +31,40 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_colored_lines); - mCharts[0] = (LineChart) findViewById(R.id.chart1); - mCharts[1] = (LineChart) findViewById(R.id.chart2); - mCharts[2] = (LineChart) findViewById(R.id.chart3); - mCharts[3] = (LineChart) findViewById(R.id.chart4); + setTitle("LineChartActivityColored"); - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); + charts[0] = findViewById(R.id.chart1); + charts[1] = findViewById(R.id.chart2); + charts[2] = findViewById(R.id.chart3); + charts[3] = findViewById(R.id.chart4); - LineData data = getData(36, 100); - data.setValueTypeface(mTf); + Typeface mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Bold.ttf"); + + for (int i = 0; i < charts.length; i++) { + + LineData data = getData(36, 100); + data.setValueTypeface(mTf); - for (int i = 0; i < mCharts.length; i++) // add some transparency to the color with "& 0x90FFFFFF" - setupChart(mCharts[i], data, mColors[i % mColors.length]); + setupChart(charts[i], data, colors[i % colors.length]); + } } - private int[] mColors = new int[] { - Color.rgb(137, 230, 81), - Color.rgb(240, 240, 30), + private final int[] colors = new int[] { + Color.rgb(137, 230, 81), + Color.rgb(240, 240, 30), Color.rgb(89, 199, 250), Color.rgb(250, 104, 104) }; private void setupChart(LineChart chart, LineData data, int color) { + ((LineDataSet) data.getDataSetByIndex(0)).setCircleHoleColor(color); + // no description text - chart.setDescription(""); - chart.setNoDataTextDescription("You need to provide data for the chart."); - - // mChart.setDrawHorizontalGrid(false); + chart.getDescription().setEnabled(false); + + // chart.setDrawHorizontalGrid(false); // // enable / disable grid background chart.setDrawGridBackground(false); @@ -73,7 +81,7 @@ private void setupChart(LineChart chart, LineData data, int color) { chart.setPinchZoom(false); chart.setBackgroundColor(color); - + // set custom chart offsets (automatic offset calculation is hereby disabled) chart.setViewPortOffsets(10, 0, 10, 0); @@ -85,6 +93,8 @@ private void setupChart(LineChart chart, LineData data, int color) { l.setEnabled(false); chart.getAxisLeft().setEnabled(false); + chart.getAxisLeft().setSpaceTop(40); + chart.getAxisLeft().setSpaceBottom(40); chart.getAxisRight().setEnabled(false); chart.getXAxis().setEnabled(false); @@ -92,39 +102,54 @@ private void setupChart(LineChart chart, LineData data, int color) { // animate calls invalidate()... chart.animateX(2500); } - - private LineData getData(int count, float range) { - ArrayList xVals = new ArrayList(); - for (int i = 0; i < count; i++) { - xVals.add(mMonths[i % 12]); - } + private LineData getData(int count, float range) { - ArrayList yVals = new ArrayList(); + ArrayList values = new ArrayList<>(); for (int i = 0; i < count; i++) { float val = (float) (Math.random() * range) + 3; - yVals.add(new Entry(val, i)); + values.add(new Entry(i, val)); } // create a dataset and give it a type - LineDataSet set1 = new LineDataSet(yVals, "DataSet 1"); + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); // set1.setFillAlpha(110); // set1.setFillColor(Color.RED); set1.setLineWidth(1.75f); - set1.setCircleRadius(3f); + set1.setCircleRadius(5f); + set1.setCircleHoleRadius(2.5f); set1.setColor(Color.WHITE); set1.setCircleColor(Color.WHITE); set1.setHighLightColor(Color.WHITE); set1.setDrawValues(false); - ArrayList dataSets = new ArrayList(); - dataSets.add(set1); // add the datasets + // create a data object with the data sets + return new LineData(set1); + } - // create a data object with the datasets - LineData data = new LineData(xVals, dataSets); + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } - return data; + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivityColored.java")); + startActivity(i); + break; + } + } + + return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java new file mode 100644 index 0000000000..212b90ff87 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/LineChartTime.java @@ -0,0 +1,318 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class LineChartTime extends DemoBase implements OnSeekBarChangeListener { + + private LineChart chart; + private SeekBar seekBarX; + private TextView tvX; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart_time); + + setTitle("LineChartTime"); + + tvX = findViewById(R.id.tvXMax); + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + + // no description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + chart.setDragDecelerationFrictionCoef(0.9f); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); + chart.setHighlightPerDragEnabled(true); + + // set an alternative background color + chart.setBackgroundColor(Color.WHITE); + chart.setViewPortOffsets(0f, 0f, 0f, 0f); + + // add data + seekBarX.setProgress(100); + + // get the legend (only possible after setting data) + Legend l = chart.getLegend(); + l.setEnabled(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); + xAxis.setTypeface(tfLight); + xAxis.setTextSize(10f); + xAxis.setTextColor(Color.WHITE); + xAxis.setDrawAxisLine(false); + xAxis.setDrawGridLines(true); + xAxis.setTextColor(Color.rgb(255, 192, 56)); + xAxis.setCenterAxisLabels(true); + xAxis.setGranularity(1f); // one hour + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH); + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + long millis = TimeUnit.HOURS.toMillis((long) value); + return mFormat.format(new Date(millis)); + } + }); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART); + leftAxis.setTypeface(tfLight); + leftAxis.setTextColor(ColorTemplate.getHoloBlue()); + leftAxis.setDrawGridLines(true); + leftAxis.setGranularityEnabled(true); + leftAxis.setAxisMinimum(0f); + leftAxis.setAxisMaximum(170f); + leftAxis.setYOffset(-9f); + leftAxis.setTextColor(Color.rgb(255, 192, 56)); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); + } + + private void setData(int count, float range) { + + // now in hours + long now = TimeUnit.MILLISECONDS.toHours(System.currentTimeMillis()); + + ArrayList values = new ArrayList<>(); + + // count = hours + float to = now + count; + + // increment by 1 hour + for (float x = now; x < to; x++) { + + float y = getRandom(range, 50); + values.add(new Entry(x, y)); // add one entry per hour + } + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); + set1.setAxisDependency(AxisDependency.LEFT); + set1.setColor(ColorTemplate.getHoloBlue()); + set1.setValueTextColor(ColorTemplate.getHoloBlue()); + set1.setLineWidth(1.5f); + set1.setDrawCircles(false); + set1.setDrawValues(false); + set1.setFillAlpha(65); + set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setHighLightColor(Color.rgb(244, 117, 117)); + set1.setDrawCircleHole(false); + + // create a data object with the data sets + LineData data = new LineData(set1); + data.setValueTextColor(Color.WHITE); + data.setValueTextSize(9f); + + // set data + chart.setData(data); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartTime.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.getMode() == LineDataSet.Mode.CUBIC_BEZIER) + set.setMode(LineDataSet.Mode.LINEAR); + else + set.setMode(LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.getMode() == LineDataSet.Mode.STEPPED) + set.setMode(LineDataSet.Mode.LINEAR); + else + set.setMode(LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + + setData(seekBarX.getProgress(), 50); + + // redraw + chart.invalidate(); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "LineChartTime"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java similarity index 61% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index e1c69086bc..1466e5f9de 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java @@ -1,18 +1,22 @@ package com.xxmassdeveloper.mpchartexample; +import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.graphics.Color; -import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import androidx.annotation.NonNull; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ArrayAdapter; import android.widget.ListView; -import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; @@ -29,8 +33,8 @@ /** * Demonstrates the use of charts inside a ListView. IMPORTANT: provide a - * specific height attribute for the chart inside your listview-item - * + * specific height attribute for the chart inside your ListView item + * * @author Philipp Jahoda */ public class ListViewBarChartActivity extends DemoBase { @@ -41,10 +45,12 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - - ListView lv = (ListView) findViewById(R.id.listView1); - ArrayList list = new ArrayList(); + setTitle("ListViewBarChartActivity"); + + ListView lv = findViewById(R.id.listView1); + + ArrayList list = new ArrayList<>(); // 20 items for (int i = 0; i < 20; i++) { @@ -57,20 +63,18 @@ protected void onCreate(Bundle savedInstanceState) { private class ChartDataAdapter extends ArrayAdapter { - private Typeface mTf; - - public ChartDataAdapter(Context context, List objects) { + ChartDataAdapter(Context context, List objects) { super(context, 0, objects); - - mTf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); } + @SuppressLint("InflateParams") + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { BarData data = getItem(position); - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -78,7 +82,7 @@ public View getView(int position, View convertView, ViewGroup parent) { convertView = LayoutInflater.from(getContext()).inflate( R.layout.list_item_barchart, null); - holder.chart = (BarChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); @@ -87,32 +91,35 @@ public View getView(int position, View convertView, ViewGroup parent) { } // apply styling - data.setValueTypeface(mTf); - data.setValueTextColor(Color.BLACK); - holder.chart.setDescription(""); + if (data != null) { + data.setValueTypeface(tfLight); + data.setValueTextColor(Color.BLACK); + } + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); xAxis.setPosition(XAxisPosition.BOTTOM); - xAxis.setTypeface(mTf); + xAxis.setTypeface(tfLight); xAxis.setDrawGridLines(false); - + YAxis leftAxis = holder.chart.getAxisLeft(); - leftAxis.setTypeface(mTf); + leftAxis.setTypeface(tfLight); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(15f); - + YAxis rightAxis = holder.chart.getAxisRight(); - rightAxis.setTypeface(mTf); + rightAxis.setTypeface(tfLight); rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(15f); // set data holder.chart.setData(data); - + holder.chart.setFitBars(true); + // do not forget to refresh the chart // holder.chart.invalidate(); - holder.chart.animateY(700, Easing.EasingOption.EaseInCubic); + holder.chart.animateY(700); return convertView; } @@ -125,45 +132,50 @@ private class ViewHolder { /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Bar data */ private BarData generateData(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (float) (Math.random() * 70) + 30)); } - BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); + BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setBarShadowColor(Color.rgb(203, 203, 203)); - - ArrayList sets = new ArrayList(); + + ArrayList sets = new ArrayList<>(); sets.add(d); - - BarData cd = new BarData(getMonths(), sets); + + BarData cd = new BarData(sets); + cd.setBarWidth(0.9f); return cd; } - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java similarity index 60% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index ad242e99ec..1455691620 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java @@ -2,8 +2,13 @@ package com.xxmassdeveloper.mpchartexample; import android.content.Context; +import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; +import androidx.annotation.NonNull; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -18,6 +23,7 @@ import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.listviewitems.BarChartItem; @@ -31,8 +37,8 @@ /** * Demonstrates the use of charts inside a ListView. IMPORTANT: provide a - * specific height attribute for the chart inside your listview-item - * + * specific height attribute for the chart inside your ListView item + * * @author Philipp Jahoda */ public class ListViewMultiChartActivity extends DemoBase { @@ -43,20 +49,22 @@ protected void onCreate(Bundle savedInstanceState) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_listview_chart); - - ListView lv = (ListView) findViewById(R.id.listView1); - ArrayList list = new ArrayList(); + setTitle("ListViewMultiChartActivity"); + + ListView lv = findViewById(R.id.listView1); + + ArrayList list = new ArrayList<>(); // 30 items for (int i = 0; i < 30; i++) { - + if(i % 3 == 0) { list.add(new LineChartItem(generateDataLine(i + 1), getApplicationContext())); } else if(i % 3 == 1) { list.add(new BarChartItem(generateDataBar(i + 1), getApplicationContext())); } else if(i % 3 == 2) { - list.add(new PieChartItem(generateDataPie(i + 1), getApplicationContext())); + list.add(new PieChartItem(generateDataPie(), getApplicationContext())); } } @@ -66,141 +74,136 @@ protected void onCreate(Bundle savedInstanceState) { /** adapter that supports 3 different item types */ private class ChartDataAdapter extends ArrayAdapter { - - public ChartDataAdapter(Context context, List objects) { + + ChartDataAdapter(Context context, List objects) { super(context, 0, objects); } + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + //noinspection ConstantConditions return getItem(position).getView(position, convertView, getContext()); } - + @Override - public int getItemViewType(int position) { + public int getItemViewType(int position) { // return the views type - return getItem(position).getItemType(); + ChartItem ci = getItem(position); + return ci != null ? ci.getItemType() : 0; } - + @Override public int getViewTypeCount() { return 3; // we have 3 different item-types } } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Line data */ private LineData generateDataLine(int cnt) { - ArrayList e1 = new ArrayList(); + ArrayList values1 = new ArrayList<>(); for (int i = 0; i < 12; i++) { - e1.add(new Entry((int) (Math.random() * 65) + 40, i)); + values1.add(new Entry(i, (int) (Math.random() * 65) + 40)); } - LineDataSet d1 = new LineDataSet(e1, "New DataSet " + cnt + ", (1)"); + LineDataSet d1 = new LineDataSet(values1, "New DataSet " + cnt + ", (1)"); d1.setLineWidth(2.5f); d1.setCircleRadius(4.5f); d1.setHighLightColor(Color.rgb(244, 117, 117)); d1.setDrawValues(false); - - ArrayList e2 = new ArrayList(); + + ArrayList values2 = new ArrayList<>(); for (int i = 0; i < 12; i++) { - e2.add(new Entry(e1.get(i).getVal() - 30, i)); + values2.add(new Entry(i, values1.get(i).getY() - 30)); } - LineDataSet d2 = new LineDataSet(e2, "New DataSet " + cnt + ", (2)"); + LineDataSet d2 = new LineDataSet(values2, "New DataSet " + cnt + ", (2)"); d2.setLineWidth(2.5f); d2.setCircleRadius(4.5f); d2.setHighLightColor(Color.rgb(244, 117, 117)); d2.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[0]); d2.setDrawValues(false); - - ArrayList sets = new ArrayList(); + + ArrayList sets = new ArrayList<>(); sets.add(d1); sets.add(d2); - - LineData cd = new LineData(getMonths(), sets); - return cd; + + return new LineData(sets); } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Bar data */ private BarData generateDataBar(int cnt) { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 12; i++) { - entries.add(new BarEntry((int) (Math.random() * 70) + 30, i)); + entries.add(new BarEntry(i, (int) (Math.random() * 70) + 30)); } BarDataSet d = new BarDataSet(entries, "New DataSet " + cnt); - d.setBarSpacePercent(20f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); d.setHighLightAlpha(255); - - BarData cd = new BarData(getMonths(), d); + + BarData cd = new BarData(d); + cd.setBarWidth(0.9f); return cd; } - + /** * generates a random ChartData object with just one DataSet - * - * @return + * + * @return Pie data */ - private PieData generateDataPie(int cnt) { + private PieData generateDataPie() { - ArrayList entries = new ArrayList(); + ArrayList entries = new ArrayList<>(); for (int i = 0; i < 4; i++) { - entries.add(new Entry((int) (Math.random() * 70) + 30, i)); + entries.add(new PieEntry((float) ((Math.random() * 70) + 30), "Quarter " + (i+1))); } PieDataSet d = new PieDataSet(entries, ""); - + // space between slices d.setSliceSpace(2f); d.setColors(ColorTemplate.VORDIPLOM_COLORS); - - PieData cd = new PieData(getQuarters(), d); - return cd; + + return new PieData(d); } - - private ArrayList getQuarters() { - - ArrayList q = new ArrayList(); - q.add("1st Quarter"); - q.add("2nd Quarter"); - q.add("3rd Quarter"); - q.add("4th Quarter"); - - return q; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; } - private ArrayList getMonths() { - - ArrayList m = new ArrayList(); - m.add("Jan"); - m.add("Feb"); - m.add("Mar"); - m.add("Apr"); - m.add("May"); - m.add("Jun"); - m.add("Jul"); - m.add("Aug"); - m.add("Sep"); - m.add("Okt"); - m.add("Nov"); - m.add("Dec"); - - return m; + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java")); + startActivity(i); + break; + } + } + + return true; } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java new file mode 100644 index 0000000000..11d69d8dae --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java @@ -0,0 +1,354 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.listener.ChartTouchListener; +import com.github.mikephil.charting.listener.OnChartGestureListener; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class MultiLineChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartGestureListener, OnChartValueSelectedListener { + + private LineChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_linechart); + + setTitle("MultiLineChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); + chart.setDrawBorders(false); + + chart.getAxisLeft().setEnabled(false); + chart.getAxisRight().setDrawAxisLine(false); + chart.getAxisRight().setDrawGridLines(false); + chart.getXAxis().setDrawAxisLine(false); + chart.getXAxis().setDrawGridLines(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + chart.setPinchZoom(false); + + seekBarX.setProgress(20); + seekBarY.setProgress(100); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + } + + private final int[] colors = new int[] { + ColorTemplate.VORDIPLOM_COLORS[0], + ColorTemplate.VORDIPLOM_COLORS[1], + ColorTemplate.VORDIPLOM_COLORS[2] + }; + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + chart.resetTracking(); + + progress = seekBarX.getProgress(); + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList dataSets = new ArrayList<>(); + + for (int z = 0; z < 3; z++) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < progress; i++) { + double val = (Math.random() * seekBarY.getProgress()) + 3; + values.add(new Entry(i, (float) val)); + } + + LineDataSet d = new LineDataSet(values, "DataSet " + (z + 1)); + d.setLineWidth(2.5f); + d.setCircleRadius(4f); + + int color = colors[z % colors.length]; + d.setColor(color); + d.setCircleColor(color); + dataSets.add(d); + } + + // make the first DataSet dashed + ((LineDataSet) dataSets.get(0)).enableDashedLine(10, 10, 0); + ((LineDataSet) dataSets.get(0)).setColors(ColorTemplate.VORDIPLOM_COLORS); + ((LineDataSet) dataSets.get(0)).setCircleColors(ColorTemplate.VORDIPLOM_COLORS); + + LineData data = new LineData(dataSets); + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.line, menu); + menu.removeItem(R.id.actionToggleIcons); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/MultiLineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + /* + case R.id.actionToggleIcons: { break; } + */ + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleFilled: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCircles: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + if (set.isDrawCirclesEnabled()) + set.setDrawCircles(false); + else + set.setDrawCircles(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.CUBIC_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.CUBIC_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionToggleStepped: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.STEPPED + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.STEPPED); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHorizontalCubic: { + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; + set.setMode(set.getMode() == LineDataSet.Mode.HORIZONTAL_BEZIER + ? LineDataSet.Mode.LINEAR + : LineDataSet.Mode.HORIZONTAL_BEZIER); + } + chart.invalidate(); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + chart.animateXY(2000, 2000); + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "MultiLineChartActivity"); + } + + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "START, x: " + me.getX() + ", y: " + me.getY()); + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "END, lastGesture: " + lastPerformedGesture); + + // un-highlight values after the gesture is finished and no single-tap + if(lastPerformedGesture != ChartTouchListener.ChartGesture.SINGLE_TAP) + chart.highlightValues(null); // or highlightTouch(null) for callback to onNothingSelected(...) + } + + @Override + public void onChartLongPressed(MotionEvent me) { + Log.i("LongPress", "Chart long pressed."); + } + + @Override + public void onChartDoubleTapped(MotionEvent me) { + Log.i("DoubleTap", "Chart double-tapped."); + } + + @Override + public void onChartSingleTapped(MotionEvent me) { + Log.i("SingleTap", "Chart single-tapped."); + } + + @Override + public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { + Log.i("Fling", "Chart fling. VelocityX: " + velocityX + ", VelocityY: " + velocityY); + } + + @Override + public void onChartScale(MotionEvent me, float scaleX, float scaleY) { + Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); + } + + @Override + public void onChartTranslate(MotionEvent me, float dX, float dY) { + Log.i("Translate / Move", "dX: " + dX + ", dY: " + dY); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() {} + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java new file mode 100644 index 0000000000..c557323451 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java @@ -0,0 +1,144 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +@SuppressWarnings("SameParameterValue") +public class PerformanceLineChart extends DemoBase implements OnSeekBarChangeListener { + + private LineChart chart; + private SeekBar seekBarValues; + private TextView tvCount; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_performance_linechart); + + setTitle("PerformanceLineChart"); + + tvCount = findViewById(R.id.tvValueCount); + seekBarValues = findViewById(R.id.seekbarValues); + seekBarValues.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setDrawGridBackground(false); + + // no description text + chart.getDescription().setEnabled(false); + + // enable touch gestures + chart.setTouchEnabled(true); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + + // if disabled, scaling can be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.getAxisLeft().setDrawGridLines(false); + chart.getAxisRight().setEnabled(false); + chart.getXAxis().setDrawGridLines(true); + chart.getXAxis().setDrawAxisLine(false); + + seekBarValues.setProgress(9000); + + // don't forget to refresh the drawing + chart.invalidate(); + } + + private void setData(int count, float range) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * (range + 1)) + 3; + values.add(new Entry(i * 0.001f, val)); + } + + // create a dataset and give it a type + LineDataSet set1 = new LineDataSet(values, "DataSet 1"); + + set1.setColor(Color.BLACK); + set1.setLineWidth(0.5f); + set1.setDrawValues(false); + set1.setDrawCircles(false); + set1.setMode(LineDataSet.Mode.LINEAR); + set1.setDrawFilled(false); + + // create a data object with the data sets + LineData data = new LineData(set1); + + // set data + chart.setData(data); + + // get the legend (only possible after setting data) + Legend l = chart.getLegend(); + l.setEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + int count = seekBarValues.getProgress() + 1000; + tvCount.setText(String.valueOf(count)); + + chart.resetTracking(); + + setData(count, 500f); + + // redraw + chart.invalidate(); + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java new file mode 100644 index 0000000000..830025fbb1 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PieChartActivity.java @@ -0,0 +1,326 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class PieChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private PieChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart); + + setTitle("PieChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarX.setOnSeekBarChangeListener(this); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); + chart.setExtraOffsets(5, 10, 5, 5); + + chart.setDragDecelerationFrictionCoef(0.95f); + + chart.setCenterTextTypeface(tfLight); + chart.setCenterText(generateCenterSpannableText()); + + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); + + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); + + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); + + chart.setDrawCenterText(true); + + chart.setRotationAngle(0); + // enable rotation of the chart by touch + chart.setRotationEnabled(true); + chart.setHighlightPerTapEnabled(true); + + // chart.setUnit(" €"); + // chart.setDrawUnitsInChart(true); + + // add a selection listener + chart.setOnChartValueSelectedListener(this); + + seekBarX.setProgress(4); + seekBarY.setProgress(10); + + chart.animateY(1400, Easing.EaseInOutQuad); + // chart.spin(2000, 0, 360); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setXEntrySpace(7f); + l.setYEntrySpace(0f); + l.setYOffset(0f); + + // entry label styling + chart.setEntryLabelColor(Color.WHITE); + chart.setEntryLabelTypeface(tfRegular); + chart.setEntryLabelTextSize(12f); + } + + private void setData(int count, float range) { + ArrayList entries = new ArrayList<>(); + + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. + for (int i = 0; i < count ; i++) { + entries.add(new PieEntry((float) ((Math.random() * range) + range / 5), + parties[i % parties.length], + getResources().getDrawable(R.drawable.star))); + } + + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); + + dataSet.setDrawIcons(false); + + dataSet.setSliceSpace(3f); + dataSet.setIconsOffset(new MPPointF(0, 40)); + dataSet.setSelectionShift(5f); + + // add a lot of colors + + ArrayList colors = new ArrayList<>(); + + for (int c : ColorTemplate.VORDIPLOM_COLORS) + colors.add(c); + + for (int c : ColorTemplate.JOYFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.COLORFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.LIBERTY_COLORS) + colors.add(c); + + for (int c : ColorTemplate.PASTEL_COLORS) + colors.add(c); + + colors.add(ColorTemplate.getHoloBlue()); + + dataSet.setColors(colors); + //dataSet.setSelectionShift(0f); + + PieData data = new PieData(dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.WHITE); + data.setValueTypeface(tfLight); + chart.setData(data); + + // undo all highlights + chart.highlightValues(null); + + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.pie, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawIcons(!set.isDrawIconsEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHole: { + if (chart.isDrawHoleEnabled()) + chart.setDrawHoleEnabled(false); + else + chart.setDrawHoleEnabled(true); + chart.invalidate(); + break; + } + case R.id.actionToggleMinAngles: { + if (chart.getMinAngleForSlices() == 0f) + chart.setMinAngleForSlices(36f); + else + chart.setMinAngleForSlices(0f); + chart.notifyDataSetChanged(); + chart.invalidate(); + break; + } + case R.id.actionToggleCurvedSlices: { + boolean toSet = !chart.isDrawRoundedSlicesEnabled() || !chart.isDrawHoleEnabled(); + chart.setDrawRoundedSlices(toSet); + if (toSet && !chart.isDrawHoleEnabled()) { + chart.setDrawHoleEnabled(true); + } + if (toSet && chart.isDrawSlicesUnderHoleEnabled()) { + chart.setDrawSlicesUnderHole(false); + } + chart.invalidate(); + break; + } + case R.id.actionDrawCenter: { + if (chart.isDrawCenterTextEnabled()) + chart.setDrawCenterText(false); + else + chart.setDrawCenterText(true); + chart.invalidate(); + break; + } + case R.id.actionToggleXValues: { + + chart.setDrawEntryLabels(!chart.isDrawEntryLabelsEnabled()); + chart.invalidate(); + break; + } + case R.id.actionTogglePercent: + chart.setUsePercentValues(!chart.isUsePercentValuesEnabled()); + chart.invalidate(); + break; + case R.id.animateX: { + chart.animateX(1400); + break; + } + case R.id.animateY: { + chart.animateY(1400); + break; + } + case R.id.animateXY: { + chart.animateXY(1400, 1400); + break; + } + case R.id.actionToggleSpin: { + chart.spin(1000, chart.getRotationAngle(), chart.getRotationAngle() + 360, Easing.EaseInOutCubic); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "PieChartActivity"); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.7f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.8f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", index: " + h.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() { + Log.i("PieChart", "nothing selected"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java new file mode 100644 index 0000000000..dd3bd575da --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java @@ -0,0 +1,320 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StyleSpan; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private PieChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + private Typeface tf; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_piechart); + + setTitle("PiePolylineChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarY = findViewById(R.id.seekBar2); + + seekBarX.setOnSeekBarChangeListener(this); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setUsePercentValues(true); + chart.getDescription().setEnabled(false); + chart.setExtraOffsets(5, 10, 5, 5); + + chart.setDragDecelerationFrictionCoef(0.95f); + + tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + + chart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); + chart.setCenterText(generateCenterSpannableText()); + + chart.setExtraOffsets(20.f, 0.f, 20.f, 0.f); + + chart.setDrawHoleEnabled(true); + chart.setHoleColor(Color.WHITE); + + chart.setTransparentCircleColor(Color.WHITE); + chart.setTransparentCircleAlpha(110); + + chart.setHoleRadius(58f); + chart.setTransparentCircleRadius(61f); + + chart.setDrawCenterText(true); + + chart.setRotationAngle(0); + // enable rotation of the chart by touch + chart.setRotationEnabled(true); + chart.setHighlightPerTapEnabled(true); + + // chart.setUnit(" €"); + // chart.setDrawUnitsInChart(true); + + // add a selection listener + chart.setOnChartValueSelectedListener(this); + + seekBarX.setProgress(4); + seekBarY.setProgress(100); + + chart.animateY(1400, Easing.EaseInOutQuad); + // chart.spin(2000, 0, 360); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setEnabled(false); + } + + private void setData(int count, float range) { + + ArrayList entries = new ArrayList<>(); + + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. + for (int i = 0; i < count; i++) { + entries.add(new PieEntry((float) (Math.random() * range) + range / 5, parties[i % parties.length])); + } + + PieDataSet dataSet = new PieDataSet(entries, "Election Results"); + dataSet.setSliceSpace(3f); + dataSet.setSelectionShift(5f); + + // add a lot of colors + + ArrayList colors = new ArrayList<>(); + + for (int c : ColorTemplate.VORDIPLOM_COLORS) + colors.add(c); + + for (int c : ColorTemplate.JOYFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.COLORFUL_COLORS) + colors.add(c); + + for (int c : ColorTemplate.LIBERTY_COLORS) + colors.add(c); + + for (int c : ColorTemplate.PASTEL_COLORS) + colors.add(c); + + colors.add(ColorTemplate.getHoloBlue()); + + dataSet.setColors(colors); + //dataSet.setSelectionShift(0f); + + + dataSet.setValueLinePart1OffsetPercentage(80.f); + dataSet.setValueLinePart1Length(0.2f); + dataSet.setValueLinePart2Length(0.4f); + + //dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE); + + PieData data = new PieData(dataSet); + data.setValueFormatter(new PercentFormatter()); + data.setValueTextSize(11f); + data.setValueTextColor(Color.BLACK); + data.setValueTypeface(tf); + chart.setData(data); + + // undo all highlights + chart.highlightValues(null); + + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.pie, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PiePolylineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHole: { + if (chart.isDrawHoleEnabled()) + chart.setDrawHoleEnabled(false); + else + chart.setDrawHoleEnabled(true); + chart.invalidate(); + break; + } + case R.id.actionToggleMinAngles: { + if (chart.getMinAngleForSlices() == 0f) + chart.setMinAngleForSlices(36f); + else + chart.setMinAngleForSlices(0f); + chart.notifyDataSetChanged(); + chart.invalidate(); + break; + } + case R.id.actionToggleCurvedSlices: { + boolean toSet = !chart.isDrawRoundedSlicesEnabled() || !chart.isDrawHoleEnabled(); + chart.setDrawRoundedSlices(toSet); + if (toSet && !chart.isDrawHoleEnabled()) { + chart.setDrawHoleEnabled(true); + } + if (toSet && chart.isDrawSlicesUnderHoleEnabled()) { + chart.setDrawSlicesUnderHole(false); + } + chart.invalidate(); + break; + } + case R.id.actionDrawCenter: { + if (chart.isDrawCenterTextEnabled()) + chart.setDrawCenterText(false); + else + chart.setDrawCenterText(true); + chart.invalidate(); + break; + } + case R.id.actionToggleXValues: { + + chart.setDrawEntryLabels(!chart.isDrawEntryLabelsEnabled()); + chart.invalidate(); + break; + } + case R.id.actionTogglePercent: + chart.setUsePercentValues(!chart.isUsePercentValuesEnabled()); + chart.invalidate(); + break; + case R.id.animateX: { + chart.animateX(1400); + break; + } + case R.id.animateY: { + chart.animateY(1400); + break; + } + case R.id.animateXY: { + chart.animateXY(1400, 1400); + break; + } + case R.id.actionToggleSpin: { + chart.spin(1000, chart.getRotationAngle(), chart.getRotationAngle() + 360, Easing.EaseInOutCubic); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + setData(seekBarX.getProgress(), seekBarY.getProgress()); + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "PiePolylineChartActivity"); + } + + private SpannableString generateCenterSpannableText() { + + SpannableString s = new SpannableString("MPAndroidChart\ndeveloped by Philipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.5f), 0, 14, 0); + s.setSpan(new StyleSpan(Typeface.NORMAL), 14, s.length() - 15, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, s.length() - 15, 0); + s.setSpan(new RelativeSizeSpan(.65f), 14, s.length() - 15, 0); + s.setSpan(new StyleSpan(Typeface.ITALIC), s.length() - 14, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), s.length() - 14, s.length(), 0); + return s; + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + + if (e == null) + return; + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() { + Log.i("PieChart", "nothing selected"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java new file mode 100644 index 0000000000..883eb7dfc1 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java @@ -0,0 +1,260 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.RadarDataSet; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.xxmassdeveloper.mpchartexample.custom.RadarMarkerView; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +public class RadarChartActivity extends DemoBase { + + private RadarChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_radarchart); + + setTitle("RadarChartActivity"); + + chart = findViewById(R.id.chart1); + chart.setBackgroundColor(Color.rgb(60, 65, 82)); + + chart.getDescription().setEnabled(false); + + chart.setWebLineWidth(1f); + chart.setWebColor(Color.LTGRAY); + chart.setWebLineWidthInner(1f); + chart.setWebColorInner(Color.LTGRAY); + chart.setWebAlpha(100); + + // create a custom MarkerView (extend MarkerView) and specify the layout + // to use for it + MarkerView mv = new RadarMarkerView(this, R.layout.radar_markerview); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); // Set the marker to the chart + + setData(); + + chart.animateXY(1400, 1400, Easing.EaseInOutQuad); + + XAxis xAxis = chart.getXAxis(); + xAxis.setTypeface(tfLight); + xAxis.setTextSize(9f); + xAxis.setYOffset(0f); + xAxis.setXOffset(0f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private final String[] mActivities = new String[]{"Burger", "Steak", "Salad", "Pasta", "Pizza"}; + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mActivities[(int) value % mActivities.length]; + } + }); + xAxis.setTextColor(Color.WHITE); + + YAxis yAxis = chart.getYAxis(); + yAxis.setTypeface(tfLight); + yAxis.setLabelCount(5, false); + yAxis.setTextSize(9f); + yAxis.setAxisMinimum(0f); + yAxis.setAxisMaximum(80f); + yAxis.setDrawLabels(false); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setTypeface(tfLight); + l.setXEntrySpace(7f); + l.setYEntrySpace(5f); + l.setTextColor(Color.WHITE); + } + + private void setData() { + + float mul = 80; + float min = 20; + int cnt = 5; + + ArrayList entries1 = new ArrayList<>(); + ArrayList entries2 = new ArrayList<>(); + + // NOTE: The order of the entries when being added to the entries array determines their position around the center of + // the chart. + for (int i = 0; i < cnt; i++) { + float val1 = (float) (Math.random() * mul) + min; + entries1.add(new RadarEntry(val1)); + + float val2 = (float) (Math.random() * mul) + min; + entries2.add(new RadarEntry(val2)); + } + + RadarDataSet set1 = new RadarDataSet(entries1, "Last Week"); + set1.setColor(Color.rgb(103, 110, 129)); + set1.setFillColor(Color.rgb(103, 110, 129)); + set1.setDrawFilled(true); + set1.setFillAlpha(180); + set1.setLineWidth(2f); + set1.setDrawHighlightCircleEnabled(true); + set1.setDrawHighlightIndicators(false); + + RadarDataSet set2 = new RadarDataSet(entries2, "This Week"); + set2.setColor(Color.rgb(121, 162, 175)); + set2.setFillColor(Color.rgb(121, 162, 175)); + set2.setDrawFilled(true); + set2.setFillAlpha(180); + set2.setLineWidth(2f); + set2.setDrawHighlightCircleEnabled(true); + set2.setDrawHighlightIndicators(false); + + ArrayList sets = new ArrayList<>(); + sets.add(set1); + sets.add(set2); + + RadarData data = new RadarData(sets); + data.setValueTypeface(tfLight); + data.setValueTextSize(8f); + data.setDrawValues(false); + data.setValueTextColor(Color.WHITE); + + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.radar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + for (IDataSet set : chart.getData().getDataSets()) + set.setDrawValues(!set.isDrawValuesEnabled()); + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionToggleRotate: { + if (chart.isRotationEnabled()) + chart.setRotationEnabled(false); + else + chart.setRotationEnabled(true); + chart.invalidate(); + break; + } + case R.id.actionToggleFilled: { + + ArrayList sets = (ArrayList) chart.getData() + .getDataSets(); + + for (IRadarDataSet set : sets) { + if (set.isDrawFilledEnabled()) + set.setDrawFilled(false); + else + set.setDrawFilled(true); + } + chart.invalidate(); + break; + } + case R.id.actionToggleHighlightCircle: { + + ArrayList sets = (ArrayList) chart.getData() + .getDataSets(); + + for (IRadarDataSet set : sets) { + set.setDrawHighlightCircleEnabled(!set.isDrawHighlightCircleEnabled()); + } + chart.invalidate(); + break; + } + case R.id.actionToggleXLabels: { + chart.getXAxis().setEnabled(!chart.getXAxis().isEnabled()); + chart.notifyDataSetChanged(); + chart.invalidate(); + break; + } + case R.id.actionToggleYLabels: { + + chart.getYAxis().setEnabled(!chart.getYAxis().isEnabled()); + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(1400); + break; + } + case R.id.animateY: { + chart.animateY(1400); + break; + } + case R.id.animateXY: { + chart.animateXY(1400, 1400); + break; + } + case R.id.actionToggleSpin: { + chart.spin(2000, chart.getRotationAngle(), chart.getRotationAngle() + 360, Easing.EaseInOutCubic); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "RadarChartActivity"); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java similarity index 59% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index a1f31d1043..e26c460ffa 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java @@ -1,9 +1,13 @@ package com.xxmassdeveloper.mpchartexample; +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; -import android.graphics.Typeface; +import android.net.Uri; import android.os.Bundle; +import androidx.core.content.ContextCompat; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -19,16 +23,16 @@ import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.highlight.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; public class RealtimeLineChartActivity extends DemoBase implements OnChartValueSelectedListener { - private LineChart mChart; + private LineChart chart; @Override protected void onCreate(Bundle savedInstanceState) { @@ -37,96 +41,64 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_realtime_linechart); - mChart = (LineChart) findViewById(R.id.chart1); - mChart.setOnChartValueSelectedListener(this); + setTitle("RealtimeLineChartActivity"); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); + // enable description text + chart.getDescription().setEnabled(true); // enable touch gestures - mChart.setTouchEnabled(true); + chart.setTouchEnabled(true); // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - mChart.setDrawGridBackground(false); + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + chart.setDrawGridBackground(false); // if disabled, scaling can be done on x- and y-axis separately - mChart.setPinchZoom(true); + chart.setPinchZoom(true); // set an alternative background color - mChart.setBackgroundColor(Color.LTGRAY); + chart.setBackgroundColor(Color.LTGRAY); LineData data = new LineData(); data.setValueTextColor(Color.WHITE); // add empty data - mChart.setData(data); - - Typeface tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + chart.setData(data); // get the legend (only possible after setting data) - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); // modify the legend ... - // l.setPosition(LegendPosition.LEFT_OF_CHART); l.setForm(LegendForm.LINE); - l.setTypeface(tf); + l.setTypeface(tfLight); l.setTextColor(Color.WHITE); - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfLight); xl.setTextColor(Color.WHITE); xl.setDrawGridLines(false); xl.setAvoidFirstLastClipping(true); - xl.setSpaceBetweenLabels(5); xl.setEnabled(true); - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tfLight); leftAxis.setTextColor(Color.WHITE); - leftAxis.setAxisMaxValue(100f); - leftAxis.setAxisMinValue(0f); + leftAxis.setAxisMaximum(100f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.realtime, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()) { - case R.id.actionAdd: { - addEntry(); - break; - } - case R.id.actionClear: { - mChart.clearValues(); - Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); - break; - } - case R.id.actionFeedMultiple: { - feedMultiple(); - break; - } - } - return true; - } - - private int year = 2015; - private void addEntry() { - LineData data = mChart.getData(); + LineData data = chart.getData(); if (data != null) { @@ -138,24 +110,21 @@ private void addEntry() { data.addDataSet(set); } - // add a new x-value first - data.addXValue(mMonths[data.getXValCount() % 12] + " " - + (year + data.getXValCount() / 12)); - data.addEntry(new Entry((float) (Math.random() * 40) + 30f, set.getEntryCount()), 0); - + data.addEntry(new Entry(set.getEntryCount(), (float) (Math.random() * 40) + 30f), 0); + data.notifyDataChanged(); // let the chart know it's data has changed - mChart.notifyDataSetChanged(); + chart.notifyDataSetChanged(); // limit the number of visible entries - mChart.setVisibleXRangeMaximum(120); - // mChart.setVisibleYRange(30, AxisDependency.LEFT); + chart.setVisibleXRangeMaximum(120); + // chart.setVisibleYRange(30, AxisDependency.LEFT); // move to the latest entry - mChart.moveViewToX(data.getXValCount() - 121); + chart.moveViewToX(data.getEntryCount()); // this automatically refreshes the chart (calls invalidate()) - // mChart.moveViewTo(data.getXValCount()-7, 55f, + // chart.moveViewTo(data.getXValCount()-7, 55f, // AxisDependency.LEFT); } } @@ -177,35 +146,90 @@ private LineDataSet createSet() { return set; } + private Thread thread; + private void feedMultiple() { - new Thread(new Runnable() { + if (thread != null) + thread.interrupt(); + + final Runnable runnable = new Runnable() { @Override public void run() { - for(int i = 0; i < 500; i++) { + addEntry(); + } + }; - runOnUiThread(new Runnable() { + thread = new Thread(new Runnable() { - @Override - public void run() { - addEntry(); - } - }); + @Override + public void run() { + for (int i = 0; i < 1000; i++) { + + // Don't generate garbage runnables inside the loop. + runOnUiThread(runnable); try { - Thread.sleep(35); + Thread.sleep(25); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } } - }).start(); + }); + + thread.start(); } @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.realtime, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionAdd: { + addEntry(); + break; + } + case R.id.actionClear: { + chart.clearValues(); + Toast.makeText(this, "Chart cleared!", Toast.LENGTH_SHORT).show(); + break; + } + case R.id.actionFeedMultiple: { + feedMultiple(); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "RealtimeLineChartActivity"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { Log.i("Entry selected", e.toString()); } @@ -213,4 +237,13 @@ public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { public void onNothingSelected() { Log.i("Nothing selected", "Nothing selected."); } + + @Override + protected void onPause() { + super.onPause(); + + if (thread != null) { + thread.interrupt(); + } + } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java new file mode 100644 index 0000000000..e51755ad0b --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java @@ -0,0 +1,247 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.ScatterChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.custom.CustomScatterShapeRenderer; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class ScatterChartActivity extends DemoBase implements OnSeekBarChangeListener, + OnChartValueSelectedListener { + + private ScatterChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_scatterchart); + + setTitle("ScatterChartActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.getDescription().setEnabled(false); + chart.setOnChartValueSelectedListener(this); + + chart.setDrawGridBackground(false); + chart.setTouchEnabled(true); + chart.setMaxHighlightDistance(50f); + + // enable scaling and dragging + chart.setDragEnabled(true); + chart.setScaleEnabled(true); + + chart.setMaxVisibleValueCount(200); + chart.setPinchZoom(true); + + seekBarX.setProgress(45); + seekBarY.setProgress(100); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + l.setTypeface(tfLight); + l.setXOffset(5f); + + YAxis yl = chart.getAxisLeft(); + yl.setTypeface(tfLight); + yl.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + chart.getAxisRight().setEnabled(false); + + XAxis xl = chart.getXAxis(); + xl.setTypeface(tfLight); + xl.setDrawGridLines(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values1 = new ArrayList<>(); + ArrayList values2 = new ArrayList<>(); + ArrayList values3 = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values1.add(new Entry(i, val)); + } + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values2.add(new Entry(i+0.33f, val)); + } + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float val = (float) (Math.random() * seekBarY.getProgress()) + 3; + values3.add(new Entry(i+0.66f, val)); + } + + // create a dataset and give it a type + ScatterDataSet set1 = new ScatterDataSet(values1, "DS 1"); + set1.setScatterShape(ScatterChart.ScatterShape.SQUARE); + set1.setColor(ColorTemplate.COLORFUL_COLORS[0]); + ScatterDataSet set2 = new ScatterDataSet(values2, "DS 2"); + set2.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + set2.setScatterShapeHoleColor(ColorTemplate.COLORFUL_COLORS[3]); + set2.setScatterShapeHoleRadius(3f); + set2.setColor(ColorTemplate.COLORFUL_COLORS[1]); + ScatterDataSet set3 = new ScatterDataSet(values3, "DS 3"); + set3.setShapeRenderer(new CustomScatterShapeRenderer()); + set3.setColor(ColorTemplate.COLORFUL_COLORS[2]); + + set1.setScatterShapeSize(8f); + set2.setScatterShapeSize(8f); + set3.setScatterShapeSize(8f); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); // add the data sets + dataSets.add(set2); + dataSets.add(set3); + + // create a data object with the data sets + ScatterData data = new ScatterData(dataSets); + data.setValueTypeface(tfLight); + + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.scatter, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (IScatterDataSet iSet : sets) { + + ScatterDataSet set = (ScatterDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.animateX: { + chart.animateX(3000); + break; + } + case R.id.animateY: { + chart.animateY(3000); + break; + } + case R.id.animateXY: { + + chart.animateXY(3000, 3000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "ScatterChartActivity"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + Log.i("VAL SELECTED", + "Value: " + e.getY() + ", xIndex: " + e.getX() + + ", DataSet index: " + h.getDataSetIndex()); + } + + @Override + public void onNothingSelected() {} + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java new file mode 100644 index 0000000000..37de64d728 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java @@ -0,0 +1,101 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; + +@SuppressWarnings("SameParameterValue") +public class ScrollViewActivity extends DemoBase { + + private BarChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_scrollview); + + setTitle("ScrollViewActivity"); + + chart = findViewById(R.id.chart1); + + chart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawBarShadow(false); + chart.setDrawGridBackground(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTTOM); + xAxis.setDrawGridLines(false); + + chart.getAxisLeft().setDrawGridLines(false); + + chart.getLegend().setEnabled(false); + + setData(10); + chart.setFitBars(true); + } + + private void setData(int count) { + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + float val = (float) (Math.random() * count) + 15; + values.add(new BarEntry(i, (int) val)); + } + + BarDataSet set = new BarDataSet(values, "Data Set"); + set.setColors(ColorTemplate.VORDIPLOM_COLORS); + set.setDrawValues(false); + + BarData data = new BarData(set); + + chart.setData(data); + chart.invalidate(); + chart.animateY(800); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScrollViewActivity.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java new file mode 100644 index 0000000000..676e0e62b0 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java @@ -0,0 +1,284 @@ +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +import com.github.mikephil.charting.charts.BarChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +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.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.xxmassdeveloper.mpchartexample.custom.MyAxisValueFormatter; +import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.util.ArrayList; +import java.util.List; + +public class StackedBarActivity extends DemoBase implements OnSeekBarChangeListener, OnChartValueSelectedListener { + + private BarChart chart; + private SeekBar seekBarX, seekBarY; + private TextView tvX, tvY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_barchart); + + setTitle("StackedBarActivity"); + + tvX = findViewById(R.id.tvXMax); + tvY = findViewById(R.id.tvYMax); + + seekBarX = findViewById(R.id.seekBar1); + seekBarX.setOnSeekBarChangeListener(this); + + seekBarY = findViewById(R.id.seekBar2); + seekBarY.setOnSeekBarChangeListener(this); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + + chart.getDescription().setEnabled(false); + + // if more than 60 entries are displayed in the chart, no values will be + // drawn + chart.setMaxVisibleValueCount(40); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + + chart.setDrawValueAboveBar(false); + chart.setHighlightFullBarEnabled(false); + + // change the position of the y-labels + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setValueFormatter(new MyAxisValueFormatter()); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + chart.getAxisRight().setEnabled(false); + + XAxis xLabels = chart.getXAxis(); + xLabels.setPosition(XAxisPosition.TOP); + + // chart.setDrawXLabels(false); + // chart.setDrawYLabels(false); + + // setting data + seekBarX.setProgress(12); + seekBarY.setProgress(100); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setFormToTextSpace(4f); + l.setXEntrySpace(6f); + + // chart.setDrawLegend(false); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + + tvX.setText(String.valueOf(seekBarX.getProgress())); + tvY.setText(String.valueOf(seekBarY.getProgress())); + + ArrayList values = new ArrayList<>(); + + for (int i = 0; i < seekBarX.getProgress(); i++) { + float mul = (seekBarY.getProgress() + 1); + float val1 = (float) (Math.random() * mul) + mul / 3; + float val2 = (float) (Math.random() * mul) + mul / 3; + float val3 = (float) (Math.random() * mul) + mul / 3; + + values.add(new BarEntry( + i, + new float[]{val1, val2, val3}, + getResources().getDrawable(R.drawable.star))); + } + + BarDataSet set1; + + if (chart.getData() != null && + chart.getData().getDataSetCount() > 0) { + set1 = (BarDataSet) chart.getData().getDataSetByIndex(0); + set1.setValues(values); + chart.getData().notifyDataChanged(); + chart.notifyDataSetChanged(); + } else { + set1 = new BarDataSet(values, "Statistics Vienna 2014"); + set1.setDrawIcons(false); + set1.setColors(getColors()); + set1.setStackLabels(new String[]{"Births", "Divorces", "Marriages"}); + + ArrayList dataSets = new ArrayList<>(); + dataSets.add(set1); + + BarData data = new BarData(dataSets); + data.setValueFormatter(new MyValueFormatter()); + data.setValueTextColor(Color.WHITE); + + chart.setData(data); + } + + chart.setFitBars(true); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if (chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet) set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(2000); + break; + } + case R.id.animateY: { + chart.animateY(2000); + break; + } + case R.id.animateXY: { + + chart.animateXY(2000, 2000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "StackedBarActivity"); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) {} + + @Override + public void onValueSelected(Entry e, Highlight h) { + + BarEntry entry = (BarEntry) e; + + if (entry.getYVals() != null) + Log.i("VAL SELECTED", "Value: " + entry.getYVals()[h.getStackIndex()]); + else + Log.i("VAL SELECTED", "Value: " + entry.getY()); + } + + @Override + public void onNothingSelected() {} + + private int[] getColors() { + + // have as many colors as stack-values per entry + int[] colors = new int[3]; + + System.arraycopy(ColorTemplate.MATERIAL_COLORS, 0, colors, 0, 3); + + return colors; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java new file mode 100644 index 0000000000..7af58c85ca --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java @@ -0,0 +1,265 @@ + +package com.xxmassdeveloper.mpchartexample; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import androidx.core.content.ContextCompat; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.WindowManager; + +import com.github.mikephil.charting.charts.HorizontalBarChart; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.listener.OnChartValueSelectedListener; +import com.github.mikephil.charting.utils.ViewPortHandler; +import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +public class StackedBarActivityNegative extends DemoBase implements + OnChartValueSelectedListener { + + private HorizontalBarChart chart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.activity_age_distribution); + + setTitle("StackedBarActivityNegative"); + + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); + chart.setDrawGridBackground(false); + chart.getDescription().setEnabled(false); + + // scaling can now only be done on x- and y-axis separately + chart.setPinchZoom(false); + + chart.setDrawBarShadow(false); + chart.setDrawValueAboveBar(true); + chart.setHighlightFullBarEnabled(false); + + chart.getAxisLeft().setEnabled(false); + chart.getAxisRight().setAxisMaximum(25f); + chart.getAxisRight().setAxisMinimum(-25f); + chart.getAxisRight().setDrawGridLines(false); + chart.getAxisRight().setDrawZeroLine(true); + chart.getAxisRight().setLabelCount(7, false); + chart.getAxisRight().setValueFormatter(new CustomFormatter()); + chart.getAxisRight().setTextSize(9f); + + XAxis xAxis = chart.getXAxis(); + xAxis.setPosition(XAxisPosition.BOTH_SIDED); + xAxis.setDrawGridLines(false); + xAxis.setDrawAxisLine(false); + xAxis.setTextSize(9f); + xAxis.setAxisMinimum(0f); + xAxis.setAxisMaximum(110f); + xAxis.setCenterAxisLabels(true); + xAxis.setLabelCount(12); + xAxis.setGranularity(10f); + xAxis.setValueFormatter(new IAxisValueFormatter() { + + private final DecimalFormat format = new DecimalFormat("###"); + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return format.format(value) + "-" + format.format(value + 10); + } + }); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.HORIZONTAL); + l.setDrawInside(false); + l.setFormSize(8f); + l.setFormToTextSpace(4f); + l.setXEntrySpace(6f); + + // IMPORTANT: When using negative values in stacked bars, always make sure the negative values are in the array first + ArrayList values = new ArrayList<>(); + values.add(new BarEntry(5, new float[]{ -10, 10 })); + values.add(new BarEntry(15, new float[]{ -12, 13 })); + values.add(new BarEntry(25, new float[]{ -15, 15 })); + values.add(new BarEntry(35, new float[]{ -17, 17 })); + values.add(new BarEntry(45, new float[]{ -19, 20 })); + values.add(new BarEntry(45, new float[]{ -19, 20 }, getResources().getDrawable(R.drawable.star))); + values.add(new BarEntry(55, new float[]{ -19, 19 })); + values.add(new BarEntry(65, new float[]{ -16, 16 })); + values.add(new BarEntry(75, new float[]{ -13, 14 })); + values.add(new BarEntry(85, new float[]{ -10, 11 })); + values.add(new BarEntry(95, new float[]{ -5, 6 })); + values.add(new BarEntry(105, new float[]{ -1, 2 })); + + BarDataSet set = new BarDataSet(values, "Age Distribution"); + set.setDrawIcons(false); + set.setValueFormatter(new CustomFormatter()); + set.setValueTextSize(7f); + set.setAxisDependency(YAxis.AxisDependency.RIGHT); + set.setColors(Color.rgb(67,67,72), Color.rgb(124,181,236)); + set.setStackLabels(new String[]{ + "Men", "Women" + }); + + BarData data = new BarData(set); + data.setBarWidth(8.5f); + chart.setData(data); + chart.invalidate(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.bar, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivityNegative.java")); + startActivity(i); + break; + } + case R.id.actionToggleValues: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawValues(!set.isDrawValuesEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleIcons: { + List sets = chart.getData() + .getDataSets(); + + for (IBarDataSet iSet : sets) { + + BarDataSet set = (BarDataSet) iSet; + set.setDrawIcons(!set.isDrawIconsEnabled()); + } + + chart.invalidate(); + break; + } + case R.id.actionToggleHighlight: { + if(chart.getData() != null) { + chart.getData().setHighlightEnabled(!chart.getData().isHighlightEnabled()); + chart.invalidate(); + } + break; + } + case R.id.actionTogglePinch: { + if (chart.isPinchZoomEnabled()) + chart.setPinchZoom(false); + else + chart.setPinchZoom(true); + + chart.invalidate(); + break; + } + case R.id.actionToggleAutoScaleMinMax: { + chart.setAutoScaleMinMaxEnabled(!chart.isAutoScaleMinMaxEnabled()); + chart.notifyDataSetChanged(); + break; + } + case R.id.actionToggleBarBorders: { + for (IBarDataSet set : chart.getData().getDataSets()) + ((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f); + + chart.invalidate(); + break; + } + case R.id.animateX: { + chart.animateX(3000); + break; + } + case R.id.animateY: { + chart.animateY(3000); + break; + } + case R.id.animateXY: { + + chart.animateXY(3000, 3000); + break; + } + case R.id.actionSave: { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + requestStoragePermission(chart); + } + break; + } + } + return true; + } + + @Override + protected void saveToGallery() { + saveToGallery(chart, "StackedBarActivityNegative"); + } + + @Override + public void onValueSelected(Entry e, Highlight h) { + BarEntry entry = (BarEntry) e; + Log.i("VAL SELECTED", + "Value: " + Math.abs(entry.getYVals()[h.getStackIndex()])); + } + + @Override + public void onNothingSelected() { + Log.i("NOTING SELECTED", ""); + } + + private class CustomFormatter implements IValueFormatter, IAxisValueFormatter { + + private final DecimalFormat mFormat; + + CustomFormatter() { + mFormat = new DecimalFormat("###"); + } + + // data + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(Math.abs(value)) + "m"; + } + + // YAxis + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(Math.abs(value)) + "m"; + } + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java new file mode 100644 index 0000000000..6279e395f6 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/CustomScatterShapeRenderer.java @@ -0,0 +1,30 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Custom shape renderer that draws a single line. + * Created by philipp on 26/06/16. + */ +public class CustomScatterShapeRenderer implements IShapeRenderer +{ + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java new file mode 100644 index 0000000000..ba4d860d92 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/DayAxisValueFormatter.java @@ -0,0 +1,139 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +/** + * Created by philipp on 02/06/16. + */ +public class DayAxisValueFormatter implements IAxisValueFormatter +{ + + private final String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + private final BarLineChartBase chart; + + public DayAxisValueFormatter(BarLineChartBase chart) { + this.chart = chart; + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + int days = (int) value; + + int year = determineYear(days); + + int month = determineMonth(days); + String monthName = mMonths[month % mMonths.length]; + String yearName = String.valueOf(year); + + if (chart.getVisibleXRange() > 30 * 6) { + + return monthName + " " + yearName; + } else { + + int dayOfMonth = determineDayOfMonth(days, month + 12 * (year - 2016)); + + String appendix = "th"; + + switch (dayOfMonth) { + case 1: + appendix = "st"; + break; + case 2: + appendix = "nd"; + break; + case 3: + appendix = "rd"; + break; + case 21: + appendix = "st"; + break; + case 22: + appendix = "nd"; + break; + case 23: + appendix = "rd"; + break; + case 31: + appendix = "st"; + break; + } + + return dayOfMonth == 0 ? "" : dayOfMonth + appendix + " " + monthName; + } + } + + private int getDaysForMonth(int month, int year) { + + // month is 0-based + + if (month == 1) { + boolean is29Feb = false; + + if (year < 1582) + is29Feb = (year < 1 ? year + 1 : year) % 4 == 0; + else if (year > 1582) + is29Feb = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + + return is29Feb ? 29 : 28; + } + + if (month == 3 || month == 5 || month == 8 || month == 10) + return 30; + else + return 31; + } + + private int determineMonth(int dayOfYear) { + + int month = -1; + int days = 0; + + while (days < dayOfYear) { + month = month + 1; + + if (month >= 12) + month = 0; + + int year = determineYear(days); + days += getDaysForMonth(month, year); + } + + return Math.max(month, 0); + } + + private int determineDayOfMonth(int days, int month) { + + int count = 0; + int daysForMonths = 0; + + while (count < month) { + + int year = determineYear(daysForMonths); + daysForMonths += getDaysForMonth(count % 12, year); + count++; + } + + return days - daysForMonths; + } + + private int determineYear(int days) { + + if (days <= 366) + return 2016; + else if (days <= 730) + return 2017; + else if (days <= 1094) + return 2018; + else if (days <= 1458) + return 2019; + else + return 2020; + + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java new file mode 100644 index 0000000000..e7cdbfcd10 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyAxisValueFormatter.java @@ -0,0 +1,21 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +import java.text.DecimalFormat; + +public class MyAxisValueFormatter implements IAxisValueFormatter +{ + + private final DecimalFormat mFormat; + + public MyAxisValueFormatter() { + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " $"; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java new file mode 100644 index 0000000000..bea4908ef2 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyCustomXAxisValueFormatter.java @@ -0,0 +1,43 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Created by Philipp Jahoda on 14/09/15. + * + * @deprecated The {@link MyAxisValueFormatter} does exactly the same thing and is more functional. + */ +@Deprecated +public class MyCustomXAxisValueFormatter implements IAxisValueFormatter +{ + + private final DecimalFormat mFormat; + private final ViewPortHandler mViewPortHandler; + + public MyCustomXAxisValueFormatter(ViewPortHandler viewPortHandler) { + mViewPortHandler = viewPortHandler; + // maybe do something here or provide parameters in constructor + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + //Log.i("TRANS", "x: " + viewPortHandler.getTransX() + ", y: " + viewPortHandler.getTransY()); + + // e.g. adjust the x-axis values depending on scale / zoom level + final float xScale = mViewPortHandler.getScaleX(); + if (xScale > 5) + return "4"; + else if (xScale > 3) + return "3"; + else if (xScale > 1) + return "2"; + else + return mFormat.format(value); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java similarity index 59% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java index 2ca92f61eb..2ca87b2f0f 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -1,23 +1,25 @@ package com.xxmassdeveloper.mpchartexample.custom; -import com.github.mikephil.charting.formatter.FillFormatter; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; /** * Created by Philipp Jahoda on 12/09/15. */ -public class MyFillFormatter implements FillFormatter { +@SuppressWarnings("unused") +public class MyFillFormatter implements IFillFormatter +{ - private float mFillPos = 0f; + private float fillPos; - public MyFillFormatter(float fillpos) { - this.mFillPos = fillpos; + public MyFillFormatter(float fillPos) { + this.fillPos = fillPos; } @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { // your logic could be here - return mFillPos; + return fillPos; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java similarity index 59% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 4198d14f1c..2c1da9b4e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.widget.TextView; @@ -8,25 +9,27 @@ import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; /** * Custom implementation of the MarkerView. - * + * * @author Philipp Jahoda */ +@SuppressLint("ViewConstructor") public class MyMarkerView extends MarkerView { - private TextView tvContent; + private final TextView tvContent; public MyMarkerView(Context context, int layoutResource) { super(context, layoutResource); - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { @@ -35,22 +38,17 @@ public void refreshContent(Entry e, Highlight highlight) { CandleEntry ce = (CandleEntry) e; - tvContent.setText("" + Utils.formatNumber(ce.getHigh(), 0, true)); + tvContent.setText(Utils.formatNumber(ce.getHigh(), 0, true)); } else { - tvContent.setText("" + Utils.formatNumber(e.getVal(), 0, true)); + tvContent.setText(Utils.formatNumber(e.getY(), 0, true)); } - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java similarity index 74% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java index a374b60e7f..ec1c119818 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -1,15 +1,16 @@ package com.xxmassdeveloper.mpchartexample.custom; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; -public class MyValueFormatter implements ValueFormatter { +public class MyValueFormatter implements IValueFormatter +{ + + private final DecimalFormat mFormat; - private DecimalFormat mFormat; - public MyValueFormatter() { mFormat = new DecimalFormat("###,###,###,##0.0"); } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java new file mode 100644 index 0000000000..ca057a5aa3 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/RadarMarkerView.java @@ -0,0 +1,48 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Typeface; +import android.widget.TextView; + +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +@SuppressLint("ViewConstructor") +public class RadarMarkerView extends MarkerView { + + private final TextView tvContent; + private final DecimalFormat format = new DecimalFormat("##0"); + + public RadarMarkerView(Context context, int layoutResource) { + super(context, layoutResource); + + tvContent = findViewById(R.id.tvContent); + tvContent.setTypeface(Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf")); + } + + // runs every time the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + tvContent.setText(String.format("%s %%", format.format(e.getY()))); + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight() - 10); + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java similarity index 56% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java index 9be2f30acb..554ef2fe27 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -1,22 +1,25 @@ package com.xxmassdeveloper.mpchartexample.custom; +import android.annotation.SuppressLint; import android.content.Context; import android.widget.TextView; import com.github.mikephil.charting.components.MarkerView; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.xxmassdeveloper.mpchartexample.R; /** * Custom implementation of the MarkerView. - * + * * @author Philipp Jahoda */ +@SuppressWarnings("unused") +@SuppressLint("ViewConstructor") public class StackedBarsMarkerView extends MarkerView { private TextView tvContent; @@ -24,10 +27,10 @@ public class StackedBarsMarkerView extends MarkerView { public StackedBarsMarkerView(Context context, int layoutResource) { super(context, layoutResource); - tvContent = (TextView) findViewById(R.id.tvContent); + tvContent = findViewById(R.id.tvContent); } - // callbacks everytime the MarkerView is redrawn, can be used to update the + // runs every time the MarkerView is redrawn, can be used to update the // content (user-interface) @Override public void refreshContent(Entry e, Highlight highlight) { @@ -36,28 +39,23 @@ public void refreshContent(Entry e, Highlight highlight) { BarEntry be = (BarEntry) e; - if(be.getVals() != null) { + if(be.getYVals() != null) { // draw the stack value - tvContent.setText("" + Utils.formatNumber(be.getVals()[highlight.getStackIndex()], 0, true)); + tvContent.setText(Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); } else { - tvContent.setText("" + Utils.formatNumber(be.getVal(), 0, true)); + tvContent.setText(Utils.formatNumber(be.getY(), 0, true)); } } else { - tvContent.setText("" + Utils.formatNumber(e.getVal(), 0, true)); + tvContent.setText(Utils.formatNumber(e.getY(), 0, true)); } - } - @Override - public int getXOffset(float xpos) { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset(float ypos) { - // this will cause the marker-view to be above the selected value - return -getHeight(); + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); } } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java new file mode 100644 index 0000000000..51e4247d35 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/XYMarkerView.java @@ -0,0 +1,52 @@ + +package com.xxmassdeveloper.mpchartexample.custom; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.widget.TextView; + +import com.github.mikephil.charting.components.MarkerView; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.utils.MPPointF; +import com.xxmassdeveloper.mpchartexample.R; + +import java.text.DecimalFormat; + +/** + * Custom implementation of the MarkerView. + * + * @author Philipp Jahoda + */ +@SuppressLint("ViewConstructor") +public class XYMarkerView extends MarkerView { + + private final TextView tvContent; + private final IAxisValueFormatter xAxisValueFormatter; + + private final DecimalFormat format; + + public XYMarkerView(Context context, IAxisValueFormatter xAxisValueFormatter) { + super(context, R.layout.custom_marker_view); + + this.xAxisValueFormatter = xAxisValueFormatter; + tvContent = findViewById(R.id.tvContent); + format = new DecimalFormat("###.0"); + } + + // runs every time the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + + tvContent.setText(String.format("x: %s, y: %s", xAxisValueFormatter.getFormattedValue(e.getX(), null), format.format(e.getY()))); + + super.refreshContent(e, highlight); + } + + @Override + public MPPointF getOffset() { + return new MPPointF(-(getWidth() / 2), -getHeight()); + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java new file mode 100644 index 0000000000..7122e0d80c --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/YearXAxisFormatter.java @@ -0,0 +1,27 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.formatter.IAxisValueFormatter; + +/** + * Created by Philipp Jahoda on 14/09/15. + */ +@SuppressWarnings("unused") +public class YearXAxisFormatter implements IAxisValueFormatter +{ + + private final String[] mMonths = new String[]{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + public YearXAxisFormatter() { + // take parameters to change behavior of formatter + } + + @Override + public String getFormattedValue(float value, AxisBase axis) { + + float percent = value / axis.mAxisRange; + return mMonths[(int) (mMonths.length * percent)]; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java similarity index 65% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index 29b378d44b..4bcc543722 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java @@ -1,7 +1,8 @@ package com.xxmassdeveloper.mpchartexample.fragments; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -21,48 +22,49 @@ public class BarChartFrag extends SimpleFragment implements OnChartGestureListener { + @NonNull public static Fragment newInstance() { return new BarChartFrag(); } - private BarChart mChart; - + private BarChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_bar, container, false); - + // create a new chart object - mChart = new BarChart(getActivity()); - mChart.setDescription(""); - mChart.setOnChartGestureListener(this); - + chart = new BarChart(getActivity()); + chart.getDescription().setEnabled(false); + chart.setOnChartGestureListener(this); + MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); + + chart.setDrawGridBackground(false); + chart.setDrawBarShadow(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); - mChart.setMarkerView(mv); + chart.setData(generateBarData(1, 20000, 12)); - mChart.setDrawGridBackground(false); - mChart.setDrawBarShadow(false); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - mChart.setData(generateBarData(1, 20000, 12)); - - Legend l = mChart.getLegend(); + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + chart.getAxisRight().setEnabled(false); - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - - // programatically add the chart - FrameLayout parent = (FrameLayout) v.findViewById(R.id.parentLayout); - parent.addView(mChart); - + + // programmatically add the chart + FrameLayout parent = v.findViewById(R.id.parentLayout); + parent.addView(chart); + return v; } @@ -74,12 +76,12 @@ public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture @Override public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { Log.i("Gesture", "END"); - mChart.highlightValues(null); + chart.highlightValues(null); } @Override public void onChartLongPressed(MotionEvent me) { - Log.i("LongPress", "Chart longpressed."); + Log.i("LongPress", "Chart long pressed."); } @Override @@ -94,9 +96,9 @@ public void onChartSingleTapped(MotionEvent me) { @Override public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) { - Log.i("Fling", "Chart flinged. VeloX: " + velocityX + ", VeloY: " + velocityY); + Log.i("Fling", "Chart fling. VelocityX: " + velocityX + ", VelocityY: " + velocityY); } - + @Override public void onChartScale(MotionEvent me, float scaleX, float scaleY) { Log.i("Scale / Zoom", "ScaleX: " + scaleX + ", ScaleY: " + scaleY); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java similarity index 50% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java index 8213f4f017..9edee8bdfb 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -1,7 +1,8 @@ package com.xxmassdeveloper.mpchartexample.fragments; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,39 +16,40 @@ public class ComplexityFragment extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new ComplexityFragment(); } - private LineChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private LineChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_line, container, false); - - mChart = (LineChart) v.findViewById(R.id.lineChart1); - - mChart.setDescription(""); - - mChart.setDrawGridBackground(false); - - mChart.setData(getComplexity()); - mChart.animateX(3000); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); + + chart = v.findViewById(R.id.lineChart1); + + chart.getDescription().setEnabled(false); + + chart.setDrawGridBackground(false); + + chart.setData(getComplexity()); + chart.animateX(3000); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(false); - + return v; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java similarity index 52% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java index ae7ca9f19c..5de9a46ea3 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -2,7 +2,8 @@ import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.RelativeSizeSpan; @@ -12,41 +13,45 @@ import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.xxmassdeveloper.mpchartexample.R; public class PieChartFrag extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new PieChartFrag(); } - private PieChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private PieChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_pie, container, false); - - mChart = (PieChart) v.findViewById(R.id.pieChart1); - mChart.setDescription(""); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Light.ttf"); - - mChart.setCenterTextTypeface(tf); - mChart.setCenterText(generateCenterText()); - mChart.setCenterTextSize(10f); - mChart.setCenterTextTypeface(tf); - + + chart = v.findViewById(R.id.pieChart1); + chart.getDescription().setEnabled(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + chart.setCenterTextTypeface(tf); + chart.setCenterText(generateCenterText()); + chart.setCenterTextSize(10f); + chart.setCenterTextTypeface(tf); + // radius of the center hole in percent of maximum radius - mChart.setHoleRadius(45f); - mChart.setTransparentCircleRadius(50f); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - - mChart.setData(generatePieData()); - + chart.setHoleRadius(45f); + chart.setTransparentCircleRadius(50f); + + Legend l = chart.getLegend(); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); + + chart.setData(generatePieData()); + return v; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java similarity index 59% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java index a93038c1e2..d5d292bf0b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -1,7 +1,9 @@ package com.xxmassdeveloper.mpchartexample.fragments; + import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -9,57 +11,59 @@ import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; +import com.github.mikephil.charting.components.YAxis; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; public class ScatterChartFrag extends SimpleFragment { + @NonNull public static Fragment newInstance() { return new ScatterChartFrag(); } - private ScatterChart mChart; - + @SuppressWarnings("FieldCanBeLocal") + private ScatterChart chart; + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); - - mChart = (ScatterChart) v.findViewById(R.id.scatterChart1); - mChart.setDescription(""); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - + + chart = v.findViewById(R.id.scatterChart1); + chart.getDescription().setEnabled(false); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); + mv.setChartView(chart); // For bounds control + chart.setMarker(mv); - mChart.setMarkerView(mv); + chart.setDrawGridBackground(false); + chart.setData(generateScatterData(6, 10000, 200)); - mChart.setDrawGridBackground(false); - mChart.setData(generateScatterData(6, 10000, 200)); - - XAxis xAxis = mChart.getXAxis(); + XAxis xAxis = chart.getXAxis(); xAxis.setEnabled(true); xAxis.setPosition(XAxisPosition.BOTTOM); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); - - YAxis rightAxis = mChart.getAxisRight(); + + YAxis rightAxis = chart.getAxisRight(); rightAxis.setTypeface(tf); rightAxis.setDrawGridLines(false); - - Legend l = mChart.getLegend(); + + Legend l = chart.getLegend(); l.setWordWrapEnabled(true); l.setTypeface(tf); l.setFormSize(14f); l.setTextSize(9f); - + // increase the space between legend & bottom and legend & content - l.setYOffset(13f); - mChart.setExtraBottomOffset(16f); - + l.setYOffset(13f); + chart.setExtraBottomOffset(16f); + return v; } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java similarity index 63% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java index 7af599e927..32b78142b5 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java @@ -4,11 +4,15 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; +import android.view.Menu; +import android.view.MenuItem; import android.view.WindowManager; import com.xxmassdeveloper.mpchartexample.R; @@ -16,7 +20,7 @@ /** * Demonstrates how to keep your charts straight forward, simple and beautiful with the MPAndroidChart library. - * + * * @author Philipp Jahoda */ public class SimpleChartDemo extends DemoBase { @@ -26,21 +30,22 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); - setContentView(R.layout.activity_awesomedesign); - ViewPager pager = (ViewPager) findViewById(R.id.pager); + setTitle("SimpleChartDemo"); + + ViewPager pager = findViewById(R.id.pager); pager.setOffscreenPageLimit(3); - + PageAdapter a = new PageAdapter(getSupportFragmentManager()); pager.setAdapter(a); - - + + AlertDialog.Builder b = new AlertDialog.Builder(this); b.setTitle("This is a ViewPager."); b.setMessage("Swipe left and right for more awesome design examples!"); b.setPositiveButton("OK", new OnClickListener() { - + @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); @@ -48,17 +53,17 @@ public void onClick(DialogInterface dialog, int which) { }); b.show(); } - + private class PageAdapter extends FragmentPagerAdapter { - public PageAdapter(FragmentManager fm) { - super(fm); + PageAdapter(FragmentManager fm) { + super(fm); } @Override - public Fragment getItem(int pos) { + public Fragment getItem(int pos) { Fragment f = null; - + switch(pos) { case 0: f = SineCosineFragment.newInstance(); @@ -83,6 +88,30 @@ public Fragment getItem(int pos) { @Override public int getCount() { return 5; - } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.only_github, menu); + return true; } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + switch (item.getItemId()) { + case R.id.viewGithub: { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleChartDemo.java")); + startActivity(i); + break; + } + } + + return true; + } + + @Override + public void saveToGallery() { /* Intentionally left empty */ } } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java similarity index 53% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index 25d103e8ce..ab70041e60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -1,24 +1,25 @@ package com.xxmassdeveloper.mpchartexample.fragments; +import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; import com.github.mikephil.charting.data.ScatterData; import com.github.mikephil.charting.data.ScatterDataSet; import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; @@ -29,58 +30,64 @@ import java.util.ArrayList; +@SuppressWarnings({"SameParameterValue", "WeakerAccess"}) public abstract class SimpleFragment extends Fragment { - + private Typeface tf; - + protected Context context; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + this.context = context; + } + public SimpleFragment() { - + } - + @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - tf = Typeface.createFromAsset(getActivity().getAssets(), "OpenSans-Regular.ttf"); + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); return super.onCreateView(inflater, container, savedInstanceState); } protected BarData generateBarData(int dataSets, float range, int count) { - - ArrayList sets = new ArrayList(); - + + ArrayList sets = new ArrayList<>(); + for(int i = 0; i < dataSets; i++) { - - ArrayList entries = new ArrayList(); - -// entries = FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "stacked_bars.txt"); - - for(int j = 0; j < count; j++) { - entries.add(new BarEntry((float) (Math.random() * range) + range / 4, j)); + + ArrayList entries = new ArrayList<>(); + + for(int j = 0; j < count; j++) { + entries.add(new BarEntry(j, (float) (Math.random() * range) + range / 4)); } - + BarDataSet ds = new BarDataSet(entries, getLabel(i)); ds.setColors(ColorTemplate.VORDIPLOM_COLORS); sets.add(ds); } - - BarData d = new BarData(ChartData.generateXVals(0, count), sets); + + BarData d = new BarData(sets); d.setValueTypeface(tf); return d; } - + protected ScatterData generateScatterData(int dataSets, float range, int count) { - - ArrayList sets = new ArrayList(); - - ScatterShape[] shapes = ScatterChart.getAllPossibleShapes(); - + + ArrayList sets = new ArrayList<>(); + + ScatterChart.ScatterShape[] shapes = ScatterChart.ScatterShape.getAllDefaultShapes(); + for(int i = 0; i < dataSets; i++) { - - ArrayList entries = new ArrayList(); - - for(int j = 0; j < count; j++) { - entries.add(new Entry((float) (Math.random() * range) + range / 4, j)); + + ArrayList entries = new ArrayList<>(); + + for(int j = 0; j < count; j++) { + entries.add(new Entry(j, (float) (Math.random() * range) + range / 4)); } - + ScatterDataSet ds = new ScatterDataSet(entries, getLabel(i)); ds.setScatterShapeSize(12f); ds.setScatterShape(shapes[i % shapes.length]); @@ -88,102 +95,81 @@ protected ScatterData generateScatterData(int dataSets, float range, int count) ds.setScatterShapeSize(9f); sets.add(ds); } - - ScatterData d = new ScatterData(ChartData.generateXVals(0, count), sets); + + ScatterData d = new ScatterData(sets); d.setValueTypeface(tf); return d; } - + /** * generates less data (1 DataSet, 4 values) - * @return + * @return PieData */ protected PieData generatePieData() { - + int count = 4; - - ArrayList entries1 = new ArrayList(); - ArrayList xVals = new ArrayList(); - - xVals.add("Quarter 1"); - xVals.add("Quarter 2"); - xVals.add("Quarter 3"); - xVals.add("Quarter 4"); - + + ArrayList entries1 = new ArrayList<>(); + for(int i = 0; i < count; i++) { - xVals.add("entry" + (i+1)); - - entries1.add(new Entry((float) (Math.random() * 60) + 40, i)); + entries1.add(new PieEntry((float) ((Math.random() * 60) + 40), "Quarter " + (i+1))); } - + PieDataSet ds1 = new PieDataSet(entries1, "Quarterly Revenues 2015"); ds1.setColors(ColorTemplate.VORDIPLOM_COLORS); ds1.setSliceSpace(2f); ds1.setValueTextColor(Color.WHITE); ds1.setValueTextSize(12f); - - PieData d = new PieData(xVals, ds1); + + PieData d = new PieData(ds1); d.setValueTypeface(tf); return d; } - + protected LineData generateLineData() { - -// DataSet ds1 = new DataSet(n, "O(n)"); -// DataSet ds2 = new DataSet(nlogn, "O(nlogn)"); -// DataSet ds3 = new DataSet(nsquare, "O(n\u00B2)"); -// DataSet ds4 = new DataSet(nthree, "O(n\u00B3)"); - - ArrayList sets = new ArrayList(); - - LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "sine.txt"), "Sine function"); - LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "cosine.txt"), "Cosine function"); - + + ArrayList sets = new ArrayList<>(); + LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "sine.txt"), "Sine function"); + LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "cosine.txt"), "Cosine function"); + ds1.setLineWidth(2f); ds2.setLineWidth(2f); - + ds1.setDrawCircles(false); ds2.setDrawCircles(false); - + ds1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setColor(ColorTemplate.VORDIPLOM_COLORS[1]); - - // load DataSets from textfiles in assets folders + + // load DataSets from files in assets folder sets.add(ds1); sets.add(ds2); - -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "n.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "nlogn.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "square.txt")); -// sets.add(FileUtils.dataSetFromAssets(getActivity().getAssets(), "three.txt")); - - int max = Math.max(sets.get(0).getEntryCount(), sets.get(1).getEntryCount()); - - LineData d = new LineData(ChartData.generateXVals(0, max), sets); + + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } - + protected LineData getComplexity() { - - ArrayList sets = new ArrayList(); - - LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "n.txt"), "O(n)"); - LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "nlogn.txt"), "O(nlogn)"); - LineDataSet ds3 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "square.txt"), "O(n\u00B2)"); - LineDataSet ds4 = new LineDataSet(FileUtils.loadEntriesFromAssets(getActivity().getAssets(), "three.txt"), "O(n\u00B3)"); - + + ArrayList sets = new ArrayList<>(); + + LineDataSet ds1 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "n.txt"), "O(n)"); + LineDataSet ds2 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "nlogn.txt"), "O(nlogn)"); + LineDataSet ds3 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "square.txt"), "O(n\u00B2)"); + LineDataSet ds4 = new LineDataSet(FileUtils.loadEntriesFromAssets(context.getAssets(), "three.txt"), "O(n\u00B3)"); + ds1.setColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setColor(ColorTemplate.VORDIPLOM_COLORS[1]); ds3.setColor(ColorTemplate.VORDIPLOM_COLORS[2]); ds4.setColor(ColorTemplate.VORDIPLOM_COLORS[3]); - + ds1.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[0]); ds2.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[1]); ds3.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[2]); ds4.setCircleColor(ColorTemplate.VORDIPLOM_COLORS[3]); - + ds1.setLineWidth(2.5f); ds1.setCircleRadius(3f); ds2.setLineWidth(2.5f); @@ -192,22 +178,21 @@ protected LineData getComplexity() { ds3.setCircleRadius(3f); ds4.setLineWidth(2.5f); ds4.setCircleRadius(3f); - - - // load DataSets from textfiles in assets folders - sets.add(ds1); + + + // load DataSets from files in assets folder + sets.add(ds1); sets.add(ds2); sets.add(ds3); sets.add(ds4); - - LineData d = new LineData(ChartData.generateXVals(0, ds1.getEntryCount()), sets); + + LineData d = new LineData(sets); d.setValueTypeface(tf); return d; } - - private String[] mLabels = new String[] { "Company A", "Company B", "Company C", "Company D", "Company E", "Company F" }; -// private String[] mXVals = new String[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" }; - + + private final String[] mLabels = new String[] { "Company A", "Company B", "Company C", "Company D", "Company E", "Company F" }; + private String getLabel(int i) { return mLabels[i]; } diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java new file mode 100644 index 0000000000..0581529308 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java @@ -0,0 +1,57 @@ +package com.xxmassdeveloper.mpchartexample.fragments; +import android.graphics.Typeface; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; +import com.xxmassdeveloper.mpchartexample.R; + + +public class SineCosineFragment extends SimpleFragment { + + @NonNull + public static Fragment newInstance() { + return new SineCosineFragment(); + } + + @SuppressWarnings("FieldCanBeLocal") + private LineChart chart; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.frag_simple_line, container, false); + + chart = v.findViewById(R.id.lineChart1); + + chart.getDescription().setEnabled(false); + + chart.setDrawGridBackground(false); + + chart.setData(generateLineData()); + chart.animateX(3000); + + Typeface tf = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + + Legend l = chart.getLegend(); + l.setTypeface(tf); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tf); + leftAxis.setAxisMaximum(1.2f); + leftAxis.setAxisMinimum(-1.2f); + + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setEnabled(false); + + return v; + } +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java similarity index 83% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index 70463a7534..eeb1791fe1 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java @@ -1,5 +1,6 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; import android.view.LayoutInflater; @@ -14,9 +15,9 @@ import com.xxmassdeveloper.mpchartexample.R; public class BarChartItem extends ChartItem { - - private Typeface mTf; - + + private final Typeface mTf; + public BarChartItem(ChartData cd, Context c) { super(cd); @@ -28,10 +29,11 @@ public int getItemType() { return TYPE_BARCHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -39,7 +41,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_barchart, null); - holder.chart = (BarChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); @@ -48,7 +50,7 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); holder.chart.setDrawBarShadow(false); @@ -57,31 +59,32 @@ public View getView(int position, View convertView, Context c) { xAxis.setTypeface(mTf); xAxis.setDrawGridLines(false); xAxis.setDrawAxisLine(true); - + YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(20f); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(20f); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) mChartData.setValueTypeface(mTf); - + // set data holder.chart.setData((BarData) mChartData); - + holder.chart.setFitBars(true); + // do not forget to refresh the chart // holder.chart.invalidate(); holder.chart.animateY(700); return convertView; } - + private static class ViewHolder { BarChart chart; } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java similarity index 51% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java index 0e6182165c..2a8ed0561d 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/ChartItem.java @@ -6,23 +6,24 @@ import com.github.mikephil.charting.data.ChartData; /** - * baseclass of the chart-listview items + * Base class of the Chart ListView items * @author philipp * */ +@SuppressWarnings("unused") public abstract class ChartItem { - - protected static final int TYPE_BARCHART = 0; - protected static final int TYPE_LINECHART = 1; - protected static final int TYPE_PIECHART = 2; - - protected ChartData mChartData; - - public ChartItem(ChartData cd) { - this.mChartData = cd; + + static final int TYPE_BARCHART = 0; + static final int TYPE_LINECHART = 1; + static final int TYPE_PIECHART = 2; + + ChartData mChartData; + + ChartItem(ChartData cd) { + this.mChartData = cd; } - + public abstract int getItemType(); - + public abstract View getView(int position, View convertView, Context c); } diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java similarity index 84% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index e43b64d1bd..d69b010322 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Typeface; import android.view.LayoutInflater; @@ -16,7 +17,7 @@ public class LineChartItem extends ChartItem { - private Typeface mTf; + private final Typeface mTf; public LineChartItem(ChartData cd, Context c) { super(cd); @@ -29,10 +30,11 @@ public int getItemType() { return TYPE_LINECHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -40,7 +42,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_linechart, null); - holder.chart = (LineChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); @@ -50,7 +52,7 @@ public View getView(int position, View convertView, Context c) { // apply styling // holder.chart.setValueTypeface(mTf); - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setDrawGridBackground(false); XAxis xAxis = holder.chart.getXAxis(); @@ -62,13 +64,13 @@ public View getView(int position, View convertView, Context c) { YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); leftAxis.setLabelCount(5, false); - leftAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) - + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); rightAxis.setLabelCount(5, false); rightAxis.setDrawGridLines(false); - rightAxis.setAxisMinValue(0f); // this replaces setStartAtZero(true) + rightAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) // set data holder.chart.setData((LineData) mChartData); diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java similarity index 84% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index f803eb8072..916f8dc5e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -1,6 +1,7 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; @@ -12,7 +13,6 @@ import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.Legend.LegendPosition; import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.formatter.PercentFormatter; @@ -21,8 +21,8 @@ public class PieChartItem extends ChartItem { - private Typeface mTf; - private SpannableString mCenterText; + private final Typeface mTf; + private final SpannableString mCenterText; public PieChartItem(ChartData cd, Context c) { super(cd); @@ -36,10 +36,11 @@ public int getItemType() { return TYPE_PIECHART; } + @SuppressLint("InflateParams") @Override public View getView(int position, View convertView, Context c) { - ViewHolder holder = null; + ViewHolder holder; if (convertView == null) { @@ -47,7 +48,7 @@ public View getView(int position, View convertView, Context c) { convertView = LayoutInflater.from(c).inflate( R.layout.list_item_piechart, null); - holder.chart = (PieChart) convertView.findViewById(R.id.chart); + holder.chart = convertView.findViewById(R.id.chart); convertView.setTag(holder); @@ -56,7 +57,7 @@ public View getView(int position, View convertView, Context c) { } // apply styling - holder.chart.setDescription(""); + holder.chart.getDescription().setEnabled(false); holder.chart.setHoleRadius(52f); holder.chart.setTransparentCircleRadius(57f); holder.chart.setCenterText(mCenterText); @@ -73,7 +74,10 @@ public View getView(int position, View convertView, Context c) { holder.chart.setData((PieData) mChartData); Legend l = holder.chart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); + l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP); + l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT); + l.setOrientation(Legend.LegendOrientation.VERTICAL); + l.setDrawInside(false); l.setYEntrySpace(0f); l.setYOffset(0f); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java new file mode 100644 index 0000000000..f322090d72 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/ContentItem.java @@ -0,0 +1,22 @@ +package com.xxmassdeveloper.mpchartexample.notimportant; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +class ContentItem { + + final String name; + final String desc; + boolean isSection = false; + + ContentItem(String n) { + name = n; + desc = ""; + isSection = true; + } + + ContentItem(String n, String d) { + name = n; + desc = d; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java new file mode 100644 index 0000000000..a963609aff --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/DemoBase.java @@ -0,0 +1,99 @@ + +package com.xxmassdeveloper.mpchartexample.notimportant; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.graphics.Typeface; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.material.snackbar.Snackbar; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import android.view.View; +import android.widget.Toast; + +import com.github.mikephil.charting.charts.Chart; +import com.xxmassdeveloper.mpchartexample.R; + +/** + * Base class of all Activities of the Demo Application. + * + * @author Philipp Jahoda + */ +public abstract class DemoBase extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback { + + protected final String[] months = new String[] { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec" + }; + + protected final String[] parties = new String[] { + "Party A", "Party B", "Party C", "Party D", "Party E", "Party F", "Party G", "Party H", + "Party I", "Party J", "Party K", "Party L", "Party M", "Party N", "Party O", "Party P", + "Party Q", "Party R", "Party S", "Party T", "Party U", "Party V", "Party W", "Party X", + "Party Y", "Party Z" + }; + + private static final int PERMISSION_STORAGE = 0; + + protected Typeface tfRegular; + protected Typeface tfLight; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + tfRegular = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); + tfLight = Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf"); + } + + protected float getRandom(float range, float start) { + return (float) (Math.random() * range) + start; + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + overridePendingTransition(R.anim.move_left_in_activity, R.anim.move_right_out_activity); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PERMISSION_STORAGE) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + saveToGallery(); + } else { + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + } + } + } + + protected void requestStoragePermission(View view) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + Snackbar.make(view, "Write permission is required to save image to gallery", Snackbar.LENGTH_INDEFINITE) + .setAction(android.R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View v) { + ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE); + } + }).show(); + } else { + Toast.makeText(getApplicationContext(), "Permission Required!", Toast.LENGTH_SHORT) + .show(); + ActivityCompat.requestPermissions(DemoBase.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_STORAGE); + } + } + + protected void saveToGallery(Chart chart, String name) { + if (chart.saveToGallery(name + "_" + System.currentTimeMillis(), 70)) + Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!", + Toast.LENGTH_SHORT).show(); + else + Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT) + .show(); + } + + protected abstract void saveToGallery(); +} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java similarity index 54% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java index bd6139c63a..88e5dc8d8b 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -1,7 +1,6 @@ package com.xxmassdeveloper.mpchartexample.notimportant; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -24,31 +23,35 @@ import com.xxmassdeveloper.mpchartexample.CombinedChartActivity; import com.xxmassdeveloper.mpchartexample.CubicLineChartActivity; import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity; +import com.xxmassdeveloper.mpchartexample.FilledLineActivity; +import com.xxmassdeveloper.mpchartexample.HalfPieChartActivity; import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; +import com.xxmassdeveloper.mpchartexample.HorizontalBarNegativeChartActivity; import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; import com.xxmassdeveloper.mpchartexample.LineChartActivity1; import com.xxmassdeveloper.mpchartexample.LineChartActivity2; import com.xxmassdeveloper.mpchartexample.LineChartActivityColored; +import com.xxmassdeveloper.mpchartexample.LineChartTime; import com.xxmassdeveloper.mpchartexample.ListViewBarChartActivity; import com.xxmassdeveloper.mpchartexample.ListViewMultiChartActivity; import com.xxmassdeveloper.mpchartexample.MultiLineChartActivity; import com.xxmassdeveloper.mpchartexample.PerformanceLineChart; import com.xxmassdeveloper.mpchartexample.PieChartActivity; +import com.xxmassdeveloper.mpchartexample.PiePolylineChartActivity; import com.xxmassdeveloper.mpchartexample.R; -import com.xxmassdeveloper.mpchartexample.RadarChartActivitry; +import com.xxmassdeveloper.mpchartexample.RadarChartActivity; import com.xxmassdeveloper.mpchartexample.RealtimeLineChartActivity; import com.xxmassdeveloper.mpchartexample.ScatterChartActivity; import com.xxmassdeveloper.mpchartexample.ScrollViewActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivity; import com.xxmassdeveloper.mpchartexample.StackedBarActivityNegative; import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; -import com.xxmassdeveloper.mpchartexample.realm.RealmDatabaseActivityBar; -import com.xxmassdeveloper.mpchartexample.realm.RealmDatabaseActivityLine; -import com.xxmassdeveloper.mpchartexample.realm.RealmMainActivity; import java.util.ArrayList; -public class MainActivity extends Activity implements OnItemClickListener { +import androidx.appcompat.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity implements OnItemClickListener { @Override protected void onCreate(Bundle savedInstanceState) { @@ -62,81 +65,68 @@ protected void onCreate(Bundle savedInstanceState) { // initialize the utilities Utils.init(this); - ArrayList objects = new ArrayList(); - - objects.add(new ContentItem("Line Chart", "A simple demonstration of the linechart.")); - objects.add(new ContentItem("Line Chart (Dual YAxis)", - "Demonstration of the linechart with dual y-axis.")); - objects.add(new ContentItem("Bar Chart", "A simple demonstration of the bar chart.")); - objects.add(new ContentItem("Horizontal Bar Chart", - "A simple demonstration of the horizontal bar chart.")); - objects.add(new ContentItem("Combined Chart", - "Demonstrates how to create a combined chart (bar and line in this case).")); - objects.add(new ContentItem("Pie Chart", "A simple demonstration of the pie chart.")); - objects.add(new ContentItem("Scatter Chart", "A simple demonstration of the scatter chart.")); - objects.add(new ContentItem("Bubble Chart", "A simple demonstration of the bubble chart.")); - objects.add(new ContentItem("Stacked Bar Chart", - "A simple demonstration of a bar chart with stacked bars.")); - objects.add(new ContentItem("Stacked Bar Chart Negative", - "A simple demonstration of stacked bars with negative and positive values.")); - objects.add(new ContentItem("Another Bar Chart", - "Implementation of a BarChart that only shows values at the bottom.")); - objects.add(new ContentItem("Multiple Lines Chart", - "A line chart with multiple DataSet objects. One color per DataSet.")); - objects.add(new ContentItem("Multiple Bars Chart", - "A bar chart with multiple DataSet objects. One multiple colors per DataSet.")); - objects.add(new ContentItem( - "Charts in ViewPager Fragments", - "Demonstration of charts inside ViewPager Fragments. In this example the focus was on the design and look and feel of the chart.")); - objects.add(new ContentItem( - "BarChart inside ListView", - "Demonstrates the usage of a BarChart inside a ListView item.")); - objects.add(new ContentItem( - "Multiple charts inside ListView", - "Demonstrates the usage of different chart types inside a ListView.")); - objects.add(new ContentItem( - "Inverted Line Chart", - "Demonstrates the feature of inverting the y-axis.")); - objects.add(new ContentItem( - "Candle Stick Chart", - "Demonstrates usage of the CandleStickChart.")); - objects.add(new ContentItem( - "Cubic Line Chart", - "Demonstrates cubic lines in a LineChart.")); - objects.add(new ContentItem( - "Radar Chart", - "Demonstrates the use of a spider-web like (net) chart.")); - objects.add(new ContentItem( - "Colored Line Chart", - "Shows a LineChart with different background and line color.")); - objects.add(new ContentItem( - "Realtime Chart", - "This chart is fed with new data in realtime. It also restrains the view on the x-axis.")); - objects.add(new ContentItem( - "Dynamical data adding", - "This Activity demonstrates dynamical adding of Entries and DataSets (real time graph).")); - objects.add(new ContentItem( - "Performance Line Chart", - "Renders up to 30.000 objects smoothly.")); - objects.add(new ContentItem( - "Sinus Bar Chart", - "A Bar Chart plotting the sinus function with 8.000 values.")); - objects.add(new ContentItem( - "Chart in ScrollView", - "This demonstrates how to use a chart inside a ScrollView.")); - objects.add(new ContentItem( - "BarChart positive / negative", - "This demonstrates how to create a BarChart with positive and negative values in different colors.")); - - ContentItem realm = new ContentItem( - "Realm.io Database", - "This demonstrates how to use this library with Realm.io mobile database."); - realm.isNew = true; - objects.add(realm); + ArrayList objects = new ArrayList<>(); + + //// + objects.add(0, new ContentItem("Line Charts")); + + objects.add(1, new ContentItem("Basic", "Simple line chart.")); + objects.add(2, new ContentItem("Multiple", "Show multiple data sets.")); + objects.add(3, new ContentItem("Dual Axis", "Line chart with dual y-axes.")); + objects.add(4, new ContentItem("Inverted Axis", "Inverted y-axis.")); + objects.add(5, new ContentItem("Cubic", "Line chart with a cubic line shape.")); + objects.add(6, new ContentItem("Colorful", "Colorful line chart.")); + objects.add(7, new ContentItem("Performance", "Render 30.000 data points smoothly.")); + objects.add(8, new ContentItem("Filled", "Colored area between two lines.")); + + //// + objects.add(9, new ContentItem("Bar Charts")); + + objects.add(10, new ContentItem("Basic", "Simple bar chart.")); + objects.add(11, new ContentItem("Basic 2", "Variation of the simple bar chart.")); + objects.add(12, new ContentItem("Multiple", "Show multiple data sets.")); + objects.add(13, new ContentItem("Horizontal", "Render bar chart horizontally.")); + objects.add(14, new ContentItem("Stacked", "Stacked bar chart.")); + objects.add(15, new ContentItem("Negative", "Positive and negative values with unique colors.")); + objects.add(16, new ContentItem("Negative Horizontal", "demonstrates how to create a HorizontalBarChart with positive and negative values.")); + objects.add(17, new ContentItem("Stacked 2", "Stacked bar chart with negative values.")); + objects.add(18, new ContentItem("Sine", "Sine function in bar chart format.")); + + //// + objects.add(19, new ContentItem("Pie Charts")); + + objects.add(20, new ContentItem("Basic", "Simple pie chart.")); + objects.add(21, new ContentItem("Value Lines", "Stylish lines drawn outward from slices.")); + objects.add(22, new ContentItem("Half Pie", "180° (half) pie chart.")); + + //// + objects.add(23, new ContentItem("Other Charts")); + + objects.add(24, new ContentItem("Combined Chart", "Bar and line chart together.")); + objects.add(25, new ContentItem("Scatter Plot", "Simple scatter plot.")); + objects.add(26, new ContentItem("Bubble Chart", "Simple bubble chart.")); + objects.add(27, new ContentItem("Candlestick", "Simple financial chart.")); + objects.add(28, new ContentItem("Radar Chart", "Simple web chart.")); + + //// + objects.add(29, new ContentItem("Scrolling Charts")); + + objects.add(30, new ContentItem("Multiple", "Various types of charts as fragments.")); + objects.add(31, new ContentItem("View Pager", "Swipe through different charts.")); + objects.add(32, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!")); + objects.add(33, new ContentItem("Many Bar Charts", "More bars than your screen can handle!")); + + //// + objects.add(34, new ContentItem("Even More Line Charts")); + + objects.add(35, new ContentItem("Dynamic", "Build a line chart by adding points and sets.")); + objects.add(36, new ContentItem("Realtime", "Add data points in realtime.")); + objects.add(37, new ContentItem("Hourly", "Uses the current time to add a data point for each hour.")); + //objects.add(38, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database.")); MyAdapter adapter = new MyAdapter(this, objects); - ListView lv = (ListView) findViewById(R.id.listView1); + ListView lv = findViewById(R.id.listView1); lv.setAdapter(adapter); lv.setOnItemClickListener(this); @@ -145,123 +135,112 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onItemClick(AdapterView av, View v, int pos, long arg3) { - Intent i; + Intent i = null; switch (pos) { - case 0: - i = new Intent(this, LineChartActivity1.class); - startActivity(i); - break; case 1: - i = new Intent(this, LineChartActivity2.class); - startActivity(i); + i = new Intent(this, LineChartActivity1.class); break; case 2: - i = new Intent(this, BarChartActivity.class); - startActivity(i); + i = new Intent(this, MultiLineChartActivity.class); break; case 3: - i = new Intent(this, HorizontalBarChartActivity.class); - startActivity(i); + i = new Intent(this, LineChartActivity2.class); break; case 4: - i = new Intent(this, CombinedChartActivity.class); - startActivity(i); + i = new Intent(this, InvertedLineChartActivity.class); break; case 5: - i = new Intent(this, PieChartActivity.class); - startActivity(i); + i = new Intent(this, CubicLineChartActivity.class); break; case 6: - i = new Intent(this, ScatterChartActivity.class); - startActivity(i); + i = new Intent(this, LineChartActivityColored.class); break; case 7: - i = new Intent(this, BubbleChartActivity.class); - startActivity(i); + i = new Intent(this, PerformanceLineChart.class); break; case 8: - i = new Intent(this, StackedBarActivity.class); - startActivity(i); - break; - case 9: - i = new Intent(this, StackedBarActivityNegative.class); - startActivity(i); + i = new Intent(this, FilledLineActivity.class); break; case 10: - i = new Intent(this, AnotherBarActivity.class); - startActivity(i); + i = new Intent(this, BarChartActivity.class); break; case 11: - i = new Intent(this, MultiLineChartActivity.class); - startActivity(i); + i = new Intent(this, AnotherBarActivity.class); break; case 12: i = new Intent(this, BarChartActivityMultiDataset.class); - startActivity(i); break; case 13: - i = new Intent(this, SimpleChartDemo.class); - startActivity(i); + i = new Intent(this, HorizontalBarChartActivity.class); break; case 14: - i = new Intent(this, ListViewBarChartActivity.class); - startActivity(i); + i = new Intent(this, StackedBarActivity.class); break; case 15: - i = new Intent(this, ListViewMultiChartActivity.class); - startActivity(i); + i = new Intent(this, BarChartPositiveNegative.class); break; case 16: - i = new Intent(this, InvertedLineChartActivity.class); - startActivity(i); + i = new Intent(this, HorizontalBarNegativeChartActivity.class); break; case 17: - i = new Intent(this, CandleStickChartActivity.class); - startActivity(i); + i = new Intent(this, StackedBarActivityNegative.class); break; case 18: - i = new Intent(this, CubicLineChartActivity.class); - startActivity(i); - break; - case 19: - i = new Intent(this, RadarChartActivitry.class); - startActivity(i); + i = new Intent(this, BarChartActivitySinus.class); break; case 20: - i = new Intent(this, LineChartActivityColored.class); - startActivity(i); + i = new Intent(this, PieChartActivity.class); break; case 21: - i = new Intent(this, RealtimeLineChartActivity.class); - startActivity(i); + i = new Intent(this, PiePolylineChartActivity.class); break; case 22: - i = new Intent(this, DynamicalAddingActivity.class); - startActivity(i); - break; - case 23: - i = new Intent(this, PerformanceLineChart.class); - startActivity(i); + i = new Intent(this, HalfPieChartActivity.class); break; case 24: - i = new Intent(this, BarChartActivitySinus.class); - startActivity(i); + i = new Intent(this, CombinedChartActivity.class); break; case 25: - i = new Intent(this, ScrollViewActivity.class); - startActivity(i); + i = new Intent(this, ScatterChartActivity.class); break; case 26: - i = new Intent(this, BarChartPositiveNegative.class); - startActivity(i); + i = new Intent(this, BubbleChartActivity.class); break; case 27: - i = new Intent(this, RealmMainActivity.class); - startActivity(i); + i = new Intent(this, CandleStickChartActivity.class); + break; + case 28: + i = new Intent(this, RadarChartActivity.class); + break; + case 30: + i = new Intent(this, ListViewMultiChartActivity.class); + break; + case 31: + i = new Intent(this, SimpleChartDemo.class); + break; + case 32: + i = new Intent(this, ScrollViewActivity.class); break; + case 33: + i = new Intent(this, ListViewBarChartActivity.class); + break; + case 35: + i = new Intent(this, DynamicalAddingActivity.class); + break; + case 36: + i = new Intent(this, RealtimeLineChartActivity.class); + break; + case 37: + i = new Intent(this, LineChartTime.class); + break; + /*case 38: + i = new Intent(this, RealmMainActivity.class); + break;*/ } + if (i != null) startActivity(i); + overridePendingTransition(R.anim.move_right_in_activity, R.anim.move_left_out_activity); } @@ -274,7 +253,7 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - Intent i = null; + Intent i; switch (item.getItemId()) { case R.id.viewGithub: @@ -289,11 +268,6 @@ public boolean onOptionsItemSelected(MenuItem item) { i.putExtra(Intent.EXTRA_TEXT, "Your error report here..."); startActivity(Intent.createChooser(i, "Report Problem")); break; - case R.id.blog: - i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("http://www.xxmassdeveloper.com")); - startActivity(i); - break; case R.id.website: i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse("http://at.linkedin.com/in/philippjahoda")); diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java new file mode 100644 index 0000000000..9a22b51c9a --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MyAdapter.java @@ -0,0 +1,71 @@ +package com.xxmassdeveloper.mpchartexample.notimportant; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Typeface; +import androidx.annotation.NonNull; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import com.xxmassdeveloper.mpchartexample.R; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 07/12/15. + */ +class MyAdapter extends ArrayAdapter { + + private final Typeface mTypeFaceLight; + private final Typeface mTypeFaceRegular; + + MyAdapter(Context context, List objects) { + super(context, 0, objects); + + mTypeFaceLight = Typeface.createFromAsset(context.getAssets(), "OpenSans-Light.ttf"); + mTypeFaceRegular = Typeface.createFromAsset(context.getAssets(), "OpenSans-Regular.ttf"); + } + + @SuppressLint("InflateParams") + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + + ContentItem c = getItem(position); + + ViewHolder holder; + + holder = new ViewHolder(); + + if (c != null && c.isSection) { + convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item_section, null); + } else { + convertView = LayoutInflater.from(getContext()).inflate(R.layout.list_item, null); + } + + holder.tvName = convertView.findViewById(R.id.tvName); + holder.tvDesc = convertView.findViewById(R.id.tvDesc); + + convertView.setTag(holder); + + if (c != null && c.isSection) + holder.tvName.setTypeface(mTypeFaceRegular); + else + holder.tvName.setTypeface(mTypeFaceLight); + holder.tvDesc.setTypeface(mTypeFaceLight); + + holder.tvName.setText(c != null ? c.name : null); + holder.tvDesc.setText(c != null ? c.desc : null); + + return convertView; + } + + private class ViewHolder { + + TextView tvName, tvDesc; + } +} diff --git a/MPChartExample/res/anim/move_left_in_activity.xml b/MPChartExample/src/main/res/anim/move_left_in_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_left_in_activity.xml rename to MPChartExample/src/main/res/anim/move_left_in_activity.xml diff --git a/MPChartExample/res/anim/move_left_out_activity.xml b/MPChartExample/src/main/res/anim/move_left_out_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_left_out_activity.xml rename to MPChartExample/src/main/res/anim/move_left_out_activity.xml diff --git a/MPChartExample/res/anim/move_right_in_activity.xml b/MPChartExample/src/main/res/anim/move_right_in_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_right_in_activity.xml rename to MPChartExample/src/main/res/anim/move_right_in_activity.xml diff --git a/MPChartExample/res/anim/move_right_out_activity.xml b/MPChartExample/src/main/res/anim/move_right_out_activity.xml similarity index 100% rename from MPChartExample/res/anim/move_right_out_activity.xml rename to MPChartExample/src/main/res/anim/move_right_out_activity.xml diff --git a/MPChartExample/res/drawable-hdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-hdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-hdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/MPChartExample/src/main/res/drawable-hdpi/star.png b/MPChartExample/src/main/res/drawable-hdpi/star.png new file mode 100644 index 0000000000..c7811ef2b8 Binary files /dev/null and b/MPChartExample/src/main/res/drawable-hdpi/star.png differ diff --git a/MPChartExample/res/drawable-mdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-mdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable-nodpi/marker2.png b/MPChartExample/src/main/res/drawable-nodpi/marker2.png similarity index 100% rename from MPChartExample/res/drawable-nodpi/marker2.png rename to MPChartExample/src/main/res/drawable-nodpi/marker2.png diff --git a/MPChartExample/src/main/res/drawable-nodpi/radar_marker.png b/MPChartExample/src/main/res/drawable-nodpi/radar_marker.png new file mode 100644 index 0000000000..a84b93b539 Binary files /dev/null and b/MPChartExample/src/main/res/drawable-nodpi/radar_marker.png differ diff --git a/MPChartExample/res/drawable-xhdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-xhdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable-xxhdpi/ic_launcher.png b/MPChartExample/src/main/res/drawable-xxhdpi/ic_launcher.png similarity index 100% rename from MPChartExample/res/drawable-xxhdpi/ic_launcher.png rename to MPChartExample/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/MPChartExample/res/drawable/fade_red.xml b/MPChartExample/src/main/res/drawable/fade_red.xml similarity index 100% rename from MPChartExample/res/drawable/fade_red.xml rename to MPChartExample/src/main/res/drawable/fade_red.xml diff --git a/MPChartExample/res/layout/activity_age_distribution.xml b/MPChartExample/src/main/res/layout/activity_age_distribution.xml similarity index 90% rename from MPChartExample/res/layout/activity_age_distribution.xml rename to MPChartExample/src/main/res/layout/activity_age_distribution.xml index b023d3ab2d..574510fa0b 100644 --- a/MPChartExample/res/layout/activity_age_distribution.xml +++ b/MPChartExample/src/main/res/layout/activity_age_distribution.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - - + + + android:layout_height="match_parent"> - + + android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" /> diff --git a/MPChartExample/res/layout/activity_barchart_noseekbar.xml b/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml similarity index 84% rename from MPChartExample/res/layout/activity_barchart_noseekbar.xml rename to MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml index 5180e07b78..90a8c0f521 100644 --- a/MPChartExample/res/layout/activity_barchart_noseekbar.xml +++ b/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml @@ -1,11 +1,11 @@ + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_barchart_sinus.xml b/MPChartExample/src/main/res/layout/activity_barchart_sinus.xml similarity index 97% rename from MPChartExample/res/layout/activity_barchart_sinus.xml rename to MPChartExample/src/main/res/layout/activity_barchart_sinus.xml index 78b849081f..f39f2c7739 100644 --- a/MPChartExample/res/layout/activity_barchart_sinus.xml +++ b/MPChartExample/src/main/res/layout/activity_barchart_sinus.xml @@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_alignBottom="@+id/seekbarValues" android:layout_alignParentRight="true" - android:text="0" + android:text="@string/dash" android:layout_marginBottom="15dp" android:layout_marginRight="10dp" android:gravity="right" diff --git a/MPChartExample/res/layout/activity_bubblechart.xml b/MPChartExample/src/main/res/layout/activity_bubblechart.xml similarity index 94% rename from MPChartExample/res/layout/activity_bubblechart.xml rename to MPChartExample/src/main/res/layout/activity_bubblechart.xml index 1cc55dfb42..d3df042fd0 100644 --- a/MPChartExample/res/layout/activity_bubblechart.xml +++ b/MPChartExample/src/main/res/layout/activity_bubblechart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - + + android:orientation="vertical"> - + diff --git a/MPChartExample/res/layout/activity_draw_chart.xml b/MPChartExample/src/main/res/layout/activity_draw_chart.xml similarity index 89% rename from MPChartExample/res/layout/activity_draw_chart.xml rename to MPChartExample/src/main/res/layout/activity_draw_chart.xml index 5b3792395b..5e2f1a21fc 100644 --- a/MPChartExample/res/layout/activity_draw_chart.xml +++ b/MPChartExample/src/main/res/layout/activity_draw_chart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> - + - @@ -8,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/seekBar1" /> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/src/main/res/layout/activity_linechart_time.xml b/MPChartExample/src/main/res/layout/activity_linechart_time.xml new file mode 100644 index 0000000000..c63d3e6f41 --- /dev/null +++ b/MPChartExample/src/main/res/layout/activity_linechart_time.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/MPChartExample/res/layout/activity_listview_chart.xml b/MPChartExample/src/main/res/layout/activity_listview_chart.xml similarity index 90% rename from MPChartExample/res/layout/activity_listview_chart.xml rename to MPChartExample/src/main/res/layout/activity_listview_chart.xml index b11c3d1ef8..12aa2f8500 100644 --- a/MPChartExample/res/layout/activity_listview_chart.xml +++ b/MPChartExample/src/main/res/layout/activity_listview_chart.xml @@ -2,7 +2,7 @@ + android:orientation="vertical"> + android:layout_height="fill_parent" + android:scrollbarFadeDuration="0"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_performance_linechart.xml b/MPChartExample/src/main/res/layout/activity_performance_linechart.xml similarity index 92% rename from MPChartExample/res/layout/activity_performance_linechart.xml rename to MPChartExample/src/main/res/layout/activity_performance_linechart.xml index d7cd5747fe..515321e1de 100644 --- a/MPChartExample/res/layout/activity_performance_linechart.xml +++ b/MPChartExample/src/main/res/layout/activity_performance_linechart.xml @@ -1,8 +1,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> - + + android:layout_height="match_parent"> - \ No newline at end of file + diff --git a/MPChartExample/res/layout/activity_radarchart.xml b/MPChartExample/src/main/res/layout/activity_radarchart.xml similarity index 89% rename from MPChartExample/res/layout/activity_radarchart.xml rename to MPChartExample/src/main/res/layout/activity_radarchart.xml index a197875bb8..aff98010c8 100644 --- a/MPChartExample/res/layout/activity_radarchart.xml +++ b/MPChartExample/src/main/res/layout/activity_radarchart.xml @@ -1,7 +1,7 @@ + android:layout_height="match_parent"> - + diff --git a/MPChartExample/res/layout/activity_scatterchart.xml b/MPChartExample/src/main/res/layout/activity_scatterchart.xml similarity index 94% rename from MPChartExample/res/layout/activity_scatterchart.xml rename to MPChartExample/src/main/res/layout/activity_scatterchart.xml index 947f8ce56d..41df167e5a 100644 --- a/MPChartExample/res/layout/activity_scatterchart.xml +++ b/MPChartExample/src/main/res/layout/activity_scatterchart.xml @@ -1,14 +1,14 @@ + android:layout_height="match_parent"> - + + android:layout_height="wrap_content"> - + + android:text="@string/scrollViewStart" /> @@ -30,13 +30,13 @@ - + - + android:text="@string/scrollViewEnd" /> + - \ No newline at end of file + diff --git a/MPChartExample/res/layout/custom_marker_view.xml b/MPChartExample/src/main/res/layout/custom_marker_view.xml similarity index 82% rename from MPChartExample/res/layout/custom_marker_view.xml rename to MPChartExample/src/main/res/layout/custom_marker_view.xml index 12cb53c2e2..f8444bf8c4 100644 --- a/MPChartExample/res/layout/custom_marker_view.xml +++ b/MPChartExample/src/main/res/layout/custom_marker_view.xml @@ -1,8 +1,10 @@ + android:background="@drawable/marker2" + tools:ignore="Overdraw"> - - + diff --git a/MPChartExample/res/layout/frag_simple_line.xml b/MPChartExample/src/main/res/layout/frag_simple_line.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_line.xml rename to MPChartExample/src/main/res/layout/frag_simple_line.xml diff --git a/MPChartExample/res/layout/frag_simple_pie.xml b/MPChartExample/src/main/res/layout/frag_simple_pie.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_pie.xml rename to MPChartExample/src/main/res/layout/frag_simple_pie.xml diff --git a/MPChartExample/res/layout/frag_simple_scatter.xml b/MPChartExample/src/main/res/layout/frag_simple_scatter.xml similarity index 100% rename from MPChartExample/res/layout/frag_simple_scatter.xml rename to MPChartExample/src/main/res/layout/frag_simple_scatter.xml diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/src/main/res/layout/list_item.xml similarity index 52% rename from MPChartExample/res/layout/list_item.xml rename to MPChartExample/src/main/res/layout/list_item.xml index c9c11e93ba..420add1193 100644 --- a/MPChartExample/res/layout/list_item.xml +++ b/MPChartExample/src/main/res/layout/list_item.xml @@ -12,7 +12,8 @@ android:layout_alignParentTop="true" android:layout_marginLeft="4dp" android:text="Medium Text" - android:textSize="16dp"/> + android:textColor="@android:color/black" + android:textSize="16sp"/> - - + android:textSize="12sp" + android:layout_marginRight="10dp" /> diff --git a/MPChartExample/res/layout/list_item_barchart.xml b/MPChartExample/src/main/res/layout/list_item_barchart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_barchart.xml rename to MPChartExample/src/main/res/layout/list_item_barchart.xml diff --git a/MPChartExample/res/layout/list_item_linechart.xml b/MPChartExample/src/main/res/layout/list_item_linechart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_linechart.xml rename to MPChartExample/src/main/res/layout/list_item_linechart.xml diff --git a/MPChartExample/res/layout/list_item_piechart.xml b/MPChartExample/src/main/res/layout/list_item_piechart.xml similarity index 100% rename from MPChartExample/res/layout/list_item_piechart.xml rename to MPChartExample/src/main/res/layout/list_item_piechart.xml diff --git a/MPChartExample/src/main/res/layout/list_item_section.xml b/MPChartExample/src/main/res/layout/list_item_section.xml new file mode 100644 index 0000000000..19707f1777 --- /dev/null +++ b/MPChartExample/src/main/res/layout/list_item_section.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/MPChartExample/src/main/res/layout/radar_markerview.xml b/MPChartExample/src/main/res/layout/radar_markerview.xml new file mode 100644 index 0000000000..d94768dd67 --- /dev/null +++ b/MPChartExample/src/main/res/layout/radar_markerview.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/MPChartExample/src/main/res/menu/bar.xml b/MPChartExample/src/main/res/menu/bar.xml new file mode 100644 index 0000000000..e05fc59797 --- /dev/null +++ b/MPChartExample/src/main/res/menu/bar.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/res/menu/bar.xml b/MPChartExample/src/main/res/menu/bubble.xml similarity index 53% rename from MPChartExample/res/menu/bar.xml rename to MPChartExample/src/main/res/menu/bubble.xml index 2cce21aacf..7b9ab5cd11 100644 --- a/MPChartExample/res/menu/bar.xml +++ b/MPChartExample/src/main/res/menu/bubble.xml @@ -2,44 +2,44 @@ + android:id="@+id/viewGithub" + android:title="@string/viewGithub"> + android:id="@+id/actionToggleValues" + android:title="@string/actionToggleValues"> + android:id="@+id/actionToggleIcons" + android:title="@string/actionToggleIcons"> + android:id="@+id/actionToggleHighlight" + android:title="@string/actionToggleHighlight"> + android:id="@+id/actionTogglePinch" + android:title="@string/actionTogglePinch"> + android:id="@+id/actionToggleAutoScaleMinMax" + android:title="@string/actionToggleAutoScale"> + android:id="@+id/animateX" + android:title="@string/animateX"> + android:id="@+id/animateY" + android:title="@string/animateY"> + android:id="@+id/animateXY" + android:title="@string/animateXY"> + android:id="@+id/actionSave" + android:title="@string/actionSave"> - \ No newline at end of file + diff --git a/MPChartExample/src/main/res/menu/candle.xml b/MPChartExample/src/main/res/menu/candle.xml new file mode 100644 index 0000000000..42a1a7e050 --- /dev/null +++ b/MPChartExample/src/main/res/menu/candle.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/src/main/res/menu/combined.xml b/MPChartExample/src/main/res/menu/combined.xml new file mode 100644 index 0000000000..c7def2509c --- /dev/null +++ b/MPChartExample/src/main/res/menu/combined.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/MPChartExample/res/menu/draw.xml b/MPChartExample/src/main/res/menu/draw.xml similarity index 57% rename from MPChartExample/res/menu/draw.xml rename to MPChartExample/src/main/res/menu/draw.xml index 50f35239e7..36383db54f 100644 --- a/MPChartExample/res/menu/draw.xml +++ b/MPChartExample/src/main/res/menu/draw.xml @@ -3,34 +3,30 @@ + android:title="@string/actionToggleValues"> + android:title="@string/actionToggleFilled"> + android:title="@string/actionToggleCircles"> - - - - + android:title="@string/actionToggleHighlight"> + android:title="@string/actionTogglePinch"> + android:title="@string/actionToggleAutoScale"> + + - \ No newline at end of file + diff --git a/MPChartExample/src/main/res/menu/dynamical.xml b/MPChartExample/src/main/res/menu/dynamical.xml new file mode 100644 index 0000000000..68d4fab0c9 --- /dev/null +++ b/MPChartExample/src/main/res/menu/dynamical.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/src/main/res/menu/line.xml b/MPChartExample/src/main/res/menu/line.xml new file mode 100644 index 0000000000..a812b91b5a --- /dev/null +++ b/MPChartExample/src/main/res/menu/line.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/res/menu/main.xml b/MPChartExample/src/main/res/menu/main.xml similarity index 55% rename from MPChartExample/res/menu/main.xml rename to MPChartExample/src/main/res/menu/main.xml index b45d3bbb9f..9ac13dbbae 100644 --- a/MPChartExample/res/menu/main.xml +++ b/MPChartExample/src/main/res/menu/main.xml @@ -3,19 +3,15 @@ + android:title="@string/viewGithub"> - - + android:title="@string/reportProblem"> + android:title="@string/viewWebsite"> - \ No newline at end of file + diff --git a/MPChartExample/res/menu/realm.xml b/MPChartExample/src/main/res/menu/only_github.xml similarity index 57% rename from MPChartExample/res/menu/realm.xml rename to MPChartExample/src/main/res/menu/only_github.xml index f954443b30..c0a9b66934 100644 --- a/MPChartExample/res/menu/realm.xml +++ b/MPChartExample/src/main/res/menu/only_github.xml @@ -1,8 +1,7 @@ - - + + android:id="@+id/viewGithub" + android:title="@string/viewGithub"> - \ No newline at end of file + diff --git a/MPChartExample/src/main/res/menu/pie.xml b/MPChartExample/src/main/res/menu/pie.xml new file mode 100644 index 0000000000..09a05a9ccd --- /dev/null +++ b/MPChartExample/src/main/res/menu/pie.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/src/main/res/menu/radar.xml b/MPChartExample/src/main/res/menu/radar.xml new file mode 100644 index 0000000000..2a5c19cf81 --- /dev/null +++ b/MPChartExample/src/main/res/menu/radar.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MPChartExample/src/main/res/menu/realtime.xml b/MPChartExample/src/main/res/menu/realtime.xml new file mode 100644 index 0000000000..48cc7ccd0a --- /dev/null +++ b/MPChartExample/src/main/res/menu/realtime.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/src/main/res/menu/scatter.xml similarity index 52% rename from MPChartExample/res/menu/scatter.xml rename to MPChartExample/src/main/res/menu/scatter.xml index a25afd9861..eb8e0efa67 100644 --- a/MPChartExample/res/menu/scatter.xml +++ b/MPChartExample/src/main/res/menu/scatter.xml @@ -1,41 +1,45 @@ + + + android:title="@string/actionToggleValues"> + android:id="@+id/actionToggleIcons" + android:title="@string/actionToggleIcons"> + android:id="@+id/actionToggleHighlight" + android:title="@string/actionToggleHighlight"> + android:title="@string/animateX"> + android:title="@string/animateY"> - - + android:title="@string/animateXY"> + android:title="@string/actionTogglePinch"> + android:title="@string/actionToggleAutoScale"> + + - \ No newline at end of file + diff --git a/MPChartExample/src/main/res/values/strings.xml b/MPChartExample/src/main/res/values/strings.xml new file mode 100644 index 0000000000..91a25bb89e --- /dev/null +++ b/MPChartExample/src/main/res/values/strings.xml @@ -0,0 +1,55 @@ + + + + MPAndroidChart Example + + View on GitHub + Problem Report + Developer Website + Save to Gallery + + Animate X + Animate Y + Animate XY + + Toggle Values + Toggle Y-Values + Toggle X-Values + + Toggle Icons + Toggle Highlight + Toggle PinchZoom + Toggle Auto Scale + + Toggle Line Values + Toggle Bar Values + Toggle Bar Borders + Toggle Filled + Toggle Circles + Toggle Shadow Color + + Toggle Cubic + Toggle Stepped + Toggle Horizontal Cubic + + Add Entry + Add Multiple + Remove Entry + Add Data Set + Remove Data Set + Clear chart + + Toggle Percent + Toggle Minimum Angles + Toggle Hole + Toggle Curved Slices + Draw Center Text + Toggle Highlight Circle + Toggle Rotation + Spin Animation + + - + START OF SCROLLVIEW + END OF SCROLLVIEW + + diff --git a/MPChartExample/src/main/res/values/styles.xml b/MPChartExample/src/main/res/values/styles.xml new file mode 100644 index 0000000000..9d5b53bd6c --- /dev/null +++ b/MPChartExample/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs b/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs deleted file mode 100644 index 77dc3a2d85..0000000000 --- a/MPChartLib/.settings/gradle/org.springsource.ide.eclipse.gradle.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -#org.springsource.ide.eclipse.gradle.core.preferences.GradleProjectPreferences -#Mon Jan 18 23:02:46 CET 2016 -org.springsource.ide.eclipse.gradle.linkedresources= -org.springsource.ide.eclipse.gradle.rootprojectloc=.. diff --git a/MPChartLib/build.gradle b/MPChartLib/build.gradle index 1c7ca0e099..0fb6dc7036 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,25 +1,16 @@ apply plugin: 'com.android.library' -apply plugin: 'maven' apply plugin: 'com.github.dcendents.android-maven' +group='com.github.philjay' + android { - compileSdkVersion 23 - buildToolsVersion '23.0.2' - // resourcePrefix 'mpcht' + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { - minSdkVersion 8 - targetSdkVersion 23 - versionCode 1 - versionName '1.0' - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } + minSdkVersion 14 + targetSdkVersion 28 + versionCode 3 + versionName '3.1.0' } buildTypes { release { @@ -27,29 +18,14 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - lintOptions { - abortOnError false - } -} - -repositories { - maven { - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' + testOptions { + unitTests.returnDefaultValues = true // this prevents "not mocked" error } } dependencies { - //compile fileTree(dir: 'libs', include: ['*.jar']) - //compile 'com.android.support:support-v4:19.+' - provided 'io.realm:realm-android:0.87.5' // "optional" dependency to realm-database API -} - -android.libraryVariants.all { variant -> - def name = variant.buildType.name - def task = project.tasks.create "jar${name.capitalize()}", Jar - task.dependsOn variant.javaCompile - task.from variant.javaCompile.destinationDir - artifacts.add('archives', task); + implementation 'androidx.annotation:annotation:1.0.0' + testImplementation 'junit:junit:4.12' } task sourcesJar(type: Jar) { @@ -58,6 +34,7 @@ task sourcesJar(type: Jar) { } task javadoc(type: Javadoc) { + options.charSet = 'UTF-8' failOnError false source = android.sourceSets.main.java.sourceFiles classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) diff --git a/MPChartLib/proguard-project.txt b/MPChartLib/proguard-project.txt deleted file mode 100644 index f2fe1559a2..0000000000 --- a/MPChartLib/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/MPChartLib/project.properties b/MPChartLib/project.properties deleted file mode 100644 index b2ef7dccc5..0000000000 --- a/MPChartLib/project.properties +++ /dev/null @@ -1,15 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-23 -android.library=true diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java deleted file mode 100644 index b325eba738..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/animation/ChartAnimator.java +++ /dev/null @@ -1,311 +0,0 @@ - -package com.github.mikephil.charting.animation; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; - -/** - * Object responsible for all animations in the Chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @author Philipp Jahoda - */ -@SuppressLint("NewApi") -public class ChartAnimator { - - /** object that is updated upon animation update */ - private AnimatorUpdateListener mListener; - - public ChartAnimator() { - - } - - public ChartAnimator(AnimatorUpdateListener listener) { - mListener = listener; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** the phase that is animated and influences the drawn values on the y-axis */ - protected float mPhaseY = 1f; - - /** the phase that is animated and influences the drawn values on the x-axis */ - protected float mPhaseX = 1f; - - /** - * ################ ################ ################ ################ - */ - /** METHODS FOR CUSTOM EASING */ - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX - * @param durationMillisY - * @param easingX - * @param easingY - */ - public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, - EasingFunction easingY) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(easingY); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(easingX); - animatorX.setDuration( - durationMillisX); - - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); - } else { - animatorY.addUpdateListener(mListener); - } - - animatorX.start(); - animatorY.start(); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - * @param easing - */ - public void animateX(int durationMillis, EasingFunction easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(easing); - animatorX.setDuration(durationMillis); - animatorX.addUpdateListener(mListener); - animatorX.start(); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - * @param easing - */ - public void animateY(int durationMillis, EasingFunction easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(easing); - animatorY.setDuration(durationMillis); - animatorY.addUpdateListener(mListener); - animatorY.start(); - } - - /** - * ################ ################ ################ ################ - */ - /** METHODS FOR PREDEFINED EASING */ - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX - * @param durationMillisY - * @param easingX - * @param easingY - */ - public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, - Easing.EasingOption easingY) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easingY)); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easingX)); - animatorX.setDuration( - durationMillisX); - - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); - } else { - animatorY.addUpdateListener(mListener); - } - - animatorX.start(); - animatorY.start(); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - * @param easing - */ - public void animateX(int durationMillis, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorX.setDuration(durationMillis); - animatorX.addUpdateListener(mListener); - animatorX.start(); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - * @param easing - */ - public void animateY(int durationMillis, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - animatorY.setDuration(durationMillis); - animatorY.addUpdateListener(mListener); - animatorY.start(); - } - - /** - * ################ ################ ################ ################ - */ - /** METHODS FOR ANIMATION WITHOUT EASING */ - - /** - * Animates the drawing / rendering of the chart on both x- and y-axis with - * the specified animation time. If animate(...) is called, no further - * calling of invalidate() is necessary to refresh the chart. - * - * @param durationMillisX - * @param durationMillisY - */ - public void animateXY(int durationMillisX, int durationMillisY) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setDuration( - durationMillisY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setDuration( - durationMillisX); - - // make sure only one animator produces update-callbacks (which then - // call invalidate()) - if (durationMillisX > durationMillisY) { - animatorX.addUpdateListener(mListener); - } else { - animatorY.addUpdateListener(mListener); - } - - animatorX.start(); - animatorY.start(); - } - - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - */ - public void animateX(int durationMillis) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f); - animatorX.setDuration(durationMillis); - animatorX.addUpdateListener(mListener); - animatorX.start(); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. - * - * @param durationMillis - */ - public void animateY(int durationMillis) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f); - animatorY.setDuration(durationMillis); - animatorY.addUpdateListener(mListener); - animatorY.start(); - } - - /** - * This gets the y-phase that is used to animate the values. - * - * @return - */ - public float getPhaseY() { - return mPhaseY; - } - - /** - * This modifys the y-phase that is used to animate the values. - * - * @param phase - */ - public void setPhaseY(float phase) { - mPhaseY = phase; - } - - /** - * This gets the x-phase that is used to animate the values. - * - * @return - */ - public float getPhaseX() { - return mPhaseX; - } - - /** - * This modifys the x-phase that is used to animate the values. - * - * @param phase - */ - public void setPhaseX(float phase) { - mPhaseX = phase; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/com/github/mikephil/charting/animation/Easing.java deleted file mode 100644 index 1741f6f511..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/animation/Easing.java +++ /dev/null @@ -1,721 +0,0 @@ - -package com.github.mikephil.charting.animation; - -/** - * Easing options. - * - * @author Daniel Cohen Gindi - */ -public class Easing { - - /** - * Use EasingOption instead of EasingFunction to avoid crashes below Android - * 3.0 - */ - public enum EasingOption { - Linear, - EaseInQuad, - EaseOutQuad, - EaseInOutQuad, - EaseInCubic, - EaseOutCubic, - EaseInOutCubic, - EaseInQuart, - EaseOutQuart, - EaseInOutQuart, - EaseInSine, - EaseOutSine, - EaseInOutSine, - EaseInExpo, - EaseOutExpo, - EaseInOutExpo, - EaseInCirc, - EaseOutCirc, - EaseInOutCirc, - EaseInElastic, - EaseOutElastic, - EaseInOutElastic, - EaseInBack, - EaseOutBack, - EaseInOutBack, - EaseInBounce, - EaseOutBounce, - EaseInOutBounce, - } - - public static EasingFunction getEasingFunctionFromOption(EasingOption easing) { - switch (easing) { - default: - case Linear: - return Easing.EasingFunctions.Linear; - case EaseInQuad: - return Easing.EasingFunctions.EaseInQuad; - case EaseOutQuad: - return Easing.EasingFunctions.EaseOutQuad; - case EaseInOutQuad: - return Easing.EasingFunctions.EaseInOutQuad; - case EaseInCubic: - return Easing.EasingFunctions.EaseInCubic; - case EaseOutCubic: - return Easing.EasingFunctions.EaseOutCubic; - case EaseInOutCubic: - return Easing.EasingFunctions.EaseInOutCubic; - case EaseInQuart: - return Easing.EasingFunctions.EaseInQuart; - case EaseOutQuart: - return Easing.EasingFunctions.EaseOutQuart; - case EaseInOutQuart: - return Easing.EasingFunctions.EaseInOutQuart; - case EaseInSine: - return Easing.EasingFunctions.EaseInSine; - case EaseOutSine: - return Easing.EasingFunctions.EaseOutSine; - case EaseInOutSine: - return Easing.EasingFunctions.EaseInOutSine; - case EaseInExpo: - return Easing.EasingFunctions.EaseInExpo; - case EaseOutExpo: - return Easing.EasingFunctions.EaseOutExpo; - case EaseInOutExpo: - return Easing.EasingFunctions.EaseInOutExpo; - case EaseInCirc: - return Easing.EasingFunctions.EaseInCirc; - case EaseOutCirc: - return Easing.EasingFunctions.EaseOutCirc; - case EaseInOutCirc: - return Easing.EasingFunctions.EaseInOutCirc; - case EaseInElastic: - return Easing.EasingFunctions.EaseInElastic; - case EaseOutElastic: - return Easing.EasingFunctions.EaseOutElastic; - case EaseInOutElastic: - return Easing.EasingFunctions.EaseInOutElastic; - case EaseInBack: - return Easing.EasingFunctions.EaseInBack; - case EaseOutBack: - return Easing.EasingFunctions.EaseOutBack; - case EaseInOutBack: - return Easing.EasingFunctions.EaseInOutBack; - case EaseInBounce: - return Easing.EasingFunctions.EaseInBounce; - case EaseOutBounce: - return Easing.EasingFunctions.EaseOutBounce; - case EaseInOutBounce: - return Easing.EasingFunctions.EaseInOutBounce; - } - } - - private static class EasingFunctions { - - /** - * ########## ########## ########## ########## ########## ########## - * PREDEFINED EASING FUNCTIONS BELOW THIS - */ - - public static final EasingFunction Linear = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return elapsed / (float) duration; - // } - - @Override - public float getInterpolation(float input) { - return input; - } - }; - - public static final EasingFunction EaseInQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return position * position; - // } - - @Override - public float getInterpolation(float input) { - return input * input; - } - }; - - public static final EasingFunction EaseOutQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -position * (position - 2.f); - // } - - @Override - public float getInterpolation(float input) { - return -input * (input - 2f); - } - }; - - public static final EasingFunction EaseInOutQuad = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * position * position; - // } - // return -0.5f * ((--position) * (position - 2.f) - 1.f); - // } - - @Override - public float getInterpolation(float input) { - - float position = input / 0.5f; - - if (position < 1.f) { - return 0.5f * position * position; - } - - return -0.5f * ((--position) * (position - 2.f) - 1.f); - } - }; - - public static final EasingFunction EaseInCubic = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return position * position * position; - // } - - @Override - public float getInterpolation(float input) { - return input * input * input; - } - }; - - public static final EasingFunction EaseOutCubic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // position--; - // return (position * position * position + 1.f); - // } - - @Override - public float getInterpolation(float input) { - input--; - return (input * input * input + 1.f); - } - }; - - public static final EasingFunction EaseInOutCubic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * position * position * position; - // } - // position -= 2.f; - // return 0.5f * (position * position * position + 2.f); - // } - - @Override - public float getInterpolation(float input) { - - float position = input / 0.5f; - if (position < 1.f) { - return 0.5f * position * position * position; - } - position -= 2.f; - return 0.5f * (position * position * position + 2.f); - } - }; - - public static final EasingFunction EaseInQuart = new EasingFunction() { - - public float getInterpolation(float input) { - return input * input * input * input; - } - }; - - public static final EasingFunction EaseOutQuart = new EasingFunction() { - - public float getInterpolation(float input) { - input--; - return -(input * input * input * input - 1f); - } - }; - - public static final EasingFunction EaseInOutQuart = new - EasingFunction() { - @Override - public float getInterpolation(float input) { - float position = input / 0.5f; - if (position < 1.f) { - return 0.5f * position * position * position * position; - } - position -= 2.f; - return -0.5f * (position * position * position * position - 2.f); - } - }; - - public static final EasingFunction EaseInSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -(float) Math.cos(position * (Math.PI / 2.f)) + 1.f; - // } - @Override - public float getInterpolation(float input) { - return -(float) Math.cos(input * (Math.PI / 2.f)) + 1.f; - } - }; - - public static final EasingFunction EaseOutSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return (float) Math.sin(position * (Math.PI / 2.f)); - // } - @Override - public float getInterpolation(float input) { - return (float) Math.sin(input * (Math.PI / 2.f)); - } - }; - - public static final EasingFunction EaseInOutSine = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -0.5f * ((float) Math.cos(Math.PI * position) - 1.f); - // } - - @Override - public float getInterpolation(float input) { - return -0.5f * ((float) Math.cos(Math.PI * input) - 1.f); - } - }; - - public static final EasingFunction EaseInExpo = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return (elapsed == 0) ? 0.f : (float) Math.pow(2.f, 10.f * (elapsed - // / (float) duration - 1.f)); - // } - @Override - public float getInterpolation(float input) { - return (input == 0) ? 0.f : (float) Math.pow(2.f, 10.f * (input - 1.f)); - } - }; - - public static final EasingFunction EaseOutExpo = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return (elapsed == duration) ? 1.f : (-(float) Math.pow(2.f, -10.f * - // elapsed - // / (float) duration) + 1.f); - // } - - @Override - public float getInterpolation(float input) { - return (input == 1f) ? 1.f : (-(float) Math.pow(2.f, -10.f * (input + 1.f))); - } - }; - - public static final EasingFunction EaseInOutExpo = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // if (elapsed == duration) - // { - // return 1.f; - // } - // - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * (float) Math.pow(2.f, 10.f * (position - 1.f)); - // } - // return 0.5f * (-(float) Math.pow(2.f, -10.f * --position) + - // 2.f); - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - if (input == 1f) - { - return 1.f; - } - - float position = input / 0.5f; - if (position < 1.f) - { - return 0.5f * (float) Math.pow(2.f, 10.f * (position - 1.f)); - } - return 0.5f * (-(float) Math.pow(2.f, -10.f * --position) + 2.f); - } - }; - - public static final EasingFunction EaseInCirc = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // return -((float) Math.sqrt(1.f - position * position) - 1.f); - // } - - @Override - public float getInterpolation(float input) { - return -((float) Math.sqrt(1.f - input * input) - 1.f); - } - }; - - public static final EasingFunction EaseOutCirc = new EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // position--; - // return (float) Math.sqrt(1.f - position * position); - // } - @Override - public float getInterpolation(float input) { - input--; - return (float) Math.sqrt(1.f - input * input); - } - }; - - public static final EasingFunction EaseInOutCirc = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return -0.5f * ((float) Math.sqrt(1.f - position * position) - // - 1.f); - // } - // return 0.5f * ((float) Math.sqrt(1.f - (position -= 2.f) * - // position) - // + 1.f); - // } - - @Override - public float getInterpolation(float input) { - float position = input / 0.5f; - if (position < 1.f) - { - return -0.5f * ((float) Math.sqrt(1.f - position * position) - 1.f); - } - return 0.5f * ((float) Math.sqrt(1.f - (position -= 2.f) * position) - + 1.f); - } - }; - - public static final EasingFunction EaseInElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (float) duration; - // if (position == 1) - // { - // return 1.f; - // } - // - // float p = duration * .3f; - // float s = p / (2.f * (float) Math.PI) * (float) - // Math.asin(1.f); - // return -((float) Math.pow(2.f, 10.f * (position -= 1.f)) * - // (float) - // Math - // .sin((position * duration - s) * (2.f * Math.PI) / p)); - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input; - if (position == 1) - { - return 1.f; - } - - float p = .3f; - float s = p / (2.f * (float) Math.PI) * (float) Math.asin(1.f); - return -((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) - Math - .sin((position - s) * (2.f * Math.PI) / p)); - } - }; - - public static final EasingFunction EaseOutElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (float) duration; - // if (position == 1) - // { - // return 1.f; - // } - // - // float p = duration * .3f; - // float s = p / (2 * (float) Math.PI) * (float) Math.asin(1.f); - // return (float) Math.pow(2, -10 * position) - // * (float) Math.sin((position * duration - s) * (2.f * - // Math.PI) / p) + - // 1.f; - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input; - if (position == 1) - { - return 1.f; - } - - float p = .3f; - float s = p / (2 * (float) Math.PI) * (float) Math.asin(1.f); - return (float) Math.pow(2, -10 * position) - * (float) Math.sin((position - s) * (2.f * Math.PI) / p) + - 1.f; - } - }; - - public static final EasingFunction EaseInOutElastic = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed == 0) - // { - // return 0.f; - // } - // - // float position = elapsed / (duration / 2.f); - // if (position == 2) - // { - // return 1.f; - // } - // - // float p = duration * (.3f * 1.5f); - // float s = p / (2.f * (float) Math.PI) * (float) - // Math.asin(1.f); - // if (position < 1.f) - // { - // return -.5f - // * ((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) - // Math - // .sin((position * duration - s) * (2.f * Math.PI) / p)); - // } - // return (float) Math.pow(2.f, -10.f * (position -= 1.f)) - // * (float) Math.sin((position * duration - s) * (2.f * - // Math.PI) / p) * - // .5f - // + 1.f; - // } - - @Override - public float getInterpolation(float input) { - if (input == 0) - { - return 0.f; - } - - float position = input / 0.5f; - if (position == 2) - { - return 1.f; - } - - float p = (.3f * 1.5f); - float s = p / (2.f * (float) Math.PI) * (float) Math.asin(1.f); - if (position < 1.f) - { - return -.5f - * ((float) Math.pow(2.f, 10.f * (position -= 1.f)) * (float) Math - .sin((position * 1f - s) * (2.f * Math.PI) / p)); - } - return (float) Math.pow(2.f, -10.f * (position -= 1.f)) - * (float) Math.sin((position * 1f - s) * (2.f * Math.PI) / p) * - .5f - + 1.f; - } - }; - - public static final EasingFunction EaseInBack = new EasingFunction() - { - // @Override - // public float ease(long elapsed, long duration) { - // final float s = 1.70158f; - // float position = elapsed / (float) duration; - // return position * position * ((s + 1.f) * position - s); - // } - - @Override - public float getInterpolation(float input) { - final float s = 1.70158f; - float position = input; - return position * position * ((s + 1.f) * position - s); - } - }; - - public static final EasingFunction EaseOutBack = new EasingFunction() - { - // @Override - // public float ease(long elapsed, long duration) { - // final float s = 1.70158f; - // float position = elapsed / (float) duration; - // position--; - // return (position * position * ((s + 1.f) * position + s) + 1.f); - // } - - @Override - public float getInterpolation(float input) { - final float s = 1.70158f; - float position = input; - position--; - return (position * position * ((s + 1.f) * position + s) + 1.f); - } - }; - - public static final EasingFunction EaseInOutBack = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float s = 1.70158f; - // float position = elapsed / (duration / 2.f); - // if (position < 1.f) - // { - // return 0.5f * (position * position * (((s *= (1.525f)) + 1.f) - // * - // position - s)); - // } - // return 0.5f * ((position -= 2.f) * position - // * (((s *= (1.525f)) + 1.f) * position + s) + 2.f); - // } - - @Override - public float getInterpolation(float input) { - float s = 1.70158f; - float position = input / 0.5f; - if (position < 1.f) - { - return 0.5f * (position * position * (((s *= (1.525f)) + 1.f) * - position - s)); - } - return 0.5f * ((position -= 2.f) * position - * (((s *= (1.525f)) + 1.f) * position + s) + 2.f); - } - }; - - public static final EasingFunction EaseInBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // return 1.f - EaseOutBounce.ease(duration - elapsed, - // duration); - // } - - @Override - public float getInterpolation(float input) { - return 1.f - EaseOutBounce.getInterpolation(1f - input); - } - }; - - public static final EasingFunction EaseOutBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // float position = elapsed / (float) duration; - // if (position < (1.f / 2.75f)) - // { - // return (7.5625f * position * position); - // } - // else if (position < (2.f / 2.75f)) - // { - // return (7.5625f * (position -= (1.5f / 2.75f)) * position + - // .75f); - // } - // else if (position < (2.5f / 2.75f)) - // { - // return (7.5625f * (position -= (2.25f / 2.75f)) * position + - // .9375f); - // } - // else - // { - // return (7.5625f * (position -= (2.625f / 2.75f)) * position + - // .984375f); - // } - // } - - @Override - public float getInterpolation(float input) { - float position = input; - if (position < (1.f / 2.75f)) - { - return (7.5625f * position * position); - } - else if (position < (2.f / 2.75f)) - { - return (7.5625f * (position -= (1.5f / 2.75f)) * position + .75f); - } - else if (position < (2.5f / 2.75f)) - { - return (7.5625f * (position -= (2.25f / 2.75f)) * position + .9375f); - } - else - { - return (7.5625f * (position -= (2.625f / 2.75f)) * position + - .984375f); - } - } - }; - - public static final EasingFunction EaseInOutBounce = new - EasingFunction() { - // @Override - // public float ease(long elapsed, long duration) { - // if (elapsed < duration / 2.f) - // { - // return EaseInBounce.ease(elapsed * 2, duration) * .5f; - // } - // return EaseOutBounce.ease(elapsed * 2 - duration, duration) * - // .5f + - // .5f; - // } - - @Override - public float getInterpolation(float input) { - if (input < 0.5f) - { - return EaseInBounce.getInterpolation(input * 2) * .5f; - } - return EaseOutBounce.getInterpolation(input * 2 - 1f) * .5f + - .5f; - } - }; - - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java b/MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java deleted file mode 100644 index 98d934da23..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.mikephil.charting.animation; - -import android.animation.TimeInterpolator; -import android.annotation.SuppressLint; - -/** - * Interface for creating custom made easing functions. Uses the - * TimeInterpolator interface provided by Android. - */ -@SuppressLint("NewApi") -public interface EasingFunction extends TimeInterpolator { - - @Override - float getInterpolation(float input); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java deleted file mode 100644 index 2e8f753af7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java +++ /dev/null @@ -1,31 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; - -public class CircleBuffer extends AbstractBuffer { - - public CircleBuffer(int size) { - super(size); - } - - protected void addCircle(float x, float y) { - buffer[index++] = x; - buffer[index++] = y; - } - - @Override - public void feed(ILineDataSet data) { - - int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom); - - for (int i = mFrom; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - addCircle(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java deleted file mode 100644 index 723124f72d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java +++ /dev/null @@ -1,57 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; - -public class LineBuffer extends AbstractBuffer { - - public LineBuffer(int size) { - super((size < 4) ? 4 : size); - } - - public void moveTo(float x, float y) { - - if (index != 0) - return; - - buffer[index++] = x; - buffer[index++] = y; - - // in case just one entry, this is overwritten when lineTo is called - buffer[index] = x; - buffer[index + 1] = y; - } - - public void lineTo(float x, float y) { - - if (index == 2) { - buffer[index++] = x; - buffer[index++] = y; - } else { - - float prevX = buffer[index - 2]; - float prevY = buffer[index - 1]; - buffer[index++] = prevX; - buffer[index++] = prevY; - buffer[index++] = x; - buffer[index++] = y; - } - } - - @Override - public void feed(ILineDataSet data) { - moveTo(data.getEntryForIndex(mFrom).getXIndex(), data.getEntryForIndex(mFrom).getVal() * phaseY); - - int size = (int) Math.ceil((mTo - mFrom) * phaseX + mFrom); - int from = mFrom + 1; - - for (int i = from; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java deleted file mode 100644 index 68ac437ac1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java +++ /dev/null @@ -1,31 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - -public class ScatterBuffer extends AbstractBuffer { - - public ScatterBuffer(int size) { - super(size); - } - - protected void addForm(float x, float y) { - buffer[index++] = x; - buffer[index++] = y; - } - - @Override - public void feed(IScatterDataSet data) { - - float size = data.getEntryCount() * phaseX; - - for (int i = 0; i < size; i++) { - - Entry e = data.getEntryForIndex(i); - addForm(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java deleted file mode 100644 index 19aaa2fe54..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java +++ /dev/null @@ -1,221 +0,0 @@ -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.AxisDependency; -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; -import com.github.mikephil.charting.renderer.XAxisRendererBarChart; - -/** - * Chart that draws bars. - * - * @author Philipp Jahoda - */ -public class BarChart extends BarLineChartBase implements BarDataProvider { - - /** flag that enables or disables the highlighting arrow */ - private boolean mDrawHighlightArrow = 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; - - 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); - mXAxisRenderer = new XAxisRendererBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); - - setHighlighter(new BarHighlighter(this)); - - mXChartMin = -0.5f; - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - // increase deltax by 1 because the bars have a width of 1 - mDeltaX += 0.5f; - - // extend xDelta to make space for multiple datasets (if ther are one) - mDeltaX *= mData.getDataSetCount(); - - float groupSpace = mData.getGroupSpace(); - mDeltaX += mData.getXValCount() * groupSpace; - mXChartMax = mDeltaX - mXChartMin; - } - - /** - * 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 - return getHighlighter().getHighlight(x, y); - } - - /** - * 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. - * - * @param e - * @return - */ - public RectF getBarBounds(BarEntry e) { - - IBarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float barspace = set.getBarSpace(); - float y = e.getVal(); - float x = e.getXIndex(); - - float barWidth = 0.5f; - - float spaceHalf = barspace / 2f; - float left = x - barWidth + spaceHalf; - float right = x + barWidth - spaceHalf; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } - - /** - * returns true if drawing the highlighting arrow is enabled, false if not - * - * @return - */ - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - - /** - * 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; - } - - @Override - public BarData getBarData() { - return mData; - } - - /** - * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public int getLowestVisibleXIndex() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (int) ((pts[0] <= getXChartMin()) ? 0 : (pts[0] / div) + 1); - } - - /** - * Returns the highest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public int getHighestVisibleXIndex() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (int) ((pts[0] >= getXChartMax()) ? getXChartMax() / div : (pts[0] / div)); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java deleted file mode 100644 index f7c4501710..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ /dev/null @@ -1,263 +0,0 @@ -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; - -import com.github.mikephil.charting.components.Legend.LegendPosition; -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.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() { - 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); - } - - @Override - public void calculateOffsets() { - - float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f; - - // setup offsets for legend - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART || mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - offsetRight += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART - || mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - offsetLeft += Math.min(mLegend.mNeededWidth, mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) - + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - // 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 = mLegend.mTextHeightMax; - - offsetBottom += Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } else if (mLegend.getPosition() == LegendPosition.ABOVE_CHART_LEFT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_CENTER) { - - // 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 = mLegend.mTextHeightMax * 2.f; - - offsetTop += Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - } - } - - // 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, mDeltaX, mXChartMin); - mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mDeltaX, mXChartMin); - } - - @Override - protected void calcModulus() { - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = - (int) Math.ceil((mData.getXValCount() * mXAxis.mLabelRotatedHeight) - / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; - } - - @Override - public RectF getBarBounds(BarEntry e) { - - IBarDataSet set = mData.getDataSetForEntry(e); - - if (set == null) - return null; - - float barspace = set.getBarSpace(); - float y = e.getVal(); - float x = e.getXIndex(); - - float spaceHalf = barspace / 2f; - - float top = x - 0.5f + spaceHalf; - float bottom = x + 0.5f - spaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - RectF bounds = new RectF(left, top, right, bottom); - - getTransformer(set.getAxisDependency()).rectValueToPixel(bounds); - - return bounds; - } - - @Override - public PointF getPosition(Entry e, AxisDependency axis) { - - if (e == null) - return null; - - float[] vals = new float[] { e.getVal(), e.getXIndex() }; - - getTransformer(axis).pointValuesToPixel(vals); - - return new PointF(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) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } else - return getHighlighter().getHighlight(y, x); // switch x and y - } - - /** - * Returns the lowest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public int getLowestVisibleXIndex() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (int) (((pts[1] <= 0) ? 0 : ((pts[1])) / div) + 1); - } - - /** - * Returns the highest x-index (value on the x-axis) that is still visible on the chart. - * - * @return - */ - @Override - public int getHighestVisibleXIndex() { - - float step = mData.getDataSetCount(); - float div = (step <= 1) ? 1 : step + mData.getGroupSpace(); - - float[] pts = new float[] { mViewPortHandler.contentLeft(), mViewPortHandler.contentTop() }; - - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (int) ((pts[1] >= getXChartMax()) ? getXChartMax() / div : (pts[1] / div)); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java deleted file mode 100644 index 26f26b361f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ /dev/null @@ -1,299 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Color; -import android.graphics.DashPathEffect; -import android.util.Log; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Baseclass of all labels. - * - * @author Philipp Jahoda - */ -public abstract class AxisBase extends ComponentBase { - - private int mGridColor = Color.GRAY; - - private float mGridLineWidth = 1f; - - private int mAxisLineColor = Color.GRAY; - - private float mAxisLineWidth = 1f; - - /** - * 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; - - /** - * 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 mLimitLines; - - /** - * flag indicating the limit lines layer depth - */ - protected boolean mDrawLimitLineBehindData = false; - - /** - * default constructor - */ - public AxisBase() { - this.mTextSize = Utils.convertDpToPixel(10f); - this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(5f); - this.mLimitLines = new ArrayList(); - } - - /** - * 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; - } - - /** - * 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; - } - - /** - * 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 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; - } - - /** - * Returns the longest formatted label (in terms of characters), this axis - * contains. - * - * @return - */ - public abstract String getLongestLabel(); - - /** - * 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); - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/com/github/mikephil/charting/components/Legend.java deleted file mode 100644 index 3a64b3a622..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java +++ /dev/null @@ -1,771 +0,0 @@ - -package com.github.mikephil.charting.components; - -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 LegendPosition { - RIGHT_OF_CHART, RIGHT_OF_CHART_CENTER, RIGHT_OF_CHART_INSIDE, - LEFT_OF_CHART, LEFT_OF_CHART_CENTER, LEFT_OF_CHART_INSIDE, - BELOW_CHART_LEFT, BELOW_CHART_RIGHT, BELOW_CHART_CENTER, - ABOVE_CHART_LEFT, ABOVE_CHART_RIGHT, ABOVE_CHART_CENTER, - PIECHART_CENTER - } - - public enum LegendForm { - SQUARE, CIRCLE, LINE - } - - public enum LegendDirection { - LEFT_TO_RIGHT, RIGHT_TO_LEFT - } - - /** - * the legend colors array, each color is for the form drawn at the same - * index - */ - private int[] mColors; - - /** the legend text array. a null label will start a group. */ - private String[] mLabels; - - /** - * colors that will be appended to the end of the colors array after - * calculating the legend. - */ - private int[] mExtraColors; - - /** - * labels that will be appended to the end of the labels array after - * calculating the legend. a null label will start a group. - */ - private String[] mExtraLabels; - - /** - * 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; - - /** the position relative to the chart the legend is drawn on */ - private LegendPosition mPosition = LegendPosition.BELOW_CHART_LEFT; - - /** 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 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() { - - mFormSize = Utils.convertDpToPixel(8f); - mXEntrySpace = Utils.convertDpToPixel(6f); - mYEntrySpace = Utils.convertDpToPixel(0f); - mFormToTextSpace = Utils.convertDpToPixel(5f); - mTextSize = Utils.convertDpToPixel(10f); - mStackSpace = Utils.convertDpToPixel(3f); - this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(4f); // 2 - } - - /** - * Constructor. Provide colors and labels for the legend. - * - * @param colors - * @param labels - */ - public Legend(int[] colors, String[] labels) { - this(); - - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } - - if (colors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - this.mColors = colors; - this.mLabels = labels; - } - - /** - * Constructor. Provide colors and labels for the legend. - * - * @param colors - * @param labels - */ - public Legend(List colors, List labels) { - this(); - - if (colors == null || labels == null) { - throw new IllegalArgumentException("colors array or labels array is NULL"); - } - - if (colors.size() != labels.size()) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - this.mColors = Utils.convertIntegers(colors); - this.mLabels = Utils.convertStrings(labels); - } - - /** - * This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors. - * @param colors - */ - public void setComputedColors(List colors) { - mColors = Utils.convertIntegers(colors); - } - - /** - * This method sets the automatically computed labels for the legend. Use setCustom(...) to set custom labels. - * @param labels - */ - public void setComputedLabels(List labels) { - mLabels = Utils.convertStrings(labels); - } - - /** - * 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; - - for (int i = 0; i < mLabels.length; i++) { - - if (mLabels[i] != null) { - - float length = (float) Utils.calcTextWidth(p, mLabels[i]); - - if (length > max) - max = length; - } - } - - return max + mFormSize + mFormToTextSpace; - } - - /** - * 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 (int i = 0; i < mLabels.length; i++) { - - if (mLabels[i] != null) { - - float length = (float) Utils.calcTextHeight(p, mLabels[i]); - - if (length > max) - max = length; - } - } - - return max; - } - - /** - * returns all the colors the legend uses - * - * @return - */ - public int[] getColors() { - return mColors; - } - - /** - * returns all the labels the legend uses - * - * @return - */ - public String[] getLabels() { - return mLabels; - } - - /** - * Returns the legend-label at the given index. - * - * @param index - * @return - */ - public String getLabel(int index) { - return mLabels[index]; - } - - /** - * colors that will be appended to the end of the colors array after - * calculating the legend. - */ - public int[] getExtraColors() { - return mExtraColors; - } - - /** - * labels that will be appended to the end of the labels array after - * calculating the legend. a null label will start a group. - */ - public String[] getExtraLabels() { - return mExtraLabels; - } - - /** - * Colors and labels that will be appended to the end of the auto calculated - * colors and labels arrays 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(List colors, List labels) { - this.mExtraColors = Utils.convertIntegers(colors); - this.mExtraLabels = Utils.convertStrings(labels); - } - - /** - * Colors and labels that will be appended to the end of the auto calculated - * colors and labels arrays 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) { - this.mExtraColors = colors; - this.mExtraLabels = labels; - } - - /** - * Sets a custom legend's labels and colors arrays. The colors count should - * match the labels count. * Each color is for the form drawn at the same - * index. * A null label will start a group. * A ColorTemplate.COLOR_SKIP - * color will avoid drawing a form This will disable the feature that - * automatically calculates the legend labels and colors from the datasets. - * Call resetCustom() to re-enable automatic calculation (and then - * notifyDataSetChanged() is needed to auto-calculate the legend again) - */ - public void setCustom(int[] colors, String[] labels) { - - if (colors.length != labels.length) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - mLabels = labels; - mColors = colors; - mIsLegendCustom = true; - } - - /** - * Sets a custom legend's labels and colors arrays. The colors count should - * match the labels count. * Each color is for the form drawn at the same - * index. * A null label will start a group. * A ColorTemplate.COLOR_SKIP - * color will avoid drawing a form This will disable the feature that - * automatically calculates the legend labels and colors 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 colors, List labels) { - - if (colors.size() != labels.size()) { - throw new IllegalArgumentException( - "colors array and labels array need to be of same size"); - } - - mColors = Utils.convertIntegers(colors); - mLabels = Utils.convertStrings(labels); - mIsLegendCustom = true; - } - - /** - * Calling this will disable the custom legend labels (set by - * setCustom(...)). Instead, the labels will again be calculated - * automatically (after notifyDataSetChanged() is called). - */ - public void resetCustom() { - mIsLegendCustom = false; - } - - /** - * @return true if a custom legend labels and colors has been set default - * false (automatic legend) - */ - public boolean isLegendCustom() { - return mIsLegendCustom; - } - - /** - * returns the position of the legend relative to the chart - * - * @return - */ - public LegendPosition getPosition() { - return mPosition; - } - - /** - * sets the position of the legend relative to the whole chart - * - * @param pos - */ - public void setPosition(LegendPosition pos) { - mPosition = pos; - } - - /** - * 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 pixels of the legend forms, this is internally converted - * in dp, default 8f - * - * @param size - */ - public void setFormSize(float size) { - mFormSize = Utils.convertDpToPixel(size); - } - - /** - * returns the size in dp of the legend forms - * - * @return - */ - public float getFormSize() { - return mFormSize; - } - - /** - * 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 = Utils.convertDpToPixel(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 = Utils.convertDpToPixel(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 mFormToTextSpace - */ - public void setFormToTextSpace(float space) { - this.mFormToTextSpace = Utils.convertDpToPixel(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; - } - - /** - * calculates the full width the fully drawn legend will use in pixels - * - * @return - */ - public float getFullWidth(Paint labelpaint) { - - float width = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - // make a step to the left - if (mColors[i] != ColorTemplate.COLOR_SKIP) - width += mFormSize + mFormToTextSpace; - - width += Utils.calcTextWidth(labelpaint, mLabels[i]); - - if (i < mLabels.length - 1) - width += mXEntrySpace; - } else { - width += mFormSize; - if (i < mLabels.length - 1) - width += mStackSpace; - } - } - - return width; - } - - /** - * Calculates the full height of the drawn legend. - * - * @param mLegendLabelPaint - * @return - */ - public float getFullHeight(Paint labelpaint) { - - float height = 0f; - - for (int i = 0; i < mLabels.length; i++) { - - // grouped forms have null labels - if (mLabels[i] != null) { - - height += Utils.calcTextHeight(labelpaint, mLabels[i]); - - if (i < mLabels.length - 1) - height += mYEntrySpace; - } - } - - return height; - } - - /** 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. / 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%) - * - * @param maxSize - */ - public void setMaxSizePercent(float maxSize) { - mMaxSizePercent = maxSize; - } - - private FSize[] mCalculatedLabelSizes = new FSize[] {}; - private Boolean[] mCalculatedLabelBreakPoints = new Boolean[] {}; - private FSize[] mCalculatedLineSizes = new FSize[] {}; - - public FSize[] getCalculatedLabelSizes() { - return mCalculatedLabelSizes; - } - - public Boolean[] getCalculatedLabelBreakPoints() { - return mCalculatedLabelBreakPoints; - } - - public 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) { - - if (mPosition == LegendPosition.RIGHT_OF_CHART - || mPosition == LegendPosition.RIGHT_OF_CHART_CENTER - || mPosition == LegendPosition.LEFT_OF_CHART - || mPosition == LegendPosition.LEFT_OF_CHART_CENTER - || mPosition == LegendPosition.PIECHART_CENTER) { - mNeededWidth = getMaximumEntryWidth(labelpaint); - mNeededHeight = getFullHeight(labelpaint); - mTextWidthMax = mNeededWidth; - mTextHeightMax = getMaximumEntryHeight(labelpaint); - - } else if (mPosition == LegendPosition.BELOW_CHART_LEFT - || mPosition == LegendPosition.BELOW_CHART_RIGHT - || mPosition == LegendPosition.BELOW_CHART_CENTER - || mPosition == LegendPosition.ABOVE_CHART_LEFT - || mPosition == LegendPosition.ABOVE_CHART_RIGHT - || mPosition == LegendPosition.ABOVE_CHART_CENTER) { - - int labelCount = mLabels.length; - float labelLineHeight = Utils.getLineHeight(labelpaint); - float labelLineSpacing = Utils.getLineSpacing(labelpaint) + mYEntrySpace; - float contentWidth = viewPortHandler.contentWidth(); - - // Prepare arrays for calculated layout - ArrayList calculatedLabelSizes = new ArrayList(labelCount); - ArrayList calculatedLabelBreakPoints = new ArrayList(labelCount); - ArrayList calculatedLineSizes = new ArrayList(); - - // Start calculating layout - float maxLineWidth = 0.f; - float currentLineWidth = 0.f; - float requiredWidth = 0.f; - int stackedStartIndex = -1; - - for (int i = 0; i < labelCount; i++) { - - boolean drawingForm = mColors[i] != ColorTemplate.COLOR_SKIP; - - calculatedLabelBreakPoints.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 += mStackSpace; - } - - // grouped forms have null labels - if (mLabels[i] != null) { - - calculatedLabelSizes.add(Utils.calcTextSize(labelpaint, mLabels[i])); - requiredWidth += drawingForm ? mFormToTextSpace + mFormSize : 0.f; - requiredWidth += calculatedLabelSizes.get(i).width; - } else { - - calculatedLabelSizes.add(new FSize(0.f, 0.f)); - requiredWidth += drawingForm ? mFormSize : 0.f; - - if (stackedStartIndex == -1) { - // mark this index as we might want to break here later - stackedStartIndex = i; - } - } - - if (mLabels[i] != null || i == labelCount - 1) { - - float requiredSpacing = currentLineWidth == 0.f ? 0.f : mXEntrySpace; - - if (!mWordWrapEnabled || // No word wrapping, it must fit. - currentLineWidth == 0.f || // The line is empty, it - // must fit. - (contentWidth - currentLineWidth >= requiredSpacing + requiredWidth)) // It - // simply - // fits - { - // Expand current line - currentLineWidth += requiredSpacing + requiredWidth; - - } else { // It doesn't fit, we need to wrap a line - - // Add current line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); - maxLineWidth = Math.max(maxLineWidth, currentLineWidth); - - // Start a new line - calculatedLabelBreakPoints.set(stackedStartIndex > -1 ? stackedStartIndex - : i, true); - currentLineWidth = requiredWidth; - } - - if (i == labelCount - 1) { - // Add last line size to array - calculatedLineSizes.add(new FSize(currentLineWidth, labelLineHeight)); - maxLineWidth = Math.max(maxLineWidth, currentLineWidth); - } - } - - stackedStartIndex = mLabels[i] != null ? -1 : stackedStartIndex; - } - - mCalculatedLabelSizes = calculatedLabelSizes.toArray( - new FSize[calculatedLabelSizes.size()]); - mCalculatedLabelBreakPoints = calculatedLabelBreakPoints - .toArray(new Boolean[calculatedLabelBreakPoints.size()]); - mCalculatedLineSizes = calculatedLineSizes - .toArray(new FSize[calculatedLineSizes.size()]); - - mTextWidthMax = getMaximumEntryWidth(labelpaint); - mTextHeightMax = getMaximumEntryHeight(labelpaint); - mNeededWidth = maxLineWidth; - mNeededHeight = labelLineHeight - * (float) (mCalculatedLineSizes.length) - + labelLineSpacing * - (float) (mCalculatedLineSizes.length == 0 - ? 0 - : (mCalculatedLineSizes.length - 1)); - - } else { - /* RIGHT_OF_CHART_INSIDE, LEFT_OF_CHART_INSIDE */ - - mNeededWidth = getFullWidth(labelpaint); - mNeededHeight = getMaximumEntryHeight(labelpaint); - mTextWidthMax = getMaximumEntryWidth(labelpaint); - mTextHeightMax = mNeededHeight; - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java deleted file mode 100644 index 523376c786..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java +++ /dev/null @@ -1,93 +0,0 @@ -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.data.Entry; -import com.github.mikephil.charting.highlight.Highlight; - -/** - * 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 abstract class MarkerView extends RelativeLayout { - - /** - * 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()); - } - - /** - * Draws the MarkerView on the given position on the screen with the given Canvas object. - * - * @param canvas - * @param posx - * @param posy - */ - public void draw(Canvas canvas, float posx, float posy) { - - // take offsets into consideration - posx += getXOffset(posx); - posy += getYOffset(posy); - - // translate to the correct position and draw - canvas.translate(posx, posy); - draw(canvas); - canvas.translate(-posx, -posy); - } - - /** - * This method enables a specified custom MarkerView to update it's content everytime the MarkerView is redrawn. - * - * @param e The Entry the MarkerView 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). - */ - public abstract void refreshContent(Entry e, Highlight highlight); - - /** - * Use this to return the desired offset you wish the MarkerView to have on the x-axis. By returning -(getWidth() / - * 2) you will center the MarkerView horizontally. - * - * @param xpos the position on the x-axis in pixels where the marker is drawn - * @return - */ - public abstract int getXOffset(float xpos); - - /** - * Use this to return the desired position offset you wish the MarkerView to have on the y-axis. By returning - * -getHeight() you will cause the MarkerView to be above the selected value. - * - * @param ypos the position on the y-axis in pixels where the marker is drawn - * @return - */ - public abstract int getYOffset(float ypos); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java deleted file mode 100644 index 7343baaef8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ /dev/null @@ -1,257 +0,0 @@ - -package com.github.mikephil.charting.components; - -import com.github.mikephil.charting.formatter.DefaultXAxisValueFormatter; -import com.github.mikephil.charting.formatter.XAxisValueFormatter; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * 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 { - - /** the arraylist containing all the x-axis labels */ - protected List mValues = new ArrayList(); - - /** - * width of the x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers - */ - public int mLabelWidth = 1; - - /** - * height of the x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers - */ - public int mLabelHeight = 1; - - /** - * width of the (rotated) x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers - */ - public int mLabelRotatedWidth = 1; - - /** - * height of the (rotated) x-axis labels in pixels - this is automatically - * calculated by the computeAxis() methods in the renderers - */ - public int mLabelRotatedHeight = 1; - - /** - * This is the angle for drawing the X axis labels (in degrees) - */ - protected float mLabelRotationAngle = 0f; - - /** - * the space that should be left out (in characters) between the x-axis - * labels - */ - private int mSpaceBetweenLabels = 4; - - /** - * the modulus that indicates if a value at a specified index in an - * array(list) for the x-axis-labels is drawn or not. If index % modulus == - * 0 DRAW, else dont draw. - */ - public int mAxisLabelModulus = 1; - - /** - * Is axisLabelModulus a custom value or auto calculated? If false, then - * it's auto, if true, then custom. default: false (automatic modulus) - */ - private boolean mIsAxisModulusCustom = false; - /** - * 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; - - /** - * Custom formatter for adjusting x-value strings - */ - protected XAxisValueFormatter mXAxisValueFormatter = new DefaultXAxisValueFormatter(); - - /** 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; - } - - /** - * Sets the space (in characters) that should be left out between the x-axis - * labels, default 4. This only applies if the number of labels that will be - * skipped in between drawn axis labels is not custom set. - * - * @param spaceCharacters - */ - public void setSpaceBetweenLabels(int spaceCharacters) { - mSpaceBetweenLabels = spaceCharacters; - } - - /** - * Sets the number of labels that should be skipped on the axis before the - * next label is drawn. This will disable the feature that automatically - * calculates an adequate space between the axis labels and set the number - * of labels to be skipped to the fixed number provided by this method. Call - * resetLabelsToSkip(...) to re-enable automatic calculation. - * - * @param count - */ - public void setLabelsToSkip(int count) { - - if (count < 0) - count = 0; - - mIsAxisModulusCustom = true; - mAxisLabelModulus = count + 1; - } - - /** - * Calling this will disable a custom number of labels to be skipped (set by - * setLabelsToSkip(...)) while drawing the x-axis. Instead, the number of - * values to skip will again be calculated automatically. - */ - public void resetLabelsToSkip() { - mIsAxisModulusCustom = false; - } - - /** - * Returns true if a custom axis-modulus has been set that determines the - * number of labels to skip when drawing. - * - * @return - */ - public boolean isAxisModulusCustom() { - return mIsAxisModulusCustom; - } - - /** - * Returns the space (in characters) that should be left out between the - * x-axis labels - */ - public int getSpaceBetweenLabels() { - return mSpaceBetweenLabels; - } - - /** - * 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; - } - - /** - * Sets the labels for this axis. - * - * @param values - */ - public void setValues(List values) { - mValues = values; - } - - /** - * Returns the labels for this axis. - * - * @return - */ - public List getValues() { - return mValues; - } - - - /** - * Sets a custom XAxisValueFormatter for the data object that allows custom-formatting - * of all x-values before rendering them. Provide null to reset back to the - * default formatting. - * - * @param formatter - */ - public void setValueFormatter(XAxisValueFormatter formatter) { - if(formatter == null) - mXAxisValueFormatter = new DefaultXAxisValueFormatter(); - else - mXAxisValueFormatter = formatter; - } - - /** - * Returns the custom XAxisValueFormatter that is set for this data object. - * @return - */ - public XAxisValueFormatter getValueFormatter() { - return mXAxisValueFormatter; - } - - @Override - public String getLongestLabel() { - - String longest = ""; - - for (int i = 0; i < mValues.size(); i++) { - String text = mValues.get(i); - - if (longest.length() < text.length()) - longest = text; - } - - return longest; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarData.java deleted file mode 100644 index 43b6e88e86..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java +++ /dev/null @@ -1,114 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; - -import java.util.ArrayList; -import java.util.List; - -/** - * Data object that represents all data for the BarChart. - * - * @author Philipp Jahoda - */ -public class BarData extends BarLineScatterCandleBubbleData { - - /** the space that is left between groups of bars */ - private float mGroupSpace = 0.8f; - - // /** - // * The maximum space (in pixels on the screen) a single bar can consume. - // */ - // private float mMaximumBarWidth = 100f; - - public BarData() { - super(); - } - - public BarData(List xVals) { - super(xVals); - } - - public BarData(String[] xVals) { - super(xVals); - } - - public BarData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public BarData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public BarData(List xVals, IBarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BarData(String[] xVals, IBarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IBarDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * Returns the space that is left out between groups of bars. Always returns - * 0 if the BarData object only contains one DataSet (because for one - * DataSet, there is no group-space needed). - * - * @return - */ - public float getGroupSpace() { - - if (mDataSets.size() <= 1) - return 0f; - else - return mGroupSpace; - } - - /** - * Sets the space between groups of bars of different datasets in percent of - * the total width of one bar. 100 = space is exactly one bar width, - * default: 80 - * - * @param percent - */ - public void setGroupSpace(float percent) { - mGroupSpace = percent / 100f; - } - - /** - * Returns true if this BarData object contains grouped DataSets (more than - * 1 DataSet). - * - * @return - */ - public boolean isGrouped() { - return mDataSets.size() > 1 ? true : false; - } - - // - // /** - // * Sets the maximum width (in density pixels) a single bar in the barchart - // * should consume. - // * - // * @param max - // */ - // public void setBarWidthMaximum(float max) { - // mMaximumBarWidth = Utils.convertDpToPixel(max); - // } - // - // /** - // * Returns the maximum width (in density pixels) a single bar in the - // * barchart should consume. - // * - // * @return - // */ - // public float getBarWidthMaximum() { - // return mMaximumBarWidth; - // } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java deleted file mode 100644 index c21bf791cf..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.github.mikephil.charting.data; - -/** - * Entry class for the BarChart. (especially stacked bars) - * - * @author Philipp Jahoda - */ -public class BarEntry extends Entry { - - /** the values the stacked barchart holds */ - private float[] mVals; - - /** 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 stacked bar entries. - * - * @param vals - * - the stack values, use at lest 2 - * @param xIndex - */ - public BarEntry(float[] vals, int xIndex) { - super(calcSum(vals), xIndex); - - this.mVals = vals; - calcPosNegSum(); - } - - /** - * Constructor for normal bars (not stacked). - * - * @param val - * @param xIndex - */ - public BarEntry(float val, int xIndex) { - super(val, xIndex); - } - - /** - * Constructor for stacked bar entries. - * - * @param vals - * - the stack values, use at least 2 - * @param xIndex - * @param label - * Additional description label. - */ - public BarEntry(float[] vals, int xIndex, String label) { - super(calcSum(vals), xIndex, label); - - this.mVals = vals; - calcPosNegSum(); - } - - /** - * Constructor for normal bars (not stacked). - * - * @param val - * @param xIndex - * @param data - * Spot for additional data this Entry represents. - */ - public BarEntry(float val, int xIndex, Object data) { - super(val, xIndex, data); - } - - /** - * Returns an exact copy of the BarEntry. - */ - public BarEntry copy() { - - BarEntry copied = new BarEntry(getVal(), getXIndex(), getData()); - copied.setVals(mVals); - return copied; - } - - /** - * Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use - * getVal()). - * - * @return - */ - public float[] getVals() { - return mVals; - } - - /** - * Set the array of values this BarEntry should represent. - * - * @param vals - */ - public void setVals(float[] vals) { - setVal(calcSum(vals)); - mVals = vals; - calcPosNegSum(); - } - - /** - * Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values. - * - * @return - */ - @Override - public float getVal() { - return super.getVal(); - } - - /** - * Returns true if this BarEntry is stacked (has a values array), false if not. - * - * @return - */ - public boolean isStacked() { - return mVals != null; - } - - public float getBelowSum(int stackIndex) { - - if (mVals == null) - return 0; - - float remainder = 0f; - int index = mVals.length - 1; - - while (index > stackIndex && index >= 0) { - remainder += mVals[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 (mVals == null) { - mNegativeSum = 0; - mPositiveSum = 0; - return; - } - - float sumNeg = 0f; - float sumPos = 0f; - - for (float f : mVals) { - 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java deleted file mode 100644 index 53945ac0e5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java +++ /dev/null @@ -1,56 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; - -import java.util.ArrayList; -import java.util.List; - -public class BubbleData extends BarLineScatterCandleBubbleData { - - public BubbleData() { - super(); - } - - public BubbleData(List xVals) { - super(xVals); - } - - public BubbleData(String[] xVals) { - super(xVals); - } - - public BubbleData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public BubbleData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public BubbleData(List xVals, IBubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BubbleData(String[] xVals, IBubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IBubbleDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * 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); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java deleted file mode 100644 index 96396d3241..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java +++ /dev/null @@ -1,141 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Color; - -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 implements IBubbleDataSet { - - // NOTE: Do not initialize these, as the calcMinMax is called by the super, - // and the initializers are called after that and can reset the values - protected float mXMax; - protected float mXMin; - protected float mMaxSize; - - private float mHighlightCircleWidth = 2.5f; - - public BubbleDataSet(List yVals, String label) { - super(yVals, label); - } - - @Override - public void setHighlightCircleWidth(float width) { - mHighlightCircleWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getHighlightCircleWidth() { - return mHighlightCircleWidth; - } - - @Override - public void calcMinMax(int start, int end) { - - if (mYVals == null) - return; - - if (mYVals.size() == 0) - return; - - int endValue; - - if (end == 0 || end >= mYVals.size()) - endValue = mYVals.size() - 1; - else - endValue = end; - - mYMin = yMin(mYVals.get(start)); - mYMax = yMax(mYVals.get(start)); - - // need chart width to guess this properly - - for (int i = start; i <= endValue; i++) { - - final BubbleEntry entry = mYVals.get(i); - - float ymin = yMin(entry); - float ymax = yMax(entry); - - if (ymin < mYMin) { - mYMin = ymin; - } - - if (ymax > mYMax) { - mYMax = ymax; - } - - final float xmin = xMin(entry); - final float xmax = xMax(entry); - - if (xmin < mXMin) { - mXMin = xmin; - } - - if (xmax > mXMax) { - mXMax = xmax; - } - - final float size = largestSize(entry); - - if (size > mMaxSize) { - mMaxSize = size; - } - } - } - - @Override - public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - BubbleDataSet copied = new BubbleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mHighLightColor = mHighLightColor; - - return copied; - } - - @Override - public float getXMax() { - return mXMax; - } - - @Override - public float getXMin() { - return mXMin; - } - - @Override - public float getMaxSize() { - return mMaxSize; - } - - private float yMin(BubbleEntry entry) { - return entry.getVal(); - } - - private float yMax(BubbleEntry entry) { - return entry.getVal(); - } - - private float xMin(BubbleEntry entry) { - return (float) entry.getXIndex(); - } - - private float xMax(BubbleEntry entry) { - return (float) entry.getXIndex(); - } - - private float largestSize(BubbleEntry entry) { - return entry.getSize(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BubbleEntry.java b/MPChartLib/src/com/github/mikephil/charting/data/BubbleEntry.java deleted file mode 100644 index ae4875c1f4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleEntry.java +++ /dev/null @@ -1,63 +0,0 @@ - -package com.github.mikephil.charting.data; - -/** - * 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 - */ -public class BubbleEntry extends Entry { - - /** size value */ - private float mSize = 0f; - - /** - * Constructor. - * - * @param xIndex The index on the x-axis. - * @param val The value on the y-axis. - * @param size The size of the bubble. - */ - public BubbleEntry(int xIndex, float val, float size) { - super(val, xIndex); - - this.mSize = size; - } - - /** - * Constructor. - * - * @param xIndex The index on the x-axis. - * @param val The value on the y-axis. - * @param size The size of the bubble. - * @param data Spot for additional data this Entry represents. - */ - public BubbleEntry(int xIndex, float val, float size, Object data) { - super(val, xIndex, data); - - this.mSize = size; - } - - public BubbleEntry copy() { - - BubbleEntry c = new BubbleEntry(getXIndex(), getVal(), 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; - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java deleted file mode 100644 index a1c797b4fa..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java +++ /dev/null @@ -1,43 +0,0 @@ -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 { - - public CandleData() { - super(); - } - - public CandleData(List xVals) { - super(xVals); - } - - public CandleData(String[] xVals) { - super(xVals); - } - - public CandleData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public CandleData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public CandleData(List xVals, ICandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public CandleData(String[] xVals, ICandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(ICandleDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java deleted file mode 100644 index 2d7982265d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ /dev/null @@ -1,121 +0,0 @@ - -package com.github.mikephil.charting.data; - -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> { - - private LineData mLineData; - private BarData mBarData; - private ScatterData mScatterData; - private CandleData mCandleData; - private BubbleData mBubbleData; - - public CombinedData() { - super(); - } - - public CombinedData(List xVals) { - super(xVals); - } - - public CombinedData(String[] xVals) { - super(xVals); - } - - public void setData(LineData data) { - mLineData = data; - mDataSets.addAll(data.getDataSets()); - init(); - } - - public void setData(BarData data) { - mBarData = data; - mDataSets.addAll(data.getDataSets()); - init(); - } - - public void setData(ScatterData data) { - mScatterData = data; - mDataSets.addAll(data.getDataSets()); - init(); - } - - public void setData(CandleData data) { - mCandleData = data; - mDataSets.addAll(data.getDataSets()); - init(); - } - - public void setData(BubbleData data) { - mBubbleData = data; - mDataSets.addAll(data.getDataSets()); - init(); - } - - 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 getAllData() { - - List data = new ArrayList(); - 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; - } - - @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(); - - init(); // recalculate everything - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java deleted file mode 100644 index 29f5f69c28..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ /dev/null @@ -1,358 +0,0 @@ - -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 extends BaseDataSet { - - /** - * the entries that this dataset represents / holds together - */ - protected List mYVals = null; - - /** - * maximum y-value in the y-value array - */ - protected float mYMax = 0.0f; - - /** - * the minimum y-value in the y-value array - */ - protected float mYMin = 0.0f; - - - /** - * Creates a new DataSet object with the given values 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 yVals - * @param label - */ - public DataSet(List yVals, String label) { - super(label); - this.mYVals = yVals; - - if (mYVals == null) - mYVals = new ArrayList(); - - calcMinMax(0, mYVals.size()); - } - - @Override - public void calcMinMax(int start, int end) { - - if (mYVals == null) - return; - - final int yValCount = mYVals.size(); - - if (yValCount == 0) - return; - - int endValue; - - if (end == 0 || end >= yValCount) - endValue = yValCount - 1; - else - endValue = end; - - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - - for (int i = start; i <= endValue; i++) { - - T e = mYVals.get(i); - - if (e != null && !Float.isNaN(e.getVal())) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } - } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - } - - @Override - public int getEntryCount() { - return mYVals.size(); - } - - /** - * Returns the array of y-values that this DataSet represents. - * - * @return - */ - public List getYVals() { - return mYVals; - } - - /** - * Provides an exact copy of the DataSet this method is used on. - * - * @return - */ - public abstract DataSet copy(); - - @Override - public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(toSimpleString()); - for (int i = 0; i < mYVals.size(); i++) { - buffer.append(mYVals.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: " + mYVals.size() + "\n"); - return buffer.toString(); - } - - @Override - public float getYMin() { - return mYMin; - } - - @Override - public float getYMax() { - return mYMax; - } - - @Override - public void addEntryOrdered(T e) { - - if (e == null) - return; - - float val = e.getVal(); - - if (mYVals == null) { - mYVals = new ArrayList(); - } - - if (mYVals.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - if (mYVals.size() > 0 && mYVals.get(mYVals.size() - 1).getXIndex() > e.getXIndex()) { - int closestIndex = getEntryIndex(e.getXIndex(), Rounding.UP); - mYVals.add(closestIndex, e); - return; - } - - mYVals.add(e); - } - - @Override - public void clear() { - mYVals.clear(); - notifyDataSetChanged(); - } - - @Override - public boolean addEntry(T e) { - - if (e == null) - return false; - - float val = e.getVal(); - - List yVals = getYVals(); - if (yVals == null) { - yVals = new ArrayList(); - } - - if (yVals.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - // add the entry - yVals.add(e); - return true; - } - - @Override - public boolean removeEntry(T e) { - - if (e == null) - return false; - - if (mYVals == null) - return false; - - // remove the entry - boolean removed = mYVals.remove(e); - - if (removed) { - calcMinMax(0, mYVals.size()); - } - - return removed; - } - - @Override - public int getEntryIndex(Entry e) { - return mYVals.indexOf(e); - } - - @Override - public T getEntryForXIndex(int xIndex, Rounding rounding) { - - int index = getEntryIndex(xIndex, rounding); - if (index > -1) - return mYVals.get(index); - return null; - } - - @Override - public T getEntryForXIndex(int xIndex) { - return getEntryForXIndex(xIndex, Rounding.CLOSEST); - } - - @Override - public T getEntryForIndex(int index) { - return mYVals.get(index); - } - - @Override - public int getEntryIndex(int xIndex, Rounding rounding) { - - int low = 0; - int high = mYVals.size() - 1; - int closest = -1; - - while (low <= high) { - int m = (high + low) / 2; - - if (xIndex == mYVals.get(m).getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == xIndex) - m--; - - return m; - } - - if (xIndex > mYVals.get(m).getXIndex()) - low = m + 1; - else - high = m - 1; - - closest = m; - } - - if (closest != -1) { - int closestXIndex = mYVals.get(closest).getXIndex(); - if (rounding == Rounding.UP) { - if (closestXIndex < xIndex && closest < mYVals.size() - 1) { - ++closest; - } - } else if (rounding == Rounding.DOWN) { - if (closestXIndex > xIndex && closest > 0) { - --closest; - } - } - } - - return closest; - } - - @Override - public float getYValForXIndex(int xIndex) { - - Entry e = getEntryForXIndex(xIndex); - - if (e != null && e.getXIndex() == xIndex) - return e.getVal(); - else - return Float.NaN; - } - - /** - * Returns all Entry objects at the given xIndex. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - public List getEntriesForXIndex(int xIndex) { - - List entries = new ArrayList(); - - int low = 0; - int high = mYVals.size() - 1; - - while (low <= high) { - int m = (high + low) / 2; - T entry = mYVals.get(m); - - if (xIndex == entry.getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == xIndex) - m--; - - high = mYVals.size(); - for (; m < high; m++) { - entry = mYVals.get(m); - if (entry.getXIndex() == xIndex) { - entries.add(entry); - } else { - break; - } - } - } - - if (xIndex > entry.getXIndex()) - low = m + 1; - else - high = m - 1; - } - - return entries; - } - - /** - * Determines how to round DataSet index values for - * {@link DataSet#getEntryIndex(int, Rounding)} DataSet.getEntryIndex()} - * when an exact x-index is not found. - */ - public enum Rounding { - UP, - DOWN, - CLOSEST, - } -} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/com/github/mikephil/charting/data/Entry.java deleted file mode 100644 index 35052fbcb5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java +++ /dev/null @@ -1,188 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.os.Parcel; -import android.os.ParcelFormatException; -import android.os.Parcelable; - -/** - * 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 implements Parcelable { - - /** the actual value */ - private float mVal = 0f; - - /** the index on the x-axis */ - private int mXIndex = 0; - - /** optional spot for additional data this Entry represents */ - private Object mData = null; - - /** - * A Entry represents one single entry in the chart. - * - * @param val the y value (the actual value of the entry) - * @param xIndex the corresponding index in the x value array (index on the - * x-axis of the chart, must NOT be higher than the length of the - * x-values String array) - */ - public Entry(float val, int xIndex) { - mVal = val; - mXIndex = xIndex; - } - - /** - * A Entry represents one single entry in the chart. - * - * @param val the y value (the actual value of the entry) - * @param xIndex the corresponding index in the x value array (index on the - * x-axis of the chart, must NOT be higher than the length of the - * x-values String array) - * @param data Spot for additional data this Entry represents. - */ - public Entry(float val, int xIndex, Object data) { - this(val, xIndex); - - this.mData = data; - } - - /** - * returns the x-index the value of this object is mapped to - * - * @return - */ - public int getXIndex() { - return mXIndex; - } - - /** - * sets the x-index for the entry - * - * @param x - */ - public void setXIndex(int x) { - this.mXIndex = x; - } - - /** - * Returns the total value the entry represents. - * - * @return - */ - public float getVal() { - return mVal; - } - - /** - * Sets the value for the entry. - * - * @param val - */ - public void setVal(float val) { - this.mVal = val; - } - - /** - * 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; - } - - /** - * returns an exact copy of the entry - * - * @return - */ - public Entry copy() { - Entry e = new Entry(mVal, mXIndex, mData); - 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.mData != this.mData) - return false; - if (e.mXIndex != this.mXIndex) - return false; - - if (Math.abs(e.mVal - this.mVal) > 0.00001f) - return false; - - return true; - } - - /** - * returns a string representation of the entry containing x-index and value - */ - @Override - public String toString() { - return "Entry, xIndex: " + mXIndex + " val (sum): " + getVal(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeFloat(this.mVal); - dest.writeInt(this.mXIndex); - if (mData != null) { - if (mData instanceof Parcelable) { - dest.writeInt(1); - dest.writeParcelable((Parcelable) this.mData, flags); - } else { - throw new ParcelFormatException("Cannot parcel an Entry with non-parcelable data"); - } - } else { - dest.writeInt(0); - } - } - - protected Entry(Parcel in) { - this.mVal = in.readFloat(); - this.mXIndex = in.readInt(); - if (in.readInt() == 1) { - this.mData = in.readParcelable(Object.class.getClassLoader()); - } - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public Entry createFromParcel(Parcel source) { - return new Entry(source); - } - - public Entry[] newArray(int size) { - return new Entry[size]; - } - }; -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/com/github/mikephil/charting/data/LineData.java deleted file mode 100644 index 9adb07b60d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java +++ /dev/null @@ -1,49 +0,0 @@ - -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 { - - public LineData() { - super(); - } - - public LineData(List xVals) { - super(xVals); - } - - public LineData(String[] xVals) { - super(xVals); - } - - public LineData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public LineData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public LineData(List xVals, ILineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public LineData(String[] xVals, ILineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(ILineDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java deleted file mode 100644 index 3f4b555da4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ /dev/null @@ -1,74 +0,0 @@ - -package com.github.mikephil.charting.data; - -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 implements IPieDataSet { - - /** the space in pixels between the chart-slices, default 0f */ - private float mSliceSpace = 0f; - - /** indicates the selection distance of a pie slice */ - private float mShift = 18f; - - public PieDataSet(List yVals, String label) { - super(yVals, label); -// mShift = Utils.convertDpToPixel(12f); - } - - @Override - public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - PieDataSet copied = new PieDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mSliceSpace = mSliceSpace; - copied.mShift = mShift; - return copied; - } - - /** - * 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; - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java deleted file mode 100644 index 48494d3d65..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java +++ /dev/null @@ -1,49 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; - -import java.util.ArrayList; -import java.util.List; - -/** - * Data container for the RadarChart. - * - * @author Philipp Jahoda - */ -public class RadarData extends ChartData { - - public RadarData() { - super(); - } - - public RadarData(List xVals) { - super(xVals); - } - - public RadarData(String[] xVals) { - super(xVals); - } - - public RadarData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public RadarData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public RadarData(List xVals, IRadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public RadarData(String[] xVals, IRadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IRadarDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java deleted file mode 100644 index 189ac032a9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java +++ /dev/null @@ -1,30 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; - -import java.util.ArrayList; -import java.util.List; - -public class RadarDataSet extends LineRadarDataSet implements IRadarDataSet { - - public RadarDataSet(List yVals, String label) { - super(yVals, label); - } - - @Override - public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - RadarDataSet copied = new RadarDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mHighLightColor = mHighLightColor; - - return copied; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java deleted file mode 100644 index 99422dd916..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java +++ /dev/null @@ -1,63 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - -import java.util.ArrayList; -import java.util.List; - -public class ScatterData extends BarLineScatterCandleBubbleData { - - public ScatterData() { - super(); - } - - public ScatterData(List xVals) { - super(xVals); - } - - public ScatterData(String[] xVals) { - super(xVals); - } - - public ScatterData(List xVals, List dataSets) { - super(xVals, dataSets); - } - - public ScatterData(String[] xVals, List dataSets) { - super(xVals, dataSets); - } - - public ScatterData(List xVals, IScatterDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public ScatterData(String[] xVals, IScatterDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IScatterDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java deleted file mode 100644 index 199a20638b..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ /dev/null @@ -1,124 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; - -import java.util.ArrayList; -import java.util.List; - -public class ScatterDataSet extends LineScatterCandleRadarDataSet implements IScatterDataSet { - - /** - * the size the scattershape will have, in density pixels - */ - private float mShapeSize = 15f; - - /** - * the type of shape that is set to be drawn where the values are at, - * default ScatterShape.SQUARE - */ - private ScatterShape mScatterShape = ScatterShape.SQUARE; - - /** - * 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; - - /** - * Custom path object the user can provide that is drawn where the values - * are at. This is used when ScatterShape.CUSTOM is set for a DataSet. - */ - //private Path mCustomScatterPath = null; - public ScatterDataSet(List yVals, String label) { - super(yVals, label); - } - - @Override - public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); - } - - ScatterDataSet copied = new ScatterDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShapeSize = mShapeSize; - copied.mScatterShape = mScatterShape; - copied.mScatterShapeHoleRadius = mScatterShapeHoleRadius; - copied.mScatterShapeHoleColor = mScatterShapeHoleColor; - //copied.mCustomScatterPath = mCustomScatterPath; - copied.mHighLightColor = mHighLightColor; - - return copied; - } - - /** - * 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 shape that is drawn on the position where the values are at. - * - * @param shape - */ - public void setScatterShape(ScatterShape shape) { - mScatterShape = shape; - } - - @Override - public ScatterShape getScatterShape() { - return mScatterShape; - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java deleted file mode 100644 index 75e57edabd..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/filter/Approximator.java +++ /dev/null @@ -1,282 +0,0 @@ - -package com.github.mikephil.charting.data.filter; - -import com.github.mikephil.charting.data.Entry; - -import java.util.ArrayList; -import java.util.List; - -/** - * Implemented according to Wiki-Pseudocode {@link} - * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm - * - * @author Philipp Baldauf & Phliipp Jahoda - */ -public class Approximator { - - /** the type of filtering algorithm to use */ - private ApproximatorType mType = ApproximatorType.DOUGLAS_PEUCKER; - - /** the tolerance to be filtered with */ - private double mTolerance = 0; - - private float mScaleRatio = 1f; - private float mDeltaRatio = 1f; - - /** - * array that contains "true" on all indices that will be kept after - * filtering - */ - private boolean[] keep; - - /** enums for the different types of filtering algorithms */ - public enum ApproximatorType { - NONE, DOUGLAS_PEUCKER - } - - /** - * Initializes the approximator with type NONE - */ - public Approximator() { - this.mType = ApproximatorType.NONE; - } - - /** - * Initializes the approximator with the given type and tolerance. If - * toleranec <= 0, no filtering will be done. - * - * @param type - */ - public Approximator(ApproximatorType type, double tolerance) { - setup(type, tolerance); - } - - /** - * sets type and tolerance, if tolerance <= 0, no filtering will be done - * - * @param type - * @param tolerance - */ - public void setup(ApproximatorType type, double tolerance) { - mType = type; - mTolerance = tolerance; - } - - /** - * sets the tolerance for the Approximator. When using the - * Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that - * will trigger the filtering. - */ - public void setTolerance(double tolerance) { - mTolerance = tolerance; - } - - /** - * Sets the filtering algorithm that should be used. - * - * @param type - */ - public void setType(ApproximatorType type) { - this.mType = type; - } - - /** - * Sets the ratios for x- and y-axis, as well as the ratio of the scale - * levels - * - * @param deltaRatio - * @param scaleRatio - */ - public void setRatios(float deltaRatio, float scaleRatio) { - mDeltaRatio = deltaRatio; - mScaleRatio = scaleRatio; - } - - /** - * Filters according to type. Uses the pre set set tolerance - * - * @param points the points to filter - * @return - */ - public List filter(List points) { - return filter(points, mTolerance); - } - - /** - * Filters according to type. - * - * @param points the points to filter - * @param tolerance the angle in degrees that will trigger the filtering - * @return - */ - public List filter(List points, double tolerance) { - - if (tolerance <= 0) - return points; - - keep = new boolean[points.size()]; - - switch (mType) { - case DOUGLAS_PEUCKER: - return reduceWithDouglasPeuker(points, tolerance); - case NONE: - return points; - default: - return points; - } - } - - /** - * uses the douglas peuker algorithm to reduce the given List of - * entries - * - * @param entries - * @param epsilon - * @return - */ - private List reduceWithDouglasPeuker(List entries, double epsilon) { - // if a shape has 2 or less points it cannot be reduced - if (epsilon <= 0 || entries.size() < 3) { - return entries; - } - - // first and last always stay - keep[0] = true; - keep[entries.size() - 1] = true; - - // first and last entry are entry point to recursion - algorithmDouglasPeucker(entries, epsilon, 0, entries.size() - 1); - - // create a new array with series, only take the kept ones - List reducedEntries = new ArrayList(); - for (int i = 0; i < entries.size(); i++) { - if (keep[i]) { - Entry curEntry = entries.get(i); - reducedEntries.add(new Entry(curEntry.getVal(), curEntry.getXIndex())); - } - } - return reducedEntries; - } - - /** - * apply the Douglas-Peucker-Reduction to an List of Entry with a given - * epsilon (tolerance) - * - * @param entries - * @param epsilon as y-value - * @param start - * @param end - */ - private void algorithmDouglasPeucker(List entries, double epsilon, int start, - int end) { - if (end <= start + 1) { - // recursion finished - return; - } - - // find the greatest distance between start and endpoint - int maxDistIndex = 0; - double distMax = 0; - - Entry firstEntry = entries.get(start); - Entry lastEntry = entries.get(end); - - for (int i = start + 1; i < end; i++) { - double dist = calcAngleBetweenLines(firstEntry, lastEntry, firstEntry, entries.get(i)); - - // keep the point with the greatest distance - if (dist > distMax) { - distMax = dist; - maxDistIndex = i; - } - } - - // Log.i("maxangle", "" + distMax); - - if (distMax > epsilon) { - // keep max dist point - keep[maxDistIndex] = true; - - // recursive call - algorithmDouglasPeucker(entries, epsilon, start, maxDistIndex); - algorithmDouglasPeucker(entries, epsilon, maxDistIndex, end); - } // else don't keep the point... - } - - /** - * calculate the distance between a line between two entries and an entry - * (point) - * - * @param startEntry line startpoint - * @param endEntry line endpoint - * @param entryPoint the point to which the distance is measured from the - * line - * @return - */ - public double calcPointToLineDistance(Entry startEntry, Entry endEntry, Entry entryPoint) { - - float xDiffEndStart = (float) endEntry.getXIndex() - (float) startEntry.getXIndex(); - float xDiffEntryStart = (float) entryPoint.getXIndex() - (float) startEntry.getXIndex(); - - double normalLength = Math.sqrt((xDiffEndStart) - * (xDiffEndStart) - + (endEntry.getVal() - startEntry.getVal()) - * (endEntry.getVal() - startEntry.getVal())); - return Math.abs((xDiffEntryStart) - * (endEntry.getVal() - startEntry.getVal()) - - (entryPoint.getVal() - startEntry.getVal()) - * (xDiffEndStart)) - / normalLength; - } - - /** - * Calculates the angle between two given lines. The provided Entry objects - * mark the starting and end points of the lines. - * - * @param start1 - * @param end1 - * @param start2 - * @param end2 - * @return - */ - public double calcAngleBetweenLines(Entry start1, Entry end1, Entry start2, Entry end2) { - - double angle1 = calcAngleWithRatios(start1, end1); - double angle2 = calcAngleWithRatios(start2, end2); - - return Math.abs(angle1 - angle2); - } - - /** - * calculates the angle between two Entries (points) in the chart taking - * ratios into consideration - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngleWithRatios(Entry p1, Entry p2) { - - float dx = p2.getXIndex() * mDeltaRatio - p1.getXIndex() * mDeltaRatio; - float dy = p2.getVal() * mScaleRatio - p1.getVal() * mScaleRatio; - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; - - return angle; - } - - /** - * calculates the angle between two Entries (points) in the chart - * - * @param p1 - * @param p2 - * @return - */ - public double calcAngle(Entry p1, Entry p2) { - - float dx = p2.getXIndex() - p1.getXIndex(); - float dy = p2.getVal() - p1.getVal(); - double angle = Math.atan2(dy, dx) * 180.0 / Math.PI; - - return angle; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java deleted file mode 100644 index cfff7b8b28..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBarLineScatterCandleBubbleDataSet.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.Color; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmBarLineScatterCandleBubbleDataSet extends RealmBaseDataSet implements IBarLineScatterCandleBubbleDataSet { - - /** default highlight color */ - protected int mHighLightColor = Color.rgb(255, 187, 115); - - public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param yValuesField - * @param xIndexField - */ - public RealmBarLineScatterCandleBubbleDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java deleted file mode 100644 index 02b769e8ac..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmBaseDataSet.java +++ /dev/null @@ -1,349 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import com.github.mikephil.charting.data.BaseDataSet; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; -import io.realm.Sort; - -/** - * Created by Philipp Jahoda on 06/11/15. - */ -public abstract class RealmBaseDataSet extends BaseDataSet { - - /** - * a list of queried realm objects - */ - protected RealmResults results; - - /** - * a cached list of all data read from the database - */ - protected List mValues; - - /** - * maximum y-value in the y-value array - */ - protected float mYMax = 0.0f; - - /** - * the minimum y-value in the y-value array - */ - protected float mYMin = 0.0f; - - /** - * fieldname of the column that contains the y-values of this dataset - */ - protected String mValuesField; - - /** - * fieldname of the column that contains the x-indices of this dataset - */ - protected String mIndexField; - - public RealmBaseDataSet(RealmResults results, String yValuesField) { - this.results = results; - this.mValuesField = yValuesField; - this.mValues = new ArrayList(); - - if (mIndexField != null) - this.results.sort(mIndexField, Sort.ASCENDING); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param yValuesField - * @param xIndexField - */ - public RealmBaseDataSet(RealmResults results, String yValuesField, String xIndexField) { - this.results = results; - this.mValuesField = yValuesField; - this.mIndexField = xIndexField; - this.mValues = new ArrayList(); - - if (mIndexField != null) - this.results.sort(mIndexField, Sort.ASCENDING); - } - - /** - * Rebuilds the DataSet based on the given RealmResults. - */ - public abstract void build(RealmResults results); - - @Override - public float getYMin() { - //return results.min(mValuesField).floatValue(); - return mYMin; - } - - @Override - public float getYMax() { - //return results.max(mValuesField).floatValue(); - return mYMax; - } - - @Override - public int getEntryCount() { - return mValues.size(); - } - - @Override - public void calcMinMax(int start, int end) { - - if (mValues == null) - return; - - final int yValCount = mValues.size(); - - if (yValCount == 0) - return; - - int endValue; - - if (end == 0 || end >= yValCount) - endValue = yValCount - 1; - else - endValue = end; - - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - - for (int i = start; i <= endValue; i++) { - - S e = mValues.get(i); - - if (e != null && !Float.isNaN(e.getVal())) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } - } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - } - - @Override - public S getEntryForXIndex(int xIndex) { - //DynamicRealmObject o = new DynamicRealmObject(results.where().equalTo(mIndexField, xIndex).findFirst()); - //return new Entry(o.getFloat(mValuesField), o.getInt(mIndexField)); - return getEntryForXIndex(xIndex, DataSet.Rounding.CLOSEST); - } - - @Override - public S getEntryForXIndex(int xIndex, DataSet.Rounding rounding) { - int index = getEntryIndex(xIndex, rounding); - if (index > -1) - return mValues.get(index); - return null; - } - - @Override - public S getEntryForIndex(int index) { - //DynamicRealmObject o = new DynamicRealmObject(results.get(index)); - //return new Entry(o.getFloat(mValuesField), o.getInt(mIndexField)); - return mValues.get(index); - } - - @Override - public int getEntryIndex(int x, DataSet.Rounding rounding) { - - int low = 0; - int high = mValues.size() - 1; - int closest = -1; - - while (low <= high) { - int m = (high + low) / 2; - - if (x == mValues.get(m).getXIndex()) { - while (m > 0 && mValues.get(m - 1).getXIndex() == x) - m--; - - return m; - } - - if (x > mValues.get(m).getXIndex()) - low = m + 1; - else - high = m - 1; - - closest = m; - } - - if (closest != -1) { - int closestXIndex = mValues.get(closest).getXIndex(); - if (rounding == DataSet.Rounding.UP) { - if (closestXIndex < x && closest < mValues.size() - 1) { - ++closest; - } - } else if (rounding == DataSet.Rounding.DOWN) { - if (closestXIndex > x && closest > 0) { - --closest; - } - } - } - - return closest; - } - - @Override - public int getEntryIndex(S e) { - return mValues.indexOf(e); - } - - @Override - public float getYValForXIndex(int xIndex) { - //return new DynamicRealmObject(results.where().greaterThanOrEqualTo(mIndexField, xIndex).findFirst()).getFloat(mValuesField); - Entry e = getEntryForXIndex(xIndex); - - if (e != null && e.getXIndex() == xIndex) - return e.getVal(); - else - return Float.NaN; - } - - @Override - public boolean addEntry(S e) { - - if (e == null) - return false; - - float val = e.getVal(); - - if (mValues == null) { - mValues = new ArrayList(); - } - - if (mValues.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - // add the entry - mValues.add(e); - return true; - } - - @Override - public boolean removeEntry(S e) { - - if (e == null) - return false; - - if (mValues == null) - return false; - - // remove the entry - boolean removed = mValues.remove(e); - - if (removed) { - calcMinMax(0, mValues.size()); - } - - return removed; - } - - @Override - public void addEntryOrdered(S e) { - - if (e == null) - return; - - float val = e.getVal(); - - if (mValues == null) { - mValues = new ArrayList(); - } - - if (mValues.size() == 0) { - mYMax = val; - mYMin = val; - } else { - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - } - - if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getXIndex() > e.getXIndex()) { - int closestIndex = getEntryIndex(e.getXIndex(), DataSet.Rounding.UP); - mValues.add(closestIndex, e); - return; - } - - mValues.add(e); - } - - /** - * Returns the List of values that has been extracted from the RealmResults - * using the provided fieldnames. - * - * @return - */ - public List getValues() { - return mValues; - } - - @Override - public void clear() { - mValues.clear(); - notifyDataSetChanged(); - } - - public RealmResults getResults() { - return results; - } - - /** - * Returns the fieldname that represents the "y-values" in the realm-data. - * - * @return - */ - public String getValuesField() { - return mValuesField; - } - - /** - * Sets the field name that is used for getting the y-values out of the RealmResultSet. - * - * @param yValuesField - */ - public void setValuesField(String yValuesField) { - this.mValuesField = yValuesField; - } - - /** - * Returns the fieldname that represents the "x-index" in the realm-data. - * - * @return - */ - public String getIndexField() { - return mIndexField; - } - - /** - * Sets the field name that is used for getting the x-indices out of the RealmResultSet. - * - * @param xIndexField - */ - public void setIndexField(String xIndexField) { - this.mIndexField = xIndexField; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java deleted file mode 100644 index f8ea4df17d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineRadarDataSet.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.Color; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmLineRadarDataSet extends RealmLineScatterCandleRadarDataSet implements ILineRadarDataSet { - - /** 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 RealmLineRadarDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param yValuesField - * @param xIndexField - */ - public RealmLineRadarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); - } - - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - - @Override - public int getFillColor() { - return mFillColor; - } - - /** - * sets the color that is used for filling the line surface - * - * @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 - */ - 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.2f) - width = 0.2f; - 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java deleted file mode 100644 index d86b98d0b0..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmLineScatterCandleRadarDataSet.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import android.graphics.DashPathEffect; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 08/11/15. - */ -public abstract class RealmLineScatterCandleRadarDataSet extends RealmBarLineScatterCandleBubbleDataSet implements ILineScatterCandleRadarDataSet { - - 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 RealmLineScatterCandleRadarDataSet(RealmResults results, String yValuesField) { - super(results, yValuesField); - } - - /** - * Constructor that takes the realm RealmResults, sorts & stores them. - * - * @param results - * @param yValuesField - * @param xIndexField - */ - public RealmLineScatterCandleRadarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java deleted file mode 100644 index 0327a9a060..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/base/RealmUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.mikephil.charting.data.realm.base; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public final class RealmUtils { - - /** - * Transforms the given Realm-ResultSet into a String array by using the provided xValuesField. - * - * @param result - * @param xValuesField - * @return - */ - public static List toXVals(RealmResults result, String xValuesField) { - - List xVals = new ArrayList<>(); - - for (RealmObject object : result) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - xVals.add(dynamicObject.getString(xValuesField)); - } - - return xVals; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java deleted file mode 100644 index ec124e6dee..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmBarData extends BarData { - - public RealmBarData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java deleted file mode 100644 index 1b78c278cb..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBarDataSet.java +++ /dev/null @@ -1,244 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.graphics.Color; - -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; - -import io.realm.DynamicRealmObject; -import io.realm.RealmList; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmBarDataSet extends RealmBarLineScatterCandleBubbleDataSet implements IBarDataSet { - - private String mStackValueFieldName; - - /** - * space indicator between the bars 0.1f == 10 % - */ - private float mBarSpace = 0.15f; - - /** - * 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); - - /** - * the alpha value used to draw the highlight indicator bar - */ - private int mHighLightAlpha = 120; - - /** - * array of labels used to describe the different values of the stacked bars - */ - private String[] mStackLabels = new String[]{ - "Stack" - }; - - public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField) { - super(results, yValuesField, xIndexField); - mHighLightColor = Color.rgb(0, 0, 0); - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for supporting stacked values. - * - * @param results - * @param yValuesField - * @param xIndexField - * @param stackValueFieldName - */ - public RealmBarDataSet(RealmResults results, String yValuesField, String xIndexField, String stackValueFieldName) { - super(results, yValuesField, xIndexField); - this.mStackValueFieldName = stackValueFieldName; - mHighLightColor = Color.rgb(0, 0, 0); - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - - for (T realmObject : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - - try { // normal entry - - float value = dynamicObject.getFloat(mValuesField); - mValues.add(new BarEntry(value, dynamicObject.getInt(mIndexField))); - - } catch (IllegalArgumentException e) { // stacked entry - - RealmList list = dynamicObject.getList(mValuesField); - float[] values = new float[list.size()]; - - int i = 0; - for (DynamicRealmObject o : list) { - values[i] = o.getFloat(mStackValueFieldName); - i++; - } - - mValues.add(new BarEntry(values, dynamicObject.getInt(mIndexField))); - } - } - - calcStackSize(); - } - - @Override - public void calcMinMax(int start, int end) { - - if (mValues == null) - return; - - final int yValCount = mValues.size(); - - if (yValCount == 0) - return; - - int endValue; - - if (end == 0 || end >= yValCount) - endValue = yValCount - 1; - else - endValue = end; - - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - - for (int i = start; i <= endValue; i++) { - - BarEntry e = mValues.get(i); - - if (e != null && !Float.isNaN(e.getVal())) { - - if (e.getVals() == null) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } else { - - if (-e.getNegativeSum() < mYMin) - mYMin = -e.getNegativeSum(); - - if (e.getPositiveSum() > mYMax) - mYMax = e.getPositiveSum(); - } - } - } - - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } - } - - private void calcStackSize() { - - for (int i = 0; i < mValues.size(); i++) { - - float[] vals = mValues.get(i).getVals(); - - if (vals != null && vals.length > mStackSize) - mStackSize = vals.length; - } - } - - @Override - public int getStackSize() { - return mStackSize; - } - - @Override - public boolean isStacked() { - return mStackSize > 1 ? true : false; - } - - /** - * returns the space between bars in percent of the whole width of one value - * - * @return - */ - public float getBarSpacePercent() { - return mBarSpace * 100f; - } - - @Override - public float getBarSpace() { - return mBarSpace; - } - - /** - * sets the space between the bars in percent (0-100) of the total bar width - * - * @param percent - */ - public void setBarSpacePercent(float percent) { - mBarSpace = percent / 100f; - } - - /** - * 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; - } - - /** - * 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; - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java deleted file mode 100644 index 617f098dd2..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmBubbleData extends BubbleData { - - public RealmBubbleData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java deleted file mode 100644 index 7a8ae13f08..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmBubbleDataSet.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.BubbleEntry; -import com.github.mikephil.charting.data.realm.base.RealmBarLineScatterCandleBubbleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmBubbleDataSet extends RealmBarLineScatterCandleBubbleDataSet implements IBubbleDataSet { - - private String mSizeField; - - protected float mXMax; - protected float mXMin; - protected float mMaxSize; - - private float mHighlightCircleWidth = 2.5f; - - /** - * Constructor for creating a CandleDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param sizeField the name of the field in your data object that represents the bubble size - */ - public RealmBubbleDataSet(RealmResults result, String yValuesField, String sizeField) { - super(result, yValuesField); - this.mSizeField = sizeField; - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for creating a CandleDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - * @param sizeField the name of the field in your data object that represents the bubble size - */ - public RealmBubbleDataSet(RealmResults result, String yValuesField, String xIndexField, String sizeField) { - super(result, yValuesField, xIndexField); - this.mSizeField = sizeField; - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - - if(mIndexField == null) { - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new BubbleEntry(xIndex, dynamicObject.getFloat(mValuesField), dynamicObject.getFloat(mSizeField))); - xIndex++; - } - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new BubbleEntry(dynamicObject.getInt(mIndexField), dynamicObject.getFloat(mValuesField), dynamicObject.getFloat(mSizeField))); - } - } - } - - @Override - public void calcMinMax(int start, int end) { - - if (mValues == null) - return; - - if (mValues.size() == 0) - return; - - int endValue; - - if (end == 0 || end >= mValues.size()) - endValue = mValues.size() - 1; - else - endValue = end; - - mYMin = yMin(mValues.get(start)); - mYMax = yMax(mValues.get(start)); - - // need chart width to guess this properly - - for (int i = start; i < endValue; i++) { - - final BubbleEntry entry = mValues.get(i); - - final float ymin = yMin(entry); - final float ymax = yMax(entry); - - if (ymin < mYMin) { - mYMin = ymin; - } - - if (ymax > mYMax) { - mYMax = ymax; - } - - final float xmin = xMin(entry); - final float xmax = xMax(entry); - - if (xmin < mXMin) { - mXMin = xmin; - } - - if (xmax > mXMax) { - mXMax = xmax; - } - - final float size = largestSize(entry); - - if (size > mMaxSize) { - mMaxSize = size; - } - } - } - - @Override - public float getXMax() { - return mXMax; - } - - @Override - public float getXMin() { - return mXMin; - } - - @Override - public float getMaxSize() { - return mMaxSize; - } - - private float yMin(BubbleEntry entry) { - return entry.getVal(); - } - - private float yMax(BubbleEntry entry) { - return entry.getVal(); - } - - private float xMin(BubbleEntry entry) { - return (float) entry.getXIndex(); - } - - private float xMax(BubbleEntry entry) { - return (float) entry.getXIndex(); - } - - private float largestSize(BubbleEntry entry) { - return entry.getSize(); - } - - @Override - public void setHighlightCircleWidth(float width) { - mHighlightCircleWidth = Utils.convertDpToPixel(width); - } - - @Override - public float getHighlightCircleWidth() { - return mHighlightCircleWidth; - } - - /** - * Sets the database fieldname for the bubble size. - * - * @param sizeField - */ - public void setSizeField(String sizeField) { - this.mSizeField = sizeField; - } - - /** - * Returns the database fieldname that stores bubble size. - * - * @return - */ - public String getSizeField() { - return mSizeField; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java deleted file mode 100644 index 769c1801c2..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmCandleData extends CandleData { - - public RealmCandleData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java deleted file mode 100644 index 8c7c9c8e78..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmCandleDataSet.java +++ /dev/null @@ -1,330 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.graphics.Paint; - -import com.github.mikephil.charting.data.CandleEntry; -import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmCandleDataSet extends RealmLineScatterCandleRadarDataSet implements ICandleDataSet { - - private String mHighField; - private String mLowField; - private String mOpenField; - private String mCloseField; - - /** - * the width of the shadow of the candle - */ - private float mShadowWidth = 3f; - - /** - * should the candle bars show? - * when false, only "ticks" will show - * - * - 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_NONE; - - /** - * color for open < close - */ - protected int mIncreasingColor = ColorTemplate.COLOR_NONE; - - /** - * color for open > close - */ - protected int mDecreasingColor = ColorTemplate.COLOR_NONE; - - /** - * shadow line color, set -1 for backward compatibility and uses default - * color - */ - protected int mShadowColor = ColorTemplate.COLOR_NONE; - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value - */ - public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField) { - super(result, null); - this.mHighField = highField; - this.mLowField = lowField; - this.mOpenField = openField; - this.mCloseField = closeField; - - build(this.results); - calcMinMax(0, this.results.size()); - } - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param highField the name of the field in your data object that represents the "high" value - * @param lowField the name of the field in your data object that represents the "low" value - * @param openField the name of the field in your data object that represents the "open" value - * @param closeField the name of the field in your data object that represents the "close" value - * @param xIndexField the name of the field in your data object that represents the x-index - */ - public RealmCandleDataSet(RealmResults result, String highField, String lowField, String openField, String closeField, String xIndexField) { - super(result, null, xIndexField); - this.mHighField = highField; - this.mLowField = lowField; - this.mOpenField = openField; - this.mCloseField = closeField; - - build(this.results); - calcMinMax(0, this.results.size()); - } - - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new CandleEntry(xIndex, dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), - dynamicObject.getFloat(mOpenField), dynamicObject.getFloat(mCloseField))); - xIndex++; - } - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new CandleEntry(dynamicObject.getInt(mIndexField), dynamicObject.getFloat(mHighField), dynamicObject.getFloat(mLowField), - dynamicObject.getFloat(mOpenField), dynamicObject.getFloat(mCloseField))); - } - } - } - - @Override - public void calcMinMax(int start, int end) { - - if (mValues == null) - return; - - if (mValues.size() == 0) - return; - - int endValue; - - if (end == 0 || end >= mValues.size()) - endValue = mValues.size() - 1; - else - endValue = end; - - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; - - for (int i = start; i <= endValue; i++) { - - CandleEntry e = mValues.get(i); - - if (e.getLow() < mYMin) - mYMin = e.getLow(); - - if (e.getHigh() > mYMax) - mYMax = e.getHigh(); - } - } - - /** - * 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 setShadowWidth(boolean showCandleBar) { - mShowCandleBar = showCandleBar; - } - - @Override - public boolean getShowCandleBar() { - return mShowCandleBar; - } - - - /** 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java deleted file mode 100644 index bcacb98952..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmLineData extends LineData { - - public RealmLineData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java deleted file mode 100644 index 775ab665ff..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmLineDataSet.java +++ /dev/null @@ -1,333 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; -import com.github.mikephil.charting.formatter.DefaultFillFormatter; -import com.github.mikephil.charting.formatter.FillFormatter; -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; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 21/10/15. - */ -public class RealmLineDataSet extends RealmLineRadarDataSet implements ILineDataSet { - - /** - * List representing all colors that are used for the circles - */ - private List mCircleColors = null; - - /** - * the color of the inner circles - */ - private int mCircleColorHole = Color.WHITE; - - /** - * the radius of the circle-shaped value indicators - */ - private float mCircleSize = 8f; - - /** - * 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 FillFormatter mFillFormatter = new DefaultFillFormatter(); - - /** - * if true, drawing circles is enabled - */ - private boolean mDrawCircles = true; - - /** - * if true, cubic lines are drawn instead of linear - */ - private boolean mDrawCubic = false; - - private boolean mDrawCircleHole = true; - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmLineDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - mCircleColors = new ArrayList(); - - // default color - mCircleColors.add(Color.rgb(140, 234, 255)); - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for creating a LineDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - */ - public RealmLineDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); - mCircleColors = new ArrayList(); - - // default color - mCircleColors.add(Color.rgb(140, 234, 255)); - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - super.build(results); - } - - /** - * 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 size (radius) of the circle shpaed value indicators, default - * size = 4f - * - * @param size - */ - public void setCircleSize(float size) { - mCircleSize = Utils.convertDpToPixel(size); - } - - @Override - public float getCircleRadius() { - return mCircleSize; - } - - /** - * 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; - } - - /** - * If set to true, the linechart lines are drawn in cubic-style instead of - * linear. This affects performance! Default: false - * - * @param enabled - */ - public void setDrawCubic(boolean enabled) { - mDrawCubic = enabled; - } - - @Override - public boolean isDrawCubicEnabled() { - return mDrawCubic; - } - - /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ - - /** - * returns all colors specified for the circles - * - * @return - */ - public List getCircleColors() { - return mCircleColors; - } - - @Override - public int getCircleColor(int index) { - return mCircleColors.get(index % 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 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 clrs = new ArrayList(); - - 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() { - mCircleColors = new ArrayList(); - } - - /** - * Sets the color of the inner circle of the line-circles. - * - * @param color - */ - public void setCircleColorHole(int color) { - mCircleColorHole = color; - } - - @Override - public int getCircleHoleColor() { - return mCircleColorHole; - } - - /** - * 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 FillFormatter 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(FillFormatter formatter) { - - if (formatter == null) - mFillFormatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - - @Override - public FillFormatter getFillFormatter() { - return mFillFormatter; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java deleted file mode 100644 index 665725118c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieData.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmPieData extends PieData { - - public RealmPieData(RealmResults result, String xValuesField, IPieDataSet dataSet) { - super(RealmUtils.toXVals(result, xValuesField), dataSet); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java deleted file mode 100644 index c11518bc54..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmPieDataSet.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.realm.base.RealmBaseDataSet; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmPieDataSet extends RealmBaseDataSet implements IPieDataSet { - - /** - * the space in pixels between the chart-slices, default 0f - */ - private float mSliceSpace = 0f; - - /** - * indicates the selection distance of a pie slice - */ - private float mShift = 18f; - - - /** - * Constructor for creating a PieDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmPieDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for creating a PieDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - */ - public RealmPieDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - - /** - * 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; - } - - /** - * 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java deleted file mode 100644 index a098a6d2a5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmRadarData extends RadarData{ - - public RealmRadarData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java deleted file mode 100644 index 2c0d8457a8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmRadarDataSet.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.realm.base.RealmLineRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmRadarDataSet extends RealmLineRadarDataSet implements IRadarDataSet { - - /** - * Constructor for creating a RadarDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmRadarDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for creating a RadarDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - */ - public RealmRadarDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - super.build(results); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java deleted file mode 100644 index 2838a86f11..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterData.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.data.realm.base.RealmUtils; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - -import java.util.List; - -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 19/12/15. - */ -public class RealmScatterData extends ScatterData { - - public RealmScatterData(RealmResults result, String xValuesField, List dataSets) { - super(RealmUtils.toXVals(result, xValuesField), dataSets); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java deleted file mode 100644 index c895bc09ec..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/realm/implementation/RealmScatterDataSet.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.github.mikephil.charting.data.realm.implementation; - -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.realm.base.RealmLineScatterCandleRadarDataSet; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import io.realm.DynamicRealmObject; -import io.realm.RealmObject; -import io.realm.RealmResults; - -/** - * Created by Philipp Jahoda on 07/11/15. - */ -public class RealmScatterDataSet extends RealmLineScatterCandleRadarDataSet implements IScatterDataSet { - - /** - * the size the scattershape will have, in density pixels - */ - private float mShapeSize = 10f; - - /** - * the type of shape that is set to be drawn where the values are at, - * default ScatterShape.SQUARE - */ - private ScatterChart.ScatterShape mScatterShape = ScatterChart.ScatterShape.SQUARE; - - /** - * 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; - - /** - * Constructor for creating a ScatterDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - */ - public RealmScatterDataSet(RealmResults result, String yValuesField) { - super(result, yValuesField); - - build(this.results); - calcMinMax(0, results.size()); - } - - /** - * Constructor for creating a ScatterDataSet with realm data. - * - * @param result the queried results from the realm database - * @param yValuesField the name of the field in your data object that represents the y-value - * @param xIndexField the name of the field in your data object that represents the x-index - */ - public RealmScatterDataSet(RealmResults result, String yValuesField, String xIndexField) { - super(result, yValuesField, xIndexField); - - build(this.results); - calcMinMax(0, results.size()); - } - - @Override - public void build(RealmResults results) { - - if (mIndexField == null) { // x-index not available - - int xIndex = 0; - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), xIndex)); - xIndex++; - } - - } else { - - for (T object : results) { - - DynamicRealmObject dynamicObject = new DynamicRealmObject(object); - mValues.add(new Entry(dynamicObject.getFloat(mValuesField), dynamicObject.getInt(mIndexField))); - } - } - } - - /** - * 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 shape that is drawn on the position where the values are at. If - * "CUSTOM" is chosen, you need to call setCustomScatterShape(...) and - * provide a path object that is drawn as the custom scattershape. - * - * @param shape - */ - public void setScatterShape(ScatterChart.ScatterShape shape) { - mScatterShape = shape; - } - - @Override - public ScatterChart.ScatterShape getScatterShape() { - return mScatterShape; - } - - /** - * Sets the radius of the hole in the shape - * - * @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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/ColorFormatter.java b/MPChartLib/src/com/github/mikephil/charting/formatter/ColorFormatter.java deleted file mode 100644 index 3a7d119e60..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/ColorFormatter.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.data.Entry; - -/** - * 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 { - - int getColor(Entry e, int index); -} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java deleted file mode 100644 index 383e195322..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultXAxisValueFormatter.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 14/09/15. - * Default formatter class for adjusting x-values before drawing them. - * This simply returns the original value unmodified. - */ -public class DefaultXAxisValueFormatter implements XAxisValueFormatter { - - @Override - public String getXValue(String original, int index, ViewPortHandler viewPortHandler) { - return original; // just return original, no adjustments - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java deleted file mode 100644 index 5b8cd01452..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultYAxisValueFormatter.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.components.YAxis; - -import java.text.DecimalFormat; - -/** - * Created by Philipp Jahoda on 20/09/15. - * Default formatter used for formatting labels of the YAxis. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min value). - */ -public class DefaultYAxisValueFormatter implements YAxisValueFormatter { - - /** decimalformat for formatting */ - private DecimalFormat mFormat; - - /** - * Constructor that specifies to how many digits the value should be - * formatted. - * - * @param digits - */ - public DefaultYAxisValueFormatter(int 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, YAxis yAxis) { - // avoid memory allocations here (for performance) - return mFormat.format(value); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/XAxisValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/formatter/XAxisValueFormatter.java deleted file mode 100644 index 1dcb72b04b..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/XAxisValueFormatter.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 14/09/15. - * An interface for providing custom x-axis Strings. - * - * @author Philipp Jahoda - */ -public interface XAxisValueFormatter { - - /** - * Returns the customized label that is drawn on the x-axis. - * For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param original the original x-axis label to be drawn - * @param index the x-index that is currently being drawn - * @param viewPortHandler provides information about the current chart state (scale, translation, ...) - * @return - */ - String getXValue(String original, int index, ViewPortHandler viewPortHandler); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/YAxisValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/formatter/YAxisValueFormatter.java deleted file mode 100644 index 4bc6c3e987..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/YAxisValueFormatter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.mikephil.charting.formatter; - -import com.github.mikephil.charting.components.YAxis; - -/** - * Created by Philipp Jahoda on 20/09/15. - * Custom formatter interface that allows formatting of - * YAxis labels before they are being drawn. - */ -public interface YAxisValueFormatter { - - /** - * Called when a value from the YAxis is formatted - * before being drawn. For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param value the YAxis value to be formatted - * @param yAxis the YAxis object the value belongs to - * @return - */ - String getFormattedValue(float value, YAxis yAxis); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java deleted file mode 100644 index 1fa547884f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/BarHighlighter.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.github.mikephil.charting.highlight; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; - -/** - * Created by Philipp Jahoda on 22/07/15. - */ -public class BarHighlighter extends ChartHighlighter { - - public BarHighlighter(BarDataProvider chart) { - super(chart); - } - - @Override - public Highlight getHighlight(float x, float y) { - - Highlight h = super.getHighlight(x, y); - - if (h == null) - return h; - else { - - IBarDataSet set = mChart.getBarData().getDataSetByIndex(h.getDataSetIndex()); - - if (set.isStacked()) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = y; - - // take any transformer to determine the x-axis value - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - - return getStackedHighlight(h, set, h.getXIndex(), h.getDataSetIndex(), pts[1]); - } else - return h; - } - } - - @Override - protected int getXIndex(float x) { - - if (!mChart.getBarData().isGrouped()) { - return super.getXIndex(x); - } else { - - float baseNoSpace = getBase(x); - - int setCount = mChart.getBarData().getDataSetCount(); - int xIndex = (int) baseNoSpace / setCount; - - int valCount = mChart.getData().getXValCount(); - - if (xIndex < 0) - xIndex = 0; - else if (xIndex >= valCount) - xIndex = valCount - 1; - - return xIndex; - } - } - - @Override - protected int getDataSetIndex(int xIndex, float x, float y) { - - if (!mChart.getBarData().isGrouped()) { - return 0; - } else { - - float baseNoSpace = getBase(x); - - int setCount = mChart.getBarData().getDataSetCount(); - int dataSetIndex = (int) baseNoSpace % setCount; - - if (dataSetIndex < 0) - dataSetIndex = 0; - else if (dataSetIndex >= setCount) - dataSetIndex = setCount - 1; - - return dataSetIndex; - } - } - - /** - * This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. - * - * @param old - * the old highlight object before looking for stacked values - * @param set - * @param xIndex - * @param dataSetIndex - * @param yValue - * @return - */ - protected Highlight getStackedHighlight(Highlight old, IBarDataSet set, int xIndex, int dataSetIndex, double yValue) { - - BarEntry entry = set.getEntryForXIndex(xIndex); - - if (entry == null || entry.getVals() == null) - return old; - - Range[] ranges = getRanges(entry); - int stackIndex = getClosestStackIndex(ranges, (float) yValue); - - Highlight h = new Highlight(xIndex, dataSetIndex, stackIndex, ranges[stackIndex]); - return h; - } - - /** - * 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; - // - // float[] vals = e.getVals(); - // - // if (vals == null) - // return -1; - // - // int index = 0; - // float remainder = e.getNegativeSum(); - // - // while (index < vals.length - 1 && value > vals[index] + remainder) { - // remainder += vals[index]; - // index++; - // } - // - // return index; - } - - /** - * Returns the base x-value to the corresponding x-touch value in pixels. - * - * @param x - * @return - */ - protected float getBase(float x) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - float xVal = pts[0]; - - int setCount = mChart.getBarData().getDataSetCount(); - - // calculate how often the group-space appears - int steps = (int) ((float) xVal / ((float) setCount + mChart.getBarData().getGroupSpace())); - - float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; - - float baseNoSpace = (float) xVal - groupSpaceSum; - return baseNoSpace; - } - - /** - * Splits up the stack-values of the given bar-entry into Range objects. - * - * @param entry - * @return - */ - protected Range[] getRanges(BarEntry entry) { - - float[] values = entry.getVals(); - - if (values == null || values.length == 0) - return null; - - float negRemain = -entry.getNegativeSum(); - float posRemain = 0f; - - Range[] ranges = new Range[values.length]; - - 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; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java deleted file mode 100644 index 04ee9bec11..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/ChartHighlighter.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.github.mikephil.charting.highlight; - -import java.util.ArrayList; -import java.util.List; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; -import com.github.mikephil.charting.utils.SelectionDetail; -import com.github.mikephil.charting.utils.Utils; - -/** - * Created by Philipp Jahoda on 21/07/15. - */ -public class ChartHighlighter { - - /** instance of the data-provider */ - protected T mChart; - - public ChartHighlighter(T chart) { - this.mChart = chart; - } - - /** - * Returns a Highlight object corresponding to the given x- and y- touch positions in pixels. - * - * @param x - * @param y - * @return - */ - public Highlight getHighlight(float x, float y) { - - int xIndex = getXIndex(x); - if (xIndex == -Integer.MAX_VALUE) - return null; - - int dataSetIndex = getDataSetIndex(xIndex, x, y); - if (dataSetIndex == -Integer.MAX_VALUE) - return null; - - return new Highlight(xIndex, dataSetIndex); - } - - /** - * Returns the corresponding x-index for a given touch-position in pixels. - * - * @param x - * @return - */ - protected int getXIndex(float x) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - - return (int) Math.round(pts[0]); - } - - /** - * Returns the corresponding dataset-index for a given xIndex and xy-touch position in pixels. - * - * @param xIndex - * @param x - * @param y - * @return - */ - protected int getDataSetIndex(int xIndex, float x, float y) { - - List valsAtIndex = getSelectionDetailsAtIndex(xIndex); - - float leftdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.LEFT); - float rightdist = Utils.getMinimumDistance(valsAtIndex, y, YAxis.AxisDependency.RIGHT); - - YAxis.AxisDependency axis = leftdist < rightdist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT; - - int dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, y, axis); - - return dataSetIndex; - } - - /** - * Returns a list of SelectionDetail object corresponding to the given xIndex. - * - * @param xIndex - * @return - */ - protected List getSelectionDetailsAtIndex(int xIndex) { - - List vals = new ArrayList(); - - float[] pts = new float[2]; - - for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { - - IDataSet dataSet = mChart.getData().getDataSetByIndex(i); - - // dont include datasets that cannot be highlighted - if (!dataSet.isHighlightEnabled()) - continue; - - // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - pts[1] = yVal; - - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], i, dataSet)); - } - } - - return vals; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java deleted file mode 100644 index 25ffb1f334..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/CombinedHighlighter.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.github.mikephil.charting.highlight; - -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.CombinedData; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; -import com.github.mikephil.charting.utils.SelectionDetail; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Philipp Jahoda on 12/09/15. - */ -public class CombinedHighlighter extends ChartHighlighter { - - public CombinedHighlighter(BarLineScatterCandleBubbleDataProvider chart) { - super(chart); - } - - /** - * Returns a list of SelectionDetail object corresponding to the given xIndex. - * - * @param xIndex - * @return - */ - @Override - protected List getSelectionDetailsAtIndex(int xIndex) { - - CombinedData data = (CombinedData) mChart.getData(); - - // get all chartdata objects - List dataObjects = data.getAllData(); - - List vals = new ArrayList(); - - float[] pts = new float[2]; - - for (int i = 0; i < dataObjects.size(); i++) { - - for(int j = 0; j < dataObjects.get(i).getDataSetCount(); j++) { - - IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j); - - // dont include datasets that cannot be highlighted - if (!dataSet.isHighlightEnabled()) - continue; - - // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - pts[1] = yVal; - - mChart.getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - - if (!Float.isNaN(pts[1])) { - vals.add(new SelectionDetail(pts[1], j, dataSet)); - } - } - } - - return vals; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java deleted file mode 100644 index 07ae315eb1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/Highlight.java +++ /dev/null @@ -1,122 +0,0 @@ - -package com.github.mikephil.charting.highlight; - -/** - * Contains information needed to determine the highlighted value. - * - * @author Philipp Jahoda - */ -public class Highlight { - - /** the x-index of the highlighted value */ - private int mXIndex; - - /** 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 range of the bar that is selected (only for stacked-barchart) */ - private Range mRange; - - /** - * constructor - * - * @param x the index of the highlighted value on the x-axis - * @param dataSet the index of the DataSet the highlighted value belongs to - */ - public Highlight(int x, int dataSet) { - this.mXIndex = x; - this.mDataSetIndex = dataSet; - } - - /** - * Constructor, only used for stacked-barchart. - * - * @param x the index of the highlighted value on the x-axis - * @param dataSet 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(int x, int dataSet, int stackIndex) { - this(x, dataSet); - mStackIndex = stackIndex; - } - - /** - * Constructor, only used for stacked-barchart. - * - * @param x the index of the highlighted value on the x-axis - * @param dataSet the index of the DataSet the highlighted value belongs to - * @param stackIndex references which value of a stacked-bar entry has been - * selected - * @param range the range the selected stack-value is in - */ - public Highlight(int x, int dataSet, int stackIndex, Range range) { - this(x, dataSet, stackIndex); - this.mRange = range; - } - - /** - * returns the index of the DataSet the highlighted value is in - * - * @return - */ - public int getDataSetIndex() { - return mDataSetIndex; - } - - /** - * returns the index of the highlighted value on the x-axis - * - * @return - */ - public int getXIndex() { - return mXIndex; - } - - /** - * Only needed if a stacked-barchart entry was highlighted. References the - * selected value within the stacked-entry. - * - * @return - */ - public int getStackIndex() { - return mStackIndex; - } - - /** - * Returns the range of values the selected value of a stacked bar is in. (this is only relevant for stacked-barchart) - * @return - */ - public Range getRange() { - return mRange; - } - - /** - * 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.mXIndex == h.mXIndex - && this.mStackIndex == h.mStackIndex) - return true; - else - return false; - } - } - - @Override - public String toString() { - return "Highlight, xIndex: " + mXIndex + ", dataSetIndex: " + mDataSetIndex - + ", stackIndex (only stacked barentry): " + mStackIndex; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java deleted file mode 100644 index 1930409a1b..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.github.mikephil.charting.highlight; - -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; - -/** - * 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) { - - Highlight h = super.getHighlight(x, y); - - if (h == null) - return h; - else { - - IBarDataSet set = mChart.getBarData().getDataSetByIndex(h.getDataSetIndex()); - - if (set.isStacked()) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = y; - - // take any transformer to determine the x-axis value - mChart.getTransformer(set.getAxisDependency()).pixelsToValue(pts); - - return getStackedHighlight(h, set, h.getXIndex(), h.getDataSetIndex(), pts[0]); - } else - return h; - } - } - - @Override - protected int getXIndex(float x) { - - if (!mChart.getBarData().isGrouped()) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = x; - - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - - return (int) Math.round(pts[1]); - } else { - - float baseNoSpace = getBase(x); - - int setCount = mChart.getBarData().getDataSetCount(); - int xIndex = (int) baseNoSpace / setCount; - - int valCount = mChart.getData().getXValCount(); - - if (xIndex < 0) - xIndex = 0; - else if (xIndex >= valCount) - xIndex = valCount - 1; - - return xIndex; - } - } - - /** - * Returns the base y-value to the corresponding x-touch value in pixels. - * - * @param y - * @return - */ - @Override - protected float getBase(float y) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[1] = y; - - // take any transformer to determine the x-axis value - mChart.getTransformer(YAxis.AxisDependency.LEFT).pixelsToValue(pts); - float yVal = pts[1]; - - int setCount = mChart.getBarData().getDataSetCount(); - - // calculate how often the group-space appears - int steps = (int) ((float) yVal / ((float) setCount + mChart.getBarData().getGroupSpace())); - - float groupSpaceSum = mChart.getBarData().getGroupSpace() * (float) steps; - - float baseNoSpace = (float) yVal - groupSpaceSum; - return baseNoSpace; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java deleted file mode 100644 index b8f85922b6..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.mikephil.charting.interfaces.datasets; - -import com.github.mikephil.charting.data.Entry; - -/** - * Created by Philipp Jahoda on 03/11/15. - */ -public interface IPieDataSet extends IDataSet { - - /** - * Returns the space that is set to be between the piechart-slices of this - * DataSet, in pixels. - * - * @return - */ - float getSliceSpace(); - - /** - * Returns the distance a highlighted piechart slice is "shifted" away from - * the chart-center in dp. - * - * @return - */ - float getSelectionShift(); -} - diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java deleted file mode 100644 index 22ea37dc72..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.mikephil.charting.interfaces.datasets; - -import com.github.mikephil.charting.data.Entry; - -/** - * Created by Philipp Jahoda on 03/11/15. - */ -public interface IRadarDataSet extends ILineRadarDataSet { - - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java deleted file mode 100644 index 128fdea36f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.mikephil.charting.jobs; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.view.View; - -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 19/02/16. - */ -public class AnimatedMoveViewJob extends AnimatedViewPortJob { - - public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { - super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - - pts[0] = xOrigin + (xValue - xOrigin) * phase; - pts[1] = yOrigin + (yValue - yOrigin) * phase; - - mTrans.pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, view); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java b/MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java deleted file mode 100644 index e96aba8052..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java +++ /dev/null @@ -1,27 +0,0 @@ - -package com.github.mikephil.charting.jobs; - -import android.view.View; - -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 19/02/16. - */ -public class MoveViewJob extends ViewPortJob { - - public MoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v) { - super(viewPortHandler, xValue, yValue, trans, v); - } - - @Override - public void run() { - - pts[0] = xValue; - pts[1] = yValue; - - mTrans.pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, view); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/com/github/mikephil/charting/jobs/ZoomJob.java deleted file mode 100644 index 108a6f3955..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/ZoomJob.java +++ /dev/null @@ -1,50 +0,0 @@ - -package com.github.mikephil.charting.jobs; - -import android.graphics.Matrix; -import android.view.View; - -import com.github.mikephil.charting.charts.BarLineChartBase; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 19/02/16. - */ -public class ZoomJob extends ViewPortJob { - - protected float scaleX; - protected float scaleY; - - protected YAxis.AxisDependency axisDependency; - - public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, YAxis.AxisDependency axis, View v) { - super(viewPortHandler, xValue, yValue, trans, v); - - this.scaleX = scaleX; - this.scaleY = scaleY; - this.axisDependency = axis; - } - - @Override - public void run() { - - Matrix save = mViewPortHandler.zoom(scaleX, scaleY); - mViewPortHandler.refresh(save, view, false); - - float valsInView = ((BarLineChartBase) view).getDeltaY(axisDependency) / mViewPortHandler.getScaleY(); - float xsInView = ((BarLineChartBase) view).getXAxis().getValues().size() / mViewPortHandler.getScaleX(); - - pts[0] = xValue - xsInView / 2f; - pts[1] = yValue + valsInView / 2f; - - mTrans.pointValuesToPixel(pts); - - save = mViewPortHandler.translate(pts); - mViewPortHandler.refresh(save, view, false); - - ((BarLineChartBase) view).calculateOffsets(); - view.postInvalidate(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java deleted file mode 100644 index 6dac9de21c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java +++ /dev/null @@ -1,120 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; - -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Baseclass of all axis renderers. - * - * @author Philipp Jahoda - */ -public abstract class AxisRenderer extends Renderer { - - protected Transformer mTrans; - - /** paint object for the grid lines */ - protected Paint mGridPaint; - - /** paint for the x-label values */ - protected Paint mAxisLabelPaint; - - /** paint for the line surrounding the chart */ - protected Paint mAxisLinePaint; - - /** paint used for the limit lines */ - protected Paint mLimitLinePaint; - - public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans) { - super(viewPortHandler); - - this.mTrans = trans; - - mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - mGridPaint = new Paint(); - mGridPaint.setColor(Color.GRAY); - mGridPaint.setStrokeWidth(1f); - mGridPaint.setStyle(Style.STROKE); - mGridPaint.setAlpha(90); - - mAxisLinePaint = new Paint(); - mAxisLinePaint.setColor(Color.BLACK); - mAxisLinePaint.setStrokeWidth(1f); - mAxisLinePaint.setStyle(Style.STROKE); - - mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLimitLinePaint.setStyle(Paint.Style.STROKE); - } - - /** - * Returns the Paint object used for drawing the axis (labels). - * - * @return - */ - public Paint getPaintAxisLabels() { - return mAxisLabelPaint; - } - - /** - * Returns the Paint object that is used for drawing the grid-lines of the - * axis. - * - * @return - */ - public Paint getPaintGrid() { - return mGridPaint; - } - - /** - * Returns the Paint object that is used for drawing the axis-line that goes - * alongside the axis. - * - * @return - */ - public Paint getPaintAxisLine() { - return mAxisLinePaint; - } - - /** - * Returns the Transformer object used for transforming the axis values. - * - * @return - */ - public Transformer getTransformer() { - return mTrans; - } - - /** - * Draws the axis labels to the screen. - * - * @param c - */ - public abstract void renderAxisLabels(Canvas c); - - /** - * Draws the grid lines belonging to the axis. - * - * @param c - */ - public abstract void renderGridLines(Canvas c); - - /** - * Draws the line that goes alongside the axis. - * - * @param c - */ - public abstract void renderAxisLine(Canvas c); - - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - public abstract void renderLimitLines(Canvas c); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java deleted file mode 100644 index 763fb87986..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ /dev/null @@ -1,399 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.RectF; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.BarBuffer; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarEntry; -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.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class BarChartRenderer extends DataRenderer { - - protected BarDataProvider mChart; - - /** the rect object that is used for drawing the bars */ - protected RectF mBarRect = new RectF(); - - protected BarBuffer[] mBarBuffers; - - protected Paint mShadowPaint; - - public BarChartRenderer(BarDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - this.mChart = chart; - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setStyle(Paint.Style.FILL); - mHighlightPaint.setColor(Color.rgb(0, 0, 0)); - // set alpha after color - mHighlightPaint.setAlpha(120); - - mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mShadowPaint.setStyle(Paint.Style.FILL); - } - - @Override - public void initBuffers() { - - BarData barData = mChart.getBarData(); - mBarBuffers = new BarBuffer[barData.getDataSetCount()]; - - for (int i = 0; i < mBarBuffers.length; i++) { - IBarDataSet set = barData.getDataSetByIndex(i); - mBarBuffers[i] = new BarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), - barData.getGroupSpace(), - barData.getDataSetCount(), set.isStacked()); - } - } - - @Override - public void drawData(Canvas c) { - - BarData barData = mChart.getBarData(); - - for (int i = 0; i < barData.getDataSetCount(); i++) { - - IBarDataSet set = barData.getDataSetByIndex(i); - - if (set.isVisible() && set.getEntryCount() > 0) { - drawDataSet(c, set, i); - } - } - } - - protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - mShadowPaint.setColor(dataSet.getBarShadowColor()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - // initialize the buffer - BarBuffer buffer = mBarBuffers[index]; - buffer.setPhases(phaseX, phaseY); - buffer.setBarSpace(dataSet.getBarSpace()); - buffer.setDataSet(index); - buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); - - buffer.feed(dataSet); - - trans.pointValuesToPixel(buffer.buffer); - - // if multiple colors - if (dataSet.getColors().size() > 1) { - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) - continue; - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - - // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j / 4)); - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } else { - - mRenderPaint.setColor(dataSet.getColor()); - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) - continue; - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; - - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(buffer.buffer[j], mViewPortHandler.contentTop(), - buffer.buffer[j + 2], - mViewPortHandler.contentBottom(), mShadowPaint); - } - - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } - } - - /** - * Prepares a bar for being highlighted. - * - * @param x the x-position - * @param y1 the y1-position - * @param y2 the y2-position - * @param barspaceHalf the space between bars - * @param trans - */ - protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, - Transformer trans) { - - float barWidth = 0.5f; - - float left = x - barWidth + barspaceHalf; - float right = x + barWidth - barspaceHalf; - float top = y1; - float bottom = y2; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixel(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public void drawValues(Canvas c) { - // if values are drawn - if (passesCheck()) { - - List dataSets = mChart.getBarData().getDataSets(); - - final float valueOffsetPlus = Utils.convertDpToPixel(4.5f); - float posOffset = 0f; - float negOffset = 0f; - boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - IBarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); - - // calculate the correct offset depending on the draw position of - // the value - float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8"); - posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus); - negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus); - - if (isInverted) { - posOffset = -posOffset - valueTextHeight; - negOffset = -negOffset - valueTextHeight; - } - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float[] valuePoints = getTransformedValues(trans, dataSet, i); - - // if only single values are drawn (sum) - if (!dataSet.isStacked()) { - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) - break; - - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) - continue; - - BarEntry entry = dataSet.getEntryForIndex(j / 2); - float val = entry.getVal(); - - drawValue(c, dataSet.getValueFormatter(), val, entry, i, valuePoints[j], - valuePoints[j + 1] + (val >= 0 ? posOffset : negOffset), dataSet.getValueTextColor(j / 2)); - } - - // if we have stacks - } else { - - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { - - BarEntry entry = dataSet.getEntryForIndex(j / 2); - - float[] vals = entry.getVals(); - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - - if (!mViewPortHandler.isInBoundsRight(valuePoints[j])) - break; - - if (!mViewPortHandler.isInBoundsY(valuePoints[j + 1]) - || !mViewPortHandler.isInBoundsLeft(valuePoints[j])) - continue; - - drawValue(c, dataSet.getValueFormatter(), entry.getVal(), entry, i, valuePoints[j], - valuePoints[j + 1] + (entry.getVal() >= 0 ? posOffset : negOffset), dataSet.getValueTextColor(j / 2)); - - // draw stack values - } else { - - int color = dataSet.getValueTextColor(j / 2); - - float[] transformed = new float[vals.length * 2]; - - float posY = 0f; - float negY = -entry.getNegativeSum(); - - for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { - - float value = vals[idx]; - float y; - - if (value >= 0f) { - posY += value; - y = posY; - } else { - y = negY; - negY -= value; - } - - transformed[k + 1] = y * mAnimator.getPhaseY(); - } - - trans.pointValuesToPixel(transformed); - - for (int k = 0; k < transformed.length; k += 2) { - - float x = valuePoints[j]; - float y = transformed[k + 1] - + (vals[k / 2] >= 0 ? posOffset : negOffset); - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - if (!mViewPortHandler.isInBoundsY(y) - || !mViewPortHandler.isInBoundsLeft(x)) - continue; - - drawValue(c, dataSet.getValueFormatter(), vals[k / 2], entry, i, x, y, color); - } - } - } - } - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - int setCount = mChart.getBarData().getDataSetCount(); - - for (int i = 0; i < indices.length; i++) { - - Highlight h = indices[i]; - int index = h.getXIndex(); - - int dataSetIndex = h.getDataSetIndex(); - IBarDataSet set = mChart.getBarData().getDataSetByIndex(dataSetIndex); - - if (set == null || !set.isHighlightEnabled()) - continue; - - float barspaceHalf = set.getBarSpace() / 2f; - - Transformer trans = mChart.getTransformer(set.getAxisDependency()); - - mHighlightPaint.setColor(set.getHighLightColor()); - mHighlightPaint.setAlpha(set.getHighLightAlpha()); - - // check outofbounds - if (index >= 0 - && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - - BarEntry e = set.getEntryForXIndex(index); - - if (e == null || e.getXIndex() != index) - continue; - - float groupspace = mChart.getBarData().getGroupSpace(); - boolean isStack = h.getStackIndex() < 0 ? false : true; - - // calculate the correct x-position - float x = index * setCount + dataSetIndex + groupspace / 2f - + groupspace * index; - - final float y1; - final float y2; - - if (isStack) { - y1 = h.getRange().from; - y2 = h.getRange().to; - } else { - y1 = e.getVal(); - y2 = 0.f; - } - - prepareBarHighlight(x, y1, y2, barspaceHalf, trans); - - c.drawRect(mBarRect, mHighlightPaint); - - if (mChart.isDrawHighlightArrowEnabled()) { - - mHighlightPaint.setAlpha(255); - - // distance between highlight arrow and bar - float offsetY = mAnimator.getPhaseY() * 0.07f; - - float[] values = new float[9]; - trans.getPixelToValueMatrix().getValues(values); - final float xToYRel = Math.abs(values[Matrix.MSCALE_Y] / values[Matrix.MSCALE_X]); - - final float arrowWidth = set.getBarSpace() / 2.f; - final float arrowHeight = arrowWidth * xToYRel; - - final float yArrow = (y1 > -y2 ? y1 : y1) * mAnimator.getPhaseY(); - - Path arrow = new Path(); - arrow.moveTo(x + 0.4f, yArrow + offsetY); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY - arrowHeight); - arrow.lineTo(x + 0.4f + arrowWidth, yArrow + offsetY + arrowHeight); - - trans.pathValueToPixel(arrow); - c.drawPath(arrow, mHighlightPaint); - } - } - } - } - - public float[] getTransformedValues(Transformer trans, IBarDataSet data, - int dataSetIndex) { - return trans.generateTransformedValuesBarChart(data, dataSetIndex, - mChart.getBarData(), - mAnimator.getPhaseY()); - } - - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX(); - } - - @Override - public void drawExtras(Canvas c) { } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java deleted file mode 100644 index facccc945c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ /dev/null @@ -1,289 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint.Align; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.BarBuffer; -import com.github.mikephil.charting.buffer.HorizontalBarBuffer; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.formatter.ValueFormatter; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -/** - * Renderer for the HorizontalBarChart. - * - * @author Philipp Jahoda - */ -public class HorizontalBarChartRenderer extends BarChartRenderer { - - public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(chart, animator, viewPortHandler); - - mValuePaint.setTextAlign(Align.LEFT); - } - - @Override - public void initBuffers() { - - BarData barData = mChart.getBarData(); - mBarBuffers = new HorizontalBarBuffer[barData.getDataSetCount()]; - - for (int i = 0; i < mBarBuffers.length; i++) { - IBarDataSet set = barData.getDataSetByIndex(i); - mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), - barData.getGroupSpace(), - barData.getDataSetCount(), set.isStacked()); - } - } - - @Override - protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - mShadowPaint.setColor(dataSet.getBarShadowColor()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - // initialize the buffer - BarBuffer buffer = mBarBuffers[index]; - buffer.setPhases(phaseX, phaseY); - buffer.setBarSpace(dataSet.getBarSpace()); - buffer.setDataSet(index); - buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); - - buffer.feed(dataSet); - - trans.pointValuesToPixel(buffer.buffer); - - for (int j = 0; j < buffer.size(); j += 4) { - - if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) - break; - - if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) - continue; - - if (mChart.isDrawBarShadowEnabled()) { - c.drawRect(mViewPortHandler.contentLeft(), buffer.buffer[j + 1], - mViewPortHandler.contentRight(), - buffer.buffer[j + 3], mShadowPaint); - } - - // Set the color for the currently drawn value. If the index - // is - // out of bounds, reuse colors. - mRenderPaint.setColor(dataSet.getColor(j / 4)); - c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], - buffer.buffer[j + 3], mRenderPaint); - } - } - - @Override - public void drawValues(Canvas c) { - // if values are drawn - if (passesCheck()) { - - List dataSets = mChart.getBarData().getDataSets(); - - final float valueOffsetPlus = Utils.convertDpToPixel(5f); - float posOffset = 0f; - float negOffset = 0f; - final boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - IBarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) - continue; - - boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; - - ValueFormatter formatter = dataSet.getValueFormatter(); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float[] valuePoints = getTransformedValues(trans, dataSet, i); - - // if only single values are drawn (sum) - if (!dataSet.isStacked()) { - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - BarEntry e = dataSet.getEntryForIndex(j / 2); - float val = e.getVal(); - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); - posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); - - if (isInverted) { - posOffset = -posOffset - valueTextWidth; - negOffset = -negOffset - valueTextWidth; - } - - drawValue(c, formattedValue, valuePoints[j] + (val >= 0 ? posOffset : negOffset), - valuePoints[j + 1] + halfTextHeight, dataSet.getValueTextColor(j / 2)); - } - - // if each value of a potential stack should be drawn - } else { - - for (int j = 0; j < (valuePoints.length - 1) * mAnimator.getPhaseX(); j += 2) { - - BarEntry e = dataSet.getEntryForIndex(j / 2); - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - float val = e.getVal(); - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); - posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); - - if (isInverted) { - posOffset = -posOffset - valueTextWidth; - negOffset = -negOffset - valueTextWidth; - } - - drawValue(c, formattedValue, valuePoints[j] - + (e.getVal() >= 0 ? posOffset : negOffset), - valuePoints[j + 1] + halfTextHeight, dataSet.getValueTextColor(j / 2)); - - } else { - - float[] transformed = new float[vals.length * 2]; - - float posY = 0f; - float negY = -e.getNegativeSum(); - - for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { - - float value = vals[idx]; - float y; - - if (value >= 0f) { - posY += value; - y = posY; - } else { - y = negY; - negY -= value; - } - - transformed[k] = y * mAnimator.getPhaseY(); - } - - trans.pointValuesToPixel(transformed); - - for (int k = 0; k < transformed.length; k += 2) { - - float val = vals[k / 2]; - String formattedValue = formatter.getFormattedValue(val, e, i, mViewPortHandler); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); - posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); - - if (isInverted) { - posOffset = -posOffset - valueTextWidth; - negOffset = -negOffset - valueTextWidth; - } - - float x = transformed[k] - + (val >= 0 ? posOffset : negOffset); - float y = valuePoints[j + 1]; - - if (!mViewPortHandler.isInBoundsTop(y)) - break; - - if (!mViewPortHandler.isInBoundsX(x)) - continue; - - if (!mViewPortHandler.isInBoundsBottom(y)) - continue; - - drawValue(c, formattedValue, x, y + halfTextHeight, dataSet.getValueTextColor(j / 2)); - } - } - } - } - } - } - } - - protected void drawValue(Canvas c, String valueText, float x, float y, int color) { - mValuePaint.setColor(color); - c.drawText(valueText, x, y, mValuePaint); - } - - @Override - protected void prepareBarHighlight(float x, float y1, float y2, float barspaceHalf, - Transformer trans) { - - float top = x - 0.5f + barspaceHalf; - float bottom = x + 0.5f - barspaceHalf; - float left = y1; - float right = y2; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixelHorizontal(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public float[] getTransformedValues(Transformer trans, IBarDataSet data, - int dataSetIndex) { - return trans.generateTransformedValuesHorizontalBarChart(data, dataSetIndex, - mChart.getBarData(), mAnimator.getPhaseY()); - } - - @Override - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleY(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java deleted file mode 100644 index a9ac981c35..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ /dev/null @@ -1,437 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Typeface; - -import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; -import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; -import com.github.mikephil.charting.interfaces.datasets.IDataSet; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; -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.Collections; -import java.util.List; - -public class LegendRenderer extends Renderer { - - /** - * paint for the legend labels - */ - protected Paint mLegendLabelPaint; - - /** - * paint used for the legend forms - */ - protected Paint mLegendFormPaint; - - /** - * the legend object this renderer renders - */ - protected Legend mLegend; - - public LegendRenderer(ViewPortHandler viewPortHandler, Legend legend) { - super(viewPortHandler); - - this.mLegend = legend; - - mLegendLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLegendLabelPaint.setTextSize(Utils.convertDpToPixel(9f)); - mLegendLabelPaint.setTextAlign(Align.LEFT); - - mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLegendFormPaint.setStyle(Paint.Style.FILL); - mLegendFormPaint.setStrokeWidth(3f); - } - - /** - * Returns the Paint object used for drawing the Legend labels. - * - * @return - */ - public Paint getLabelPaint() { - return mLegendLabelPaint; - } - - /** - * Returns the Paint object used for drawing the Legend forms. - * - * @return - */ - public Paint getFormPaint() { - return mLegendFormPaint; - } - - /** - * Prepares the legend and calculates all needed forms, labels and colors. - * - * @param data - */ - public void computeLegend(ChartData data) { - - if (!mLegend.isLegendCustom()) { - - List labels = new ArrayList(); - List colors = new ArrayList(); - - // loop for building up the colors and labels used in the legend - for (int i = 0; i < data.getDataSetCount(); i++) { - - IDataSet dataSet = data.getDataSetByIndex(i); - - List clrs = dataSet.getColors(); - int entryCount = dataSet.getEntryCount(); - - // if we have a barchart with stacked bars - if (dataSet instanceof IBarDataSet && ((IBarDataSet) dataSet).isStacked()) { - - IBarDataSet bds = (IBarDataSet) dataSet; - String[] sLabels = bds.getStackLabels(); - - for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) { - - labels.add(sLabels[j % sLabels.length]); - colors.add(clrs.get(j)); - } - - if (bds.getLabel() != null) { - // add the legend description label - colors.add(ColorTemplate.COLOR_SKIP); - labels.add(bds.getLabel()); - } - - } else if (dataSet instanceof IPieDataSet) { - - List xVals = data.getXVals(); - IPieDataSet pds = (IPieDataSet) dataSet; - - for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) { - - labels.add(xVals.get(j)); - colors.add(clrs.get(j)); - } - - if (pds.getLabel() != null) { - // add the legend description label - colors.add(ColorTemplate.COLOR_SKIP); - labels.add(pds.getLabel()); - } - - } else if(dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != ColorTemplate.COLOR_NONE) { - - colors.add(((ICandleDataSet) dataSet).getDecreasingColor()); - colors.add(((ICandleDataSet) dataSet).getIncreasingColor()); - labels.add(null); - labels.add(dataSet.getLabel()); - - } else { // all others - - for (int j = 0; j < clrs.size() && j < entryCount; j++) { - - // if multiple colors are set for a DataSet, group them - if (j < clrs.size() - 1 && j < entryCount - 1) { - - labels.add(null); - } else { // add label to the last entry - - String label = data.getDataSetByIndex(i).getLabel(); - labels.add(label); - } - - colors.add(clrs.get(j)); - } - } - } - - if (mLegend.getExtraColors() != null && mLegend.getExtraLabels() != null) { - for (int color : mLegend.getExtraColors()) - colors.add(color); - Collections.addAll(labels, mLegend.getExtraLabels()); - } - - mLegend.setComputedColors(colors); - mLegend.setComputedLabels(labels); - } - - Typeface tf = mLegend.getTypeface(); - - if (tf != null) - mLegendLabelPaint.setTypeface(tf); - - mLegendLabelPaint.setTextSize(mLegend.getTextSize()); - mLegendLabelPaint.setColor(mLegend.getTextColor()); - - // calculate all dimensions of the mLegend - mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler); - } - - public void renderLegend(Canvas c) { - - if (!mLegend.isEnabled()) - return; - - Typeface tf = mLegend.getTypeface(); - - if (tf != null) - mLegendLabelPaint.setTypeface(tf); - - mLegendLabelPaint.setTextSize(mLegend.getTextSize()); - mLegendLabelPaint.setColor(mLegend.getTextColor()); - - float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint); - float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint) + mLegend.getYEntrySpace(); - float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; - - String[] labels = mLegend.getLabels(); - int[] colors = mLegend.getColors(); - - float formToTextSpace = mLegend.getFormToTextSpace(); - float xEntrySpace = mLegend.getXEntrySpace(); - Legend.LegendDirection direction = mLegend.getDirection(); - float formSize = mLegend.getFormSize(); - - // space between the entries - float stackSpace = mLegend.getStackSpace(); - - float posX, posY; - - float yoffset = mLegend.getYOffset(); - float xoffset = mLegend.getXOffset(); - - Legend.LegendPosition legendPosition = mLegend.getPosition(); - - switch (legendPosition) { - case BELOW_CHART_LEFT: - case BELOW_CHART_RIGHT: - case BELOW_CHART_CENTER: - case ABOVE_CHART_LEFT: - case ABOVE_CHART_RIGHT: - case ABOVE_CHART_CENTER: { - float contentWidth = mViewPortHandler.contentWidth(); - - float originPosX; - - if (legendPosition == Legend.LegendPosition.BELOW_CHART_LEFT - || legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT) { - originPosX = mViewPortHandler.contentLeft() + xoffset; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - originPosX += mLegend.mNeededWidth; - } else if (legendPosition == Legend.LegendPosition.BELOW_CHART_RIGHT - || legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT) { - originPosX = mViewPortHandler.contentRight() - xoffset; - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - originPosX -= mLegend.mNeededWidth; - } else // BELOW_CHART_CENTER || ABOVE_CHART_CENTER - originPosX = mViewPortHandler.contentLeft() + contentWidth / 2.f; - - FSize[] calculatedLineSizes = mLegend.getCalculatedLineSizes(); - FSize[] calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); - Boolean[] calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); - - posX = originPosX; - - if (legendPosition == Legend.LegendPosition.ABOVE_CHART_LEFT || - legendPosition == Legend.LegendPosition.ABOVE_CHART_RIGHT || - legendPosition == Legend.LegendPosition.ABOVE_CHART_CENTER) { - posY = 0.f; - } else { - posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; - } - - int lineIndex = 0; - - for (int i = 0, count = labels.length; i < count; i++) { - if (i < calculatedLabelBreakPoints.length && calculatedLabelBreakPoints[i]) { - posX = originPosX; - posY += labelLineHeight + labelLineSpacing; - } - - if (posX == originPosX && legendPosition == Legend.LegendPosition.BELOW_CHART_CENTER && lineIndex < calculatedLineSizes.length) { - posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT ? calculatedLineSizes[lineIndex].width : -calculatedLineSizes[lineIndex].width) / 2.f; - lineIndex++; - } - - boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; - boolean isStacked = labels[i] == null; // grouped forms have null labels - - if (drawingForm) { - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= formSize; - - drawForm(c, posX, posY + formYOffset, i, mLegend); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += formSize; - } - - if (!isStacked) { - if (drawingForm) - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : formToTextSpace; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= calculatedLabelSizes[i].width; - - drawLabel(c, posX, posY + labelLineHeight, labels[i]); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += calculatedLabelSizes[i].width; - - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -xEntrySpace : xEntrySpace; - } else - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; - } - - } - break; - - case PIECHART_CENTER: - case RIGHT_OF_CHART: - case RIGHT_OF_CHART_CENTER: - case RIGHT_OF_CHART_INSIDE: - case LEFT_OF_CHART: - case LEFT_OF_CHART_CENTER: - case LEFT_OF_CHART_INSIDE: { - // contains the stacked legend size in pixels - float stack = 0f; - boolean wasStacked = false; - - if (legendPosition == Legend.LegendPosition.PIECHART_CENTER) { - posX = mViewPortHandler.getChartWidth() / 2f - + (direction == Legend.LegendDirection.LEFT_TO_RIGHT ? -mLegend.mTextWidthMax / 2f - : mLegend.mTextWidthMax / 2f); - posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f - + mLegend.getYOffset(); - } else { - boolean isRightAligned = legendPosition == Legend.LegendPosition.RIGHT_OF_CHART - || - legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_INSIDE; - - if (isRightAligned) { - posX = mViewPortHandler.getChartWidth() - xoffset; - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX -= mLegend.mTextWidthMax; - } else { - posX = xoffset; - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX += mLegend.mTextWidthMax; - } - - if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART || - legendPosition == Legend.LegendPosition.LEFT_OF_CHART) { - posY = mViewPortHandler.contentTop() + yoffset; - } else if (legendPosition == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - legendPosition == Legend.LegendPosition.LEFT_OF_CHART_CENTER) { - posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f; - } else /* - * if (legendPosition == - * Legend.LegendPosition.RIGHT_OF_CHART_INSIDE || - * legendPosition == - * Legend.LegendPosition.LEFT_OF_CHART_INSIDE) - */ { - posY = mViewPortHandler.contentTop() + yoffset; - } - } - - for (int i = 0; i < labels.length; i++) { - - Boolean drawingForm = colors[i] != ColorTemplate.COLOR_SKIP; - float x = posX; - - if (drawingForm) { - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += stack; - else - x -= formSize - stack; - - drawForm(c, x, posY + formYOffset, i, mLegend); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += formSize; - } - - if (labels[i] != null) { - - if (drawingForm && !wasStacked) - x += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace - : -formToTextSpace; - else if (wasStacked) - x = posX; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - x -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - - if (!wasStacked) { - drawLabel(c, x, posY + labelLineHeight, labels[i]); - } else { - posY += labelLineHeight + labelLineSpacing; - drawLabel(c, x, posY + labelLineHeight, labels[i]); - } - - // make a step down - posY += labelLineHeight + labelLineSpacing; - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - } - break; - } - } - - /** - * Draws the Legend-form at the given position with the color at the given - * index. - * - * @param c canvas to draw with - * @param x position - * @param y position - * @param index the index of the color to use (in the colors array) - */ - protected void drawForm(Canvas c, float x, float y, int index, Legend legend) { - - if (legend.getColors()[index] == ColorTemplate.COLOR_SKIP) - return; - - mLegendFormPaint.setColor(legend.getColors()[index]); - - float formsize = legend.getFormSize(); - float half = formsize / 2f; - - switch (legend.getForm()) { - case CIRCLE: - c.drawCircle(x + half, y, half, mLegendFormPaint); - break; - case SQUARE: - c.drawRect(x, y - half, x + formsize, y + half, mLegendFormPaint); - break; - case LINE: - c.drawLine(x, y, x + formsize, y, mLegendFormPaint); - break; - } - } - - /** - * Draws the provided label at the given position. - * - * @param c canvas to draw with - * @param x - * @param y - * @param label the label to draw - */ - protected void drawLabel(Canvas c, float x, float y, String label) { - c.drawText(label, x, y, mLegendLabelPaint); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java deleted file mode 100644 index ec266ec789..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ /dev/null @@ -1,619 +0,0 @@ -package com.github.mikephil.charting.renderer; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.CircleBuffer; -import com.github.mikephil.charting.buffer.LineBuffer; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; -import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.lang.ref.WeakReference; -import java.util.List; - -public class LineChartRenderer extends LineRadarRenderer { - - protected LineDataProvider mChart; - - /** - * paint for the inner circle of the value indicators - */ - protected Paint mCirclePaintInner; - - /** - * Bitmap object used for drawing the paths (otherwise they are too long if - * rendered directly on the canvas) - */ - protected WeakReference mDrawBitmap; - - /** - * on this canvas, the paths are rendered, it is initialized with the - * pathBitmap - */ - protected Canvas mBitmapCanvas; - - /** - * the bitmap configuration to be used - */ - protected Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; - - protected Path cubicPath = new Path(); - protected Path cubicFillPath = new Path(); - - protected LineBuffer[] mLineBuffers; - - protected CircleBuffer[] mCircleBuffers; - - public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG); - mCirclePaintInner.setStyle(Paint.Style.FILL); - mCirclePaintInner.setColor(Color.WHITE); - } - - @Override - public void initBuffers() { - - LineData lineData = mChart.getLineData(); - mLineBuffers = new LineBuffer[lineData.getDataSetCount()]; - mCircleBuffers = new CircleBuffer[lineData.getDataSetCount()]; - - for (int i = 0; i < mLineBuffers.length; i++) { - ILineDataSet set = lineData.getDataSetByIndex(i); - mLineBuffers[i] = new LineBuffer(set.getEntryCount() * 4 - 4); - mCircleBuffers[i] = new CircleBuffer(set.getEntryCount() * 2); - } - } - - @Override - public void drawData(Canvas c) { - - int width = (int) mViewPortHandler.getChartWidth(); - int height = (int) mViewPortHandler.getChartHeight(); - - if (mDrawBitmap == null - || (mDrawBitmap.get().getWidth() != width) - || (mDrawBitmap.get().getHeight() != height)) { - - if (width > 0 && height > 0) { - - mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, mBitmapConfig)); - mBitmapCanvas = new Canvas(mDrawBitmap.get()); - } else - return; - } - - mDrawBitmap.get().eraseColor(Color.TRANSPARENT); - - LineData lineData = mChart.getLineData(); - - for (ILineDataSet set : lineData.getDataSets()) { - - if (set.isVisible() && set.getEntryCount() > 0) - drawDataSet(c, set); - } - - c.drawBitmap(mDrawBitmap.get(), 0, 0, mRenderPaint); - } - - protected void drawDataSet(Canvas c, ILineDataSet dataSet) { - - if (dataSet.getEntryCount() < 1) - return; - - mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); - mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); - - // if drawing cubic lines is enabled - if (dataSet.isDrawCubicEnabled()) { - - drawCubic(c, dataSet); - - // draw normal (straight) lines - } else { - drawLinear(c, dataSet); - } - - mRenderPaint.setPathEffect(null); - } - - /** - * Draws a cubic line. - * - * @param c - * @param dataSet - */ - protected void drawCubic(Canvas c, ILineDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - int entryCount = dataSet.getEntryCount(); - - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - - int diff = (entryFrom == entryTo) ? 1 : 0; - int minx = Math.max(dataSet.getEntryIndex(entryFrom) - diff - 1, 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float intensity = dataSet.getCubicIntensity(); - - cubicPath.reset(); - - int size = (int) Math.ceil((maxx - minx) * phaseX + minx); - - if (size - minx >= 2) { - - float prevDx = 0f; - float prevDy = 0f; - float curDx = 0f; - float curDy = 0f; - - Entry prevPrev = dataSet.getEntryForIndex(minx); - Entry prev = prevPrev; - Entry cur = prev; - Entry next = dataSet.getEntryForIndex(minx + 1); - - // let the spline start - cubicPath.moveTo(cur.getXIndex(), cur.getVal() * phaseY); - - prevDx = (cur.getXIndex() - prev.getXIndex()) * intensity; - prevDy = (cur.getVal() - prev.getVal()) * intensity; - - curDx = (next.getXIndex() - cur.getXIndex()) * intensity; - curDy = (next.getVal() - cur.getVal()) * intensity; - - // the first cubic - cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, - cur.getXIndex() - curDx, - (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); - - for (int j = minx + 1, count = Math.min(size, entryCount - 1); j < count; j++) { - - prevPrev = dataSet.getEntryForIndex(j == 1 ? 0 : j - 2); - prev = dataSet.getEntryForIndex(j - 1); - cur = dataSet.getEntryForIndex(j); - next = dataSet.getEntryForIndex(j + 1); - - prevDx = (cur.getXIndex() - prevPrev.getXIndex()) * intensity; - prevDy = (cur.getVal() - prevPrev.getVal()) * intensity; - curDx = (next.getXIndex() - prev.getXIndex()) * intensity; - curDy = (next.getVal() - prev.getVal()) * intensity; - - cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, - cur.getXIndex() - curDx, - (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); - } - - if (size > entryCount - 1) { - - prevPrev = dataSet.getEntryForIndex((entryCount >= 3) ? entryCount - 3 - : entryCount - 2); - prev = dataSet.getEntryForIndex(entryCount - 2); - cur = dataSet.getEntryForIndex(entryCount - 1); - next = cur; - - prevDx = (cur.getXIndex() - prevPrev.getXIndex()) * intensity; - prevDy = (cur.getVal() - prevPrev.getVal()) * intensity; - curDx = (next.getXIndex() - prev.getXIndex()) * intensity; - curDy = (next.getVal() - prev.getVal()) * intensity; - - // the last cubic - cubicPath.cubicTo(prev.getXIndex() + prevDx, (prev.getVal() + prevDy) * phaseY, - cur.getXIndex() - curDx, - (cur.getVal() - curDy) * phaseY, cur.getXIndex(), cur.getVal() * phaseY); - } - } - - // if filled is enabled, close the path - if (dataSet.isDrawFilledEnabled()) { - - cubicFillPath.reset(); - cubicFillPath.addPath(cubicPath); - // create a new path, this is bad for performance - drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, - entryFrom.getXIndex(), entryFrom.getXIndex() + size); - } - - mRenderPaint.setColor(dataSet.getColor()); - - mRenderPaint.setStyle(Paint.Style.STROKE); - - trans.pathValueToPixel(cubicPath); - - mBitmapCanvas.drawPath(cubicPath, mRenderPaint); - - mRenderPaint.setPathEffect(null); - } - - protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transformer trans, - int from, int to) { - - if (to - from <= 1) - return; - - float fillMin = dataSet.getFillFormatter() - .getFillLinePosition(dataSet, mChart); - - // Take the from/to xIndex from the entries themselves, - // so missing entries won't screw up the filling. - // What we need to draw is line from points of the xIndexes - not arbitrary entry indexes! - - final Entry toEntry = dataSet.getEntryForIndex(to - 1); - final Entry fromEntry = dataSet.getEntryForIndex(from); - final float xTo = toEntry == null ? 0 : toEntry.getXIndex(); - final float xFrom = fromEntry == null ? 0 : fromEntry.getXIndex(); - - spline.lineTo(xTo, fillMin); - spline.lineTo(xFrom, fillMin); - spline.close(); - - trans.pathValueToPixel(spline); - - final Drawable drawable = dataSet.getFillDrawable(); - if (drawable != null) { - - drawFilledPath(c, spline, drawable); - } else { - - drawFilledPath(c, spline, dataSet.getFillColor(), dataSet.getFillAlpha()); - } - } - - /** - * Draws a normal line. - * - * @param c - * @param dataSet - */ - protected void drawLinear(Canvas c, ILineDataSet dataSet) { - - int entryCount = dataSet.getEntryCount(); - - int dataSetIndex = mChart.getLineData().getIndexOfDataSet(dataSet); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - mRenderPaint.setStyle(Paint.Style.STROKE); - - Canvas canvas = null; - - // if the data-set is dashed, draw on bitmap-canvas - if (dataSet.isDashedLineEnabled()) { - canvas = mBitmapCanvas; - } else { - canvas = c; - } - - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); - - int range = (maxx - minx) * 4 - 4; - - LineBuffer buffer = mLineBuffers[dataSetIndex]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(dataSet); - - trans.pointValuesToPixel(buffer.buffer); - - // more than 1 color - if (dataSet.getColors().size() > 1) { - - for (int j = 0; j < range; j += 4) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) - break; - - // make sure the lines don't do shitty things outside - // bounds - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2]) - || (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]) && !mViewPortHandler - .isInBoundsBottom(buffer.buffer[j + 3])) - || (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1]) && !mViewPortHandler - .isInBoundsBottom(buffer.buffer[j + 3]))) - continue; - - // get the color that is set for this line-segment - mRenderPaint.setColor(dataSet.getColor(j / 4 + minx)); - - canvas.drawLine(buffer.buffer[j], buffer.buffer[j + 1], - buffer.buffer[j + 2], buffer.buffer[j + 3], mRenderPaint); - } - - } else { // only one color per dataset - - mRenderPaint.setColor(dataSet.getColor()); - - // c.drawLines(buffer.buffer, mRenderPaint); - canvas.drawLines(buffer.buffer, 0, range, - mRenderPaint); - } - - mRenderPaint.setPathEffect(null); - - // if drawing filled is enabled - if (dataSet.isDrawFilledEnabled() && entryCount > 0) { - drawLinearFill(c, dataSet, minx, maxx, trans); - } - } - - protected void drawLinearFill(Canvas c, ILineDataSet dataSet, int minx, - int maxx, - Transformer trans) { - - Path filled = generateFilledPath( - dataSet, minx, maxx); - - trans.pathValueToPixel(filled); - - final Drawable drawable = dataSet.getFillDrawable(); - if (drawable != null) { - - drawFilledPath(c, filled, drawable); - } else { - - drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); - } - } - - /** - * Generates the path that is used for filled drawing. - * - * @param dataSet - * @return - */ - private Path generateFilledPath(ILineDataSet dataSet, int from, int to) { - - float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - Path filled = new Path(); - Entry entry = dataSet.getEntryForIndex(from); - - filled.moveTo(entry.getXIndex(), fillMin); - filled.lineTo(entry.getXIndex(), entry.getVal() * phaseY); - - // create a new path - for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) { - - Entry e = dataSet.getEntryForIndex(x); - filled.lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - // close up - filled.lineTo( - dataSet.getEntryForIndex( - Math.max( - Math.min((int) Math.ceil((to - from) * phaseX + from) - 1, - dataSet.getEntryCount() - 1), 0)).getXIndex(), fillMin); - - filled.close(); - - return filled; - } - - @Override - public void drawValues(Canvas c) { - - if (mChart.getLineData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - List dataSets = mChart.getLineData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - - ILineDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - // make sure the values do not interfear with the circles - int valOffset = (int) (dataSet.getCircleRadius() * 1.75f); - - if (!dataSet.isDrawCirclesEnabled()) - valOffset = valOffset / 2; - - int entryCount = dataSet.getEntryCount(); - - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); - - float[] positions = trans.generateTransformedValuesLine( - dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); - - for (int j = 0; j < positions.length; j += 2) { - - float x = positions[j]; - float y = positions[j + 1]; - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) - continue; - - Entry entry = dataSet.getEntryForIndex(j / 2 + minx); - - drawValue(c, dataSet.getValueFormatter(), entry.getVal(), entry, i, x, - y - valOffset, dataSet.getValueTextColor(j / 2)); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - drawCircles(c); - } - - protected void drawCircles(Canvas c) { - - mRenderPaint.setStyle(Paint.Style.FILL); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - List dataSets = mChart.getLineData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - - ILineDataSet dataSet = dataSets.get(i); - - if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || - dataSet.getEntryCount() == 0) - continue; - - mCirclePaintInner.setColor(dataSet.getCircleHoleColor()); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - int entryCount = dataSet.getEntryCount(); - - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX, DataSet.Rounding.DOWN); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX, DataSet.Rounding.UP); - - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, entryCount); - - CircleBuffer buffer = mCircleBuffers[i]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(dataSet); - - trans.pointValuesToPixel(buffer.buffer); - - float halfsize = dataSet.getCircleRadius() / 2f; - - for (int j = 0, count = (int) Math.ceil((maxx - minx) * phaseX + minx) * 2; j < count; j += 2) { - - float x = buffer.buffer[j]; - float y = buffer.buffer[j + 1]; - - if (!mViewPortHandler.isInBoundsRight(x)) - break; - - // make sure the circles don't do shitty things outside - // bounds - if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) - continue; - - int circleColor = dataSet.getCircleColor(j / 2 + minx); - - mRenderPaint.setColor(circleColor); - - c.drawCircle(x, y, dataSet.getCircleRadius(), - mRenderPaint); - - if (dataSet.isDrawCircleHoleEnabled() - && circleColor != mCirclePaintInner.getColor()) - c.drawCircle(x, y, - halfsize, - mCirclePaintInner); - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - ILineDataSet set = mChart.getLineData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null || !set.isHighlightEnabled()) - continue; - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - final float yVal = set.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - float y = yVal * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[]{ - xIndex, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - - // draw the lines - drawHighlightLines(c, pts, set); - } - } - - /** - * Sets the Bitmap.Config to be used by this renderer. - * Default: Bitmap.Config.ARGB_8888 - * Use Bitmap.Config.ARGB_4444 to consume less memory. - * - * @param config - */ - public void setBitmapConfig(Bitmap.Config config) { - mBitmapConfig = config; - releaseBitmap(); - } - - /** - * Returns the Bitmap.Config that is used by this renderer. - * - * @return - */ - public Bitmap.Config getBitmapConfig() { - return mBitmapConfig; - } - - /** - * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. - */ - public void releaseBitmap() { - if (mDrawBitmap != null) { - mDrawBitmap.get().recycle(); - mDrawBitmap.clear(); - mDrawBitmap = null; - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java deleted file mode 100644 index bea77bdad9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineRadarRenderer.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Path; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Created by Philipp Jahoda on 25/01/16. - */ -public abstract class LineRadarRenderer extends LineScatterCandleRadarRenderer { - - public LineRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - } - - /** - * Draws the provided path in filled mode with the provided drawable. - * - * @param c - * @param filledPath - * @param drawable - */ - protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { - c.save(); - c.clipPath(filledPath); - - drawable.setBounds((int) mViewPortHandler.contentLeft(), - (int) mViewPortHandler.contentTop(), - (int) mViewPortHandler.contentRight(), - (int) mViewPortHandler.contentBottom()); - drawable.draw(c); - - c.restore(); - } - - /** - * Draws the provided path in filled mode with the provided color and alpha. - * Special thanks to Angelo Suzuki (https://github.com/tinsukE) for this. - * - * @param c - * @param filledPath - * @param fillColor - * @param fillAlpha - */ - protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fillAlpha) { - c.save(); - c.clipPath(filledPath); - - int color = (fillAlpha << 24) | (fillColor & 0xffffff); - c.drawColor(color); - c.restore(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java deleted file mode 100644 index 4d47b5cba7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ /dev/null @@ -1,744 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Path; -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.Build; -import android.text.Layout; -import android.text.StaticLayout; -import android.text.TextPaint; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.LineChart; -import com.github.mikephil.charting.charts.PieChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieData; -import com.github.mikephil.charting.formatter.ValueFormatter; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.lang.ref.WeakReference; -import java.util.List; - -public class PieChartRenderer extends DataRenderer { - - protected PieChart mChart; - - /** - * paint for the hole in the center of the pie chart and the transparent - * circle - */ - protected Paint mHolePaint; - protected Paint mTransparentCirclePaint; - - /** - * paint object for the text that can be displayed in the center of the - * chart - */ - private TextPaint mCenterTextPaint; - - private StaticLayout mCenterTextLayout; - private CharSequence mCenterTextLastValue; - private RectF mCenterTextLastBounds = new RectF(); - private RectF[] mRectBuffer = {new RectF(), new RectF(), new RectF()}; - - /** - * Bitmap for drawing the center hole - */ - protected WeakReference mDrawBitmap; - - protected Canvas mBitmapCanvas; - - public PieChartRenderer(PieChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHolePaint.setColor(Color.WHITE); - mHolePaint.setStyle(Style.FILL); - - mTransparentCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mTransparentCirclePaint.setColor(Color.WHITE); - mTransparentCirclePaint.setStyle(Style.FILL); - mTransparentCirclePaint.setAlpha(105); - - mCenterTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - mCenterTextPaint.setColor(Color.BLACK); - mCenterTextPaint.setTextSize(Utils.convertDpToPixel(12f)); - - mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); - mValuePaint.setColor(Color.WHITE); - mValuePaint.setTextAlign(Align.CENTER); - } - - public Paint getPaintHole() { - return mHolePaint; - } - - public Paint getPaintTransparentCircle() { - return mTransparentCirclePaint; - } - - public TextPaint getPaintCenterText() { - return mCenterTextPaint; - } - - @Override - public void initBuffers() { - // TODO Auto-generated method stub - } - - @Override - public void drawData(Canvas c) { - - int width = (int) mViewPortHandler.getChartWidth(); - int height = (int) mViewPortHandler.getChartHeight(); - - if (mDrawBitmap == null - || (mDrawBitmap.get().getWidth() != width) - || (mDrawBitmap.get().getHeight() != height)) { - - if (width > 0 && height > 0) { - - mDrawBitmap = new WeakReference(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444)); - mBitmapCanvas = new Canvas(mDrawBitmap.get()); - } else - return; - } - - mDrawBitmap.get().eraseColor(Color.TRANSPARENT); - - PieData pieData = mChart.getData(); - - for (IPieDataSet set : pieData.getDataSets()) { - - if (set.isVisible() && set.getEntryCount() > 0) - drawDataSet(c, set); - } - } - - private Path mPathBuffer = new Path(); - private RectF mInnerRectBuffer = new RectF(); - - protected float calculateMinimumRadiusForSpacedSlice( - PointF center, - float radius, - float angle, - float arcStartPointX, - float arcStartPointY, - float startAngle, - float sweepAngle) - { - final float angleMiddle = startAngle + sweepAngle / 2.f; - - // Other point of the arc - float arcEndPointX = center.x + radius * (float) Math.cos((startAngle + sweepAngle) * Utils.FDEG2RAD); - float arcEndPointY = center.y + radius * (float) Math.sin((startAngle + sweepAngle) * Utils.FDEG2RAD); - - // Middle point on the arc - float arcMidPointX = center.x + radius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); - float arcMidPointY = center.y + radius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); - - // Middle point on straight line between the two point. - // This is the base of the contained triangle - double basePointsDistance = Math.sqrt( - Math.pow(arcEndPointX - arcStartPointX, 2) + - Math.pow(arcEndPointY - arcStartPointY, 2)); - - // After reducing space from both sides of the "slice", - // the angle of the contained triangle should stay the same. - // So let's find out the height of that triangle. - float containedTriangleHeight = (float)(basePointsDistance / 2.0 * - Math.tan((180.0 - angle) / 2.0 * Utils.DEG2RAD)); - - // Now we subtract that from the radius - float spacedRadius = radius - containedTriangleHeight; - - // And now subtract the height of the arc that's between the triangle and the outer circle - spacedRadius -= Math.sqrt( - Math.pow(arcMidPointX - (arcEndPointX + arcStartPointX) / 2.f, 2) + - Math.pow(arcMidPointY - (arcEndPointY + arcStartPointY) / 2.f, 2)); - - return spacedRadius; - } - - protected void drawDataSet(Canvas c, IPieDataSet dataSet) { - - float angle = 0; - float rotationAngle = mChart.getRotationAngle(); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - final RectF circleBox = mChart.getCircleBox(); - - final int entryCount = dataSet.getEntryCount(); - final float[] drawAngles = mChart.getDrawAngles(); - float sliceSpace = dataSet.getSliceSpace(); - final PointF center = mChart.getCenterCircleBox(); - final float radius = mChart.getRadius(); - final float userInnerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() - ? radius * (mChart.getHoleRadius() / 100.f) - : 0.f; - - int visibleAngleCount = 0; - for (int j = 0; j < entryCount; j++) { - // draw only if the value is greater than zero - if ((Math.abs(dataSet.getEntryForIndex(j).getVal()) > 0.000001)) { - visibleAngleCount++; - } - } - - for (int j = 0; j < entryCount; j++) { - - float sliceAngle = drawAngles[j]; - float innerRadius = userInnerRadius; - - Entry e = dataSet.getEntryForIndex(j); - - // draw only if the value is greater than zero - if ((Math.abs(e.getVal()) > 0.000001)) { - - if (!mChart.needsHighlight(e.getXIndex(), - mChart.getData().getIndexOfDataSet(dataSet))) { - - mRenderPaint.setColor(dataSet.getColor(j)); - - final float sliceSpaceOuterAngle = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * radius); - final float startAngleOuter = rotationAngle + (angle + sliceSpaceOuterAngle / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceOuterAngle) * phaseY; - if (sweepAngleOuter < 0.f) - { - sweepAngleOuter = 0.f; - } - - mPathBuffer.reset(); - - float arcStartPointX = 0.f, arcStartPointY = 0.f; - - if (sweepAngleOuter % 360f == 0.f) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); - } else { - - arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); - arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); - - mPathBuffer.moveTo(arcStartPointX, arcStartPointY); - - mPathBuffer.arcTo( - circleBox, - startAngleOuter, - sweepAngleOuter - ); - } - - if (sliceSpace > 0.f) { - innerRadius = Math.max(innerRadius, - calculateMinimumRadiusForSpacedSlice( - center, radius, - sliceAngle * phaseY, - arcStartPointX, arcStartPointY, - startAngleOuter, - sweepAngleOuter)); - } - - // API < 21 does not receive floats in addArc, but a RectF - mInnerRectBuffer.set( - center.x - innerRadius, - center.y - innerRadius, - center.x + innerRadius, - center.y + innerRadius); - - if (innerRadius > 0.0) - { - final float sliceSpaceInnerAngle = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceInnerAngle / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceInnerAngle) * phaseY; - if (sweepAngleInner < 0.f) - { - sweepAngleInner = 0.f; - } - final float endAngleInner = startAngleInner + sweepAngleInner; - - if (sweepAngleOuter % 360f == 0.f) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); - } else { - - mPathBuffer.lineTo( - center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), - center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - mInnerRectBuffer, - endAngleInner, - -sweepAngleInner - ); - } - } - else { - - if (sweepAngleOuter % 360f != 0.f) { - mPathBuffer.lineTo( - center.x, - center.y); - } - - } - - mPathBuffer.close(); - - mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); - } - } - - angle += sliceAngle * phaseX; - } - } - - @Override - public void drawValues(Canvas c) { - - PointF center = mChart.getCenterCircleBox(); - - // get whole the radius - float r = mChart.getRadius(); - float rotationAngle = mChart.getRotationAngle(); - float[] drawAngles = mChart.getDrawAngles(); - float[] absoluteAngles = mChart.getAbsoluteAngles(); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float off = r / 10f * 3.6f; - - if (mChart.isDrawHoleEnabled()) { - off = (r - (r / 100f * mChart.getHoleRadius())) / 2f; - } - - r -= off; // offset to keep things inside the chart - - PieData data = mChart.getData(); - List dataSets = data.getDataSets(); - - float yValueSum = data.getYValueSum(); - - boolean drawXVals = mChart.isDrawSliceTextEnabled(); - - float angle; - int xIndex = 0; - - for (int i = 0; i < dataSets.size(); i++) { - - IPieDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() && !drawXVals) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") - + Utils.convertDpToPixel(4f); - - int entryCount = dataSet.getEntryCount(); - - for (int j = 0, maxEntry = Math.min( - (int) Math.ceil(entryCount * phaseX), entryCount); j < maxEntry; j++) { - - Entry entry = dataSet.getEntryForIndex(j); - - if (xIndex == 0) - angle = 0.f; - else - angle = absoluteAngles[xIndex - 1] * phaseX; - - final float sliceAngle = drawAngles[xIndex]; - final float sliceSpace = dataSet.getSliceSpace(); - final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * r); - - // offset needed to center the drawn text in the slice - final float offset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f; - - angle = angle + offset; - - // calculate the text position - float x = (float) (r - * Math.cos(Math.toRadians(rotationAngle + angle)) - + center.x); - float y = (float) (r - * Math.sin(Math.toRadians(rotationAngle + angle)) - + center.y); - - float value = mChart.isUsePercentValuesEnabled() ? entry.getVal() - / yValueSum * 100f : entry.getVal(); - - ValueFormatter formatter = dataSet.getValueFormatter(); - - boolean drawYVals = dataSet.isDrawValuesEnabled(); - - // draw everything, depending on settings - if (drawXVals && drawYVals) { - - drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); - - if (j < data.getXValCount()) { - c.drawText(data.getXVals().get(j), x, y + lineHeight, - mValuePaint); - } - - } else if (drawXVals) { - if (j < data.getXValCount()) { - mValuePaint.setColor(dataSet.getValueTextColor(j)); - c.drawText(data.getXVals().get(j), x, y + lineHeight / 2f, mValuePaint); - } - } else if (drawYVals) { - - drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); - } - - xIndex++; - } - } - } - - @Override - public void drawExtras(Canvas c) { - // drawCircles(c); - drawHole(c); - c.drawBitmap(mDrawBitmap.get(), 0, 0, null); - drawCenterText(c); - } - - private Path mHoleCirclePath = new Path(); - - /** - * draws the hole in the center of the chart and the transparent circle / - * hole - */ - protected void drawHole(Canvas c) { - - if (mChart.isDrawHoleEnabled()) { - - float radius = mChart.getRadius(); - float holeRadius = radius * (mChart.getHoleRadius() / 100); - PointF center = mChart.getCenterCircleBox(); - - if (Color.alpha(mHolePaint.getColor()) > 0) { - // draw the hole-circle - mBitmapCanvas.drawCircle( - center.x, center.y, - holeRadius, mHolePaint); - } - - // only draw the circle if it can be seen (not covered by the hole) - if (Color.alpha(mTransparentCirclePaint.getColor()) > 0 && - mChart.getTransparentCircleRadius() > mChart.getHoleRadius()) { - - int alpha = mTransparentCirclePaint.getAlpha(); - float secondHoleRadius = radius * (mChart.getTransparentCircleRadius() / 100); - - mTransparentCirclePaint.setAlpha((int) ((float) alpha * mAnimator.getPhaseX() * mAnimator.getPhaseY())); - - // draw the transparent-circle - mHoleCirclePath.reset(); - mHoleCirclePath.addCircle(center.x, center.y, secondHoleRadius, Path.Direction.CW); - mHoleCirclePath.addCircle(center.x, center.y, holeRadius, Path.Direction.CCW); - mBitmapCanvas.drawPath(mHoleCirclePath, mTransparentCirclePaint); - - // reset alpha - mTransparentCirclePaint.setAlpha(alpha); - } - } - } - - /** - * draws the description text in the center of the pie chart makes most - * sense when center-hole is enabled - */ - protected void drawCenterText(Canvas c) { - - CharSequence centerText = mChart.getCenterText(); - - if (mChart.isDrawCenterTextEnabled() && centerText != null) { - - PointF center = mChart.getCenterCircleBox(); - - float innerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() - ? mChart.getRadius() * (mChart.getHoleRadius() / 100f) - : mChart.getRadius(); - - RectF holeRect = mRectBuffer[0]; - holeRect.left = center.x - innerRadius; - holeRect.top = center.y - innerRadius; - holeRect.right = center.x + innerRadius; - holeRect.bottom = center.y + innerRadius; - RectF boundingRect = mRectBuffer[1]; - boundingRect.set(holeRect); - - float radiusPercent = mChart.getCenterTextRadiusPercent() / 100f; - if (radiusPercent > 0.0) { - boundingRect.inset( - (boundingRect.width() - boundingRect.width() * radiusPercent) / 2.f, - (boundingRect.height() - boundingRect.height() * radiusPercent) / 2.f - ); - } - - if (!centerText.equals(mCenterTextLastValue) || !boundingRect.equals(mCenterTextLastBounds)) { - - // Next time we won't recalculate StaticLayout... - mCenterTextLastBounds.set(boundingRect); - mCenterTextLastValue = centerText; - - float width = mCenterTextLastBounds.width(); - - // If width is 0, it will crash. Always have a minimum of 1 - mCenterTextLayout = new StaticLayout(centerText, 0, centerText.length(), - mCenterTextPaint, - (int) Math.max(Math.ceil(width), 1.f), - Layout.Alignment.ALIGN_CENTER, 1.f, 0.f, false); - } - - //float layoutWidth = Utils.getStaticLayoutMaxWidth(mCenterTextLayout); - float layoutHeight = mCenterTextLayout.getHeight(); - - c.save(); - if (Build.VERSION.SDK_INT >= 18) { - Path path = new Path(); - path.addOval(holeRect, Path.Direction.CW); - c.clipPath(path); - } - - c.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2.f); - mCenterTextLayout.draw(c); - - c.restore(); - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float angle; - float rotationAngle = mChart.getRotationAngle(); - - float[] drawAngles = mChart.getDrawAngles(); - float[] absoluteAngles = mChart.getAbsoluteAngles(); - final PointF center = mChart.getCenterCircleBox(); - final float radius = mChart.getRadius(); - final float userInnerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() - ? radius * (mChart.getHoleRadius() / 100.f) - : 0.f; - - final RectF highlightedCircleBox = new RectF(); - - for (int i = 0; i < indices.length; i++) { - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - if (xIndex >= drawAngles.length) - continue; - - IPieDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null || !set.isHighlightEnabled()) - continue; - - final int entryCount = set.getEntryCount(); - int visibleAngleCount = 0; - for (int j = 0; j < entryCount; j++) { - // draw only if the value is greater than zero - if ((Math.abs(set.getEntryForIndex(j).getVal()) > 0.000001)) { - visibleAngleCount++; - } - } - - if (xIndex == 0) - angle = 0.f; - else - angle = absoluteAngles[xIndex - 1] * phaseX; - - float sliceSpace = set.getSliceSpace(); - - float sliceAngle = drawAngles[xIndex]; - final float sliceSpaceOuterAngle = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * radius); - float innerRadius = userInnerRadius; - - float shift = set.getSelectionShift(); - final float highlightedRadius = radius + shift; - highlightedCircleBox.set(mChart.getCircleBox()); - highlightedCircleBox.inset(-shift, -shift); - - mRenderPaint.setColor(set.getColor(xIndex)); - - final float startAngleOuter = rotationAngle + (angle + sliceSpaceOuterAngle / 2.f) * phaseY; - float sweepAngleOuter = (sliceAngle - sliceSpaceOuterAngle) * phaseY; - if (sweepAngleOuter < 0.f) - { - sweepAngleOuter = 0.f; - } - - mPathBuffer.reset(); - - if (sweepAngleOuter % 360f == 0.f) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); - } else { - - mPathBuffer.moveTo( - center.x + highlightedRadius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), - center.y + highlightedRadius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - highlightedCircleBox, - startAngleOuter, - sweepAngleOuter - ); - } - - if (sliceSpace > 0.f) { - innerRadius = Math.max(innerRadius, - calculateMinimumRadiusForSpacedSlice( - center, radius, - sliceAngle * phaseY, - center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), - center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), - startAngleOuter, - sweepAngleOuter)); - } - - // API < 21 does not receive floats in addArc, but a RectF - mInnerRectBuffer.set( - center.x - innerRadius, - center.y - innerRadius, - center.x + innerRadius, - center.y + innerRadius); - - if (innerRadius > 0.0) { - final float sliceSpaceInnerAngle = visibleAngleCount == 1 ? - 0.f : - sliceSpace / (Utils.FDEG2RAD * innerRadius); - final float startAngleInner = rotationAngle + (angle + sliceSpaceInnerAngle / 2.f) * phaseY; - float sweepAngleInner = (sliceAngle - sliceSpaceInnerAngle) * phaseY; - if (sweepAngleInner < 0.f) - { - sweepAngleInner = 0.f; - } - final float endAngleInner = startAngleInner + sweepAngleInner; - - if (sweepAngleOuter % 360f == 0.f) { - // Android is doing "mod 360" - mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); - } else { - - mPathBuffer.lineTo( - center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), - center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); - - mPathBuffer.arcTo( - mInnerRectBuffer, - endAngleInner, - -sweepAngleInner - ); - } - } - else { - - if (sweepAngleOuter % 360f != 0.f) { - mPathBuffer.lineTo( - center.x, - center.y); - } - - } - - mPathBuffer.close(); - - mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); - } - } - - /** - * This gives all pie-slices a rounded edge. - * - * @param c - */ - protected void drawRoundedSlices(Canvas c) { - - if (!mChart.isDrawRoundedSlicesEnabled()) - return; - - IPieDataSet dataSet = mChart.getData().getDataSet(); - - if (!dataSet.isVisible()) - return; - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - PointF center = mChart.getCenterCircleBox(); - float r = mChart.getRadius(); - - // calculate the radius of the "slice-circle" - float circleRadius = (r - (r * mChart.getHoleRadius() / 100f)) / 2f; - - float[] drawAngles = mChart.getDrawAngles(); - float angle = mChart.getRotationAngle(); - - for (int j = 0; j < dataSet.getEntryCount(); j++) { - - float sliceAngle = drawAngles[j]; - - Entry e = dataSet.getEntryForIndex(j); - - // draw only if the value is greater than zero - if ((Math.abs(e.getVal()) > 0.000001)) { - - float x = (float) ((r - circleRadius) - * Math.cos(Math.toRadians((angle + sliceAngle) - * phaseY)) + center.x); - float y = (float) ((r - circleRadius) - * Math.sin(Math.toRadians((angle + sliceAngle) - * phaseY)) + center.y); - - mRenderPaint.setColor(dataSet.getColor(j)); - mBitmapCanvas.drawCircle(x, y, circleRadius, mRenderPaint); - } - - angle += sliceAngle * phaseX; - } - } - - /** - * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. - */ - public void releaseBitmap() { - if (mDrawBitmap != null) { - mDrawBitmap.get().recycle(); - mDrawBitmap.clear(); - mDrawBitmap = null; - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java deleted file mode 100644 index 3888df9bef..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ /dev/null @@ -1,295 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PointF; -import android.graphics.drawable.Drawable; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class RadarChartRenderer extends LineRadarRenderer { - - protected RadarChart mChart; - - /** - * paint for drawing the web - */ - protected Paint mWebPaint; - - public RadarChartRenderer(RadarChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setStyle(Paint.Style.STROKE); - mHighlightPaint.setStrokeWidth(2f); - mHighlightPaint.setColor(Color.rgb(255, 187, 115)); - - mWebPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mWebPaint.setStyle(Paint.Style.STROKE); - } - - public Paint getWebPaint() { - return mWebPaint; - } - - @Override - public void initBuffers() { - // TODO Auto-generated method stub - - } - - @Override - public void drawData(Canvas c) { - - RadarData radarData = mChart.getData(); - - int mostEntries = 0; - - for (IRadarDataSet set : radarData.getDataSets()) { - if (set.getEntryCount() > mostEntries) { - mostEntries = set.getEntryCount(); - } - } - - for (IRadarDataSet set : radarData.getDataSets()) { - - if (set.isVisible() && set.getEntryCount() > 0) { - drawDataSet(c, set, mostEntries); - } - } - } - - /** - * Draws the RadarDataSet - * - * @param c - * @param dataSet - * @param mostEntries the entry count of the dataset with the most entries - */ - protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - Path surface = new Path(); - - boolean hasMovedToPoint = false; - - for (int j = 0; j < dataSet.getEntryCount(); j++) { - - mRenderPaint.setColor(dataSet.getColor(j)); - - Entry e = dataSet.getEntryForIndex(j); - - PointF p = Utils.getPosition( - center, - (e.getVal() - mChart.getYChartMin()) * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); - - if (Float.isNaN(p.x)) - continue; - - if (!hasMovedToPoint) { - surface.moveTo(p.x, p.y); - hasMovedToPoint = true; - } else - surface.lineTo(p.x, p.y); - } - - if (dataSet.getEntryCount() > mostEntries) { - // if this is not the largest set, draw a line to the center before closing - surface.lineTo(center.x, center.y); - } - - surface.close(); - - if(dataSet.isDrawFilledEnabled()) { - - final Drawable drawable = dataSet.getFillDrawable(); - if (drawable != null) { - - drawFilledPath(c, surface, drawable); - } else { - - drawFilledPath(c, surface, dataSet.getFillColor(), dataSet.getFillAlpha()); - } - } - - mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); - mRenderPaint.setStyle(Paint.Style.STROKE); - - // draw the line (only if filled is disabled or alpha is below 255) - if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) - c.drawPath(surface, mRenderPaint); -// -// // draw filled -// if (dataSet.isDrawFilledEnabled()) { -// mRenderPaint.setStyle(Paint.Style.FILL); -// mRenderPaint.setAlpha(dataSet.getFillAlpha()); -// c.drawPath(surface, mRenderPaint); -// mRenderPaint.setAlpha(255); -// } - } - - @Override - public void drawValues(Canvas c) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - float yoffset = Utils.convertDpToPixel(5f); - - for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { - - IRadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); - - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - for (int j = 0; j < dataSet.getEntryCount(); j++) { - - Entry entry = dataSet.getEntryForIndex(j); - - PointF p = Utils.getPosition( - center, - (entry.getVal() - mChart.getYChartMin()) * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); - - drawValue(c, dataSet.getValueFormatter(), entry.getVal(), entry, i, p.x, p.y - yoffset, dataSet.getValueTextColor(j)); - } - } - } - - @Override - public void drawExtras(Canvas c) { - drawWeb(c); - } - - protected void drawWeb(Canvas c) { - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - float rotationangle = mChart.getRotationAngle(); - - PointF center = mChart.getCenterOffsets(); - - // draw the web lines that come from the center - mWebPaint.setStrokeWidth(mChart.getWebLineWidth()); - mWebPaint.setColor(mChart.getWebColor()); - mWebPaint.setAlpha(mChart.getWebAlpha()); - - final int xIncrements = 1 + mChart.getSkipWebLineCount(); - - for (int i = 0; i < mChart.getData().getXValCount(); i += xIncrements) { - - PointF p = Utils.getPosition( - center, - mChart.getYRange() * factor, - sliceangle * i + rotationangle); - - c.drawLine(center.x, center.y, p.x, p.y, mWebPaint); - } - - // draw the inner-web - mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner()); - mWebPaint.setColor(mChart.getWebColorInner()); - mWebPaint.setAlpha(mChart.getWebAlpha()); - - int labelCount = mChart.getYAxis().mEntryCount; - - for (int j = 0; j < labelCount; j++) { - - for (int i = 0; i < mChart.getData().getXValCount(); i++) { - - float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; - - PointF p1 = Utils.getPosition(center, r, sliceangle * i + rotationangle); - PointF p2 = Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle); - - c.drawLine(p1.x, p1.y, p2.x, p2.y, mWebPaint); - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - float sliceangle = mChart.getSliceAngle(); - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < indices.length; i++) { - - IRadarDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null || !set.isHighlightEnabled()) - continue; - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - - Entry e = set.getEntryForXIndex(xIndex); - if (e == null || e.getXIndex() != xIndex) - continue; - - int j = set.getEntryIndex(e); - float y = (e.getVal() - mChart.getYChartMin()); - - if (Float.isNaN(y)) - continue; - - PointF p = Utils.getPosition( - center, - y * factor * phaseY, - sliceangle * j * phaseX + mChart.getRotationAngle()); - - float[] pts = new float[]{ - p.x, p.y - }; - - // draw the lines - drawHighlightLines(c, pts, set); - } - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java deleted file mode 100644 index 9e66d32329..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java +++ /dev/null @@ -1,63 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; -import com.github.mikephil.charting.utils.ViewPortHandler; - -/** - * Abstract baseclass of all Renderers. - * - * @author Philipp Jahoda - */ -public abstract class Renderer { - - /** - * the component that handles the drawing area of the chart and it's offsets - */ - protected ViewPortHandler mViewPortHandler; - - /** the minimum value on the x-axis that should be plotted */ - protected int mMinX = 0; - - /** the maximum value on the x-axis that should be plotted */ - protected int mMaxX = 0; - - public Renderer(ViewPortHandler viewPortHandler) { - this.mViewPortHandler = viewPortHandler; - } - - /** - * Returns true if the specified value fits in between the provided min - * and max bounds, false if not. - * - * @param val - * @param min - * @param max - * @return - */ - protected boolean fitsBounds(float val, float min, float max) { - - if (val < min || val > max) - return false; - else - return true; - } - - /** - * Calculates the minimum and maximum x-value the chart can currently - * display (with the given zoom level). -> mMinX, mMaxX - * - * @param dataProvider - * @param xAxisModulus - */ - public void calcXBounds(BarLineScatterCandleBubbleDataProvider dataProvider, int xAxisModulus) { - - int low = dataProvider.getLowestVisibleXIndex(); - int high = dataProvider.getHighestVisibleXIndex(); - - int subLow = (low % xAxisModulus == 0) ? xAxisModulus : 0; - - mMinX = Math.max((low / xAxisModulus) * (xAxisModulus) - subLow, 0); - mMaxX = Math.min((high / xAxisModulus) * (xAxisModulus) + xAxisModulus, (int) dataProvider.getXChartMax()); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java deleted file mode 100644 index 37a1672ac6..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ /dev/null @@ -1,410 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint.Style; -import android.graphics.Path; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.ScatterBuffer; -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class ScatterChartRenderer extends LineScatterCandleRadarRenderer { - - protected ScatterDataProvider mChart; - - protected ScatterBuffer[] mScatterBuffers; - - public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - } - - @Override - public void initBuffers() { - - ScatterData scatterData = mChart.getScatterData(); - - mScatterBuffers = new ScatterBuffer[scatterData.getDataSetCount()]; - - for (int i = 0; i < mScatterBuffers.length; i++) { - IScatterDataSet set = scatterData.getDataSetByIndex(i); - mScatterBuffers[i] = new ScatterBuffer(set.getEntryCount() * 2); - } - } - - @Override - public void drawData(Canvas c) { - - ScatterData scatterData = mChart.getScatterData(); - - for (IScatterDataSet set : scatterData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - final float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - final float shapeHalf = shapeSize / 2f; - final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); - final float shapeHoleSize = shapeHoleSizeHalf * 2.f; - final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); - final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; - final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; - - ScatterShape shape = dataSet.getScatterShape(); - - ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet( - dataSet)]; - buffer.setPhases(phaseX, phaseY); - buffer.feed(dataSet); - - trans.pointValuesToPixel(buffer.buffer); - - switch (shape) { - case SQUARE: - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeHoleSize > 0.0) { - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(shapeStrokeSize); - - c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf - shapeStrokeSizeHalf, - buffer.buffer[i + 1] - shapeHoleSizeHalf - shapeStrokeSizeHalf, - buffer.buffer[i] + shapeHoleSizeHalf + shapeStrokeSizeHalf, - buffer.buffer[i + 1] + shapeHoleSizeHalf + shapeStrokeSizeHalf, - mRenderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Style.FILL); - - mRenderPaint.setColor(shapeHoleColor); - c.drawRect(buffer.buffer[i] - shapeHoleSizeHalf, - buffer.buffer[i + 1] - shapeHoleSizeHalf, - buffer.buffer[i] + shapeHoleSizeHalf, - buffer.buffer[i + 1] + shapeHoleSizeHalf, - mRenderPaint); - } - - } else { - mRenderPaint.setStyle(Style.FILL); - - c.drawRect(buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - } - - break; - - case CIRCLE: - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - if (shapeHoleSize > 0.0) { - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(shapeStrokeSize); - - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf + shapeStrokeSizeHalf, - mRenderPaint); - - if (shapeHoleColor != ColorTemplate.COLOR_NONE) { - mRenderPaint.setStyle(Style.FILL); - - mRenderPaint.setColor(shapeHoleColor); - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHoleSizeHalf, - mRenderPaint); - } - } else { - mRenderPaint.setStyle(Style.FILL); - - c.drawCircle( - buffer.buffer[i], - buffer.buffer[i + 1], - shapeHalf, - mRenderPaint); - } - } - break; - - case TRIANGLE: - - mRenderPaint.setStyle(Style.FILL); - - // create a triangle path - Path tri = new Path(); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - tri.moveTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); - tri.lineTo(buffer.buffer[i] + shapeHalf, buffer.buffer[i + 1] + shapeHalf); - tri.lineTo(buffer.buffer[i] - shapeHalf, buffer.buffer[i + 1] + shapeHalf); - - if (shapeHoleSize > 0.0) { - tri.lineTo(buffer.buffer[i], buffer.buffer[i + 1] - shapeHalf); - - tri.moveTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i] + shapeHalf - shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf + shapeStrokeSize); - tri.lineTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - } - - tri.close(); - - c.drawPath(tri, mRenderPaint); - tri.reset(); - - if (shapeHoleSize > 0.0 && - shapeHoleColor != ColorTemplate.COLOR_NONE) { - - mRenderPaint.setColor(shapeHoleColor); - - tri.moveTo(buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf + shapeStrokeSize); - tri.lineTo(buffer.buffer[i] + shapeHalf - shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.lineTo(buffer.buffer[i] - shapeHalf + shapeStrokeSize, - buffer.buffer[i + 1] + shapeHalf - shapeStrokeSize); - tri.close(); - - c.drawPath(tri, mRenderPaint); - tri.reset(); - } - } - break; - - case CROSS: - - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1], - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1], - mRenderPaint); - c.drawLine( - buffer.buffer[i], - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i], - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - break; - - case X: - - mRenderPaint.setStyle(Style.STROKE); - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - - for (int i = 0; i < buffer.size(); i += 2) { - - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[i])) - break; - - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[i]) - || !mViewPortHandler.isInBoundsY(buffer.buffer[i + 1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i / 2)); - - c.drawLine( - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - c.drawLine( - buffer.buffer[i] + shapeHalf, - buffer.buffer[i + 1] - shapeHalf, - buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] + shapeHalf, - mRenderPaint); - } - break; - - default: - break; - } - - // else { // draw the custom-shape - // - // Path customShape = dataSet.getCustomScatterShape(); - // - // for (int j = 0; j < entries.size() * mAnimator.getPhaseX(); j += 2) { - // - // Entry e = entries.get(j / 2); - // - // if (!fitsBounds(e.getXIndex(), mMinX, mMaxX)) - // continue; - // - // if (customShape == null) - // return; - // - // mRenderPaint.setColor(dataSet.getColor(j)); - // - // Path newPath = new Path(customShape); - // newPath.offset(e.getXIndex(), e.getVal()); - // - // // transform the provided custom path - // trans.pathValueToPixel(newPath); - // c.drawPath(newPath, mRenderPaint); - // } - // } - } - - @Override - public void drawValues(Canvas c) { - - // if values are drawn - if (mChart.getScatterData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - List dataSets = mChart.getScatterData().getDataSets(); - - for (int i = 0; i < mChart.getScatterData().getDataSetCount(); i++) { - - IScatterDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesScatter(dataSet, - mAnimator.getPhaseY()); - - float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); - - for (int j = 0; j < positions.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsRight(positions[j])) - break; - - // make sure the lines don't do shitty things outside bounds - if ((!mViewPortHandler.isInBoundsLeft(positions[j]) - || !mViewPortHandler.isInBoundsY(positions[j + 1]))) - continue; - - Entry entry = dataSet.getEntryForIndex(j / 2); - - drawValue(c, dataSet.getValueFormatter(), entry.getVal(), entry, i, positions[j], - positions[j + 1] - shapeSize, dataSet.getValueTextColor(j / 2)); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - IScatterDataSet set = mChart.getScatterData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null || !set.isHighlightEnabled()) - continue; - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - final float yVal = set.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - float y = yVal * mAnimator.getPhaseY(); - - float[] pts = new float[]{ - xIndex, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - - // draw the lines - drawHighlightLines(c, pts, set); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java deleted file mode 100644 index c869bec352..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ /dev/null @@ -1,113 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.data.BarData; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class XAxisRendererBarChart extends XAxisRenderer { - - protected BarChart mChart; - - public XAxisRendererBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, - BarChart chart) { - super(viewPortHandler, xAxis, trans); - - this.mChart = chart; - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - @Override - protected void drawLabels(Canvas c, float pos, PointF anchor) { - - final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[0] += ((float) step - 1f) / 2f; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0]) && i >= 0 - && i < mXAxis.getValues().size()) { - - String label = mXAxis.getValues().get(i); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1) { - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (position[0] + width / 2.f > mViewPortHandler.contentRight()) - position[0] = mViewPortHandler.contentRight() - (width / 2.f); - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (position[0] - width / 2.f < mViewPortHandler.contentLeft()) - position[0] = mViewPortHandler.contentLeft() + (width / 2.f); - } - } - - drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); - } - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); - - for (int i = mMinX; i < mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], - mViewPortHandler.contentBottom(), mGridPaint); - } - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java deleted file mode 100644 index 1dbefe08ae..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Path; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.YAxis; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class YAxisRendererRadarChart extends YAxisRenderer { - - private RadarChart mChart; - - public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, RadarChart chart) { - super(viewPortHandler, yAxis, null); - - this.mChart = chart; - } - - @Override - public void computeAxis(float yMin, float yMax) { - computeAxisValues(yMin, yMax); - } - - @Override - protected void computeAxisValues(float min, float max) { - float yMin = min; - float yMax = max; - - int labelCount = mYAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[]{}; - mYAxis.mEntryCount = 0; - return; - } - - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); - } - - // force label count - if (mYAxis.isForceLabelsEnabled()) { - - float step = (float) range / (float) (labelCount - 1); - mYAxis.mEntryCount = labelCount; - - if (mYAxis.mEntries.length < labelCount) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[labelCount]; - } - - float v = min; - - for (int i = 0; i < labelCount; i++) { - mYAxis.mEntries[i] = v; - v += step; - } - - // no forced count - } else { - - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { - - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; - - } else { - - final double rawCount = yMin / interval; - double first = rawCount < 0.0 ? Math.floor(rawCount) * interval : Math.ceil(rawCount) * interval; - - if (first == 0.0) // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) - first = 0.0; - - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; - } - - if (Float.isNaN(mYAxis.getAxisMaxValue())) - n += 1; - - mYAxis.mEntryCount = n; - - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - mYAxis.mEntries[i] = (float) f; - } - } - } - - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; - } - - if (mYAxis.mEntries[0] < yMin) { - // If startAtZero is disabled, and the first label is lower that the axis minimum, - // Then adjust the axis minimum - mYAxis.mAxisMinimum = mYAxis.mEntries[0]; - } - - mYAxis.mAxisMaximum = mYAxis.mEntries[mYAxis.mEntryCount - 1]; - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setColor(mYAxis.getTextColor()); - - PointF center = mChart.getCenterOffsets(); - float factor = mChart.getFactor(); - - int labelCount = mYAxis.mEntryCount; - - for (int j = 0; j < labelCount; j++) { - - if (j == labelCount - 1 && mYAxis.isDrawTopYLabelEntryEnabled() == false) - break; - - float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; - - PointF p = Utils.getPosition(center, r, mChart.getRotationAngle()); - - String label = mYAxis.getFormattedLabel(j); - - c.drawText(label, p.x + 10, p.y, mAxisLabelPaint); - } - } - - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mYAxis.getLimitLines(); - - if (limitLines == null) - return; - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if (!l.isEnabled()) - continue; - - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - - float r = (l.getLimit() - mChart.getYChartMin()) * factor; - - Path limitPath = new Path(); - - for (int j = 0; j < mChart.getData().getXValCount(); j++) { - - PointF p = Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle()); - - if (j == 0) - limitPath.moveTo(p.x, p.y); - else - limitPath.lineTo(p.x, p.y); - } - - limitPath.close(); - - c.drawPath(limitPath, mLimitLinePaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java b/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java deleted file mode 100644 index f83623fecb..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/EntryXIndexComparator.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.Entry; - -import java.util.Comparator; - -/** - * Comparator for comparing Entry-objects by their x-index. - * Created by philipp on 17/06/15. - */ -public class EntryXIndexComparator implements Comparator { - @Override - public int compare(Entry entry1, Entry entry2) { - return entry1.getXIndex() - entry2.getXIndex(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FSize.java b/MPChartLib/src/com/github/mikephil/charting/utils/FSize.java deleted file mode 100644 index fa7ff4489a..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FSize.java +++ /dev/null @@ -1,45 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Immutable class for describing width and height dimensions in some arbitrary - * unit. Replacement for the android.Util.SizeF which is available only on API >= 21. - */ -public final class FSize { - - public final float width; - public final float height; - - public FSize(final float width, final float height) { - this.width = width; - this.height = height; - } - - @Override - public boolean equals(final Object obj) { - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (obj instanceof FSize) { - final FSize other = (FSize) obj; - return width == other.width && height == other.height; - } - return false; - } - - @Override - public String toString() { - return width + "x" + height; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return Float.floatToIntBits(width) ^ Float.floatToIntBits(height); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java b/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java deleted file mode 100644 index 7335d771ab..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Point encapsulating two double values. - * - * @author Philipp Jahoda - */ -public class PointD { - - public double x; - public double y; - - public PointD(double x, double y) { - this.x = x; - this.y = y; - } - - /** - * returns a string representation of the object - */ - public String toString() { - return "PointD, x: " + x + ", y: " + y; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java b/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java deleted file mode 100644 index 1704eefeb8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/SelectionDetail.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.interfaces.datasets.IDataSet; - -/** - * Class that encapsulates information of a value that has been - * selected/highlighted and its DataSet index. The SelectionDetail objects give - * information about the value at the selected index and the DataSet it belongs - * to. Needed only for highlighting onTouch(). - * - * @author Philipp Jahoda - */ -public class SelectionDetail { - - public float val; - public int dataSetIndex; - public IDataSet dataSet; - - public SelectionDetail(float val, int dataSetIndex, IDataSet set) { - this.val = val; - this.dataSetIndex = dataSetIndex; - this.dataSet = set; - } -} \ No newline at end of file diff --git a/MPChartLib/AndroidManifest.xml b/MPChartLib/src/main/AndroidManifest.xml similarity index 100% rename from MPChartLib/AndroidManifest.xml rename to MPChartLib/src/main/AndroidManifest.xml diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/ChartAnimator.java new file mode 100644 index 0000000000..e5b82db0b6 --- /dev/null +++ b/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 androidx.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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java b/MPChartLib/src/main/java/com/github/mikephil/charting/animation/Easing.java new file mode 100644 index 0000000000..acb7dcc965 --- /dev/null +++ b/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 androidx.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; + } + }; + +} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/AbstractBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/buffer/AbstractBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/AbstractBuffer.java diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java similarity index 73% rename from MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java index 3d8f114121..136a231445 100644 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/BarBuffer.java @@ -6,28 +6,28 @@ public class BarBuffer extends AbstractBuffer { - protected float mBarSpace = 0f; - protected float mGroupSpace = 0f; protected int mDataSetIndex = 0; protected int mDataSetCount = 1; protected boolean mContainsStacks = false; protected boolean mInverted = false; - public BarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { + /** 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.mGroupSpace = groupspace; this.mDataSetCount = dataSetCount; this.mContainsStacks = containsStacks; } - public void setBarSpace(float barspace) { - this.mBarSpace = barspace; + public void setBarWidth(float barWidth) { + this.mBarWidth = barWidth; } public void setDataSet(int index) { this.mDataSetIndex = index; } - + public void setInverted(boolean inverted) { this.mInverted = inverted; } @@ -44,27 +44,25 @@ protected void addBar(float left, float top, float right, float bottom) { public void feed(IBarDataSet data) { float size = data.getEntryCount() * phaseX; - - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; + float barWidthHalf = mBarWidth / 2f; for (int i = 0; i < size; i++) { BarEntry e = data.getEntryForIndex(i); - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + e.getXIndex() * dataSetOffset + mDataSetIndex - + mGroupSpace * e.getXIndex() + groupSpaceHalf; - float y = e.getVal(); - float [] vals = e.getVals(); - + if(e == null) + continue; + + float x = e.getX(); + float y = e.getY(); + float[] vals = e.getYVals(); + if (!mContainsStacks || vals == null) { - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; + float left = x - barWidthHalf; + float right = x + barWidthHalf; float bottom, top; + if (mInverted) { bottom = y >= 0 ? y : 0; top = y <= 0 ? y : 0; @@ -92,7 +90,11 @@ public void feed(IBarDataSet data) { float value = vals[k]; - if(value >= 0f) { + 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; @@ -102,9 +104,10 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; + float left = x - barWidthHalf; + float right = x + barWidthHalf; float bottom, top; + if (mInverted) { bottom = y >= yStart ? y : yStart; top = y <= yStart ? y : yStart; diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java similarity index 72% rename from MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java index 48d81a566c..6b63bc30ea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java @@ -6,34 +6,31 @@ public class HorizontalBarBuffer extends BarBuffer { - public HorizontalBarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { - super(size, groupspace, dataSetCount, containsStacks); + public HorizontalBarBuffer(int size, int dataSetCount, boolean containsStacks) { + super(size, dataSetCount, containsStacks); } @Override public void feed(IBarDataSet data) { float size = data.getEntryCount() * phaseX; - - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; + float barWidthHalf = mBarWidth / 2f; for (int i = 0; i < size; i++) { BarEntry e = data.getEntryForIndex(i); - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + e.getXIndex() * dataSetOffset + mDataSetIndex - + mGroupSpace * e.getXIndex() + groupSpaceHalf; - float y = e.getVal(); - float[] vals = e.getVals(); + if(e == null) + continue; + + float x = e.getX(); + float y = e.getY(); + float[] vals = e.getYVals(); if (!mContainsStacks || vals == null) { - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; + float bottom = x - barWidthHalf; + float top = x + barWidthHalf; float left, right; if (mInverted) { left = y >= 0 ? y : 0; @@ -72,8 +69,8 @@ public void feed(IBarDataSet data) { negY += Math.abs(value); } - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; + float bottom = x - barWidthHalf; + float top = x + barWidthHalf; float left, right; if (mInverted) { left = y >= yStart ? y : yStart; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarChart.java new file mode 100644 index 0000000000..2ba15c9118 --- /dev/null +++ b/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 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(); + } + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java similarity index 60% rename from MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java index bc810343d2..0926dff244 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BarLineChartBase.java @@ -9,18 +9,14 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; -import android.graphics.PointF; +import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; -import com.github.mikephil.charting.components.Legend.LegendPosition; -import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -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.data.Entry; import com.github.mikephil.charting.highlight.ChartHighlighter; @@ -35,7 +31,8 @@ import com.github.mikephil.charting.listener.OnDrawListener; import com.github.mikephil.charting.renderer.XAxisRenderer; import com.github.mikephil.charting.renderer.YAxisRenderer; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; @@ -45,7 +42,8 @@ * @author Philipp Jahoda */ @SuppressLint("RtlHardcoded") -public abstract class BarLineChartBase>> +public abstract class BarLineChartBase>> extends Chart implements BarLineScatterCandleBubbleDataProvider { /** @@ -57,9 +55,7 @@ public abstract class BarLineChartBase= 11) { + public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, + long duration) { - PointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - Runnable job = new AnimatedZoomJob(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis.getValues().size(), scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), xValue, yValue, (float) origin.x, (float) origin.y, duration); - addViewportJob(job); + Runnable job = AnimatedZoomJob.getInstance(mViewPortHandler, this, getTransformer(axis), getAxis(axis), mXAxis + .mAxisRange, scaleX, scaleY, mViewPortHandler.getScaleX(), mViewPortHandler.getScaleY(), + xValue, yValue, (float) origin.x, (float) origin.y, duration); + addViewportJob(job); - } else { - Log.e(LOG_TAG, "Unable to execute zoomAndCenterAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(origin); } + protected Matrix mFitScreenMatrixBuffer = new Matrix(); + /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. */ public void fitScreen() { - Matrix save = mViewPortHandler.fitScreen(); + Matrix save = mFitScreenMatrixBuffer; + mViewPortHandler.fitScreen(save); mViewPortHandler.refresh(save, this, false); calculateOffsets(); @@ -777,41 +757,40 @@ public void setScaleMinima(float scaleX, float scaleY) { /** * Sets the size of the area (range on the x-axis) that should be maximum * visible at once (no further zooming out allowed). If this is e.g. set to - * 10, no more than 10 values on the x-axis can be viewed at once without + * 10, no more than a range of 10 on the x-axis can be viewed at once without * scrolling. * * @param maxXRange The maximum visible range of x-values. */ public void setVisibleXRangeMaximum(float maxXRange) { - float xScale = mDeltaX / (maxXRange); + float xScale = mXAxis.mAxisRange / (maxXRange); mViewPortHandler.setMinimumScaleX(xScale); } /** * Sets the size of the area (range on the x-axis) that should be minimum * visible at once (no further zooming in allowed). If this is e.g. set to - * 10, no less than 10 values on the x-axis can be viewed at once without + * 10, no less than a range of 10 on the x-axis can be viewed at once without * scrolling. * * @param minXRange The minimum visible range of x-values. */ public void setVisibleXRangeMinimum(float minXRange) { - float xScale = mDeltaX / (minXRange); + float xScale = mXAxis.mAxisRange / (minXRange); mViewPortHandler.setMaximumScaleX(xScale); } /** - * Limits the maximum and minimum value count that can be visible by - * pinching and zooming. e.g. minRange=10, maxRange=100 no less than 10 - * values and no more that 100 values can be viewed at once without + * Limits the maximum and minimum x range that can be visible by pinching and zooming. e.g. minRange=10, maxRange=100 the + * smallest range to be displayed at once is 10, and no more than a range of 100 values can be viewed at once without * scrolling * * @param minXRange * @param maxXRange */ public void setVisibleXRange(float minXRange, float maxXRange) { - float maxScale = mDeltaX / minXRange; - float minScale = mDeltaX / maxXRange; + float minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; mViewPortHandler.setMinMaxScaleX(minScale, maxScale); } @@ -820,108 +799,129 @@ public void setVisibleXRange(float minXRange, float maxXRange) { * visible at once. * * @param maxYRange the maximum visible range on the y-axis - * @param axis - the axis for which this limit should apply + * @param axis the axis for which this limit should apply */ public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / maxYRange; + float yScale = getAxisRange(axis) / maxYRange; mViewPortHandler.setMinimumScaleY(yScale); } /** - * Moves the left side of the current viewport to the specified x-index. - * This also refreshes the chart by calling invalidate(). + * Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. * - * @param xIndex + * @param minYRange + * @param axis the axis for which this limit should apply */ - public void moveViewToX(float xIndex) { - - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, 0f, - getTransformer(AxisDependency.LEFT), this); + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / minYRange; + mViewPortHandler.setMaximumScaleY(yScale); + } - addViewportJob(job); + /** + * Limits the maximum and minimum y range that can be visible by pinching and zooming. + * + * @param minYRange + * @param maxYRange + * @param axis + */ + public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) { + float minScale = getAxisRange(axis) / minYRange; + float maxScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinMaxScaleY(minScale, maxScale); } + /** - * Centers the viewport to the specified y-value on the y-axis. + * Moves the left side of the current viewport to the specified x-position. * This also refreshes the chart by calling invalidate(). * - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis + * @param xValue */ - public void moveViewToY(float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + public void moveViewToX(float xValue) { - Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f, - getTransformer(axis), this); + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, 0f, + getTransformer(AxisDependency.LEFT), this); addViewportJob(job); } /** * This will move the left side of the current viewport to the specified - * x-value on the x-axis, and center the viewport to the specified y-value - * on the y-axis. + * x-value on the x-axis, and center the viewport to the specified y value on the y-axis. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue * @param yValue * @param axis - which axis should be used as a reference for the y-axis */ - public void moveViewTo(float xIndex, float yValue, AxisDependency axis) { + public void moveViewTo(float xValue, float yValue, AxisDependency axis) { - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, getTransformer(axis), this); addViewportJob(job); } /** - * This will move the left side of the current viewport to the specified x-position - * and center the viewport to the specified y-position animated. + * This will move the left side of the current viewport to the specified x-value + * and center the viewport to the y value animated. * This also refreshes the chart by calling invalidate(). * - * @param xIndex + * @param xValue * @param yValue * @param axis * @param duration the duration of the animation in milliseconds */ @TargetApi(11) - public void moveViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) { + public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { - if (android.os.Build.VERSION.SDK_INT >= 11) { + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f, - getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + addViewportJob(job); - addViewportJob(job); - } else { - Log.e(LOG_TAG, "Unable to execute moveViewToAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(bounds); } /** - * This will move the center of the current viewport to the specified - * x-value and y-value. + * Centers the viewport to the specified y value on the y-axis. * This also refreshes the chart by calling invalidate(). * - * @param xIndex * @param yValue * @param axis - which axis should be used as a reference for the y-axis */ - public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { + public void centerViewToY(float yValue, AxisDependency axis) { + + float valsInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, 0f, yValue + valsInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + + /** + * This will move the center of the current viewport to the specified + * x and y value. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + * @param yValue + * @param axis - which axis should be used as a reference for the y axis + */ + public void centerViewTo(float xValue, float yValue, AxisDependency axis) { - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX(); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - Runnable job = new MoveViewJob(mViewPortHandler, - xIndex - xsInView / 2f, yValue + valsInView / 2f, + Runnable job = MoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, getTransformer(axis), this); addViewportJob(job); @@ -929,31 +929,28 @@ public void centerViewTo(float xIndex, float yValue, AxisDependency axis) { /** * This will move the center of the current viewport to the specified - * x-value and y-value animated. + * x and y value animated. * - * @param xIndex + * @param xValue * @param yValue * @param axis * @param duration the duration of the animation in milliseconds */ @TargetApi(11) - public void centerViewToAnimated(float xIndex, float yValue, AxisDependency axis, long duration) { + public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { - if (android.os.Build.VERSION.SDK_INT >= 11) { + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); - PointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX(); + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); - Runnable job = new AnimatedMoveViewJob(mViewPortHandler, - xIndex - xsInView / 2f, yValue + valsInView / 2f, - getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + addViewportJob(job); - addViewportJob(job); - } else { - Log.e(LOG_TAG, "Unable to execute centerViewToAnimated(...) on API level < 11"); - } + MPPointD.recycleInstance(bounds); } /** @@ -1004,12 +1001,12 @@ public void resetViewPortOffsets() { /** CODE BELOW IS GETTERS AND SETTERS */ /** - * Returns the delta-y value (y-value range) of the specified axis. + * Returns the range of the specified axis. * * @param axis * @return */ - public float getDeltaY(AxisDependency axis) { + protected float getAxisRange(AxisDependency axis) { if (axis == AxisDependency.LEFT) return mAxisLeft.mAxisRange; else @@ -1034,25 +1031,27 @@ public OnDrawListener getDrawListener() { return mDrawListener; } + protected float[] mGetPositionBuffer = new float[2]; + /** + * Returns a recyclable MPPointF instance. * Returns the position (in pixels) the provided Entry has inside the chart * view or null, if the provided Entry is null. * * @param e * @return */ - public PointF getPosition(Entry e, AxisDependency axis) { + public MPPointF getPosition(Entry e, AxisDependency axis) { if (e == null) return null; - float[] vals = new float[]{ - e.getXIndex(), e.getVal() - }; + mGetPositionBuffer[0] = e.getX(); + mGetPositionBuffer[1] = e.getY(); - getTransformer(axis).pointValuesToPixel(vals); + getTransformer(axis).pointValuesToPixel(mGetPositionBuffer); - return new PointF(vals[0], vals[1]); + return MPPointF.getInstance(mGetPositionBuffer[0], mGetPositionBuffer[1]); } /** @@ -1100,7 +1099,8 @@ public void setGridBackgroundColor(int color) { * @param enabled */ public void setDragEnabled(boolean enabled) { - this.mDragEnabled = enabled; + this.mDragXEnabled = enabled; + this.mDragYEnabled = enabled; } /** @@ -1109,7 +1109,43 @@ public void setDragEnabled(boolean enabled) { * @return */ public boolean isDragEnabled() { - return mDragEnabled; + return mDragXEnabled || mDragYEnabled; + } + + /** + * Set this to true to enable dragging on the X axis + * + * @param enabled + */ + public void setDragXEnabled(boolean enabled) { + this.mDragXEnabled = enabled; + } + + /** + * Returns true if dragging on the X axis is enabled for the chart, false if not. + * + * @return + */ + public boolean isDragXEnabled() { + return mDragXEnabled; + } + + /** + * Set this to true to enable dragging on the Y axis + * + * @param enabled + */ + public void setDragYEnabled(boolean enabled) { + this.mDragYEnabled = enabled; + } + + /** + * Returns true if dragging on the Y axis is enabled for the chart, false if not. + * + * @return + */ + public boolean isDragYEnabled() { + return mDragYEnabled; } /** @@ -1168,8 +1204,8 @@ public void setDrawGridBackground(boolean enabled) { } /** - * Sets drawing the borders rectangle to true. If this is enabled, there is - * no point drawing the axis-lines of x- and y-axis. + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. * * @param enabled */ @@ -1177,6 +1213,58 @@ public void setDrawBorders(boolean enabled) { mDrawBorders = enabled; } + /** + * When enabled, the borders rectangle will be rendered. + * If this is enabled, there is no point drawing the axis-lines of x- and y-axis. + * + * @return + */ + public boolean isDrawBordersEnabled() { + return mDrawBorders; + } + + /** + * When enabled, the values will be clipped to contentRect, + * otherwise they can bleed outside the content rect. + * + * @param enabled + */ + public void setClipValuesToContent(boolean enabled) { + mClipValuesToContent = enabled; + } + + /** + * When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can + * be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines) + * that there is unwanted clipping. + * + * @param enabled + */ + public void setClipDataToContent(boolean enabled) { + mClipDataToContent = enabled; + } + + /** + * When enabled, the values will be clipped to contentRect, + * otherwise they can bleed outside the content rect. + * + * @return + */ + public boolean isClipValuesToContentEnabled() { + return mClipValuesToContent; + } + + /** + * When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can + * be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines) + * that there is unwanted clipping. + * + * @return + */ + public boolean isClipDataToContentEnabled() { + return mClipDataToContent; + } + /** * Sets the width of the border lines in dp. * @@ -1210,49 +1298,42 @@ public void setMinOffset(float minOffset) { } /** - * Returns the Highlight object (contains x-index and DataSet index) of the - * selected value at the given touch point inside the Line-, Scatter-, or - * CandleStick-Chart. - * - * @param x - * @param y - * @return + * Returns true if keeping the position on rotation is enabled and false if not. */ - public Highlight getHighlightByTouchPoint(float x, float y) { + public boolean isKeepPositionOnRotation() { + return mKeepPositionOnRotation; + } - if (mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } else - return getHighlighter().getHighlight(x, y); + /** + * Sets whether the chart should keep its position (zoom / scroll) after a rotation (orientation change) + */ + public void setKeepPositionOnRotation(boolean keepPositionOnRotation) { + mKeepPositionOnRotation = keepPositionOnRotation; } /** + * Returns a recyclable MPPointD instance * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to + * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). + * getPixelForValues(...). * * @param x * @param y * @return */ - public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - getTransformer(axis).pixelsToValue(pts); - - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; + public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, axis, result); + return result; + } - return new PointD(xTouchVal, yTouchVal); + public void getValuesByTouchPoint(float x, float y, AxisDependency axis, MPPointD outputPoint) { + getTransformer(axis).getValuesByTouchPoint(x, y, outputPoint); } /** + * Returns a recyclable MPPointD instance * Transforms the given chart values into pixels. This is the opposite * method to getValuesByTouchPoint(...). * @@ -1260,27 +1341,8 @@ public PointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { * @param y * @return */ - public PointD getPixelsForValues(float x, float y, AxisDependency axis) { - - float[] pts = new float[]{ - x, y - }; - - getTransformer(axis).pointValuesToPixel(pts); - - return new PointD(pts[0], pts[1]); - } - - /** - * returns the y-value at the given touch position (must not necessarily be - * a value contained in one of the datasets) - * - * @param x - * @param y - * @return - */ - public float getYValueByTouchPoint(float x, float y, AxisDependency axis) { - return (float) getValuesByTouchPoint(x, y, axis).y; + public MPPointD getPixelForValues(float x, float y, AxisDependency axis) { + return getTransformer(axis).getPixelForValues(x, y); } /** @@ -1313,6 +1375,11 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float return null; } + /** + * buffer for storing lowest visible x point + */ + protected MPPointD posForGetLowestVisibleX = MPPointD.getInstance(0, 0); + /** * Returns the lowest x-index (value on the x-axis) that is still visible on * the chart. @@ -1320,14 +1387,18 @@ public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float * @return */ @Override - public int getLowestVisibleXIndex() { - float[] pts = new float[]{ - mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom() - }; - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (pts[0] <= 0) ? 0 : (int) (pts[0] + 1.0f); + public float getLowestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), posForGetLowestVisibleX); + float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.x); + return result; } + /** + * buffer for storing highest visible x point + */ + protected MPPointD posForGetHighestVisibleX = MPPointD.getInstance(0, 0); + /** * Returns the highest x-index (value on the x-axis) that is still visible * on the chart. @@ -1335,12 +1406,20 @@ public int getLowestVisibleXIndex() { * @return */ @Override - public int getHighestVisibleXIndex() { - float[] pts = new float[]{ - mViewPortHandler.contentRight(), mViewPortHandler.contentBottom() - }; - getTransformer(AxisDependency.LEFT).pixelsToValue(pts); - return (pts[0] >= mData.getXValCount()) ? mData.getXValCount() - 1 : (int) pts[0]; + public float getHighestVisibleX() { + getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), posForGetHighestVisibleX); + float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.x); + return result; + } + + /** + * Returns the range visible on the x-axis. + * + * @return + */ + public float getVisibleXRange() { + return Math.abs(getHighestVisibleX() - getLowestVisibleX()); } /** @@ -1411,17 +1490,6 @@ public boolean isInverted(AxisDependency axis) { return getAxis(axis).isInverted(); } - /** - * Returns the object representing all x-labels, this method can be used to - * acquire the XAxis object and modify it (e.g. change the position of the - * labels) - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - /** * If set to true, both x and y axis can be scaled simultaneously with 2 fingers, if false, * x and y axis can be scaled separately. default: false @@ -1576,4 +1644,31 @@ public Paint getPaint(int which) { return null; } + + protected float[] mOnSizeChangedBuffer = new float[2]; + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + + // Saving current position of chart. + mOnSizeChangedBuffer[0] = mOnSizeChangedBuffer[1] = 0; + + if (mKeepPositionOnRotation) { + mOnSizeChangedBuffer[0] = mViewPortHandler.contentLeft(); + mOnSizeChangedBuffer[1] = mViewPortHandler.contentTop(); + getTransformer(AxisDependency.LEFT).pixelsToValue(mOnSizeChangedBuffer); + } + + //Superclass transforms chart. + super.onSizeChanged(w, h, oldw, oldh); + + if (mKeepPositionOnRotation) { + + //Restoring old position of chart. + getTransformer(AxisDependency.LEFT).pointValuesToPixel(mOnSizeChangedBuffer); + mViewPortHandler.centerViewPort(mOnSizeChangedBuffer, this); + } else { + mViewPortHandler.refresh(mViewPortHandler.getMatrixTouch(), this, true); + } + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/BubbleChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java similarity index 61% rename from MPChartLib/src/com/github/mikephil/charting/charts/BubbleChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java index fe7942f0a2..23dac5780f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BubbleChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/BubbleChart.java @@ -5,7 +5,6 @@ import android.util.AttributeSet; import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.renderer.BubbleChartRenderer; @@ -38,33 +37,6 @@ protected void init() { mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler); } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - - mXChartMin = -0.5f; - mXChartMax = (float) mData.getXValCount() - 0.5f; - - if (mRenderer != null) { - for (IBubbleDataSet set : mData.getDataSets()) { - - final float xmin = set.getXMin(); - final float xmax = set.getXMax(); - - if (xmin < mXChartMin) - mXChartMin = xmin; - - if (xmax > mXChartMax) - mXChartMax = xmax; - } - } - - mDeltaX = Math.abs(mXChartMax - mXChartMin); - } - public BubbleData getBubbleData() { return mData; } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java similarity index 84% rename from MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java index 12f8dad9fd..fa36e3522f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java @@ -10,7 +10,7 @@ /** * Financial chart type that draws candle-sticks (OHCL chart). - * + * * @author Philipp Jahoda */ public class CandleStickChart extends BarLineChartBase implements CandleDataProvider { @@ -32,15 +32,9 @@ protected void init() { super.init(); mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java similarity index 72% rename from MPChartLib/src/com/github/mikephil/charting/charts/Chart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java index 647d0f944f..5cf49ea9d1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/Chart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/Chart.java @@ -3,7 +3,6 @@ import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.graphics.Bitmap; @@ -12,12 +11,13 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Environment; import android.provider.MediaStore.Images; +import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -27,15 +27,18 @@ import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.animation.Easing; -import com.github.mikephil.charting.animation.EasingFunction; +import com.github.mikephil.charting.animation.Easing.EasingFunction; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.components.IMarker; import com.github.mikephil.charting.components.Legend; -import com.github.mikephil.charting.components.MarkerView; +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.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.ChartHighlighter; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.IHighlighter; import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.listener.ChartTouchListener; @@ -43,6 +46,7 @@ import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.github.mikephil.charting.renderer.DataRenderer; import com.github.mikephil.charting.renderer.LegendRenderer; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -51,14 +55,12 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.List; /** * Baseclass of all Chart-Views. * * @author Philipp Jahoda */ -@SuppressLint("NewApi") public abstract class Chart>> extends ViewGroup implements ChartInterface { @@ -97,7 +99,7 @@ public abstract class Chart= mData.getXValCount() - || dataSetIndex >= mData.getDataSetCount()) { + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + */ + public void highlightValue(float x, int dataSetIndex) { + highlightValue(x, dataSetIndex, -1, true); + } - highlightValues(null); + /** + * Highlights the value at the given x-value and y-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + */ + public void highlightValue(float x, float y, int dataSetIndex, int dataIndex) { + highlightValue(x, y, dataSetIndex, dataIndex, true); + } + + /** + * Highlights the value at the given x-value and y-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * This method will call the listener. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + */ + public void highlightValue(float x, float y, int dataSetIndex) { + highlightValue(x, y, dataSetIndex, -1, true); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, int dataSetIndex, int dataIndex, boolean callListener) { + highlightValue(x, Float.NaN, dataSetIndex, dataIndex, callListener); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, int dataSetIndex, boolean callListener) { + highlightValue(x, Float.NaN, dataSetIndex, -1, callListener); + } + + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param dataIndex The data index to search in (only used in CombinedChartView currently) + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, float y, int dataSetIndex, int dataIndex, boolean callListener) { + + if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) { + highlightValue(null, callListener); } else { - highlightValues(new Highlight[]{ - new Highlight(xIndex, dataSetIndex) - }); + highlightValue(new Highlight(x, y, dataSetIndex, dataIndex), callListener); } } + /** + * Highlights any y-value at the given x-value in the given DataSet. + * Provide -1 as the dataSetIndex to undo all highlighting. + * @param x The x-value to highlight + * @param y The y-value to highlight. Supply `NaN` for "any" + * @param dataSetIndex The dataset index to search in + * @param callListener Should the listener be called for this change + */ + public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) { + highlightValue(x, y, dataSetIndex, -1, callListener); + } + /** * Highlights the values represented by the provided Highlight object - * This DOES NOT generate a callback to the OnChartValueSelectedListener. + * This method *will not* call the listener. * * @param highlight contains information about which entry should be highlighted */ @@ -598,10 +695,11 @@ public void highlightValue(Highlight high, boolean callListener) { Log.i(LOG_TAG, "Highlighted: " + high.toString()); e = mData.getEntryForHighlight(high); - if (e == null || e.getXIndex() != high.getXIndex()) { + if (e == null) { mIndicesToHighlight = null; high = null; } else { + // set the indices to highlight mIndicesToHighlight = new Highlight[]{ high @@ -609,25 +707,38 @@ public void highlightValue(Highlight high, boolean callListener) { } } + setLastHighlighted(mIndicesToHighlight); + if (callListener && mSelectionListener != null) { if (!valuesToHighlight()) mSelectionListener.onNothingSelected(); else { // notify the listener - mSelectionListener.onValueSelected(e, high.getDataSetIndex(), high); + mSelectionListener.onValueSelected(e, high); } } + // redraw the chart invalidate(); } /** - * Deprecated. Calls highlightValue(high, true) + * Returns the Highlight object (contains x-index and DataSet index) of the + * selected value at the given touch point inside the Line-, Scatter-, or + * CandleStick-Chart. + * + * @param x + * @param y + * @return */ - @Deprecated - public void highlightTouch(Highlight high) { - highlightValue(high, true); + 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 + return getHighlighter().getHighlight(x, y); } /** @@ -640,6 +751,15 @@ public void setOnTouchListener(ChartTouchListener l) { this.mChartTouchListener = l; } + /** + * Returns an instance of the currently active touch listener. + * + * @return + */ + public ChartTouchListener getOnTouchListener() { + return mChartTouchListener; + } + /** * ################ ################ ################ ################ */ @@ -648,12 +768,12 @@ public void setOnTouchListener(ChartTouchListener l) { /** * if set to true, the marker view is drawn when a value is clicked */ - protected boolean mDrawMarkerViews = true; + protected boolean mDrawMarkers = true; /** * the view that represents the marker */ - protected MarkerView mMarkerView; + protected IMarker mMarker; /** * draws all MarkerViews on the highlighted positions @@ -661,63 +781,46 @@ public void setOnTouchListener(ChartTouchListener l) { protected void drawMarkers(Canvas canvas) { // if there is no marker view or drawing marker is disabled - if (mMarkerView == null || !mDrawMarkerViews || !valuesToHighlight()) + if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight()) return; for (int i = 0; i < mIndicesToHighlight.length; i++) { Highlight highlight = mIndicesToHighlight[i]; - int xIndex = highlight.getXIndex(); - int dataSetIndex = highlight.getDataSetIndex(); - - if (xIndex <= mDeltaX && xIndex <= mDeltaX * mAnimator.getPhaseX()) { - - Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); - // make sure entry not null - if (e == null || e.getXIndex() != mIndicesToHighlight[i].getXIndex()) - continue; + IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex()); - float[] pos = getMarkerPosition(e, highlight); + Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); + int entryIndex = set.getEntryIndex(e); - // check bounds - if (!mViewPortHandler.isInBounds(pos[0], pos[1])) - continue; + // make sure entry not null + if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) + continue; - // callbacks to update the content - mMarkerView.refreshContent(e, highlight); + float[] pos = getMarkerPosition(highlight); - // mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, - // MeasureSpec.UNSPECIFIED), - // MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - // mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - // mMarkerView.getMeasuredHeight()); - // mMarkerView.draw(mDrawCanvas, pos[0], pos[1]); + // check bounds + if (!mViewPortHandler.isInBounds(pos[0], pos[1])) + continue; - mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - mMarkerView.getMeasuredHeight()); + // callbacks to update the content + mMarker.refreshContent(e, highlight); - if (pos[1] - mMarkerView.getHeight() <= 0) { - float y = mMarkerView.getHeight() - pos[1]; - mMarkerView.draw(canvas, pos[0], pos[1] + y); - } else { - mMarkerView.draw(canvas, pos[0], pos[1]); - } - } + // draw the marker + mMarker.draw(canvas, pos[0], pos[1]); } } /** * Returns the actual position in pixels of the MarkerView for the given - * Entry in the given DataSet. + * Highlight object. * - * @param e - * @param highlight + * @param high * @return */ - protected abstract float[] getMarkerPosition(Entry e, Highlight highlight); + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getDrawX(), high.getDrawY()}; + } /** * ################ ################ ################ ################ @@ -795,43 +898,12 @@ public void setDragDecelerationFrictionCoef(float newValue) { * @param easingX a custom easing function to be used on the animation phase * @param easingY a custom easing function to be used on the animation phase */ + @RequiresApi(11) public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX, EasingFunction easingY) { mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); } - /** - * Animates the rendering of the chart on the x-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - * @param easing a custom easing function to be used on the animation phase - */ - public void animateX(int durationMillis, EasingFunction easing) { - mAnimator.animateX(durationMillis, easing); - } - - /** - * Animates the rendering of the chart on the y-axis with the specified - * animation time. If animate(...) is called, no further calling of - * invalidate() is necessary to refresh the chart. ANIMATIONS ONLY WORK FOR - * API LEVEL 11 (Android 3.0.x) AND HIGHER. - * - * @param durationMillis - * @param easing a custom easing function to be used on the animation phase - */ - public void animateY(int durationMillis, EasingFunction easing) { - mAnimator.animateY(durationMillis, easing); - } - - /** - * ################ ################ ################ ################ - * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. - */ - /** CODE BELOW FOR PREDEFINED EASING OPTIONS */ - /** * Animates the drawing / rendering of the chart on both x- and y-axis with * the specified animation time. If animate(...) is called, no further @@ -840,12 +912,11 @@ public void animateY(int durationMillis, EasingFunction easing) { * * @param durationMillisX * @param durationMillisY - * @param easingX a predefined easing option - * @param easingY a predefined easing option + * @param easing a custom easing function to be used on the animation phase */ - public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOption easingX, - Easing.EasingOption easingY) { - mAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY); + @RequiresApi(11) + public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) { + mAnimator.animateXY(durationMillisX, durationMillisY, easing); } /** @@ -855,9 +926,10 @@ public void animateXY(int durationMillisX, int durationMillisY, Easing.EasingOpt * API LEVEL 11 (Android 3.0.x) AND HIGHER. * * @param durationMillis - * @param easing a predefined easing option + * @param easing a custom easing function to be used on the animation phase */ - public void animateX(int durationMillis, Easing.EasingOption easing) { + @RequiresApi(11) + public void animateX(int durationMillis, EasingFunction easing) { mAnimator.animateX(durationMillis, easing); } @@ -868,12 +940,19 @@ public void animateX(int durationMillis, Easing.EasingOption easing) { * API LEVEL 11 (Android 3.0.x) AND HIGHER. * * @param durationMillis - * @param easing a predefined easing option + * @param easing a custom easing function to be used on the animation phase */ - public void animateY(int durationMillis, Easing.EasingOption easing) { + @RequiresApi(11) + public void animateY(int durationMillis, EasingFunction easing) { mAnimator.animateY(durationMillis, easing); } + /** + * ################ ################ ################ ################ + * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. + */ + /** CODE BELOW FOR PREDEFINED EASING OPTIONS */ + /** * ################ ################ ################ ################ * ANIMATIONS ONLY WORK FOR API LEVEL 11 (Android 3.0.x) AND HIGHER. @@ -888,6 +967,7 @@ public void animateY(int durationMillis, Easing.EasingOption easing) { * * @param durationMillis */ + @RequiresApi(11) public void animateX(int durationMillis) { mAnimator.animateX(durationMillis); } @@ -900,6 +980,7 @@ public void animateX(int durationMillis) { * * @param durationMillis */ + @RequiresApi(11) public void animateY(int durationMillis) { mAnimator.animateY(durationMillis); } @@ -913,6 +994,7 @@ public void animateY(int durationMillis) { * @param durationMillisX * @param durationMillisY */ + @RequiresApi(11) public void animateXY(int durationMillisX, int durationMillisY) { mAnimator.animateXY(durationMillisX, durationMillisY); } @@ -922,14 +1004,26 @@ public void animateXY(int durationMillisX, int durationMillisY) { */ /** BELOW THIS ONLY GETTERS AND SETTERS */ + + /** + * Returns the object representing all x-labels, this method can be used to + * acquire the XAxis object and modify it (e.g. change the position of the + * labels, styling, etc.) + * + * @return + */ + public XAxis getXAxis() { + return mXAxis; + } + /** - * Returns the default ValueFormatter that has been determined by the chart + * Returns the default IValueFormatter that has been determined by the chart * considering the provided minimum and maximum values. * * @return */ - public ValueFormatter getDefaultValueFormatter() { - return mDefaultFormatter; + public IValueFormatter getDefaultValueFormatter() { + return mDefaultValueFormatter; } /** @@ -980,103 +1074,41 @@ public float getYMin() { @Override public float getXChartMax() { - return mXChartMax; + return mXAxis.mAxisMaximum; } @Override public float getXChartMin() { - return mXChartMin; + return mXAxis.mAxisMinimum; } @Override - public int getXValCount() { - return mData.getXValCount(); - } - - /** - * Returns the total number of (y) values the chart holds (across all DataSets). - * - * @return - */ - public int getValueCount() { - return mData.getYValCount(); + public float getXRange() { + return mXAxis.mAxisRange; } /** + * Returns a recyclable MPPointF instance. * Returns the center point of the chart (the whole View) in pixels. * * @return */ - public PointF getCenter() { - return new PointF(getWidth() / 2f, getHeight() / 2f); + public MPPointF getCenter() { + return MPPointF.getInstance(getWidth() / 2f, getHeight() / 2f); } /** + * Returns a recyclable MPPointF instance. * Returns the center of the chart taking offsets under consideration. * (returns the center of the content rectangle) * * @return */ @Override - public PointF getCenterOffsets() { + public MPPointF getCenterOffsets() { return mViewPortHandler.getContentCenter(); } - /** - * set a description text that appears in the bottom right corner of the - * chart, size = Y-legend text size - * - * @param desc - */ - public void setDescription(String desc) { - if (desc == null) - desc = ""; - this.mDescription = desc; - } - - /** - * Sets a custom position for the description text in pixels on the screen. - * - * @param x - xcoordinate - * @param y - ycoordinate - */ - public void setDescriptionPosition(float x, float y) { - mDescriptionPosition = new PointF(x, y); - } - - /** - * sets the typeface for the description paint - * - * @param t - */ - public void setDescriptionTypeface(Typeface t) { - mDescPaint.setTypeface(t); - } - - /** - * sets the size of the description text in pixels, min 6f, max 16f - * - * @param size - */ - public void setDescriptionTextSize(float size) { - - if (size > 16f) - size = 16f; - if (size < 6f) - size = 6f; - - mDescPaint.setTextSize(Utils.convertDpToPixel(size)); - } - - /** - * Sets the color of the description text. - * - * @param color - */ - public void setDescriptionColor(int color) { - mDescPaint.setColor(color); - } - /** * Sets extra offsets (around the chart view) to be appended to the * auto-calculated offsets. @@ -1179,13 +1211,30 @@ public void setNoDataText(String text) { } /** - * Sets descriptive text to explain to the user why there is no chart - * available Defaults to empty if not set + * Sets the color of the no data text. * - * @param text + * @param color */ - public void setNoDataTextDescription(String text) { - mNoDataTextDescription = text; + public void setNoDataTextColor(int color) { + mInfoPaint.setColor(color); + } + + /** + * Sets the typeface to be used for the no data text. + * + * @param tf + */ + public void setNoDataTextTypeface(Typeface tf) { + mInfoPaint.setTypeface(tf); + } + + /** + * alignment of the no data text + * + * @param align + */ + public void setNoDataTextAlignment(Align align) { + mInfoPaint.setTextAlign(align); } /** @@ -1199,21 +1248,50 @@ public void setTouchEnabled(boolean enabled) { } /** - * sets the view that is displayed when a value is clicked on the chart + * sets the marker that is displayed when a value is clicked on the chart * - * @param v + * @param marker */ - public void setMarkerView(MarkerView v) { - mMarkerView = v; + public void setMarker(IMarker marker) { + mMarker = marker; } /** - * returns the view that is set as a marker view for the chart + * returns the marker that is set as a marker view for the chart * * @return */ - public MarkerView getMarkerView() { - return mMarkerView; + public IMarker getMarker() { + return mMarker; + } + + @Deprecated + public void setMarkerView(IMarker v) { + setMarker(v); + } + + @Deprecated + public IMarker getMarkerView() { + return getMarker(); + } + + /** + * Sets a new Description object for the chart. + * + * @param desc + */ + public void setDescription(Description desc) { + this.mDescription = desc; + } + + /** + * Returns the Description object of the chart that is responsible for holding all information related + * to the description text that is displayed in the bottom right corner of the chart (by default). + * + * @return + */ + public Description getDescription() { + return mDescription; } /** @@ -1334,64 +1412,35 @@ public Paint getPaint(int which) { return null; } - /** - * returns true if drawing the marker-view is enabled when tapping on values - * (use the setMarkerView(View v) method to specify a marker view) - * - * @return - */ - public boolean isDrawMarkerViewEnabled() { - return mDrawMarkerViews; + @Deprecated + public boolean isDrawMarkerViewsEnabled() { + return isDrawMarkersEnabled(); } - /** - * Set this to true to draw a user specified marker-view when tapping on - * chart values (use the setMarkerView(MarkerView mv) method to specify a - * marker view). Default: true - * - * @param enabled - */ + @Deprecated public void setDrawMarkerViews(boolean enabled) { - mDrawMarkerViews = enabled; + setDrawMarkers(enabled); } /** - * returns the x-value at the given index + * returns true if drawing the marker is enabled when tapping on values + * (use the setMarker(IMarker marker) method to specify a marker) * - * @param index * @return */ - public String getXValue(int index) { - if (mData == null || mData.getXValCount() <= index) - return null; - else - return mData.getXVals().get(index); + public boolean isDrawMarkersEnabled() { + return mDrawMarkers; } /** - * Get all Entry objects at the given index across all DataSets. - * INFORMATION: This method does calculations at runtime. Do not over-use in - * performance critical situations. + * Set this to true to draw a user specified marker when tapping on + * chart values (use the setMarker(IMarker marker) method to specify a + * marker). Default: true * - * @param xIndex - * @return + * @param enabled */ - public List getEntriesAtIndex(int xIndex) { - - List vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IDataSet set = mData.getDataSetByIndex(i); - - Entry e = set.getEntryForXIndex(xIndex); - - if (e != null) { - vals.add(e); - } - } - - return vals; + public void setDrawMarkers(boolean enabled) { + mDrawMarkers = enabled; } /** @@ -1433,7 +1482,7 @@ public void setRenderer(DataRenderer renderer) { mRenderer = renderer; } - public ChartHighlighter getHighlighter() { + public IHighlighter getHighlighter() { return mHighlighter; } @@ -1444,12 +1493,16 @@ public ChartHighlighter getHighlighter() { * @param highlighter */ public void setHighlighter(ChartHighlighter highlighter) { - mHighlighter = highlighter; } + /** + * Returns a recyclable MPPointF instance. + * + * @return + */ @Override - public PointF getCenterOfView() { + public MPPointF getCenterOfView() { return getCenter(); } @@ -1490,6 +1543,8 @@ public Bitmap getChartBitmap() { */ public boolean saveToPath(String title, String pathOnSD) { + + Bitmap b = getChartBitmap(); OutputStream stream = null; @@ -1525,7 +1580,8 @@ public boolean saveToPath(String title, String pathOnSD) { * @param quality e.g. 50, min = 0, max = 100 * @return returns true if saving was successful, false if not */ - public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat format, int quality) { + public boolean saveToGallery(String fileName, String subFolderPath, String fileDescription, Bitmap.CompressFormat + format, int quality) { // restrain quality if (quality < 0 || quality > 100) quality = 50; @@ -1604,7 +1660,18 @@ public boolean saveToGallery(String fileName, String subFolderPath, String fileD * @return returns true if saving was successful, false if not */ public boolean saveToGallery(String fileName, int quality) { - return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.JPEG, quality); + return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.PNG, quality); + } + + /** + * Saves the current state of the chart to the gallery as a PNG image. + * NOTE: Needs permission WRITE_EXTERNAL_STORAGE + * + * @param fileName e.g. "my_image" + * @return returns true if saving was successful, false if not + */ + public boolean saveToGallery(String fileName) { + return saveToGallery(fileName, "", "MPAndroidChart-Library Save", Bitmap.CompressFormat.PNG, 40); } /** @@ -1672,20 +1739,23 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { Log.i(LOG_TAG, "OnSizeChanged()"); if (w > 0 && h > 0 && w < 10000 && h < 10000) { - - mViewPortHandler.setChartDimens(w, h); - if (mLogEnabled) Log.i(LOG_TAG, "Setting chart dimens, width: " + w + ", height: " + h); + mViewPortHandler.setChartDimens(w, h); + } else { + if (mLogEnabled) + Log.w(LOG_TAG, "*Avoiding* setting chart dimens! width: " + w + ", height: " + h); + } - for (Runnable r : mJobs) { - post(r); - } + // This may cause the chart view to mutate properties affecting the view port -- + // lets do this before we try to run any pending jobs on the view port itself + notifyDataSetChanged(); - mJobs.clear(); + for (Runnable r : mJobs) { + post(r); } - notifyDataSetChanged(); + mJobs.clear(); super.onSizeChanged(w, h, oldw, oldh); } @@ -1698,16 +1768,10 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { */ public void setHardwareAccelerationEnabled(boolean enabled) { - if (android.os.Build.VERSION.SDK_INT >= 11) { - - if (enabled) - setLayerType(View.LAYER_TYPE_HARDWARE, null); - else - setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } else { - Log.e(LOG_TAG, - "Cannot enable/disable hardware acceleration for devices below API level 11."); - } + if (enabled) + setLayerType(View.LAYER_TYPE_HARDWARE, null); + else + setLayerType(View.LAYER_TYPE_SOFTWARE, null); } @Override diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java similarity index 54% rename from MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java index caa011203a..cd01f0ef73 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CombinedChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CombinedChart.java @@ -2,21 +2,21 @@ 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.interfaces.datasets.IBubbleDataSet; -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; -import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; +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; /** @@ -25,13 +25,7 @@ * * @author Philipp Jahoda */ -public class CombinedChart extends BarLineChartBase implements LineDataProvider, - BarDataProvider, ScatterDataProvider, CandleDataProvider, BubbleDataProvider { - - /** - * flag that enables or disables the highlighting arrow - */ - private boolean mDrawHighlightArrow = false; +public class CombinedChart extends BarLineChartBase implements CombinedDataProvider { /** * if set to true, all values are drawn above their bars, instead of below @@ -39,15 +33,19 @@ public class CombinedChart extends BarLineChartBase implements Lin */ 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 = new DrawOrder[]{ - DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER - }; + protected DrawOrder[] mDrawOrder; /** * enum that allows to specify the order in which the different data objects @@ -73,52 +71,58 @@ public CombinedChart(Context context, AttributeSet attrs, int defStyle) { protected void init() { super.init(); - setHighlighter(new CombinedHighlighter(this)); - - // mRenderer = new CombinedChartRenderer(this, mAnimator, - // mViewPortHandler); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (getBarData() != null || getCandleData() != null || getBubbleData() != null) { - mXChartMin = -0.5f; - mXChartMax = mData.getXVals().size() - 0.5f; - - if (getBubbleData() != null) { - - for (IBubbleDataSet set : getBubbleData().getDataSets()) { + // Default values are not ready here yet + mDrawOrder = new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER + }; - final float xmin = set.getXMin(); - final float xmax = set.getXMax(); + setHighlighter(new CombinedHighlighter(this, this)); - if (xmin < mXChartMin) - mXChartMin = xmin; + // Old default behaviour + setHighlightFullBarEnabled(true); - if (xmax > mXChartMax) - mXChartMax = xmax; - } - } - } - - mDeltaX = Math.abs(mXChartMax - mXChartMin); + mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + } - if (mDeltaX == 0.f && getLineData() != null && getLineData().getYValCount() > 0) { - mDeltaX = 1.f; - } + @Override + public CombinedData getCombinedData() { + return mData; } @Override public void setData(CombinedData data) { - mData = null; - mRenderer = null; super.setData(data); - mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + 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) @@ -164,20 +168,6 @@ public boolean isDrawValueAboveBarEnabled() { return mDrawValueAboveBar; } - @Override - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - - /** - * set this to true to draw the highlightning arrow - * - * @param enabled - */ - public void setDrawHighlightArrow(boolean enabled) { - mDrawHighlightArrow = enabled; - } - /** * If set to true, all values are drawn above their bars, instead of below * their top. @@ -199,6 +189,24 @@ 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. * @@ -221,4 +229,44 @@ public void setDrawOrder(DrawOrder[] order) { 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]); + } + } + } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java new file mode 100644 index 0000000000..9aac1ce97c --- /dev/null +++ b/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); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java similarity index 83% rename from MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java index c354217a04..aa7afc4c85 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java @@ -10,7 +10,7 @@ /** * Chart that draws lines, surfaces, circles, ... - * + * * @author Philipp Jahoda */ public class LineChart extends BarLineChartBase implements LineDataProvider { @@ -34,14 +34,6 @@ protected void init() { mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); } - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - } - @Override public LineData getLineData() { return mData; @@ -50,7 +42,7 @@ public LineData getLineData() { @Override protected void onDetachedFromWindow() { // releases the bitmap in the renderer to avoid oom error - if(mRenderer != null && mRenderer instanceof LineChartRenderer) { + if (mRenderer != null && mRenderer instanceof LineChartRenderer) { ((LineChartRenderer) mRenderer).releaseBitmap(); } super.onDetachedFromWindow(); diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java similarity index 67% rename from MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java index 8219a453a7..de11b3a844 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -4,17 +4,17 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.RectF; import android.graphics.Typeface; import android.util.AttributeSet; -import com.github.mikephil.charting.data.Entry; +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.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import java.util.List; @@ -33,19 +33,19 @@ public class PieChart extends PieRadarChartBase { private RectF mCircleBox = new RectF(); /** - * flag indicating if the x-labels should be drawn or not + * flag indicating if entry labels should be drawn or not */ - private boolean mDrawXLabels = true; + private boolean mDrawEntryLabels = true; /** * array that holds the width of each pie-slice in degrees */ - private float[] mDrawAngles; + private float[] mDrawAngles = new float[1]; /** * array that holds the absolute angle in degrees of each slice */ - private float[] mAbsoluteAngles; + private float[] mAbsoluteAngles = new float[1]; /** * if true, the white hole inside the chart will be drawn @@ -72,6 +72,8 @@ public class PieChart extends PieRadarChartBase { */ 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 @@ -92,6 +94,12 @@ public class PieChart extends PieRadarChartBase { 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); } @@ -109,6 +117,9 @@ protected void init() { super.init(); mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); + mXAxis = null; + + mHighlighter = new PieHighlighter(this); } @Override @@ -145,7 +156,7 @@ public void calculateOffsets() { float diameter = getDiameter(); float radius = diameter / 2f; - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); float shift = mData.getDataSet().getSelectionShift(); @@ -155,19 +166,19 @@ public void calculateOffsets() { c.y - radius + shift, c.x + radius - shift, c.y + radius - shift); + + MPPointF.recycleInstance(c); } @Override protected void calcMinMax() { - super.calcMinMax(); - calcAngles(); } @Override - protected float[] getMarkerPosition(Entry e, Highlight highlight) { + protected float[] getMarkerPosition(Highlight highlight) { - PointF center = getCenterCircleBox(); + MPPointF center = getCenterCircleBox(); float r = getRadius(); float off = r / 10f * 3.6f; @@ -180,19 +191,20 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { float rotationAngle = getRotationAngle(); - int i = e.getXIndex(); + int entryIndex = (int) highlight.getX(); // offset needed to center the drawn text in the slice - float offset = mDrawAngles[i] / 2; + float offset = mDrawAngles[entryIndex] / 2; // calculate the text position float x = (float) (r - * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[i] - offset) + * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) * mAnimator.getPhaseY())) + center.x); float y = (float) (r - * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[i] - offset) + * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) * mAnimator.getPhaseY())) + center.y); + MPPointF.recycleInstance(center); return new float[]{x, y}; } @@ -201,14 +213,33 @@ protected float[] getMarkerPosition(Entry e, Highlight highlight) { */ private void calcAngles() { - mDrawAngles = new float[mData.getYValCount()]; - mAbsoluteAngles = new float[mData.getYValCount()]; + 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 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++) { @@ -216,7 +247,20 @@ private void calcAngles() { for (int j = 0; j < set.getEntryCount(); j++) { - mDrawAngles[cnt] = calcAngle(Math.abs(set.getEntryForIndex(j).getVal()), yValueSum); + 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]; @@ -228,27 +272,38 @@ private void calcAngles() { } } + 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 in the given DataSet is set for highlighting or - * not + * Checks if the given index is set to be highlighted. * - * @param xIndex - * @param dataSetIndex + * @param index * @return */ - public boolean needsHighlight(int xIndex, int dataSetIndex) { + public boolean needsHighlight(int index) { // no highlight - if (!valuesToHighlight() || dataSetIndex < 0) + if (!valuesToHighlight()) return false; for (int i = 0; i < mIndicesToHighlight.length; i++) // check if the xvalue for the given dataset needs highlight - if (mIndicesToHighlight[i].getXIndex() == xIndex - && mIndicesToHighlight[i].getDataSetIndex() == dataSetIndex) + if ((int) mIndicesToHighlight[i].getX() == index) return true; return false; @@ -275,6 +330,17 @@ 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) { @@ -300,7 +366,7 @@ public int getDataSetIndexForIndex(int xIndex) { List dataSets = mData.getDataSets(); for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXIndex(xIndex) != null) + if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) return i; } @@ -446,8 +512,8 @@ public RectF getCircleBox() { * * @return */ - public PointF getCenterCircleBox() { - return new PointF(mCircleBox.centerX(), mCircleBox.centerY()); + public MPPointF getCenterCircleBox() { + return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY()); } /** @@ -478,6 +544,26 @@ 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. * @@ -536,7 +622,8 @@ public float getTransparentCircleRadius() { } /** - * Sets the amount of transparency the transparent circle should have 0 = fully transparent, 255 = fully opaque. + * Sets the amount of transparency the transparent circle should have 0 = fully transparent, + * 255 = fully opaque. * Default value is 100. * * @param alpha 0-255 @@ -546,21 +633,69 @@ public void setTransparentCircleAlpha(int alpha) { } /** - * set this to true to draw the x-value text into the pie slices + * 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) { - mDrawXLabels = enabled; + mDrawEntryLabels = enabled; } /** - * returns true if drawing x-values is enabled, false if not + * 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 isDrawSliceTextEnabled() { - return mDrawXLabels; + 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; } /** @@ -575,7 +710,7 @@ public boolean isDrawRoundedSlicesEnabled() { /** * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original value. Values provided for the ValueFormatter to + * not with their original value. Values provided for the IValueFormatter to * format are then provided in percent. * * @param enabled @@ -594,7 +729,8 @@ public boolean isUsePercentValuesEnabled() { } /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie hole + * 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) { @@ -602,7 +738,8 @@ public void setCenterTextRadiusPercent(float percent) { } /** - * the rectangular radius of the bounding box for the center text, as a percentage of the pie hole + * 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() { @@ -630,6 +767,32 @@ public void setMaxAngle(float maxangle) { 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 @@ -638,5 +801,4 @@ protected void onDetachedFromWindow() { } super.onDetachedFromWindow(); } - } diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java similarity index 57% rename from MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java index 64dc6abb01..e6d38d3a01 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -6,43 +6,48 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; import android.content.Context; -import android.graphics.PointF; 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.components.Legend.LegendPosition; +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.SelectionDetail; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; -import java.util.ArrayList; -import java.util.List; - /** * Baseclass of PieChart and RadarChart. - * + * * @author Philipp Jahoda */ public abstract class PieRadarChartBase>> extends Chart { - /** holds the normalized version of the current rotation angle of the chart */ + /** + * 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 */ + /** + * 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 */ + /** + * 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 */ + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ protected float mMinOffset = 0.f; public PieRadarChartBase(Context context) { @@ -66,7 +71,12 @@ protected void init() { @Override protected void calcMinMax() { - mDeltaX = mData.getXVals().size() - 1; + //mXAxis.mAxisRange = mData.getXVals().size() - 1; + } + + @Override + public int getMaxVisibleCount() { + return mData.getEntryCount(); } @Override @@ -103,119 +113,119 @@ public void calculateOffsets() { float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f; - if (mLegend != null && mLegend.isEnabled()) { - - float fullLegendWidth = Math.min(mLegend.mNeededWidth, - mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()) + - mLegend.getFormSize() + mLegend.getFormToTextSpace(); - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); - - legendRight = fullLegendWidth + spacing; - - } else if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART) { - - // 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; - - PointF c = getCenter(); - - PointF bottomRight = new PointF(getWidth() - legendWidth + 15, legendHeight + 15); - float distLegend = distanceToCenter(bottomRight.x, bottomRight.y); - - PointF reference = getPosition(c, getRadius(), - getAngleForPoint(bottomRight.x, bottomRight.y)); - - float distReference = distanceToCenter(reference.x, reference.y); - float min = Utils.convertDpToPixel(5f); - - if (distLegend < distReference) { - - float diff = distReference - distLegend; - legendRight = min + diff; - } - - if (bottomRight.y >= c.y && getHeight() - legendWidth > getWidth()) { - legendRight = legendWidth; - } - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); - - legendLeft = fullLegendWidth + spacing; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART) { - - // 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; - - PointF c = getCenter(); - - PointF bottomLeft = new PointF(legendWidth - 15, legendHeight + 15); - float distLegend = distanceToCenter(bottomLeft.x, bottomLeft.y); - - PointF reference = getPosition(c, getRadius(), - getAngleForPoint(bottomLeft.x, bottomLeft.y)); - - float distReference = distanceToCenter(reference.x, reference.y); - float min = Utils.convertDpToPixel(5f); - - if (distLegend < distReference) { - - float diff = distReference - distLegend; - legendLeft = min + diff; - } - - if (bottomLeft.y >= c.y && getHeight() - legendWidth > getWidth()) { - legendLeft = legendWidth; + 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; + } } - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - // 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(); - - legendBottom = Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - - } else if (mLegend.getPosition() == LegendPosition.ABOVE_CHART_LEFT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.ABOVE_CHART_CENTER) { - - // 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(); - - legendTop = Math.min(mLegend.mNeededHeight + yOffset, mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); - + 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 = ((RadarChart) this).getXAxis(); + XAxis x = this.getXAxis(); if (x.isEnabled() && x.isDrawLabelsEnabled()) { minOffset = Math.max(minOffset, x.mLabelRotatedWidth); @@ -243,14 +253,14 @@ public void calculateOffsets() { * 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) { - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); double tx = x - c.x, ty = y - c.y; double length = Math.sqrt(tx * tx + ty * ty); @@ -268,25 +278,33 @@ public float getAngleForPoint(float x, float y) { 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 + * @param angle in degrees, converted to radians internally * @return */ - protected PointF getPosition(PointF center, float dist, float angle) { + public MPPointF getPosition(MPPointF center, float dist, float angle) { - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(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. @@ -297,7 +315,7 @@ protected PointF getPosition(PointF center, float dist, float angle) { */ public float distanceToCenter(float x, float y) { - PointF c = getCenterOffsets(); + MPPointF c = getCenterOffsets(); float dist = 0f; @@ -319,13 +337,15 @@ public float distanceToCenter(float x, float 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 */ @@ -334,7 +354,7 @@ public float distanceToCenter(float x, float y) { /** * Set an offset for the rotation of the RadarChart in degrees. Default 270f * --> top (NORTH) - * + * * @param angle */ public void setRotationAngle(float angle) { @@ -367,7 +387,7 @@ public float getRotationAngle() { /** * 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) { @@ -376,43 +396,51 @@ public void setRotationEnabled(boolean 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 */ + /** + * 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 */ + /** + * 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(); @@ -420,7 +448,7 @@ public float getDiameter() { /** * Returns the base offset needed for the chart without calculating the * legend size. - * + * * @return */ protected abstract float getRequiredBaseOffset(); @@ -437,33 +465,6 @@ public float getYChartMin() { return 0; } - /** - * Returns an array of SelectionDetail objects for the given x-index. The SelectionDetail - * 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. - * - * @return - */ - public List getSelectionDetailsAtIndex(int xIndex) { - - List vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - IDataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - final float yVal = dataSet.getYValForXIndex(xIndex); - if (yVal == Float.NaN) - continue; - - vals.add(new SelectionDetail(yVal, i, dataSet)); - } - - return vals; - } - /** * ################ ################ ################ ################ */ @@ -471,23 +472,20 @@ public List getSelectionDetailsAtIndex(int xIndex) { /** * Applys a spin animation to the Chart. - * + * * @param durationmillis * @param fromangle * @param toangle */ @SuppressLint("NewApi") - public void spin(int durationmillis, float fromangle, float toangle, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; + 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.getEasingFunctionFromOption(easing)); + spinAnimator.setInterpolator(easing); spinAnimator.addUpdateListener(new AnimatorUpdateListener() { diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java similarity index 74% rename from MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java index ea012e7abe..8c0885395d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/RadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/RadarChart.java @@ -4,16 +4,13 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; -import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.RadarData; -import com.github.mikephil.charting.highlight.Highlight; +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; @@ -67,11 +64,6 @@ public class RadarChart extends PieRadarChartBase { */ private YAxis mYAxis; - /** - * the object representing the x-axis labels - */ - private XAxis mXAxis; - protected YAxisRendererRadarChart mYAxisRenderer; protected XAxisRendererRadarChart mXAxisRenderer; @@ -92,8 +84,7 @@ protected void init() { super.init(); mYAxis = new YAxis(AxisDependency.LEFT); - mXAxis = new XAxis(); - mXAxis.setSpaceBetweenLabels(0); + mYAxis.setLabelXOffset(10f); mWebLineWidth = Utils.convertDpToPixel(1.5f); mInnerWebLineWidth = Utils.convertDpToPixel(0.75f); @@ -101,54 +92,16 @@ protected void init() { 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(); - float minLeft = !Float.isNaN(mYAxis.getAxisMinValue()) - ? mYAxis.getAxisMinValue() - : mData.getYMin(AxisDependency.LEFT); - float maxLeft = !Float.isNaN(mYAxis.getAxisMaxValue()) - ? mYAxis.getAxisMaxValue() - : mData.getYMax(AxisDependency.LEFT); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - float leftRange = Math.abs(maxLeft - minLeft); - - float topSpaceLeft = leftRange / 100f * mYAxis.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mYAxis.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - // Use the values as they are - mYAxis.mAxisMinimum = !Float.isNaN(mYAxis.getAxisMinValue()) - ? mYAxis.getAxisMinValue() - : (minLeft - bottomSpaceLeft); - mYAxis.mAxisMaximum = !Float.isNaN(mYAxis.getAxisMaxValue()) - ? mYAxis.getAxisMaxValue() - : (maxLeft + topSpaceLeft); - - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); - } - - @Override - protected float[] getMarkerPosition(Entry e, Highlight highlight) { - - float angle = getSliceAngle() * e.getXIndex() + getRotationAngle(); - float val = e.getVal() * getFactor(); - PointF c = getCenterOffsets(); - - PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))), - (float) (c.y + val * Math.sin(Math.toRadians(angle)))); - - return new float[]{ - p.x, p.y - }; + mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT)); + mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount()); } @Override @@ -158,12 +111,8 @@ public void notifyDataSetChanged() { calcMinMax(); -// if (mYAxis.needsDefaultFormatter()) { -// mYAxis.setValueFormatter(mDefaultFormatter); -// } - - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum); - mXAxisRenderer.computeAxis(mData.getXValMaximumLength(), mData.getXVals()); + mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); if (mLegend != null && !mLegend.isLegendCustom()) mLegendRenderer.computeLegend(mData); @@ -178,18 +127,28 @@ protected void onDraw(Canvas 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); - mYAxisRenderer.renderLimitLines(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); @@ -208,8 +167,7 @@ protected void onDraw(Canvas canvas) { */ public float getFactor() { RectF content = mViewPortHandler.getContentRect(); - return (float) Math.min(content.width() / 2f, content.height() / 2f) - / mYAxis.mAxisRange; + return Math.min(content.width() / 2f, content.height() / 2f) / mYAxis.mAxisRange; } /** @@ -218,7 +176,7 @@ public float getFactor() { * @return */ public float getSliceAngle() { - return 360f / (float) mData.getXValCount(); + return 360f / (float) mData.getMaxEntryCountSet().getEntryCount(); } @Override @@ -229,12 +187,21 @@ public int getIndexForAngle(float angle) { float sliceangle = getSliceAngle(); - for (int i = 0; i < mData.getXValCount(); i++) { - if (sliceangle * (i + 1) - sliceangle / 2f > a) - return i; + 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 0; + return index; } /** @@ -246,16 +213,6 @@ public YAxis getYAxis() { return mYAxis; } - /** - * Returns the object that represents all x-labels that are placed around - * the RadarChart. - * - * @return - */ - public XAxis getXAxis() { - return mXAxis; - } - /** * Sets the width of the web lines that come from the center. * diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java similarity index 57% rename from MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java index 76a10cb894..37e8395b5e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java @@ -12,18 +12,11 @@ * 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 implements ScatterDataProvider { - /** - * enum that defines the shape that is drawn where the values are - */ - public enum ScatterShape { - SQUARE, CIRCLE, TRIANGLE, CROSS, X, - } - public ScatterChart(Context context) { super(context); } @@ -36,37 +29,49 @@ public ScatterChart(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } + @Override protected void init() { super.init(); mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; + + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); } @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); + public ScatterData getScatterData() { + return mData; } /** - * Returns all possible predefined ScatterShapes. - * - * @return + * 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 static ScatterShape[] getAllPossibleShapes() { - return new ScatterShape[] { - ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS - }; - } + public enum ScatterShape { - public ScatterData getScatterData() { - return mData; - }; + 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}; + } + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java new file mode 100644 index 0000000000..c90b4fc9b9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/AxisBase.java @@ -0,0 +1,816 @@ + +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.IAxisValueFormatter; +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 IAxisValueFormatter 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 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(); + } + + /** + * 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 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().getFormattedValue(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(IAxisValueFormatter f) { + + if (f == null) + mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals); + else + mAxisValueFormatter = f; + } + + /** + * Returns the formatter used for formatting the axis labels. + * + * @return + */ + public IAxisValueFormatter 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java similarity index 91% rename from MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java index 713f89dd79..d3a1d4d82a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -19,12 +19,12 @@ public abstract class ComponentBase { protected boolean mEnabled = true; /** - * the offset in pixels this axis labels have on the x-axis + * the offset in pixels this component has on the x-axis */ protected float mXOffset = 5f; /** - * the offset in pixels this axis labels have on the Y-axis + * the offset in pixels this component has on the Y-axis */ protected float mYOffset = 5f; @@ -36,13 +36,14 @@ public abstract class ComponentBase { /** * the text size of the labels */ - protected float mTextSize = 10f; + protected float mTextSize = Utils.convertDpToPixel(10f); /** * the text color to use for the labels */ protected int mTextColor = Color.BLACK; + public ComponentBase() { } @@ -106,10 +107,10 @@ public void setTypeface(Typeface tf) { } /** - * sets the size of the label text in pixels min = 6f, max = 24f, default + * sets the size of the label text in density pixels min = 6f, max = 24f, default * 10f * - * @param size + * @param size the text size, in DP */ public void setTextSize(float size) { @@ -122,7 +123,7 @@ public void setTextSize(float size) { } /** - * returns the text size that is currently set for the labels + * returns the text size that is currently set for the labels, in pixels * * @return */ @@ -130,6 +131,7 @@ 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. diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Description.java new file mode 100644 index 0000000000..18294a3270 --- /dev/null +++ b/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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/IMarker.java new file mode 100644 index 0000000000..3b8ca43c81 --- /dev/null +++ b/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); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/Legend.java new file mode 100644 index 0000000000..708129259b --- /dev/null +++ b/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 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 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 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 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 mCalculatedLabelSizes = new ArrayList<>(16); + private List mCalculatedLabelBreakPoints = new ArrayList<>(16); + private List mCalculatedLineSizes = new ArrayList<>(16); + + public List getCalculatedLabelSizes() { + return mCalculatedLabelSizes; + } + + public List getCalculatedLabelBreakPoints() { + return mCalculatedLabelBreakPoints; + } + + public List 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LegendEntry.java new file mode 100644 index 0000000000..3acec0f461 --- /dev/null +++ b/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; + +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerImage.java new file mode 100644 index 0000000000..7bd7b8e6c3 --- /dev/null +++ b/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 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); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/MarkerView.java new file mode 100644 index 0000000000..162e88e33c --- /dev/null +++ b/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 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); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java new file mode 100644 index 0000000000..77d4aaf98f --- /dev/null +++ b/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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java similarity index 50% rename from MPChartLib/src/com/github/mikephil/charting/components/YAxis.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java index ce0f19f0a8..d2071ec5a8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -3,15 +3,15 @@ import android.graphics.Color; import android.graphics.Paint; -import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.DefaultYAxisValueFormatter; -import com.github.mikephil.charting.formatter.YAxisValueFormatter; 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 + * 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 @@ -19,29 +19,9 @@ public class YAxis extends AxisBase { /** - * custom formatter that is used instead of the auto-formatter if set + * indicates if the bottom y-label entry is drawn or not */ - protected YAxisValueFormatter mYAxisValueFormatter; - - /** - * the actual array of entries - */ - public float[] mEntries = 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 y-label entries the y-labels should have, default 6 - */ - private int mLabelCount = 6; + private boolean mDrawBottomYLabelEntry = true; /** * indicates if the top y-label entry is drawn or not @@ -49,24 +29,24 @@ public class YAxis extends AxisBase { private boolean mDrawTopYLabelEntry = true; /** - * if true, the y-labels show only the minimum and maximum value + * flag that indicates if the axis is inverted or not */ - protected boolean mShowOnlyMinMax = false; + protected boolean mInverted = false; /** - * flag that indicates if the axis is inverted or not + * flag that indicates if the zero-line should be drawn regardless of other grid lines */ - protected boolean mInverted = false; + protected boolean mDrawZeroLine = false; /** - * if true, the set number of y-labels will be forced + * flag indicating that auto scale min restriction should be used */ - protected boolean mForceLabels = false; + private boolean mUseAutoScaleRestrictionMin = false; /** - * flag that indicates if the zero-line should be drawn regardless of other grid lines + * flag indicating that auto scale max restriction should be used */ - protected boolean mDrawZeroLine = true; + private boolean mUseAutoScaleRestrictionMax = false; /** * Color of the zero line @@ -78,16 +58,6 @@ public class YAxis extends AxisBase { */ protected float mZeroLineWidth = 1f; - /** - * custom minimum value this axis represents - */ - protected float mCustomAxisMin = Float.NaN; - - /** - * custom maximum value this axis represents - */ - protected float mCustomAxisMax = Float.NaN; - /** * axis space from the largest value to the top in percent of the total axis range */ @@ -98,18 +68,15 @@ public class YAxis extends AxisBase { */ protected float mSpacePercentBottom = 10f; - public float mAxisMaximum = 0f; - public float mAxisMinimum = 0f; - /** - * the total range of values this axis covers + * the position of the y-labels relative to the chart */ - public float mAxisRange = 0f; + private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; /** - * the position of the y-labels relative to the chart + * the horizontal offset of the y-label */ - private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; + private float mXLabelOffset = 0.0f; /** * enum for the position of the y-labels relative to the chart @@ -123,6 +90,20 @@ public enum YAxisLabelPosition { */ private AxisDependency mAxisDependency; + /** + * the minimum width that the axis should take (in dp). + *

+ * 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. * @@ -134,6 +115,8 @@ public enum AxisDependency { public YAxis() { super(); + + // default left this.mAxisDependency = AxisDependency.LEFT; this.mYOffset = 0f; } @@ -149,98 +132,101 @@ public AxisDependency getAxisDependency() { } /** - * returns the position of the y-labels + * @return the minimum width that the axis should take (in dp). */ - public YAxisLabelPosition getLabelPosition() { - return mPosition; + public float getMinWidth() { + return mMinWidth; } /** - * sets the position of the y-labels + * Sets the minimum width that the axis should take (in dp). * - * @param pos + * @param minWidth */ - public void setPosition(YAxisLabelPosition pos) { - mPosition = pos; + public void setMinWidth(float minWidth) { + mMinWidth = minWidth; } /** - * returns true if drawing the top y-axis label entry is enabled - * - * @return + * @return the maximum width that the axis can take (in dp). */ - public boolean isDrawTopYLabelEntryEnabled() { - return mDrawTopYLabelEntry; + public float getMaxWidth() { + return mMaxWidth; } /** - * 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 + * Sets the maximum width that the axis can take (in dp). * - * @param enabled + * @param maxWidth */ - public void setDrawTopYLabelEntry(boolean enabled) { - mDrawTopYLabelEntry = enabled; + public void setMaxWidth(float maxWidth) { + mMaxWidth = maxWidth; } /** - * 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 sould 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 + * returns the position of the y-labels */ - public void setLabelCount(int count, boolean force) { + public YAxisLabelPosition getLabelPosition() { + return mPosition; + } - if (count > 25) - count = 25; - if (count < 2) - count = 2; + /** + * sets the position of the y-labels + * + * @param pos + */ + public void setPosition(YAxisLabelPosition pos) { + mPosition = pos; + } - mLabelCount = count; - mForceLabels = force; + /** + * returns the horizontal offset of the y-label + */ + public float getLabelXOffset() { + return mXLabelOffset; } /** - * Returns the number of label entries the y-axis should have + * sets the horizontal offset of the y-label * - * @return + * @param xOffset */ - public int getLabelCount() { - return mLabelCount; + public void setLabelXOffset(float xOffset) { + mXLabelOffset = xOffset; } /** - * Returns true if focing the y-label count is enabled. Default: false + * returns true if drawing the top y-axis label entry is enabled * * @return */ - public boolean isForceLabelsEnabled() { - return mForceLabels; + public boolean isDrawTopYLabelEntryEnabled() { + return mDrawTopYLabelEntry; } /** - * If enabled, the YLabels will only show the minimum and maximum value of the chart. This will ignore/override the - * set label count. + * returns true if drawing the bottom y-axis label entry is enabled * - * @param enabled + * @return */ - public void setShowOnlyMinMax(boolean enabled) { - mShowOnlyMinMax = enabled; + public boolean isDrawBottomYLabelEntryEnabled() { + return mDrawBottomYLabelEntry; } /** - * Returns true if showing only min and max value is enabled. + * 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 * - * @return + * @param enabled */ - public boolean isShowOnlyMinMaxEnabled() { - return mShowOnlyMinMax; + 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 + * 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 @@ -260,61 +246,16 @@ public boolean isInverted() { /** * This method is deprecated. - * Use setAxisMinValue(...) / setAxisMaxValue(...) instead. + * Use setAxisMinimum(...) / setAxisMaximum(...) instead. * * @param startAtZero */ @Deprecated public void setStartAtZero(boolean startAtZero) { if (startAtZero) - setAxisMinValue(0f); + setAxisMinimum(0f); else - resetAxisMinValue(); - } - - public float getAxisMinValue() { - 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 setAxisMinValue(float min) { - mCustomAxisMin = min; - } - - /** - * By calling this method, any custom minimum value that has been previously set is reseted, and the calculation is - * done automatically. - */ - public void resetAxisMinValue() { - mCustomAxisMin = Float.NaN; - } - - public float getAxisMaxValue() { - return mCustomAxisMax; - } - - /** - * 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 setAxisMaxValue(float max) { - mCustomAxisMax = max; - } - - /** - * By calling this method, any custom maximum value that has been previously set is reseted, and the calculation is - * done automatically. - */ - public void resetAxisMaxValue() { - mCustomAxisMax = Float.NaN; + resetAxisMinimum(); } /** @@ -359,7 +300,7 @@ public boolean isDrawZeroLineEnabled() { /** * Set this to true to draw the zero-line regardless of weather other - * grid-lines are enabled or not. + * grid-lines are enabled or not. Default: false * * @param mDrawZeroLine */ @@ -404,7 +345,20 @@ public float getRequiredWidthSpace(Paint p) { p.setTextSize(mTextSize); String label = getLongestLabel(); - return (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; + 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; } /** @@ -421,87 +375,93 @@ public float getRequiredHeightSpace(Paint p) { return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f; } - @Override - public String getLongestLabel() { - - String longest = ""; - - for (int i = 0; i < mEntries.length; i++) { - String text = getFormattedLabel(i); - - if (longest.length() < text.length()) - longest = text; - } - - return longest; - } - /** - * Returns the formatted y-label at the specified index. This will either use the auto-formatter or the custom - * formatter (if one is set). + * Returns true if this axis needs horizontal offset, false if no offset is needed. * - * @param index * @return */ - public String getFormattedLabel(int index) { - - if (index < 0 || index >= mEntries.length) - return ""; + public boolean needsOffset() { + if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition + .OUTSIDE_CHART) + return true; else - return getValueFormatter().getFormattedValue(mEntries[index], this); + return false; } /** - * 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 + * Returns true if autoscale restriction for axis min value is enabled */ - public void setValueFormatter(YAxisValueFormatter f) { - - if (f == null) - mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); - else - mYAxisValueFormatter = f; + @Deprecated + public boolean isUseAutoScaleMinRestriction( ) { + return mUseAutoScaleRestrictionMin; } /** - * Returns the formatter used for formatting the axis labels. - * - * @return + * Sets autoscale restriction for axis min value as enabled/disabled */ - public YAxisValueFormatter getValueFormatter() { - - if (mYAxisValueFormatter == null) - mYAxisValueFormatter = new DefaultYAxisValueFormatter(mDecimals); - - return mYAxisValueFormatter; + @Deprecated + public void setUseAutoScaleMinRestriction( boolean isEnabled ) { + mUseAutoScaleRestrictionMin = isEnabled; } /** - * If this component has no YAxisValueFormatter or is only equipped with the default one (no custom set), return true. - * - * @return + * Returns true if autoscale restriction for axis max value is enabled */ - public boolean needsDefaultFormatter() { - if (mYAxisValueFormatter == null) - return true; - if (mYAxisValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; + @Deprecated + public boolean isUseAutoScaleMaxRestriction() { + return mUseAutoScaleRestrictionMax; } /** - * Returns true if this axis needs horizontal offset, false if no offset is needed. - * - * @return + * Sets autoscale restriction for axis max value as enabled/disabled */ - public boolean needsOffset() { - if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition.OUTSIDE_CHART) - return true; - else - return false; + @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); } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarData.java new file mode 100644 index 0000000000..16d60f6f9c --- /dev/null +++ b/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 { + + /** + * 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 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java similarity index 52% rename from MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java index fb1dccccf8..e65638805b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -4,17 +4,13 @@ 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 implements IBarDataSet { - /** - * space indicator between the bars 0.1f == 10 % - */ - private float mBarSpace = 0.15f; - /** * the maximum number of bars that are stacked upon each other, this value * is calculated from the Entries that are added to the DataSet @@ -26,6 +22,10 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl */ 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 */ @@ -39,9 +39,9 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet impl /** * array of labels used to describe the different values of the stacked bars */ - private String[] mStackLabels = new String[]{ - "Stack" - }; + private String[] mStackLabels = new String[]{}; + + protected List mFills = null; public BarDataSet(List yVals, String label) { super(yVals, label); @@ -54,23 +54,83 @@ public BarDataSet(List yVals, String label) { @Override public DataSet copy() { + List entries = new ArrayList(); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); + } + BarDataSet copied = new BarDataSet(entries, getLabel()); + copy(copied); + return copied; + } - List yVals = new ArrayList(); + protected void copy(BarDataSet barDataSet) { + super.copy(barDataSet); + barDataSet.mStackSize = mStackSize; + barDataSet.mBarShadowColor = mBarShadowColor; + barDataSet.mBarBorderWidth = mBarBorderWidth; + barDataSet.mStackLabels = mStackLabels; + barDataSet.mHighLightAlpha = mHighLightAlpha; + } - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((BarEntry) mYVals.get(i)).copy()); - } + @Override + public List getFills() { + return mFills; + } - BarDataSet copied = new BarDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mStackSize = mStackSize; - copied.mBarSpace = mBarSpace; - copied.mBarShadowColor = mBarShadowColor; - copied.mStackLabels = mStackLabels; - copied.mHighLightColor = mHighLightColor; - copied.mHighLightAlpha = mHighLightAlpha; + @Override + public Fill getFill(int index) { + return mFills.get(index % mFills.size()); + } - return copied; + /** + * This method is deprecated. + * Use getFills() instead. + */ + @Deprecated + public List 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 gradientColors) { + this.mFills = gradientColors; + } + + /** + * Sets the fills for the bars in this dataset. + * + * @param fills + */ + public void setFills(List fills) { + this.mFills = fills; } /** @@ -83,7 +143,7 @@ private void calcEntryCountIncludingStacks(List yVals) { for (int i = 0; i < yVals.size(); i++) { - float[] vals = yVals.get(i).getVals(); + float[] vals = yVals.get(i).getYVals(); if (vals == null) mEntryCountStacks++; @@ -100,7 +160,7 @@ private void calcStackSize(List yVals) { for (int i = 0; i < yVals.size(); i++) { - float[] vals = yVals.get(i).getVals(); + float[] vals = yVals.get(i).getYVals(); if (vals != null && vals.length > mStackSize) mStackSize = vals.length; @@ -108,53 +168,27 @@ private void calcStackSize(List yVals) { } @Override - public void calcMinMax(int start, int end) { - - if (mYVals == null) - return; + protected void calcMinMax(BarEntry e) { - final int yValCount = mYVals.size(); + if (e != null && !Float.isNaN(e.getY())) { - if (yValCount == 0) - return; + if (e.getYVals() == null) { - int endValue; + if (e.getY() < mYMin) + mYMin = e.getY(); - if (end == 0 || end >= yValCount) - endValue = yValCount - 1; - else - endValue = end; + if (e.getY() > mYMax) + mYMax = e.getY(); + } else { - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; + if (-e.getNegativeSum() < mYMin) + mYMin = -e.getNegativeSum(); - for (int i = start; i <= endValue; i++) { - - BarEntry e = mYVals.get(i); - - if (e != null && !Float.isNaN(e.getVal())) { - - if (e.getVals() == null) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } else { - - if (-e.getNegativeSum() < mYMin) - mYMin = -e.getNegativeSum(); - - if (e.getPositiveSum() > mYMax) - mYMax = e.getPositiveSum(); - } + if (e.getPositiveSum() > mYMax) + mYMax = e.getPositiveSum(); } - } - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; + calcMinMaxX(e); } } @@ -179,42 +213,59 @@ public int getEntryCountStacks() { } /** - * returns the space between bars in percent of the whole width of one value + * 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(...). * - * @return + * @param color */ - public float getBarSpacePercent() { - return mBarSpace * 100f; + public void setBarShadowColor(int color) { + mBarShadowColor = color; } @Override - public float getBarSpace() { - return mBarSpace; + public int getBarShadowColor() { + return mBarShadowColor; } /** - * sets the space between the bars in percent (0-100) of the total bar width + * Sets the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. * - * @param percent + * @return */ - public void setBarSpacePercent(float percent) { - mBarSpace = percent / 100f; + public void setBarBorderWidth(float width) { + mBarBorderWidth = width; } /** - * 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(...). + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. * - * @param color + * @return */ - public void setBarShadowColor(int color) { - mBarShadowColor = color; + @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 getBarShadowColor() { - return mBarShadowColor; + public int getBarBorderColor() { + return mBarBorderColor; } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarEntry.java new file mode 100644 index 0000000000..365ef51a2d --- /dev/null +++ b/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; + } + } + } +} + + diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java similarity index 55% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java index 84dc3df67e..c09eadec9b 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -16,20 +16,12 @@ public abstract class BarLineScatterCandleBubbleData xVals) { - super(xVals); - } - - public BarLineScatterCandleBubbleData(String[] xVals) { - super(xVals); - } - public BarLineScatterCandleBubbleData(List xVals, List sets) { - super(xVals, sets); + public BarLineScatterCandleBubbleData(T... sets) { + super(sets); } - public BarLineScatterCandleBubbleData(String[] xVals, List sets) { - super(xVals, sets); + public BarLineScatterCandleBubbleData(List sets) { + super(sets); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java similarity index 70% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java index fba8216c3d..eab6dccc55 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java @@ -9,12 +9,16 @@ /** * Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart. - * + * * @author Philipp Jahoda */ -public abstract class BarLineScatterCandleBubbleDataSet extends DataSet implements IBarLineScatterCandleBubbleDataSet { +public abstract class BarLineScatterCandleBubbleDataSet + extends DataSet + implements IBarLineScatterCandleBubbleDataSet { - /** default highlight color */ + /** + * default highlight color + */ protected int mHighLightColor = Color.rgb(255, 187, 115); public BarLineScatterCandleBubbleDataSet(List yVals, String label) { @@ -25,7 +29,7 @@ public BarLineScatterCandleBubbleDataSet(List yVals, String 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) { @@ -36,4 +40,9 @@ public void setHighLightColor(int color) { public int getHighLightColor() { return mHighLightColor; } + + protected void copy(BarLineScatterCandleBubbleDataSet barLineScatterCandleBubbleDataSet) { + super.copy(barLineScatterCandleBubbleDataSet); + barLineScatterCandleBubbleDataSet.mHighLightColor = mHighLightColor; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java similarity index 65% rename from MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java index 3d816c0f34..7e7445cac7 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BaseDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -2,13 +2,15 @@ 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.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; 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; @@ -49,18 +51,33 @@ public abstract class BaseDataSet implements IDataSet { /** * custom formatter that is used instead of the auto-formatter if set */ - protected transient ValueFormatter mValueFormatter; + protected transient IValueFormatter 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 */ @@ -97,7 +114,7 @@ public BaseDataSet(String label) { * Use this method to tell the data set that the underlying data has changed. */ public void notifyDataSetChanged() { - calcMinMax(0, getEntryCount() - 1); + calcMinMax(); } @@ -110,7 +127,9 @@ public List getColors() { return mColors; } - public List getValueColors() { return mValueColors; } + public List getValueColors() { + return mValueColors; + } @Override public int getColor() { @@ -148,7 +167,7 @@ public void setColors(List colors) { * * @param colors */ - public void setColors(int[] colors) { + public void setColors(int... colors) { this.mColors = ColorTemplate.createColors(colors); } @@ -164,13 +183,15 @@ public void setColors(int[] colors) { */ public void setColors(int[] colors, Context c) { - List clrs = new ArrayList(); + if (mColors == null) { + mColors = new ArrayList<>(); + } + + mColors.clear(); for (int color : colors) { - clrs.add(c.getResources().getColor(color)); + mColors.add(c.getResources().getColor(color)); } - - mColors = clrs; } /** @@ -222,10 +243,15 @@ public void setColors(int[] colors, int alpha) { * Resets all colors of this DataSet and recreates the colors array. */ public void resetColors() { - mColors = new ArrayList(); + if (mColors == null) { + mColors = new ArrayList(); + } + mColors.clear(); } - /** ###### ###### OTHER STYLING RELATED METHODS ##### ###### */ + /** + * ###### ###### OTHER STYLING RELATED METHODS ##### ###### + */ @Override public void setLabel(String label) { @@ -248,7 +274,7 @@ public boolean isHighlightEnabled() { } @Override - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; @@ -257,12 +283,17 @@ public void setValueFormatter(ValueFormatter f) { } @Override - public ValueFormatter getValueFormatter() { - if (mValueFormatter == null) - return new DefaultValueFormatter(1); + public IValueFormatter getValueFormatter() { + if (needsFormatter()) + return Utils.getDefaultValueFormatter(); return mValueFormatter; } + @Override + public boolean needsFormatter() { + return mValueFormatter == null; + } + @Override public void setValueTextColor(int color) { mValueColors.clear(); @@ -304,6 +335,42 @@ 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; @@ -314,6 +381,28 @@ 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; @@ -335,13 +424,15 @@ public void setAxisDependency(YAxis.AxisDependency dependency) { } - /** ###### ###### DATA RELATED METHODS ###### ###### */ + /** + * ###### ###### DATA RELATED METHODS ###### ###### + */ @Override public int getIndexInEntries(int xIndex) { for (int i = 0; i < getEntryCount(); i++) { - if (xIndex == getEntryForIndex(i).getXIndex()) + if (xIndex == getEntryForIndex(i).getX()) return i; } @@ -351,32 +442,65 @@ public int getIndexInEntries(int xIndex) { @Override public boolean removeFirst() { - T entry = getEntryForIndex(0); - return removeEntry(entry); + if (getEntryCount() > 0) { + + T entry = getEntryForIndex(0); + return removeEntry(entry); + } else + return false; } @Override public boolean removeLast() { - T entry = getEntryForIndex(getEntryCount() - 1); - return removeEntry(entry); + if (getEntryCount() > 0) { + + T e = getEntryForIndex(getEntryCount() - 1); + return removeEntry(e); + } else + return false; } @Override - public boolean removeEntry(int xIndex) { + public boolean removeEntryByXValue(float xValue) { - T e = getEntryForXIndex(xIndex); + 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)) + 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; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseEntry.java new file mode 100644 index 0000000000..eb1ada84d8 --- /dev/null +++ b/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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleData.java new file mode 100644 index 0000000000..8fb72f17c5 --- /dev/null +++ b/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 { + + public BubbleData() { + super(); + } + + public BubbleData(IBubbleDataSet... dataSets) { + super(dataSets); + } + + public BubbleData(List 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); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleDataSet.java new file mode 100644 index 0000000000..9ef87fb2d5 --- /dev/null +++ b/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 implements IBubbleDataSet { + + protected float mMaxSize; + protected boolean mNormalizeSize = true; + + private float mHighlightCircleWidth = 2.5f; + + public BubbleDataSet(List 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 copy() { + List entries = new ArrayList(); + 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BubbleEntry.java new file mode 100644 index 0000000000..35d8330aaa --- /dev/null +++ b/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; + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleData.java new file mode 100644 index 0000000000..1e90db2ba5 --- /dev/null +++ b/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 { + + public CandleData() { + super(); + } + + public CandleData(List dataSets) { + super(dataSets); + } + + public CandleData(ICandleDataSet... dataSets) { + super(dataSets); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java similarity index 74% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java index 03f09251bf..dcd5b76cea 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -25,7 +25,7 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet im /** * should the candle bars show? * when false, only "ticks" will show - * + *

* - default: true */ private boolean mShowCandleBar = true; @@ -55,23 +55,23 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet im /** * color for open == close */ - protected int mNeutralColor = ColorTemplate.COLOR_NONE; + protected int mNeutralColor = ColorTemplate.COLOR_SKIP; /** * color for open < close */ - protected int mIncreasingColor = ColorTemplate.COLOR_NONE; + protected int mIncreasingColor = ColorTemplate.COLOR_SKIP; /** * color for open > close */ - protected int mDecreasingColor = ColorTemplate.COLOR_NONE; + protected int mDecreasingColor = ColorTemplate.COLOR_SKIP; /** * shadow line color, set -1 for backward compatibility and uses default * color */ - protected int mShadowColor = ColorTemplate.COLOR_NONE; + protected int mShadowColor = ColorTemplate.COLOR_SKIP; public CandleDataSet(List yVals, String label) { super(yVals, label); @@ -79,56 +79,56 @@ public CandleDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((CandleEntry) mYVals.get(i)).copy()); + List entries = new ArrayList(); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } - - CandleDataSet copied = new CandleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShadowWidth = mShadowWidth; - copied.mShowCandleBar = mShowCandleBar; - copied.mBarSpace = mBarSpace; - copied.mHighLightColor = mHighLightColor; - copied.mIncreasingPaintStyle = mIncreasingPaintStyle; - copied.mDecreasingPaintStyle = mDecreasingPaintStyle; - copied.mShadowColor = mShadowColor; - + CandleDataSet copied = new CandleDataSet(entries, getLabel()); + copy(copied); return copied; } - @Override - public void calcMinMax(int start, int end) { - // super.calcMinMax(); + 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; + } - if (mYVals == null) - return; + @Override + protected void calcMinMax(CandleEntry e) { - if (mYVals.size() == 0) - return; + if (e.getLow() < mYMin) + mYMin = e.getLow(); - int endValue; + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); - if (end == 0 || end >= mYVals.size()) - endValue = mYVals.size() - 1; - else - endValue = end; + calcMinMaxX(e); + } - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; + @Override + protected void calcMinMaxY(CandleEntry e) { - for (int i = start; i <= endValue; i++) { + if (e.getHigh() < mYMin) + mYMin = e.getHigh(); - CandleEntry e = mYVals.get(i); + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); - if (e.getLow() < mYMin) - mYMin = e.getLow(); + if (e.getLow() < mYMin) + mYMin = e.getLow(); - if (e.getHigh() > mYMax) - mYMax = e.getHigh(); - } + if (e.getLow() > mYMax) + mYMax = e.getLow(); } /** @@ -171,7 +171,7 @@ public float getShadowWidth() { * * @param showCandleBar */ - public void setShadowWidth(boolean showCandleBar) { + public void setShowCandleBar(boolean showCandleBar) { mShowCandleBar = showCandleBar; } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java similarity index 53% rename from MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java index 00740171ab..5a049957a2 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleEntry.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleEntry.java @@ -1,11 +1,15 @@ 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 */ @@ -23,14 +27,14 @@ public class CandleEntry extends Entry { /** * Constructor. * - * @param xIndex The index 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. + * @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(int xIndex, float shadowH, float shadowL, float open, float close) { - super((shadowH + shadowL) / 2f, xIndex); + public CandleEntry(float x, float shadowH, float shadowL, float open, float close) { + super(x, (shadowH + shadowL) / 2f); this.mShadowHigh = shadowH; this.mShadowLow = shadowL; @@ -40,17 +44,58 @@ public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float c /** * Constructor. - * - * @param xIndex The index on the x-axis. - * @param shadowH The (shadow) high value. - * @param shadowL The (shadow) low value. + * + * @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 data Spot for additional data this Entry represents. + * @param icon Icon image + * @param data Spot for additional data this Entry represents */ - public CandleEntry(int xIndex, float shadowH, float shadowL, float open, float close, - Object data) { - super((shadowH + shadowL) / 2f, xIndex, data); + 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; @@ -82,13 +127,13 @@ public float getBodyRange() { * low) */ @Override - public float getVal() { - return super.getVal(); + public float getY() { + return super.getY(); } public CandleEntry copy() { - CandleEntry c = new CandleEntry(getXIndex(), mShadowHigh, mShadowLow, mOpen, + CandleEntry c = new CandleEntry(getX(), mShadowHigh, mShadowLow, mOpen, mClose, getData()); return c; diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java similarity index 53% rename from MPChartLib/src/com/github/mikephil/charting/data/ChartData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java index d9ef299dcd..bfc5ed7016 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -5,12 +5,11 @@ import android.util.Log; import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IDataSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -22,167 +21,81 @@ public abstract class ChartData> { /** - * maximum y-value in the y-value array across all axes + * maximum y-value in the value array across all axes */ - protected float mYMax = 0.0f; + protected float mYMax = -Float.MAX_VALUE; /** - * the minimum y-value in the y-value array across all axes + * the minimum y-value in the value array across all axes */ - protected float mYMin = 0.0f; - - protected float mLeftAxisMax = 0.0f; - - protected float mLeftAxisMin = 0.0f; - - protected float mRightAxisMax = 0.0f; - - protected float mRightAxisMin = 0.0f; + protected float mYMin = Float.MAX_VALUE; /** - * total number of y-values across all DataSet objects + * maximum x-value in the value array */ - private int mYValCount = 0; + protected float mXMax = -Float.MAX_VALUE; /** - * contains the maximum length (in characters) an entry in the x-vals array - * has + * minimum x-value in the value array */ - private float mXValMaximumLength = 0; + protected float mXMin = Float.MAX_VALUE; - /** - * holds all x-values the chart represents - */ - protected List mXVals; - /** - * array that holds all DataSets the ChartData object represents - */ - protected List mDataSets; + protected float mLeftAxisMax = -Float.MAX_VALUE; - public ChartData() { - mXVals = new ArrayList(); - mDataSets = new ArrayList(); - } + protected float mLeftAxisMin = Float.MAX_VALUE; - /** - * Constructor for only x-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(List xVals) { - this.mXVals = xVals; - this.mDataSets = new ArrayList(); - init(); - } + protected float mRightAxisMax = -Float.MAX_VALUE; + + protected float mRightAxisMin = Float.MAX_VALUE; /** - * Constructor for only x-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals + * array that holds all DataSets the ChartData object represents */ - public ChartData(String[] xVals) { - this.mXVals = arrayToList(xVals); - this.mDataSets = new ArrayList(); - init(); - } + protected List mDataSets; /** - * constructor for chart data - * - * @param xVals The values describing the x-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array + * Default constructor. */ - public ChartData(List xVals, List sets) { - this.mXVals = xVals; - this.mDataSets = sets; - - init(); + public ChartData() { + mDataSets = new ArrayList(); } /** - * constructor that takes string array instead of List string + * Constructor taking single or multiple DataSet objects. * - * @param xVals The values describing the x-axis. Must be at least as long - * as the highest xIndex in the Entry objects across all - * DataSets. - * @param sets the dataset array + * @param dataSets */ - public ChartData(String[] xVals, List sets) { - this.mXVals = arrayToList(xVals); - this.mDataSets = sets; - - init(); + public ChartData(T... dataSets) { + mDataSets = arrayToList(dataSets); + notifyDataChanged(); } /** - * Turns an array of strings into an List of strings. + * Created because Arrays.asList(...) does not support modification. * * @param array * @return */ - private List arrayToList(String[] array) { - return Arrays.asList(array); - } - - /** - * performs all kinds of initialization calculations, such as min-max and - * value count and sum - */ - protected void init() { + private List arrayToList(T[] array) { - checkLegal(); - calcYValueCount(); - calcMinMax(0, mYValCount); - - calcXValMaximumLength(); - } + List list = new ArrayList<>(); - /** - * calculates the average length (in characters) across all x-value strings - */ - private void calcXValMaximumLength() { - - if (mXVals.size() <= 0) { - mXValMaximumLength = 1; - return; + for (T set : array) { + list.add(set); } - int max = 1; - - for (int i = 0; i < mXVals.size(); i++) { - - int length = mXVals.get(i).length(); - - if (length > max) - max = length; - } - - mXValMaximumLength = max; + return list; } /** - * Checks if the combination of x-values array and DataSet array is legal or - * not. + * constructor for chart data + * + * @param sets the dataset array */ - private void checkLegal() { - - if (mDataSets == null) - return; - - if (this instanceof ScatterData) - return; - - for (int i = 0; i < mDataSets.size(); i++) { - if (mDataSets.get(i).getEntryCount() > mXVals.size()) { - throw new IllegalArgumentException( - "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); - } - } + public ChartData(List sets) { + this.mDataSets = sets; + notifyDataChanged(); } /** @@ -191,103 +104,85 @@ private void checkLegal() { * the contained data has changed. */ public void notifyDataChanged() { - init(); + calcMinMax(); } /** - * calc minimum and maximum y value over all datasets + * 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 calcMinMax(int start, int end) { + public void calcMinMaxY(float fromX, float toX) { - if (mDataSets == null || mDataSets.size() < 1) { - - mYMax = 0f; - mYMin = 0f; - } else { + for (T set : mDataSets) { + set.calcMinMaxY(fromX, toX); + } - mYMin = Float.MAX_VALUE; - mYMax = -Float.MAX_VALUE; + // apply the new data + calcMinMax(); + } - for (int i = 0; i < mDataSets.size(); i++) { + /** + * Calc minimum and maximum values (both x and y) over all DataSets. + */ + protected void calcMinMax() { - IDataSet set = mDataSets.get(i); - set.calcMinMax(start, end); + if (mDataSets == null) + return; - if (set.getYMin() < mYMin) - mYMin = set.getYMin(); + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; - if (set.getYMax() > mYMax) - mYMax = set.getYMax(); - } + for (T set : mDataSets) { + calcMinMax(set); + } - if (mYMin == Float.MAX_VALUE) { - mYMin = 0.f; - mYMax = 0.f; - } + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; - // left axis - T firstLeft = getFirstLeft(); + // left axis + T firstLeft = getFirstLeft(mDataSets); - if (firstLeft != null) { + if (firstLeft != null) { - mLeftAxisMax = firstLeft.getYMax(); - mLeftAxisMin = firstLeft.getYMin(); + mLeftAxisMax = firstLeft.getYMax(); + mLeftAxisMin = firstLeft.getYMin(); - for (IDataSet dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) { - if (dataSet.getYMin() < mLeftAxisMin) - mLeftAxisMin = dataSet.getYMin(); + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) { + if (dataSet.getYMin() < mLeftAxisMin) + mLeftAxisMin = dataSet.getYMin(); - if (dataSet.getYMax() > mLeftAxisMax) - mLeftAxisMax = dataSet.getYMax(); - } + if (dataSet.getYMax() > mLeftAxisMax) + mLeftAxisMax = dataSet.getYMax(); } } + } - // right axis - T firstRight = getFirstRight(); + // right axis + T firstRight = getFirstRight(mDataSets); - if (firstRight != null) { + if (firstRight != null) { - mRightAxisMax = firstRight.getYMax(); - mRightAxisMin = firstRight.getYMin(); + mRightAxisMax = firstRight.getYMax(); + mRightAxisMin = firstRight.getYMin(); - for (IDataSet dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { - if (dataSet.getYMin() < mRightAxisMin) - mRightAxisMin = dataSet.getYMin(); + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { + if (dataSet.getYMin() < mRightAxisMin) + mRightAxisMin = dataSet.getYMin(); - if (dataSet.getYMax() > mRightAxisMax) - mRightAxisMax = dataSet.getYMax(); - } + if (dataSet.getYMax() > mRightAxisMax) + mRightAxisMax = dataSet.getYMax(); } } - - // in case there is only one axis, adjust the second axis - handleEmptyAxis(firstLeft, firstRight); - } - } - - /** - * Calculates the total number of y-values across all DataSets the ChartData - * represents. - * - * @return - */ - protected void calcYValueCount() { - - mYValCount = 0; - - if (mDataSets == null) - return; - - int count = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - count += mDataSets.get(i).getEntryCount(); } - - mYValCount = count; } /** ONLY GETTERS AND SETTERS BELOW THIS */ @@ -319,10 +214,18 @@ public float getYMin() { * @return */ public float getYMin(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMin; - else - return mRightAxisMin; + 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; + } } /** @@ -341,63 +244,43 @@ public float getYMax() { * @return */ public float getYMax(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMax; - else - return mRightAxisMax; - } + if (axis == AxisDependency.LEFT) { - /** - * returns the maximum length (in characters) across all values in the - * x-vals array - * - * @return - */ - public float getXValMaximumLength() { - return mXValMaximumLength; + if (mLeftAxisMax == -Float.MAX_VALUE) { + return mRightAxisMax; + } else + return mLeftAxisMax; + } else { + if (mRightAxisMax == -Float.MAX_VALUE) { + return mLeftAxisMax; + } else + return mRightAxisMax; + } } /** - * Returns the total number of y-values across all DataSet objects the this - * object represents. + * Returns the minimum x-value this data object contains. * * @return */ - public int getYValCount() { - return mYValCount; + public float getXMin() { + return mXMin; } /** - * returns the x-values the chart represents + * Returns the maximum x-value this data object contains. * * @return */ - public List getXVals() { - return mXVals; - } - - /** - * Adds a new x-value to the chart data. - * - * @param xVal - */ - public void addXValue(String xVal) { - - if (xVal != null && xVal.length() > mXValMaximumLength) - mXValMaximumLength = xVal.length(); - - mXVals.add(xVal); + public float getXMax() { + return mXMax; } /** - * Removes the x-value at the specified index. + * Returns all DataSet objects this ChartData object holds. * - * @param index + * @return */ - public void removeXValue(int index) { - mXVals.remove(index); - } - public List getDataSets() { return mDataSets; } @@ -429,22 +312,12 @@ protected int getDataSetIndexByLabel(List dataSets, String label, return -1; } - /** - * returns the total number of x-values this ChartData object represents - * (the size of the x-values array) - * - * @return - */ - public int getXValCount() { - return mXVals.size(); - } - /** * Returns the labels of all DataSets as a string array. * * @return */ - protected String[] getDataSetLabels() { + public String[] getDataSetLabels() { String[] types = new String[mDataSets.size()]; @@ -464,9 +337,9 @@ protected String[] getDataSetLabels() { public Entry getEntryForHighlight(Highlight highlight) { if (highlight.getDataSetIndex() >= mDataSets.size()) return null; - else - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXIndex( - highlight.getXIndex()); + else { + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY()); + } } /** @@ -506,63 +379,9 @@ public void addDataSet(T d) { if (d == null) return; - mYValCount += d.getEntryCount(); - - if (mDataSets.size() <= 0) { - - mYMax = d.getYMax(); - mYMin = d.getYMin(); - - if (d.getAxisDependency() == AxisDependency.LEFT) { - - mLeftAxisMax = d.getYMax(); - mLeftAxisMin = d.getYMin(); - } else { - mRightAxisMax = d.getYMax(); - mRightAxisMin = d.getYMin(); - } - } else { - - if (mYMax < d.getYMax()) - mYMax = d.getYMax(); - if (mYMin > d.getYMin()) - mYMin = d.getYMin(); - - 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(); - } - } + calcMinMax(d); mDataSets.add(d); - - handleEmptyAxis(getFirstLeft(), getFirstRight()); - } - - /** - * This adjusts the other axis if one axis is empty and the other is not. - * - * @param firstLeft - * @param firstRight - */ - private void handleEmptyAxis(T firstLeft, T firstRight) { - - // in case there is only one axis, adjust the second axis - if (firstLeft == null) { - mLeftAxisMax = mRightAxisMax; - mLeftAxisMin = mRightAxisMin; - } else if (firstRight == null) { - mRightAxisMax = mLeftAxisMax; - mRightAxisMin = mLeftAxisMin; - } } /** @@ -581,10 +400,7 @@ public boolean removeDataSet(T d) { // if a DataSet was removed if (removed) { - - mYValCount -= d.getEntryCount(); - - calcMinMax(0, mYValCount); + notifyDataChanged(); } return removed; @@ -622,47 +438,73 @@ public void addEntry(Entry e, int dataSetIndex) { if (!set.addEntry(e)) return; - float val = e.getVal(); + calcMinMax(e, set.getAxisDependency()); - if (mYValCount == 0) { - mYMin = val; - mYMax = val; + } else { + Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + } + } - if (set.getAxisDependency() == AxisDependency.LEFT) { + /** + * Adjusts the current minimum and maximum values based on the provided Entry object. + * + * @param e + * @param axis + */ + protected void calcMinMax(Entry e, AxisDependency axis) { - mLeftAxisMax = e.getVal(); - mLeftAxisMin = e.getVal(); - } else { - mRightAxisMax = e.getVal(); - mRightAxisMin = e.getVal(); - } - } else { - - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - - if (set.getAxisDependency() == AxisDependency.LEFT) { - - if (mLeftAxisMax < e.getVal()) - mLeftAxisMax = e.getVal(); - if (mLeftAxisMin > e.getVal()) - mLeftAxisMin = e.getVal(); - } else { - if (mRightAxisMax < e.getVal()) - mRightAxisMax = e.getVal(); - if (mRightAxisMin > e.getVal()) - mRightAxisMin = e.getVal(); - } - } + if (mYMax < e.getY()) + mYMax = e.getY(); + if (mYMin > e.getY()) + mYMin = e.getY(); - mYValCount += 1; + if (mXMax < e.getX()) + mXMax = e.getX(); + if (mXMin > e.getX()) + mXMin = e.getX(); - handleEmptyAxis(getFirstLeft(), getFirstRight()); + if (axis == AxisDependency.LEFT) { + if (mLeftAxisMax < e.getY()) + mLeftAxisMax = e.getY(); + if (mLeftAxisMin > e.getY()) + mLeftAxisMin = e.getY(); } else { - Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + 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(); } } @@ -685,9 +527,7 @@ public boolean removeEntry(Entry e, int dataSetIndex) { boolean removed = set.removeEntry(e); if (removed) { - mYValCount -= 1; - - calcMinMax(0, mYValCount); + notifyDataChanged(); } return removed; @@ -696,23 +536,23 @@ public boolean removeEntry(Entry e, int dataSetIndex) { } /** - * Removes the Entry object at the given xIndex from the DataSet at the + * 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 xIndex + * @param xValue * @param dataSetIndex * @return */ - public boolean removeEntry(int xIndex, int dataSetIndex) { + public boolean removeEntry(float xValue, int dataSetIndex) { if (dataSetIndex >= mDataSets.size()) return false; IDataSet dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXIndex(xIndex); + Entry e = dataSet.getEntryForXValue(xValue, Float.NaN); - if (e == null || e.getXIndex() != xIndex) + if (e == null) return false; return removeEntry(e, dataSetIndex); @@ -735,7 +575,7 @@ public T getDataSetForEntry(Entry e) { T set = mDataSets.get(i); for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXIndex(e.getXIndex()))) + if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY()))) return set; } } @@ -776,13 +616,14 @@ public int[] getColors() { 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) { - for (int i = 0; i < mDataSets.size(); i++) { - if (mDataSets.get(i) == dataSet) - return i; - } - - return -1; + return mDataSets.indexOf(dataSet); } /** @@ -791,12 +632,11 @@ public int getIndexOfDataSet(T dataSet) { * * @return */ - public T getFirstLeft() { - for (T dataSet : mDataSets) { + protected T getFirstLeft(List sets) { + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.LEFT) return dataSet; } - return null; } @@ -806,38 +646,20 @@ public T getFirstLeft() { * * @return */ - public T getFirstRight() { - for (T dataSet : mDataSets) { + public T getFirstRight(List sets) { + for (T dataSet : sets) { if (dataSet.getAxisDependency() == AxisDependency.RIGHT) return dataSet; } - return null; } /** - * Generates an x-values array filled with numbers in range specified by the - * parameters. Can be used for convenience. - * - * @return - */ - public static List generateXVals(int from, int to) { - - List xvals = new ArrayList(); - - for (int i = from; i < to; i++) { - xvals.add("" + i); - } - - return xvals; - } - - /** - * Sets a custom ValueFormatter for all DataSets this data object contains. + * Sets a custom IValueFormatter for all DataSets this data object contains. * * @param f */ - public void setValueFormatter(ValueFormatter f) { + public void setValueFormatter(IValueFormatter f) { if (f == null) return; else { @@ -937,28 +759,12 @@ public boolean isHighlightEnabled() { * forget to invalidate the chart after this. */ public void clearValues() { - mDataSets.clear(); + if (mDataSets != null) { + mDataSets.clear(); + } notifyDataChanged(); } -// /** -// * Checks if this data object contains the specified Entry. Returns true if -// * so, false if not. NOTE: Performance is pretty bad on this one, do not -// * over-use in performance critical situations. -// * -// * @param e -// * @return -// */ -// public boolean contains(Entry e) { -// -// for (T set : mDataSets) { -// if (set.contains(e)) -// return true; -// } -// -// return false; -// } - /** * Checks if this data object contains the specified DataSet. Returns true * if so, false if not. @@ -975,4 +781,41 @@ public boolean contains(T dataSet) { 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; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CombinedData.java new file mode 100644 index 0000000000..0b36aa3bef --- /dev/null +++ b/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> { + + 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 allData = getAllData(); + + for (ChartData data : allData) { + + data.calcMinMax(); + + List> 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 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 getAllData() { + + List data = new ArrayList(); + 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 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 getDataSetByHighlight(Highlight highlight) { + if (highlight.getDataIndex() >= getAllData().size()) + return null; + + BarLineScatterCandleBubbleData data = getDataByIndex(highlight.getDataIndex()); + + if (highlight.getDataSetIndex() >= data.getDataSetCount()) + return null; + + return (IBarLineScatterCandleBubbleDataSet) + data.getDataSets().get(highlight.getDataSetIndex()); + } + + public int getDataIndex(ChartData data) { + return getAllData().indexOf(data); + } + + @Override + public boolean removeDataSet(IBarLineScatterCandleBubbleDataSet d) { + + List 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/DataSet.java new file mode 100644 index 0000000000..fda07efef2 --- /dev/null +++ b/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 extends BaseDataSet { + + /** + * the entries that this DataSet represents / holds together + */ + protected List 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 entries, String label) { + super(label); + this.mEntries = entries; + + if (mEntries == null) + mEntries = new ArrayList(); + + 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 getValues() { + return mEntries; + } + + /** + * Returns the array of entries that this DataSet represents. + * + * @return + */ + public List getEntries() { + return mEntries; + } + + /** + * This method is deprecated. + * Use setEntries(...) instead. + * + * @param values + */ + @Deprecated + public void setValues(List values) { + setEntries(values); + } + + /** + * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() + * + * @return + */ + public void setEntries(List entries) { + mEntries = entries; + notifyDataSetChanged(); + } + + /** + * Provides an exact copy of the DataSet this method is used on. + * + * @return + */ + public abstract DataSet 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(); + } + + 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 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 getEntriesForXValue(float xValue) { + + List entries = new ArrayList(); + + 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, + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/Entry.java new file mode 100644 index 0000000000..b7a887990d --- /dev/null +++ b/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 CREATOR = new Parcelable.Creator() { + public Entry createFromParcel(Parcel source) { + return new Entry(source); + } + + public Entry[] newArray(int size) { + return new Entry[size]; + } + }; +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineData.java new file mode 100644 index 0000000000..4cf544874b --- /dev/null +++ b/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 { + + public LineData() { + super(); + } + + public LineData(ILineDataSet... dataSets) { + super(dataSets); + } + + public LineData(List dataSets) { + super(dataSets); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java similarity index 61% rename from MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java index 0f22668500..10d1837ecd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineDataSet.java @@ -4,12 +4,12 @@ import android.content.Context; import android.graphics.Color; import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; +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.formatter.DefaultFillFormatter; -import com.github.mikephil.charting.formatter.FillFormatter; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; @@ -17,30 +17,51 @@ public class LineDataSet extends LineRadarDataSet implements ILineDataSet { - /** List representing all colors that are used for the circles */ + /** + * Drawing mode for this line dataset + **/ + private LineDataSet.Mode mMode = Mode.LINEAR; + + /** + * List representing all colors that are used for the circles + */ private List mCircleColors = null; - /** the color of the inner circles */ - private int mCircleColorHole = Color.WHITE; + /** + * the color of the inner circles + */ + private int mCircleHoleColor = Color.WHITE; - /** the radius of the circle-shaped value indicators */ + /** + * the radius of the circle-shaped value indicators + */ private float mCircleRadius = 8f; - /** sets the intensity of the cubic lines */ + /** + * 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 */ + /** + * 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 FillFormatter mFillFormatter = new DefaultFillFormatter(); + /** + * formatter for customizing the position of the fill-line + */ + private IFillFormatter mFillFormatter = new DefaultFillFormatter(); - /** if true, drawing circles is enabled */ + /** + * if true, drawing circles is enabled + */ private boolean mDrawCircles = true; - /** if true, cubic lines are drawn instead of linear */ - private boolean mDrawCubic = false; - private boolean mDrawCircleHole = true; @@ -50,7 +71,10 @@ public LineDataSet(List yVals, String label) { // mCircleRadius = Utils.convertDpToPixel(4f); // mLineWidth = Utils.convertDpToPixel(1f); - mCircleColors = new ArrayList(); + if (mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); // default colors // mColors.add(Color.rgb(192, 255, 140)); @@ -60,29 +84,52 @@ public LineDataSet(List yVals, String label) { @Override public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(mYVals.get(i).copy()); + List entries = new ArrayList(); + for (int i = 0; i < mEntries.size(); i++) { + entries.add(mEntries.get(i).copy()); } + LineDataSet copied = new LineDataSet(entries, getLabel()); + copy(copied); + return copied; + } - LineDataSet copied = new LineDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mCircleRadius = mCircleRadius; - copied.mCircleColors = mCircleColors; - copied.mDashPathEffect = mDashPathEffect; - copied.mDrawCircles = mDrawCircles; - copied.mDrawCubic = mDrawCubic; - copied.mHighLightColor = mHighLightColor; + 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; + } - return copied; + /** + * 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) { @@ -102,13 +149,18 @@ public float getCubicIntensity() { /** - * sets the radius of the drawn circles. - * Default radius = 4f + * Sets the radius of the drawn circles. + * Default radius = 4f, Min = 1f * * @param radius */ public void setCircleRadius(float radius) { - mCircleRadius = Utils.convertDpToPixel(radius); + + if (radius >= 1f) { + mCircleRadius = Utils.convertDpToPixel(radius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 1"); + } } @Override @@ -116,10 +168,30 @@ 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 - * + *

* This method is deprecated because of unclarity. Use setCircleRadius instead. * * @param size @@ -130,9 +202,7 @@ public void setCircleSize(float size) { } /** - * * This function is deprecated because of unclarity. Use getCircleRadius instead. - * */ @Deprecated public float getCircleSize() { @@ -143,13 +213,13 @@ public float getCircleSize() { * 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 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) + * @param phase offset, in degrees (normally, use 0) */ public void enableDashedLine(float lineLength, float spaceLength, float phase) { - mDashPathEffect = new DashPathEffect(new float[] { + mDashPathEffect = new DashPathEffect(new float[]{ lineLength, spaceLength }, phase); } @@ -174,7 +244,7 @@ public DashPathEffect getDashPathEffect() { /** * set this to true to enable the drawing of circle indicators for this * DataSet, default true - * + * * @param enabled */ public void setDrawCircles(boolean enabled) { @@ -186,26 +256,23 @@ public boolean isDrawCirclesEnabled() { return mDrawCircles; } - /** - * If set to true, the linechart lines are drawn in cubic-style instead of - * linear. This affects performance! Default: false - * - * @param enabled - */ - public void setDrawCubic(boolean enabled) { - mDrawCubic = enabled; + @Deprecated + @Override + public boolean isDrawCubicEnabled() { + return mMode == Mode.CUBIC_BEZIER; } + @Deprecated @Override - public boolean isDrawCubicEnabled() { - return mDrawCubic; + public boolean isDrawSteppedEnabled() { + return mMode == Mode.STEPPED; } /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ /** * returns all colors specified for the circles - * + * * @return */ public List getCircleColors() { @@ -214,7 +281,12 @@ public List getCircleColors() { @Override public int getCircleColor(int index) { - return mCircleColors.get(index % mCircleColors.size()); + return mCircleColors.get(index); + } + + @Override + public int getCircleColorCount() { + return mCircleColors.size(); } /** @@ -223,7 +295,7 @@ public int getCircleColor(int index) { * 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 colors) { @@ -236,10 +308,10 @@ public void setCircleColors(List colors) { * 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) { + public void setCircleColors(int... colors) { this.mCircleColors = ColorTemplate.createColors(colors); } @@ -250,12 +322,16 @@ public void setCircleColors(int[] colors) { * "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 clrs = new ArrayList(); + List clrs = mCircleColors; + if (clrs == null) { + clrs = new ArrayList<>(); + } + clrs.clear(); for (int color : colors) { clrs.add(c.getResources().getColor(color)); @@ -267,7 +343,7 @@ public void setCircleColors(int[] colors, Context c) { /** * 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) { @@ -279,26 +355,29 @@ public void setCircleColor(int color) { * resets the circle-colors array and creates a new one */ public void resetCircleColors() { - mCircleColors = new ArrayList(); + if (mCircleColors == null) { + mCircleColors = new ArrayList(); + } + mCircleColors.clear(); } /** * Sets the color of the inner circle of the line-circles. - * + * * @param color */ - public void setCircleColorHole(int color) { - mCircleColorHole = color; + public void setCircleHoleColor(int color) { + mCircleHoleColor = color; } @Override public int getCircleHoleColor() { - return mCircleColorHole; + return mCircleHoleColor; } /** * Set this to true to allow drawing a hole in each data circle. - * + * * @param enabled */ public void setDrawCircleHole(boolean enabled) { @@ -311,12 +390,12 @@ public boolean isDrawCircleHoleEnabled() { } /** - * Sets a custom FillFormatter to the chart that handles the position of the + * 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(FillFormatter formatter) { + public void setFillFormatter(IFillFormatter formatter) { if (formatter == null) mFillFormatter = new DefaultFillFormatter(); @@ -325,7 +404,14 @@ public void setFillFormatter(FillFormatter formatter) { } @Override - public FillFormatter getFillFormatter() { + public IFillFormatter getFillFormatter() { return mFillFormatter; } + + public enum Mode { + LINEAR, + STEPPED, + CUBIC_BEZIER, + HORIZONTAL_BEZIER + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java similarity index 84% rename from MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java index 2289113dbb..b4347e4647 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineRadarDataSet.java @@ -1,6 +1,7 @@ package com.github.mikephil.charting.data; +import android.annotation.TargetApi; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -16,6 +17,7 @@ */ public abstract class LineRadarDataSet extends LineScatterCandleRadarDataSet implements ILineRadarDataSet { + // TODO: Move to using `Fill` class /** * the color that is used for filling the line surface */ @@ -72,6 +74,7 @@ public Drawable getFillDrawable() { * * @param drawable */ + @TargetApi(18) public void setFillDrawable(Drawable drawable) { this.mFillDrawable = drawable; } @@ -99,8 +102,8 @@ public void setFillAlpha(int alpha) { */ public void setLineWidth(float width) { - if (width < 0.2f) - width = 0.2f; + if (width < 0.0f) + width = 0.0f; if (width > 10.0f) width = 10.0f; mLineWidth = Utils.convertDpToPixel(width); @@ -120,4 +123,13 @@ public void setDrawFilled(boolean filled) { 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; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java similarity index 86% rename from MPChartLib/src/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java index 90a0a43fb3..d4618d809e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java @@ -109,4 +109,12 @@ public boolean isDashedHighlightLineEnabled() { 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; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java similarity index 73% rename from MPChartLib/src/com/github/mikephil/charting/data/PieData.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java index 9a3151e6c4..423ce19b16 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java @@ -1,6 +1,9 @@ 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; @@ -20,26 +23,8 @@ public PieData() { super(); } - public PieData(List xVals) { - super(xVals); - } - - public PieData(String[] xVals) { - super(xVals); - } - - public PieData(List xVals, IPieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public PieData(String[] xVals, IPieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(IPieDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; + public PieData(IPieDataSet dataSet) { + super(dataSet); } /** @@ -50,7 +35,7 @@ private static List toList(IPieDataSet dataSet) { public void setDataSet(IPieDataSet dataSet) { mDataSets.clear(); mDataSets.add(dataSet); - init(); + notifyDataChanged(); } /** @@ -63,6 +48,18 @@ public IPieDataSet getDataSet() { return mDataSets.get(0); } + @Override + public List getDataSets() { + List 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. * @@ -80,6 +77,11 @@ public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) { : 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. * @@ -90,7 +92,7 @@ public float getYValueSum() { float sum = 0; for (int i = 0; i < getDataSet().getEntryCount(); i++) - sum += getDataSet().getEntryForIndex(i).getVal(); + sum += getDataSet().getEntryForIndex(i).getY(); return sum; diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java new file mode 100644 index 0000000000..c83b24547b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieDataSet.java @@ -0,0 +1,261 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.Utils; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class PieDataSet extends DataSet 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 yVals, String label) { + super(yVals, label); +// mShift = Utils.convertDpToPixel(12f); + } + + @Override + public DataSet copy() { + List 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 + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieEntry.java new file mode 100644 index 0000000000..65741ef1da --- /dev/null +++ b/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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarData.java new file mode 100644 index 0000000000..0c1dbe5505 --- /dev/null +++ b/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 { + + private List mLabels; + + public RadarData() { + super(); + } + + public RadarData(List 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 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 getLabels() { + return mLabels; + } + + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSetByIndex(highlight.getDataSetIndex()).getEntryForIndex((int) highlight.getX()); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarDataSet.java new file mode 100644 index 0000000000..8a9740b6b5 --- /dev/null +++ b/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 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 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 copy() { + List entries = new ArrayList(); + 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/RadarEntry.java new file mode 100644 index 0000000000..02fdce7d32 --- /dev/null +++ b/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(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterData.java new file mode 100644 index 0000000000..ba142360f9 --- /dev/null +++ b/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 { + + public ScatterData() { + super(); + } + + public ScatterData(List 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ScatterDataSet.java new file mode 100644 index 0000000000..85ed808160 --- /dev/null +++ b/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 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 yVals, String label) { + super(yVals, label); + } + + @Override + public DataSet copy() { + List entries = new ArrayList(); + 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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/Approximator.java new file mode 100644 index 0000000000..542188e602 --- /dev/null +++ b/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; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/filter/ApproximatorN.java new file mode 100644 index 0000000000..9351341c76 --- /dev/null +++ b/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 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 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java b/MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/exception/DrawingDataSetNotCreatedException.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/ColorFormatter.java new file mode 100644 index 0000000000..2db66fd43f --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java new file mode 100644 index 0000000000..552c150e69 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultAxisValueFormatter.java @@ -0,0 +1,56 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; + +import java.text.DecimalFormat; + +/** + * Created by philipp on 02/06/16. + */ +public class DefaultAxisValueFormatter implements IAxisValueFormatter +{ + + /** + * decimalformat for formatting + */ + protected DecimalFormat mFormat; + + /** + * the number of decimal digits this formatter uses + */ + protected int digits = 0; + + /** + * 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, AxisBase axis) { + // 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java similarity index 94% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultFillFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java index 54e9cca8b3..851faeb333 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultFillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java @@ -10,7 +10,8 @@ * * @author Philipp Jahoda */ -public class DefaultFillFormatter implements FillFormatter { +public class DefaultFillFormatter implements IFillFormatter +{ @Override public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java similarity index 65% rename from MPChartLib/src/com/github/mikephil/charting/formatter/DefaultValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java index bd66f51d08..e2fea4b079 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/DefaultValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -12,18 +12,34 @@ * * @author Philipp Jahoda */ -public class DefaultValueFormatter implements ValueFormatter { +public class DefaultValueFormatter implements IValueFormatter +{ - /** decimalformat for formatting */ - private DecimalFormat mFormat; + /** + * 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++) { @@ -43,4 +59,13 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View return mFormat.format(value); } + + /** + * Returns the number of decimal digits this formatter uses. + * + * @return + */ + public int getDecimalDigits() { + return mDecimalDigits; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java new file mode 100644 index 0000000000..51939b5432 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IAxisValueFormatter.java @@ -0,0 +1,23 @@ +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. + */ +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 + */ + String getFormattedValue(float value, AxisBase axis); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/FillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java similarity index 95% rename from MPChartLib/src/com/github/mikephil/charting/formatter/FillFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java index 66895d77e7..e096cc6528 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/FillFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java @@ -9,7 +9,8 @@ * * @author Philipp Jahoda */ -public interface FillFormatter { +public interface IFillFormatter +{ /** * Returns the vertical (y-axis) position where the filled-line of the diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/ValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java similarity index 90% rename from MPChartLib/src/com/github/mikephil/charting/formatter/ValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java index 1f056bf786..75d2363f26 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/ValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -6,12 +6,13 @@ /** * Interface that allows custom formatting of all values inside the chart before they are * being drawn to the screen. Simply create your own formatting class and let - * it implement ValueFormatter. Then override the getFormattedValue(...) method + * it implement IValueFormatter. Then override the getFormattedValue(...) method * and return whatever you want. * * @author Philipp Jahoda */ -public interface ValueFormatter { +public interface IValueFormatter +{ /** * Called when a value (from labels inside the chart) is formatted diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java new file mode 100644 index 0000000000..07349a6a0e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IndexAxisValueFormatter.java @@ -0,0 +1,69 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Collection; + +/** + * This formatter is used for passing an array of x-axis labels, on whole x steps. + */ +public class IndexAxisValueFormatter implements IAxisValueFormatter +{ + private String[] mValues = new String[] {}; + private 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 values) { + if (values != null) + setValues(values.toArray(new String[values.size()])); + } + + public String getFormattedValue(float value, AxisBase axis) { + 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java similarity index 66% rename from MPChartLib/src/com/github/mikephil/charting/formatter/LargeValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java index 96b4ead74f..211401ad8a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/LargeValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -1,7 +1,7 @@ package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,17 +17,18 @@ * @author Philipp Jahoda * @author Oleksandr Tyshkovets */ -public class LargeValueFormatter implements ValueFormatter, YAxisValueFormatter { +public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter +{ - private static String[] SUFFIX = new String[]{ + private String[] mSuffix = new String[]{ "", "k", "m", "b", "t" }; - private static final int MAX_LENGTH = 4; + private int mMaxLength = 5; private DecimalFormat mFormat; private String mText = ""; public LargeValueFormatter() { - mFormat = new DecimalFormat("###E0"); + mFormat = new DecimalFormat("###E00"); } /** @@ -40,15 +41,15 @@ public LargeValueFormatter(String appendix) { mText = appendix; } - // ValueFormatter + // IValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return makePretty(value) + mText; } - // YAxisValueFormatter + // IAxisValueFormatter @Override - public String getFormattedValue(float value, YAxis yAxis) { + public String getFormattedValue(float value, AxisBase axis) { return makePretty(value) + mText; } @@ -65,12 +66,14 @@ public void setAppendix(String appendix) { * Set custom suffix to be appended after the values. * Default suffix: ["", "k", "m", "b", "t"] * - * @param suff new suffix + * @param suffix new suffix */ - public void setSuffix(String[] suff) { - if (suff.length == 5) { - SUFFIX = suff; - } + public void setSuffix(String[] suffix) { + this.mSuffix = suffix; + } + + public void setMaxLength(int maxLength) { + this.mMaxLength = maxLength; } /** @@ -81,12 +84,20 @@ private String makePretty(double number) { String r = mFormat.format(number); - r = r.replaceAll("E[0-9]", SUFFIX[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]); + int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1)); + int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2)); + int combined = Integer.valueOf(numericValue2 + "" + numericValue1); - while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { + 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; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java similarity index 67% rename from MPChartLib/src/com/github/mikephil/charting/formatter/PercentFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java index 6e3535d8b5..de8a10255a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/PercentFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -1,19 +1,20 @@ package com.github.mikephil.charting.formatter; -import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.components.AxisBase; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.utils.ViewPortHandler; import java.text.DecimalFormat; /** - * This ValueFormatter is just for convenience and simply puts a "%" sign after + * This IValueFormatter is just for convenience and simply puts a "%" sign after * each value. (Recommeded for PieChart) * * @author Philipp Jahoda */ -public class PercentFormatter implements ValueFormatter, YAxisValueFormatter { +public class PercentFormatter implements IValueFormatter, IAxisValueFormatter +{ protected DecimalFormat mFormat; @@ -30,15 +31,19 @@ public PercentFormatter(DecimalFormat format) { this.mFormat = format; } - // ValueFormatter + // IValueFormatter @Override public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { return mFormat.format(value) + " %"; } - // YAxisValueFormatter + // IAxisValueFormatter @Override - public String getFormattedValue(float value, YAxis yAxis) { + public String getFormattedValue(float value, AxisBase axis) { return mFormat.format(value) + " %"; } + + public int getDecimalDigits() { + return 1; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java similarity index 92% rename from MPChartLib/src/com/github/mikephil/charting/formatter/StackedValueFormatter.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java index 7a8ef0a193..0e8351634f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/formatter/StackedValueFormatter.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -12,7 +12,8 @@ * 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 implements ValueFormatter { +public class StackedValueFormatter implements IValueFormatter +{ /** * if true, all stack values of the stacked bar entry are drawn, else only top @@ -53,7 +54,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View if (!mDrawWholeStack && entry instanceof BarEntry) { BarEntry barEntry = (BarEntry) entry; - float[] vals = barEntry.getVals(); + float[] vals = barEntry.getYVals(); if (vals != null) { @@ -61,7 +62,7 @@ public String getFormattedValue(float value, Entry entry, int dataSetIndex, View if (vals[vals.length - 1] == value) { // return the "sum" across all stack values - return mFormat.format(barEntry.getVal()) + mAppendix; + return mFormat.format(barEntry.getY()) + mAppendix; } else { return ""; // return empty } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/BarHighlighter.java new file mode 100644 index 0000000000..af83a4539f --- /dev/null +++ b/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 { + + 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(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/ChartHighlighter.java new file mode 100644 index 0000000000..f889bf19d4 --- /dev/null +++ b/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 implements IHighlighter +{ + + /** + * instance of the data-provider + */ + protected T mChart; + + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + + 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 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 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 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 buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List 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 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(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/CombinedHighlighter.java new file mode 100644 index 0000000000..76788af6e0 --- /dev/null +++ b/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 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 getHighlightsAtXValue(float xVal, float x, float y) { + + mHighlightBuffer.clear(); + + List 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 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; +// } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Highlight.java new file mode 100644 index 0000000000..62307cbeaf --- /dev/null +++ b/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; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/HorizontalBarHighlighter.java new file mode 100644 index 0000000000..d96298e07d --- /dev/null +++ b/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 buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) { + + ArrayList highlights = new ArrayList<>(); + + //noinspection unchecked + List 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); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/IHighlighter.java new file mode 100644 index 0000000000..d0ca0cfe57 --- /dev/null +++ b/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); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieHighlighter.java new file mode 100644 index 0000000000..b4c5a1b24b --- /dev/null +++ b/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 { + + 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()); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/PieRadarHighlighter.java new file mode 100644 index 0000000000..a19906d75c --- /dev/null +++ b/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 implements IHighlighter +{ + + protected T mChart; + + /** + * buffer for storing previously highlighted values + */ + protected List mHighlightBuffer = new ArrayList(); + + 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); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/RadarHighlighter.java new file mode 100644 index 0000000000..3c4f6d03ac --- /dev/null +++ b/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 { + + public RadarHighlighter(RadarChart chart) { + super(chart); + } + + @Override + protected Highlight getClosestHighlight(int index, float x, float y) { + + List 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 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; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/highlight/Range.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java similarity index 87% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java index 2f0675fc4a..9dfee07f9c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java @@ -7,5 +7,5 @@ public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider BarData getBarData(); boolean isDrawBarShadowEnabled(); boolean isDrawValueAboveBarEnabled(); - boolean isDrawHighlightArrowEnabled(); + boolean isHighlightFullBarEnabled(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java similarity index 83% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java index faab82a265..68beeb8d62 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java @@ -7,11 +7,10 @@ public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface { Transformer getTransformer(AxisDependency axis); - int getMaxVisibleCount(); boolean isInverted(AxisDependency axis); - int getLowestVisibleXIndex(); - int getHighestVisibleXIndex(); + float getLowestVisibleX(); + float getHighestVisibleX(); BarLineScatterCandleBubbleData getData(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java index 350d35e644..219b46bd82 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -1,10 +1,10 @@ package com.github.mikephil.charting.interfaces.dataprovider; -import android.graphics.PointF; import android.graphics.RectF; import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; /** * Interface that provides everything there is to know about the dimensions, @@ -15,46 +15,55 @@ public interface ChartInterface { /** - * Returns the minimum x-value of the chart, regardless of zoom or translation. + * 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. + * 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. + * 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. + * Returns the maximum y value of the chart, regardless of zoom or translation. * * @return */ float getYChartMax(); - int getXValCount(); + /** + * 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(); - PointF getCenterOfView(); + MPPointF getCenterOfView(); - PointF getCenterOffsets(); + MPPointF getCenterOffsets(); RectF getContentRect(); - ValueFormatter getDefaultValueFormatter(); + IValueFormatter getDefaultValueFormatter(); ChartData getData(); + + int getMaxVisibleCount(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CombinedDataProvider.java new file mode 100644 index 0000000000..574d26a2a2 --- /dev/null +++ b/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(); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java similarity index 75% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java index bc3bb9b3ea..5e82a48420 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java @@ -1,18 +1,18 @@ package com.github.mikephil.charting.interfaces.datasets; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.utils.Fill; + +import java.util.List; /** * Created by philipp on 21/10/15. */ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { - /** - * Returns the space between bars as the actual value (0 - 1.0f) - * - * @return - */ - float getBarSpace(); + List getFills(); + + Fill getFill(int index); /** * Returns true if this DataSet is stacked (stacksize > 1) or not. @@ -37,6 +37,21 @@ public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet { */ float getYMax(); + /** + * returns the minimum x-value this DataSet holds + * + * @return + */ + float getXMin(); + + /** + * returns the maximum x-value this DataSet holds + * + * @return + */ + float getXMax(); + /** * Returns the number of y-values this DataSet represents -> the size of the y-values array * -> yvals.size() @@ -39,37 +57,62 @@ public interface IDataSet { int getEntryCount(); /** - * Calculates the minimum and maximum y value (mYMin, mYMax). From the specified starting to ending index. + * Calculates the minimum and maximum x and y values (mXMin, mXMax, mYMin, mYMax). + */ + void calcMinMax(); + + /** + * Calculates the min and max y-values from the Entry closest to the given fromX to the Entry closest to the given toX value. + * This is only needed for the autoScaleMinMax feature. * - * @param start starting index in your data list - * @param end ending index in your data list + * @param fromX + * @param toX */ - void calcMinMax(int start, int end); + void calcMinMaxY(float fromX, float toX); /** - * Returns the first Entry object found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns null if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * Returns the first Entry object found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value * @return + * + * */ - T getEntryForXIndex(int xIndex); + T getEntryForXValue(float xValue, float closestToY, DataSet.Rounding rounding); /** - * Returns the first Entry object found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns null if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * Returns the first Entry object found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @return + */ + T getEntryForXValue(float xValue, float closestToY); + + /** + * Returns all Entry objects found at the given x-value with binary + * search. An empty array if no Entry object at that x-value. + * INFORMATION: This method does calculations at runtime. Do + * not over-use in performance critical situations. + * + * @param xValue * @return */ - T getEntryForXIndex(int xIndex, DataSet.Rounding rounding); + List getEntriesForXValue(float xValue); /** * Returns the Entry object found at the given index (NOT xIndex) in the values array. @@ -80,17 +123,20 @@ public interface IDataSet { T getEntryForIndex(int index); /** - * Returns the first Entry index found at the given xIndex with binary - * search. If the no Entry at the specified x-index is found, this method - * returns the index at the closest x-index. Returns -1 if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do + * Returns the first Entry index found at the given x-value with binary + * search. + * If the no Entry at the specified x-value is found, this method + * returns the Entry at the closest x-value according to the rounding. + * INFORMATION: This method does calculations at runtime. Do * not over-use in performance critical situations. * - * @param xIndex - * @param rounding determine to round up/down/closest if there is no Entry matching the provided x-index + * @param xValue the x-value + * @param closestToY If there are multiple y-values for the specified x-value, + * @param rounding determine whether to round up/down/closest + * if there is no Entry matching the provided x-value * @return */ - int getEntryIndex(int xIndex, DataSet.Rounding rounding); + int getEntryIndex(float xValue, float closestToY, DataSet.Rounding rounding); /** * Returns the position of the provided entry in the DataSets Entry array. @@ -101,16 +147,6 @@ public interface IDataSet { */ int getEntryIndex(T e); - /** - * Returns the value of the Entry object at the given xIndex. Returns - * Float.NaN if no value is at the given x-index. INFORMATION: This method - * does calculations at runtime. Do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - float getYValForXIndex(int xIndex); /** * This method returns the actual @@ -133,19 +169,10 @@ public interface IDataSet { */ boolean addEntry(T e); - /** - * Removes an Entry from the DataSets entries array. This will also - * recalculate the current minimum and maximum values of the DataSet and the - * value-sum. Returns true if an Entry was removed, false if no Entry could - * be removed. - * - * @param e - */ - boolean removeEntry(T e); /** * Adds an Entry to the DataSet dynamically. - * Entries are added to their appropriate index respective to it's x-index. + * Entries are added to their appropriate index in the values array respective to their x-position. * This will also recalculate the current minimum and maximum * values of the DataSet and the value-sum. * @@ -170,12 +197,31 @@ public interface IDataSet { boolean removeLast(); /** - * Removes the Entry object that has the given xIndex from the DataSet. + * Removes an Entry from the DataSets entries array. This will also + * recalculate the current minimum and maximum values of the DataSet and the + * value-sum. Returns true if an Entry was removed, false if no Entry could + * be removed. + * + * @param e + */ + boolean removeEntry(T e); + + /** + * Removes the Entry object closest to the given x-value from the DataSet. * Returns true if an Entry was removed, false if no Entry could be removed. * - * @param xIndex + * @param xValue + */ + boolean removeEntryByXValue(float xValue); + + /** + * Removes the Entry object at the given index in the values array from the DataSet. + * Returns true if an Entry was removed, false if no Entry could be removed. + * + * @param index + * @return */ - boolean removeEntry(int xIndex); + boolean removeEntry(int index); /** * Checks if this DataSet contains the specified Entry. Returns true if so, @@ -272,14 +318,21 @@ public interface IDataSet { * * @param f */ - void setValueFormatter(ValueFormatter f); + void setValueFormatter(IValueFormatter f); /** * Returns the formatter used for drawing the values inside the chart. * * @return */ - ValueFormatter getValueFormatter(); + IValueFormatter getValueFormatter(); + + /** + * Returns true if the valueFormatter object of this DataSet is null. + * + * @return + */ + boolean needsFormatter(); /** * Sets the color the value-labels of this DataSet should have. @@ -340,10 +393,38 @@ public interface IDataSet { float getValueTextSize(); /** - * set this to true to draw y-values on the chart NOTE (for bar and - * linechart): if "maxvisiblecount" is reached, no values will be drawn even - * if this is enabled + * The form to draw for this dataset in the legend. + *

+ * Return `DEFAULT` to use the default legend form. + */ + Legend.LegendForm getForm(); + + /** + * The form size to draw for this dataset in the legend. + *

+ * Return `Float.NaN` to use the default legend form size. + */ + float getFormSize(); + + /** + * The line width for drawing the form of this dataset in the legend + *

+ * Return `Float.NaN` to use the default legend form line width. + */ + float getFormLineWidth(); + + /** + * The line dash path effect used for shapes that consist of lines. + *

+ * Return `null` to use the default legend form line dash effect. + */ + DashPathEffect getFormLineDashEffect(); + + /** + * set this to true to draw y-values on the chart. * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no values will be drawn even + * if this is enabled * @param enabled */ void setDrawValues(boolean enabled); @@ -355,6 +436,38 @@ public interface IDataSet { */ boolean isDrawValuesEnabled(); + /** + * Set this to true to draw y-icons on the chart. + * + * NOTE (for bar and line charts): if `maxVisibleCount` is reached, no icons will be drawn even + * if this is enabled + * + * @param enabled + */ + void setDrawIcons(boolean enabled); + + /** + * Returns true if y-icon drawing is enabled, false if not + * + * @return + */ + boolean isDrawIconsEnabled(); + + /** + * Offset of icons drawn on the chart. + * + * For all charts except Pie and Radar it will be ordinary (x offset,y offset). + * + * For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint. + * @param offset + */ + void setIconsOffset(MPPointF offset); + + /** + * Get the offset for drawing icons. + */ + MPPointF getIconsOffset(); + /** * Set the visibility of this DataSet. If not visible, the DataSet will not * be drawn to the chart upon refreshing it. diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java similarity index 73% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java index 9b7dd0558e..3f534fe848 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -1,10 +1,10 @@ package com.github.mikephil.charting.interfaces.datasets; import android.graphics.DashPathEffect; -import android.graphics.drawable.Drawable; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.FillFormatter; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; /** * Created by Philpp Jahoda on 21/10/15. @@ -12,25 +12,36 @@ public interface ILineDataSet extends ILineRadarDataSet { /** - * Returns the intensity of the cubic lines (the effect intensity). - * Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f + * Returns the drawing mode for this line dataset * * @return */ - float getCubicIntensity(); + LineDataSet.Mode getMode(); /** - * Returns true if drawing cubic lines is enabled, false if not. + * Returns the intensity of the cubic lines (the effect intensity). + * Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f * * @return */ + float getCubicIntensity(); + + @Deprecated boolean isDrawCubicEnabled(); + @Deprecated + boolean isDrawSteppedEnabled(); + /** * Returns the size of the drawn circles. */ float getCircleRadius(); + /** + * Returns the hole radius of the drawn circles. + */ + float getCircleHoleRadius(); + /** * Returns the color at the given index of the DataSet's circle-color array. * Performs a IndexOutOfBounds check by modulus. @@ -40,6 +51,13 @@ public interface ILineDataSet extends ILineRadarDataSet { */ int getCircleColor(int index); + /** + * Returns the number of colors in this DataSet's circle-color array. + * + * @return + */ + int getCircleColorCount(); + /** * Returns true if drawing circles for this DataSet is enabled, false if not * @@ -77,9 +95,9 @@ public interface ILineDataSet extends ILineRadarDataSet { boolean isDashedLineEnabled(); /** - * Returns the FillFormatter that is set for this DataSet. + * Returns the IFillFormatter that is set for this DataSet. * * @return */ - FillFormatter getFillFormatter(); + IFillFormatter getFillFormatter(); } \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java similarity index 83% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java index 4ed43f03e8..ce89822716 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -47,8 +47,10 @@ public interface ILineRadarDataSet extends ILineScatterCandleRa /** * Set to true if the DataSet should be drawn filled (surface), and not just - * as a line, disabling this will give great performance boost! default: - * false + * as a line, disabling this will give great performance boost. Please note that this method + * uses the canvas.clipPath(...) method for drawing the filled area. + * For devices with API level < 18 (Android 4.3), hardware acceleration of the chart should + * be turned off. Default: false * * @param enabled */ diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java new file mode 100644 index 0000000000..b228fca0e4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IPieDataSet.java @@ -0,0 +1,82 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import androidx.annotation.Nullable; + +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; + +/** + * Created by Philipp Jahoda on 03/11/15. + */ +public interface IPieDataSet extends IDataSet { + + /** + * Returns the space that is set to be between the piechart-slices of this + * DataSet, in pixels. + * + * @return + */ + float getSliceSpace(); + + /** + * When enabled, slice spacing will be 0.0 when the smallest value is going to be + * smaller than the slice spacing itself. + * + * @return + */ + boolean isAutomaticallyDisableSliceSpacingEnabled(); + + /** + * Returns the distance a highlighted piechart slice is "shifted" away from + * the chart-center in dp. + * + * @return + */ + float getSelectionShift(); + + PieDataSet.ValuePosition getXValuePosition(); + PieDataSet.ValuePosition getYValuePosition(); + + /** + * When valuePosition is OutsideSlice, indicates line color + * */ + int getValueLineColor(); + + /** + * When valuePosition is OutsideSlice and enabled, line will have the same color as the slice + * */ + boolean isUseValueColorForLineEnabled(); + + /** + * When valuePosition is OutsideSlice, indicates line width + * */ + float getValueLineWidth(); + + /** + * When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size + * */ + float getValueLinePart1OffsetPercentage(); + + /** + * When valuePosition is OutsideSlice, indicates length of first half of the line + * */ + float getValueLinePart1Length(); + + /** + * When valuePosition is OutsideSlice, indicates length of second half of the line + * */ + float getValueLinePart2Length(); + + /** + * When valuePosition is OutsideSlice, this allows variable line length + * */ + boolean isValueLineVariableLength(); + + /** + * Gets the color for the highlighted sector + * */ + @Nullable + Integer getHighlightColor(); + +} + diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java new file mode 100644 index 0000000000..8af00d5376 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IRadarDataSet.java @@ -0,0 +1,30 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.RadarEntry; + +/** + * Created by Philipp Jahoda on 03/11/15. + */ +public interface IRadarDataSet extends ILineRadarDataSet { + + /// flag indicating whether highlight circle should be drawn or not + boolean isDrawHighlightCircleEnabled(); + + /// Sets whether highlight circle should be drawn or not + void setDrawHighlightCircleEnabled(boolean enabled); + + int getHighlightCircleFillColor(); + + /// The stroke color for highlight circle. + /// If Utils.COLOR_NONE, the color of the dataset is taken. + int getHighlightCircleStrokeColor(); + + int getHighlightCircleStrokeAlpha(); + + float getHighlightCircleInnerRadius(); + + float getHighlightCircleOuterRadius(); + + float getHighlightCircleStrokeWidth(); + +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java similarity index 78% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java index d3a6d3400d..ac6122742a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java @@ -1,7 +1,7 @@ package com.github.mikephil.charting.interfaces.datasets; -import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; /** * Created by philipp on 21/10/15. @@ -16,23 +16,23 @@ public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { float getScatterShapeSize(); /** - * Returns all the different scattershapes the chart uses + * Returns radius of the hole in the shape * * @return */ - ScatterChart.ScatterShape getScatterShape(); + float getScatterShapeHoleRadius(); /** - * Returns radius of the hole in the shape + * Returns the color for the hole in the shape * * @return */ - float getScatterShapeHoleRadius(); + int getScatterShapeHoleColor(); /** - * Returns the color for the hole in the shape + * Returns the IShapeRenderer responsible for rendering this DataSet. * * @return */ - int getScatterShapeHoleColor(); + IShapeRenderer getShapeRenderer(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java new file mode 100644 index 0000000000..8f953a06aa --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedMoveViewJob.java @@ -0,0 +1,65 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.view.View; + +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +@SuppressLint("NewApi") +public class AnimatedMoveViewJob extends AnimatedViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new AnimatedMoveViewJob(null,0,0,null,null,0,0,0)); + pool.setReplenishPercentage(0.5f); + } + + public static AnimatedMoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration){ + AnimatedMoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + //result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + + public static void recycleInstance(AnimatedMoveViewJob instance){ + pool.recycle(instance); + } + + + public AnimatedMoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { + super(viewPortHandler, xValue, yValue, trans, v, xOrigin, yOrigin, duration); + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + pts[0] = xOrigin + (xValue - xOrigin) * phase; + pts[1] = yOrigin + (yValue - yOrigin) * phase; + + mTrans.pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, view); + } + + public void recycleSelf(){ + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedMoveViewJob(null,0,0,null,null,0,0,0); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java similarity index 56% rename from MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java index ea17745ee8..f8b520a419 100644 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java @@ -1,5 +1,6 @@ package com.github.mikephil.charting.jobs; +import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; @@ -12,7 +13,7 @@ * Created by Philipp Jahoda on 19/02/16. */ @SuppressLint("NewApi") -public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener { +public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { protected ObjectAnimator animator; @@ -28,6 +29,7 @@ public AnimatedViewPortJob(ViewPortHandler viewPortHandler, float xValue, float animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f); animator.setDuration(duration); animator.addUpdateListener(this); + animator.addListener(this); } @SuppressLint("NewApi") @@ -51,4 +53,47 @@ public float getXOrigin() { public float getYOrigin() { return yOrigin; } + + public abstract void recycleSelf(); + + protected void resetAnimator(){ + animator.removeAllListeners(); + animator.removeAllUpdateListeners(); + animator.reverse(); + animator.addUpdateListener(this); + animator.addListener(this); + } + + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationCancel(Animator animation) { + try{ + recycleSelf(); + }catch (IllegalArgumentException e){ + // don't worry about it. + } + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java similarity index 55% rename from MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedZoomJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java index 293032207a..e5e4c417d3 100644 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/AnimatedZoomJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java @@ -8,6 +8,7 @@ import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -17,6 +18,28 @@ @SuppressLint("NewApi") public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.AnimatorListener { + private static ObjectPool pool; + + static { + pool = ObjectPool.create(8, new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0)); + } + + public static AnimatedZoomJob getInstance(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + AnimatedZoomJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = scaleX; + result.yValue = scaleY; + result.mTrans = trans; + result.view = v; + result.xOrigin = xOrigin; + result.yOrigin = yOrigin; + result.yAxis = axis; + result.xAxisRange = xAxisRange; + result.resetAnimator(); + result.animator.setDuration(duration); + return result; + } + protected float zoomOriginX; protected float zoomOriginY; @@ -25,10 +48,10 @@ public class AnimatedZoomJob extends AnimatedViewPortJob implements Animator.Ani protected YAxis yAxis; - protected float xValCount; + protected float xAxisRange; @SuppressLint("NewApi") - public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xValCount, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { + public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer trans, YAxis axis, float xAxisRange, float scaleX, float scaleY, float xOrigin, float yOrigin, float zoomCenterX, float zoomCenterY, float zoomOriginX, float zoomOriginY, long duration) { super(viewPortHandler, scaleX, scaleY, trans, v, xOrigin, yOrigin, duration); this.zoomCenterX = zoomCenterX; @@ -37,27 +60,29 @@ public AnimatedZoomJob(ViewPortHandler viewPortHandler, View v, Transformer tran this.zoomOriginY = zoomOriginY; this.animator.addListener(this); this.yAxis = axis; - this.xValCount = xValCount; + this.xAxisRange = xAxisRange; } + protected Matrix mOnAnimationUpdateMatrixBuffer = new Matrix(); @Override public void onAnimationUpdate(ValueAnimator animation) { float scaleX = xOrigin + (xValue - xOrigin) * phase; float scaleY = yOrigin + (yValue - yOrigin) * phase; - Matrix save = mViewPortHandler.setZoom(scaleX, scaleY); + Matrix save = mOnAnimationUpdateMatrixBuffer; + mViewPortHandler.setZoom(scaleX, scaleY, save); mViewPortHandler.refresh(save, view, false); float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY(); - float xsInView = xValCount / mViewPortHandler.getScaleX(); + float xsInView = xAxisRange / mViewPortHandler.getScaleX(); pts[0] = zoomOriginX + ((zoomCenterX - xsInView / 2f) - zoomOriginX) * phase; pts[1] = zoomOriginY + ((zoomCenterY + valsInView / 2f) - zoomOriginY) * phase; mTrans.pointValuesToPixel(pts); - save = mViewPortHandler.translate(pts); + mViewPortHandler.translate(pts, save); mViewPortHandler.refresh(save, view, true); } @@ -77,8 +102,18 @@ public void onAnimationRepeat(Animator animation) { } + @Override + public void recycleSelf() { + + } + @Override public void onAnimationStart(Animator animation) { } + + @Override + protected ObjectPool.Poolable instantiate() { + return new AnimatedZoomJob(null,null,null,null,0,0,0,0,0,0,0,0,0,0); + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java new file mode 100644 index 0000000000..46b56b1347 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/MoveViewJob.java @@ -0,0 +1,56 @@ + +package com.github.mikephil.charting.jobs; + +import android.view.View; + +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +public class MoveViewJob extends ViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(2, new MoveViewJob(null,0,0,null,null)); + pool.setReplenishPercentage(0.5f); + } + + public static MoveViewJob getInstance(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v){ + MoveViewJob result = pool.get(); + result.mViewPortHandler = viewPortHandler; + result.xValue = xValue; + result.yValue = yValue; + result.mTrans = trans; + result.view = v; + return result; + } + + public static void recycleInstance(MoveViewJob instance){ + pool.recycle(instance); + } + + public MoveViewJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v) { + super(viewPortHandler, xValue, yValue, trans, v); + } + + @Override + public void run() { + + pts[0] = xValue; + pts[1] = yValue; + + mTrans.pointValuesToPixel(pts); + mViewPortHandler.centerViewPort(pts, view); + + this.recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MoveViewJob(mViewPortHandler, xValue, yValue, mTrans, view); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/jobs/ViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java similarity index 89% rename from MPChartLib/src/com/github/mikephil/charting/jobs/ViewPortJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java index 912ab38431..c424e4b87a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/ViewPortJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java @@ -3,6 +3,7 @@ import android.view.View; +import com.github.mikephil.charting.utils.ObjectPool; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -15,7 +16,7 @@ * * @author Philipp Jahoda */ -public abstract class ViewPortJob implements Runnable { +public abstract class ViewPortJob extends ObjectPool.Poolable implements Runnable { protected float[] pts = new float[2]; @@ -33,6 +34,7 @@ public ViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, this.yValue = yValue; this.mTrans = trans; this.view = v; + } public float getXValue() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java new file mode 100644 index 0000000000..c39586ca87 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ZoomJob.java @@ -0,0 +1,87 @@ + +package com.github.mikephil.charting.jobs; + +import android.graphics.Matrix; +import android.view.View; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.ObjectPool; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 19/02/16. + */ +public class ZoomJob extends ViewPortJob { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(1, new ZoomJob(null, 0, 0, 0, 0, null, null, null)); + pool.setReplenishPercentage(0.5f); + } + + public static ZoomJob getInstance(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, + Transformer trans, YAxis.AxisDependency axis, View v) { + ZoomJob result = pool.get(); + result.xValue = xValue; + result.yValue = yValue; + result.scaleX = scaleX; + result.scaleY = scaleY; + result.mViewPortHandler = viewPortHandler; + result.mTrans = trans; + result.axisDependency = axis; + result.view = v; + return result; + } + + public static void recycleInstance(ZoomJob instance) { + pool.recycle(instance); + } + + protected float scaleX; + protected float scaleY; + + protected YAxis.AxisDependency axisDependency; + + public ZoomJob(ViewPortHandler viewPortHandler, float scaleX, float scaleY, float xValue, float yValue, Transformer trans, + YAxis.AxisDependency axis, View v) { + super(viewPortHandler, xValue, yValue, trans, v); + + this.scaleX = scaleX; + this.scaleY = scaleY; + this.axisDependency = axis; + } + + protected Matrix mRunMatrixBuffer = new Matrix(); + + @Override + public void run() { + + Matrix save = mRunMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, save); + mViewPortHandler.refresh(save, view, false); + + float yValsInView = ((BarLineChartBase) view).getAxis(axisDependency).mAxisRange / mViewPortHandler.getScaleY(); + float xValsInView = ((BarLineChartBase) view).getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + pts[0] = xValue - xValsInView / 2f; + pts[1] = yValue + yValsInView / 2f; + + mTrans.pointValuesToPixel(pts); + + mViewPortHandler.translate(pts, save); + mViewPortHandler.refresh(save, view, false); + + ((BarLineChartBase) view).calculateOffsets(); + view.postInvalidate(); + + recycleInstance(this); + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new ZoomJob(null, 0, 0, 0, 0, null, null, null); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java similarity index 66% rename from MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java index 34771283c6..5685d32fa0 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/BarLineChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/BarLineChartTouchListener.java @@ -1,4 +1,3 @@ - package com.github.mikephil.charting.listener; import android.annotation.SuppressLint; @@ -17,28 +16,38 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; /** * TouchListener for Bar-, Line-, Scatter- and CandleStickChart with handles all * touch interaction. Longpress == Zoom out. Double-Tap == Zoom in. - * + * * @author Philipp Jahoda */ -public class BarLineChartTouchListener extends ChartTouchListener>>> { +public class BarLineChartTouchListener extends ChartTouchListener>>> { - /** the original touch-matrix from the chart */ + /** + * the original touch-matrix from the chart + */ private Matrix mMatrix = new Matrix(); - /** matrix for saving the original matrix state */ + /** + * matrix for saving the original matrix state + */ private Matrix mSavedMatrix = new Matrix(); - /** point where the touch action started */ - private PointF mTouchStartPoint = new PointF(); + /** + * point where the touch action started + */ + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); - /** center between two pointers (fingers on the display) */ - private PointF mTouchPointCenter = new PointF(); + /** + * center between two pointers (fingers on the display) + */ + private MPPointF mTouchPointCenter = MPPointF.getInstance(0,0); private float mSavedXDist = 1f; private float mSavedYDist = 1f; @@ -46,16 +55,41 @@ public class BarLineChartTouchListener extends ChartTouchListener>> chart, Matrix touchMatrix) { + /** + * Constructor with initialization parameters. + * + * @param chart instance of the chart + * @param touchMatrix the touch-matrix of the chart + * @param dragTriggerDistance the minimum movement distance that will be interpreted as a "drag" gesture in dp (3dp equals + * to about 9 pixels on a 5.5" FHD screen) + */ + public BarLineChartTouchListener(BarLineChartBase>> chart, Matrix touchMatrix, float dragTriggerDistance) { super(chart); this.mMatrix = touchMatrix; + + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + + this.mMinScalePointerDistance = Utils.convertDpToPixel(3.5f); } @SuppressLint("ClickableViewAccessibility") @@ -93,6 +127,7 @@ public boolean onTouch(View v, MotionEvent event) { saveTouchStart(event); break; + case MotionEvent.ACTION_POINTER_DOWN: if (event.getPointerCount() >= 2) { @@ -115,10 +150,11 @@ public boolean onTouch(View v, MotionEvent event) { if (mChart.isPinchZoomEnabled()) { mTouchMode = PINCH_ZOOM; } else { - if (mSavedXDist > mSavedYDist) - mTouchMode = X_ZOOM; - else - mTouchMode = Y_ZOOM; + if (mChart.isScaleXEnabled() != mChart.isScaleYEnabled()) { + mTouchMode = mChart.isScaleXEnabled() ? X_ZOOM : Y_ZOOM; + } else { + mTouchMode = mSavedXDist > mSavedYDist ? X_ZOOM : Y_ZOOM; + } } } @@ -126,12 +162,17 @@ public boolean onTouch(View v, MotionEvent event) { midPoint(mTouchPointCenter, event); } break; + case MotionEvent.ACTION_MOVE: if (mTouchMode == DRAG) { mChart.disableScroll(); - performDrag(event); + + float x = mChart.isDragXEnabled() ? event.getX() - mTouchStartPoint.x : 0.f; + float y = mChart.isDragYEnabled() ? event.getY() - mTouchStartPoint.y : 0.f; + + performDrag(event, x, y); } else if (mTouchMode == X_ZOOM || mTouchMode == Y_ZOOM || mTouchMode == PINCH_ZOOM) { @@ -142,25 +183,38 @@ public boolean onTouch(View v, MotionEvent event) { } else if (mTouchMode == NONE && Math.abs(distance(event.getX(), mTouchStartPoint.x, event.getY(), - mTouchStartPoint.y)) > 5f) { + mTouchStartPoint.y)) > mDragTriggerDist) { + + if (mChart.isDragEnabled()) { + + boolean shouldPan = !mChart.isFullyZoomedOut() || + !mChart.hasNoDragOffset(); + if (shouldPan) { - if (mChart.hasNoDragOffset()) { + float distanceX = Math.abs(event.getX() - mTouchStartPoint.x); + float distanceY = Math.abs(event.getY() - mTouchStartPoint.y); + + // Disable dragging in a direction that's disallowed + if ((mChart.isDragXEnabled() || distanceY >= distanceX) && + (mChart.isDragYEnabled() || distanceY <= distanceX)) { + + mLastGesture = ChartGesture.DRAG; + mTouchMode = DRAG; + } - if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) { - mTouchMode = DRAG; } else { - mLastGesture = ChartGesture.DRAG; + if (mChart.isHighlightPerDragEnabled()) { + mLastGesture = ChartGesture.DRAG; - if (mChart.isHighlightPerDragEnabled()) - performHighlightDrag(event); + if (mChart.isHighlightPerDragEnabled()) + performHighlightDrag(event); + } } - } else if (mChart.isDragEnabled()) { - mLastGesture = ChartGesture.DRAG; - mTouchMode = DRAG; } + } break; @@ -180,10 +234,15 @@ public boolean onTouch(View v, MotionEvent event) { stopDeceleration(); mDecelerationLastTime = AnimationUtils.currentAnimationTimeMillis(); - mDecelerationCurrentPoint = new PointF(event.getX(), event.getY()); - mDecelerationVelocity = new PointF(velocityX, velocityY); - Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google + mDecelerationCurrentPoint.x = event.getX(); + mDecelerationCurrentPoint.y = event.getY(); + + mDecelerationVelocity.x = velocityX; + mDecelerationVelocity.y = velocityY; + + Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by + // Google } } @@ -223,8 +282,7 @@ public boolean onTouch(View v, MotionEvent event) { break; } - // Perform the transformation, update the chart - // if (needsRefresh()) + // perform the transformation, update the chart mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, true); return true; // indicate event was handled @@ -237,23 +295,24 @@ public boolean onTouch(View v, MotionEvent event) { /** * Saves the current Matrix state and the touch-start point. - * + * * @param event */ private void saveTouchStart(MotionEvent event) { mSavedMatrix.set(mMatrix); - mTouchStartPoint.set(event.getX(), event.getY()); + mTouchStartPoint.x = event.getX(); + mTouchStartPoint.y = event.getY(); mClosestDataSetToTouch = mChart.getDataSetByTouchPoint(event.getX(), event.getY()); } /** * Performs all necessary operations needed for dragging. - * + * * @param event */ - private void performDrag(MotionEvent event) { + private void performDrag(MotionEvent event, float distanceX, float distanceY) { mLastGesture = ChartGesture.DRAG; @@ -261,54 +320,44 @@ private void performDrag(MotionEvent event) { OnChartGestureListener l = mChart.getOnChartGestureListener(); - float dX, dY; - // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.getAxis(mClosestDataSetToTouch.getAxisDependency()).isInverted()) { + if (inverted()) { // if there is an inverted horizontalbarchart if (mChart instanceof HorizontalBarChart) { - dX = -(event.getX() - mTouchStartPoint.x); - dY = event.getY() - mTouchStartPoint.y; + distanceX = -distanceX; } else { - dX = event.getX() - mTouchStartPoint.x; - dY = -(event.getY() - mTouchStartPoint.y); + distanceY = -distanceY; } } - else { - dX = event.getX() - mTouchStartPoint.x; - dY = event.getY() - mTouchStartPoint.y; - } - mMatrix.postTranslate(dX, dY); + mMatrix.postTranslate(distanceX, distanceY); if (l != null) - l.onChartTranslate(event, dX, dY); + l.onChartTranslate(event, distanceX, distanceY); } /** * Performs the all operations necessary for pinch and axis zoom. - * + * * @param event */ private void performZoom(MotionEvent event) { - if (event.getPointerCount() >= 2) { + if (event.getPointerCount() >= 2) { // two finger zoom OnChartGestureListener l = mChart.getOnChartGestureListener(); - // get the distance between the pointers of the touch - // event + // get the distance between the pointers of the touch event float totalDist = spacing(event); - if (totalDist > 10f) { + if (totalDist > mMinScalePointerDistance) { // get the translation - PointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); + MPPointF t = getTrans(mTouchPointCenter.x, mTouchPointCenter.y); + ViewPortHandler h = mChart.getViewPortHandler(); - // take actions depending on the activated touch - // mode + // take actions depending on the activated touch mode if (mTouchMode == PINCH_ZOOM) { mLastGesture = ChartGesture.PINCH_ZOOM; @@ -316,14 +365,19 @@ private void performZoom(MotionEvent event) { float scale = totalDist / mSavedDist; // total scale boolean isZoomingOut = (scale < 1); + boolean canZoomMoreX = isZoomingOut ? - mChart.getViewPortHandler().canZoomOutMoreX() : - mChart.getViewPortHandler().canZoomInMoreX(); + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); float scaleX = (mChart.isScaleXEnabled()) ? scale : 1f; float scaleY = (mChart.isScaleYEnabled()) ? scale : 1f; - if (mChart.isScaleYEnabled() || canZoomMoreX) { + if (canZoomMoreY || canZoomMoreX) { mMatrix.set(mSavedMatrix); mMatrix.postScale(scaleX, scaleY, t.x, t.y); @@ -341,8 +395,8 @@ private void performZoom(MotionEvent event) { boolean isZoomingOut = (scaleX < 1); boolean canZoomMoreX = isZoomingOut ? - mChart.getViewPortHandler().canZoomOutMoreX() : - mChart.getViewPortHandler().canZoomInMoreX(); + h.canZoomOutMoreX() : + h.canZoomInMoreX(); if (canZoomMoreX) { @@ -360,21 +414,29 @@ private void performZoom(MotionEvent event) { float yDist = getYDist(event); float scaleY = yDist / mSavedYDist; // y-axis scale - mMatrix.set(mSavedMatrix); + boolean isZoomingOut = (scaleY < 1); + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); - // y-axis comes from top to bottom, revert y - mMatrix.postScale(1f, scaleY, t.x, t.y); + if (canZoomMoreY) { - if (l != null) - l.onChartScale(event, 1f, scaleY); + mMatrix.set(mSavedMatrix); + mMatrix.postScale(1f, scaleY, t.x, t.y); + + if (l != null) + l.onChartScale(event, 1f, scaleY); + } } + + MPPointF.recycleInstance(t); } } } /** * Highlights upon dragging, generates callbacks for the selection-listener. - * + * * @param e */ private void performHighlightDrag(MotionEvent e) { @@ -383,7 +445,7 @@ private void performHighlightDrag(MotionEvent e) { if (h != null && !h.equalTo(mLastHighlighted)) { mLastHighlighted = h; - mChart.highlightTouch(h); + mChart.highlightValue(h, true); } } @@ -395,19 +457,20 @@ private void performHighlightDrag(MotionEvent e) { /** * Determines the center point between two pointer touch points. - * + * * @param point * @param event */ - private static void midPoint(PointF point, MotionEvent event) { + private static void midPoint(MPPointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); - point.set(x / 2f, y / 2f); + point.x = (x / 2f); + point.y = (y / 2f); } /** * returns the distance between two pointer touch points - * + * * @param event * @return */ @@ -420,7 +483,7 @@ private static float spacing(MotionEvent event) { /** * calculates the distance on the x-axis between two pointers (fingers on * the display) - * + * * @param e * @return */ @@ -432,7 +495,7 @@ private static float getXDist(MotionEvent e) { /** * calculates the distance on the y-axis between two pointers (fingers on * the display) - * + * * @param e * @return */ @@ -442,14 +505,15 @@ private static float getYDist(MotionEvent e) { } /** + * Returns a recyclable MPPointF instance. * returns the correct translation depending on the provided x and y touch * points - * + * * @param x * @param y * @return */ - public PointF getTrans(float x, float y) { + public MPPointF getTrans(float x, float y) { ViewPortHandler vph = mChart.getViewPortHandler(); @@ -457,14 +521,23 @@ public PointF getTrans(float x, float y) { float yTrans = 0f; // check if axis is inverted - if (mChart.isAnyAxisInverted() && mClosestDataSetToTouch != null - && mChart.isInverted(mClosestDataSetToTouch.getAxisDependency())) { + if (inverted()) { yTrans = -(y - vph.offsetTop()); } else { yTrans = -(mChart.getMeasuredHeight() - y - vph.offsetBottom()); } - return new PointF(xTrans, yTrans); + return MPPointF.getInstance(xTrans, yTrans); + } + + /** + * Returns true if the current touch situation should be interpreted as inverted, false if not. + * + * @return + */ + private boolean inverted() { + return (mClosestDataSetToTouch == null && mChart.isAnyAxisInverted()) || (mClosestDataSetToTouch != null + && mChart.isInverted(mClosestDataSetToTouch.getAxisDependency())); } /** @@ -474,13 +547,23 @@ public PointF getTrans(float x, float y) { /** * returns the matrix object the listener holds - * + * * @return */ public Matrix getMatrix() { return mMatrix; } + /** + * Sets the minimum distance that will be interpreted as a "drag" by the chart in dp. + * Default: 3dp + * + * @param dragTriggerDistance + */ + public void setDragTriggerDist(float dragTriggerDistance) { + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + } + @Override public boolean onDoubleTap(MotionEvent e) { @@ -493,15 +576,24 @@ public boolean onDoubleTap(MotionEvent e) { } // check if double-tap zooming is enabled - if (mChart.isDoubleTapToZoomEnabled()) { + if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) { - PointF trans = getTrans(e.getX(), e.getY()); + MPPointF trans = getTrans(e.getX(), e.getY()); - mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y); + float scaleX = mChart.isScaleXEnabled() ? 1.4f : 1f; + float scaleY = mChart.isScaleYEnabled() ? 1.4f : 1f; + + mChart.zoom(scaleX, scaleY, trans.x, trans.y); if (mChart.isLogEnabled()) Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: " + trans.y); + + if (l != null) { + l.onChartScale(e, scaleX, scaleY); + } + + MPPointF.recycleInstance(trans); } return super.onDoubleTap(e); @@ -531,32 +623,16 @@ public boolean onSingleTapUp(MotionEvent e) { l.onChartSingleTapped(e); } - if(!mChart.isHighlightPerTapEnabled()) { + if (!mChart.isHighlightPerTapEnabled()) { return false; } - Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); performHighlight(h, e); return super.onSingleTapUp(e); } -// @Override -// public boolean onSingleTapConfirmed(MotionEvent e) { -// -// mLastGesture = ChartGesture.SINGLE_TAP; -// -// OnChartGestureListener l = mChart.getOnChartGestureListener(); -// -// if (l != null) { -// l.onChartSingleTapped(e); -// l.onChartGestureEnd(e, mLastGesture); -// } -// -// return super.onSingleTapConfirmed(e); -// } - @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { @@ -572,7 +648,8 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float ve } public void stopDeceleration() { - mDecelerationVelocity = new PointF(0.f, 0.f); + mDecelerationVelocity.x = 0; + mDecelerationVelocity.y = 0; } public void computeScroll() { @@ -585,7 +662,7 @@ public void computeScroll() { mDecelerationVelocity.x *= mChart.getDragDecelerationFrictionCoef(); mDecelerationVelocity.y *= mChart.getDragDecelerationFrictionCoef(); - final float timeInterval = (float)(currentTime - mDecelerationLastTime) / 1000.f; + final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f; float distanceX = mDecelerationVelocity.x * timeInterval; float distanceY = mDecelerationVelocity.y * timeInterval; @@ -593,8 +670,14 @@ public void computeScroll() { mDecelerationCurrentPoint.x += distanceX; mDecelerationCurrentPoint.y += distanceY; - MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, mDecelerationCurrentPoint.y, 0); - performDrag(event); + MotionEvent event = MotionEvent.obtain(currentTime, currentTime, MotionEvent.ACTION_MOVE, mDecelerationCurrentPoint.x, + mDecelerationCurrentPoint.y, 0); + + float dragDistanceX = mChart.isDragXEnabled() ? mDecelerationCurrentPoint.x - mTouchStartPoint.x : 0.f; + float dragDistanceY = mChart.isDragYEnabled() ? mDecelerationCurrentPoint.y - mTouchStartPoint.y : 0.f; + + performDrag(event, dragDistanceX, dragDistanceY); + event.recycle(); mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false); diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java similarity index 97% rename from MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java index eef16c8460..75c8e864b4 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/ChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -118,11 +118,11 @@ public ChartGesture getLastGesture() { protected void performHighlight(Highlight h, MotionEvent e) { if (h == null || h.equalTo(mLastHighlighted)) { - mChart.highlightTouch(null); + mChart.highlightValue(null, true); mLastHighlighted = null; } else { + mChart.highlightValue(h, true); mLastHighlighted = h; - mChart.highlightTouch(h); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java similarity index 98% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java index a17cdde941..da0c5ed180 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java @@ -57,7 +57,7 @@ public interface OnChartGestureListener { void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); /** - * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. + * Callbacks when the chart is scaled / zoomed via pinch zoom / double-tap gesture. * * @param me * @param scaleX scalefactor on the x-axis diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java similarity index 60% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java index 4c6da2e8c9..7f50232b7e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -6,21 +6,19 @@ /** * Listener for callbacks when selecting values inside the chart by * touch-gesture. - * + * * @author Philipp Jahoda */ public interface OnChartValueSelectedListener { /** * Called when a value has been selected inside the chart. - * - * @param e The selected Entry. - * @param dataSetIndex The index in the datasets array of the data object - * the Entrys DataSet is in. - * @param h the corresponding highlight object that contains information - * about the highlighted position + * + * @param e The selected Entry + * @param h The corresponding highlight object that contains information + * about the highlighted position such as dataSetIndex, ... */ - void onValueSelected(Entry e, int dataSetIndex, Highlight h); + void onValueSelected(Entry e, Highlight h); /** * Called when nothing has been selected or an "un-select" has been made. diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawLineChartTouchListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java similarity index 81% rename from MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java index 9163c823d8..d3527f924a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java @@ -7,15 +7,12 @@ import android.view.View; import android.view.animation.AnimationUtils; -import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.charts.PieRadarChartBase; -import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.highlight.Highlight; -import com.github.mikephil.charting.utils.SelectionDetail; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; -import java.util.List; /** * Touchlistener for the PieChart. @@ -24,7 +21,7 @@ */ public class PieRadarChartTouchListener extends ChartTouchListener> { - private PointF mTouchStartPoint = new PointF(); + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); /** * the angle where the dragging started @@ -48,6 +45,7 @@ public boolean onTouch(View v, MotionEvent event) { return true; // if rotation by touch is enabled + // TODO: Also check if the pie itself is being touched, rather than the entire chart area if (mChart.isRotationEnabled()) { float x = event.getX(); @@ -151,60 +149,8 @@ public boolean onSingleTapUp(MotionEvent e) { return false; } - float distance = mChart.distanceToCenter(e.getX(), e.getY()); - - // check if a slice was touched - if (distance > mChart.getRadius()) { - - // if no slice was touched, highlight nothing - - if (mLastHighlighted == null) - mChart.highlightValues(null); // no listener callback - else - mChart.highlightTouch(null); // listener callback - - mLastHighlighted = null; - - } else { - - float angle = mChart.getAngleForPoint(e.getX(), e.getY()); - - if (mChart instanceof PieChart) { - angle /= mChart.getAnimator().getPhaseY(); - } - - int index = mChart.getIndexForAngle(angle); - - // check if the index could be found - if (index < 0) { - - mChart.highlightValues(null); - mLastHighlighted = null; - - } else { - - List valsAtIndex = mChart.getSelectionDetailsAtIndex(index); - - int dataSetIndex = 0; - - // get the dataset that is closest to the selection (PieChart - // only - // has one DataSet) - if (mChart instanceof RadarChart) { - - dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, distance - / ((RadarChart) mChart).getFactor(), null); - } - - if (dataSetIndex < 0) { - mChart.highlightValues(null); - mLastHighlighted = null; - } else { - Highlight h = new Highlight(index, dataSetIndex); - performHighlight(h, e); - } - } - } + Highlight high = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(high, e); return true; } diff --git a/MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java b/MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java similarity index 100% rename from MPChartLib/src/com/github/mikephil/charting/matrix/Vector3.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/matrix/Vector3.java diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java new file mode 100644 index 0000000000..b5c8715a08 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/model/GradientColor.java @@ -0,0 +1,69 @@ +package com.github.mikephil.charting.model; + +import com.github.mikephil.charting.utils.Fill; + +/** + * Deprecated. Use `Fill` + */ +@Deprecated +public class GradientColor extends Fill +{ + /** + * Deprecated. Use `Fill.getGradientColors()` + */ + @Deprecated + public int getStartColor() + { + return getGradientColors()[0]; + } + + /** + * Deprecated. Use `Fill.setGradientColors(...)` + */ + @Deprecated + public void setStartColor(int startColor) + { + if (getGradientColors() == null || getGradientColors().length != 2) + { + setGradientColors(new int[]{ + startColor, + getGradientColors() != null && getGradientColors().length > 1 + ? getGradientColors()[1] + : 0 + }); + } else + { + getGradientColors()[0] = startColor; + } + } + + /** + * Deprecated. Use `Fill.getGradientColors()` + */ + @Deprecated + public int getEndColor() + { + return getGradientColors()[1]; + } + + /** + * Deprecated. Use `Fill.setGradientColors(...)` + */ + @Deprecated + public void setEndColor(int endColor) + { + if (getGradientColors() == null || getGradientColors().length != 2) + { + setGradientColors(new int[]{ + getGradientColors() != null && getGradientColors().length > 0 + ? getGradientColors()[0] + : 0, + endColor + }); + } else + { + getGradientColors()[1] = endColor; + } + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java new file mode 100644 index 0000000000..72ea2d17c8 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -0,0 +1,293 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; + +import com.github.mikephil.charting.components.AxisBase; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Baseclass of all axis renderers. + * + * @author Philipp Jahoda + */ +public abstract class AxisRenderer extends Renderer { + + /** base axis this axis renderer works with */ + protected AxisBase mAxis; + + /** transformer to transform values to screen pixels and return */ + protected Transformer mTrans; + + /** + * paint object for the grid lines + */ + protected Paint mGridPaint; + + /** + * paint for the x-label values + */ + protected Paint mAxisLabelPaint; + + /** + * paint for the line surrounding the chart + */ + protected Paint mAxisLinePaint; + + /** + * paint used for the limit lines + */ + protected Paint mLimitLinePaint; + + public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { + super(viewPortHandler); + + this.mTrans = trans; + this.mAxis = axis; + + if(mViewPortHandler != null) { + + mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + mGridPaint = new Paint(); + mGridPaint.setColor(Color.GRAY); + mGridPaint.setStrokeWidth(1f); + mGridPaint.setStyle(Style.STROKE); + mGridPaint.setAlpha(90); + + mAxisLinePaint = new Paint(); + mAxisLinePaint.setColor(Color.BLACK); + mAxisLinePaint.setStrokeWidth(1f); + mAxisLinePaint.setStyle(Style.STROKE); + + mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLimitLinePaint.setStyle(Paint.Style.STROKE); + } + } + + /** + * Returns the Paint object used for drawing the axis (labels). + * + * @return + */ + public Paint getPaintAxisLabels() { + return mAxisLabelPaint; + } + + /** + * Returns the Paint object that is used for drawing the grid-lines of the + * axis. + * + * @return + */ + public Paint getPaintGrid() { + return mGridPaint; + } + + /** + * Returns the Paint object that is used for drawing the axis-line that goes + * alongside the axis. + * + * @return + */ + public Paint getPaintAxisLine() { + return mAxisLinePaint; + } + + /** + * Returns the Transformer object used for transforming the axis values. + * + * @return + */ + public Transformer getTransformer() { + return mTrans; + } + + /** + * Computes the axis values. + * + * @param min - the minimum value in the data object for this axis + * @param max - the maximum value in the data object for this axis + */ + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + + if (!inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(min, max); + } + + /** + * Sets up the axis values. Computes the desired number of labels between the two given extremes. + * + * @return + */ + protected void computeAxisValues(float min, float max) { + + float yMin = min; + float yMax = max; + + int labelCount = mAxis.getLabelCount(); + double range = Math.abs(yMax - yMin); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; + mAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); + + } + + int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; + + // force label count + if (mAxis.isForceLabelsEnabled()) { + + interval = (float) range / (float) (labelCount - 1); + mAxis.mEntryCount = labelCount; + + if (mAxis.mEntries.length < labelCount) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[labelCount]; + } + + float v = min; + + for (int i = 0; i < labelCount; i++) { + mAxis.mEntries[i] = v; + v += interval; + } + + n = labelCount; + + // no forced count + } else { + + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if(mAxis.isCenterAxisLabelsEnabled()) { + first -= interval; + } + + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + + double f; + int i; + + if (interval != 0.0 && last != first) { + for (f = first; f <= last; f += interval) { + ++n; + } + } + else if (last == first && n == 0) { + n = 1; + } + + mAxis.mEntryCount = n; + + if (mAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[n]; + } + + for (f = first, i = 0; i < n; f += interval, ++i) { + + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; + } + } + + // set decimals + if (interval < 1) { + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mAxis.mDecimals = 0; + } + + if (mAxis.isCenterAxisLabelsEnabled()) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (float)interval / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } + } + } + + /** + * Draws the axis labels to the screen. + * + * @param c + */ + public abstract void renderAxisLabels(Canvas c); + + /** + * Draws the grid lines belonging to the axis. + * + * @param c + */ + public abstract void renderGridLines(Canvas c); + + /** + * Draws the line that goes alongside the axis. + * + * @param c + */ + public abstract void renderAxisLine(Canvas c); + + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + public abstract void renderLimitLines(Canvas c); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java new file mode 100644 index 0000000000..1656a3a37f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarChartRenderer.java @@ -0,0 +1,498 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.Range; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Fill; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class BarChartRenderer extends BarLineScatterCandleBubbleRenderer { + + protected BarDataProvider mChart; + + /** + * the rect object that is used for drawing the bars + */ + protected RectF mBarRect = new RectF(); + + protected BarBuffer[] mBarBuffers; + + protected Paint mShadowPaint; + protected Paint mBarBorderPaint; + + public BarChartRenderer(BarDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + this.mChart = chart; + + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setStyle(Paint.Style.FILL); + mHighlightPaint.setColor(Color.rgb(0, 0, 0)); + // set alpha after color + mHighlightPaint.setAlpha(120); + + mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mShadowPaint.setStyle(Paint.Style.FILL); + + mBarBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mBarBorderPaint.setStyle(Paint.Style.STROKE); + } + + @Override + public void initBuffers() { + + BarData barData = mChart.getBarData(); + mBarBuffers = new BarBuffer[barData.getDataSetCount()]; + + for (int i = 0; i < mBarBuffers.length; i++) { + IBarDataSet set = barData.getDataSetByIndex(i); + mBarBuffers[i] = new BarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), + barData.getDataSetCount(), set.isStacked()); + } + } + + @Override + public void drawData(Canvas c) { + + BarData barData = mChart.getBarData(); + + for (int i = 0; i < barData.getDataSetCount(); i++) { + + IBarDataSet set = barData.getDataSetByIndex(i); + + if (set.isVisible()) { + drawDataSet(c, set, i); + } + } + } + + private RectF mBarShadowRectBuffer = new RectF(); + + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + + final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + + BarData barData = mChart.getBarData(); + + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; + + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); + + x = e.getX(); + + mBarShadowRectBuffer.left = x - barWidthHalf; + mBarShadowRectBuffer.right = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) + continue; + + if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) + break; + + mBarShadowRectBuffer.top = mViewPortHandler.contentTop(); + mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + } + + // initialize the buffer + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + + buffer.feed(dataSet); + + trans.pointValuesToPixel(buffer.buffer); + + final boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); + final boolean isSingleColor = dataSet.getColors().size() == 1; + final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } + + for (int j = 0, pos = 0; j < buffer.size(); j += 4, pos++) { + + if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) + continue; + + if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) + break; + + if (!isSingleColor) { + // Set the color for the currently drawn value. If the index + // is out of bounds, reuse colors. + mRenderPaint.setColor(dataSet.getColor(pos)); + } + + if (isCustomFill) { + dataSet.getFill(pos) + .fillRect( + c, mRenderPaint, + buffer.buffer[j], + buffer.buffer[j + 1], + buffer.buffer[j + 2], + buffer.buffer[j + 3], + isInverted ? Fill.Direction.DOWN : Fill.Direction.UP); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (drawBorder) { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mBarBorderPaint); + } + } + } + + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { + + float left = x - barWidthHalf; + float right = x + barWidthHalf; + float top = y1; + float bottom = y2; + + mBarRect.set(left, top, right, bottom); + + trans.rectToPixelPhase(mBarRect, mAnimator.getPhaseY()); + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getBarData().getDataSets(); + + final float valueOffsetPlus = Utils.convertDpToPixel(4.5f); + float posOffset = 0f; + float negOffset = 0f; + boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); + + for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { + + IBarDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + // calculate the correct offset depending on the draw position of + // the value + float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8"); + posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus); + negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextHeight; + negOffset = -negOffset - valueTextHeight; + } + + // get the buffer + BarBuffer buffer = mBarBuffers[i]; + + final float phaseY = mAnimator.getPhaseY(); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + // if only single values are drawn (sum) + if (!dataSet.isStacked()) { + + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { + + float x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2f; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(buffer.buffer[j + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + BarEntry entry = dataSet.getEntryForIndex(j / 4); + float val = entry.getY(); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), val, entry, i, x, + val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset), + dataSet.getValueTextColor(j / 4)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = val >= 0 ? + (buffer.buffer[j + 1] + posOffset) : + (buffer.buffer[j + 3] + negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + // if we have stacks + } else { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; + + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + + BarEntry entry = dataSet.getEntryForIndex(index); + + float[] vals = entry.getYVals(); + float x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2f; + + int color = dataSet.getValueTextColor(index); + + // we still draw stacked bars, but there is one + // non-stacked + // in between + if (vals == null) { + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + 1]) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset), + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = x; + float py = buffer.buffer[bufferIndex + 1] + + (entry.getY() >= 0 ? posOffset : negOffset); + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + // draw stack values + } else { + + float[] transformed = new float[vals.length * 2]; + + float posY = 0f; + float negY = -entry.getNegativeSum(); + + for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { + + float value = vals[idx]; + float y; + + 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; + } else if (value >= 0.0f) { + posY += value; + y = posY; + } else { + y = negY; + negY -= value; + } + + transformed[k + 1] = y * phaseY; + } + + trans.pointValuesToPixel(transformed); + + for (int k = 0; k < transformed.length; k += 2) { + + final float val = vals[k / 2]; + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; + float y = transformed[k + 1] + + (drawBelow ? negOffset : posOffset); + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsY(y) + || !mViewPortHandler.isInBoundsLeft(x)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + vals[k / 2], + entry, + i, + x, + y, + color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + BarData barData = mChart.getBarData(); + + for (Highlight high : indices) { + + IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + BarEntry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + Transformer trans = mChart.getTransformer(set.getAxisDependency()); + + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setAlpha(set.getHighLightAlpha()); + + boolean isStack = (high.getStackIndex() >= 0 && e.isStacked()) ? true : false; + + final float y1; + final float y2; + + if (isStack) { + + if(mChart.isHighlightFullBarEnabled()) { + + y1 = e.getPositiveSum(); + y2 = -e.getNegativeSum(); + + } else { + + Range range = e.getRanges()[high.getStackIndex()]; + + y1 = range.from; + y2 = range.to; + } + + } else { + y1 = e.getY(); + y2 = 0.f; + } + + prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans); + + setHighlightDrawPos(high, mBarRect); + + c.drawRect(mBarRect, mHighlightPaint); + } + } + + /** + * Sets the drawing position of the highlight object based on the riven bar-rect. + * @param high + */ + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerX(), bar.top); + } + + @Override + public void drawExtras(Canvas c) { + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java new file mode 100644 index 0000000000..06599187d3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BarLineScatterCandleBubbleRenderer.java @@ -0,0 +1,96 @@ +package com.github.mikephil.charting.renderer; + +import com.github.mikephil.charting.animation.ChartAnimator; +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.IBarLineScatterCandleBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 09/06/16. + */ +public abstract class BarLineScatterCandleBubbleRenderer extends DataRenderer { + + /** + * buffer for storing the current minimum and maximum visible x + */ + protected XBounds mXBounds = new XBounds(); + + public BarLineScatterCandleBubbleRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Returns true if the DataSet values should be drawn, false if not. + * + * @param set + * @return + */ + protected boolean shouldDrawValues(IDataSet set) { + return set.isVisible() && (set.isDrawValuesEnabled() || set.isDrawIconsEnabled()); + } + + /** + * Checks if the provided entry object is in bounds for drawing considering the current animation phase. + * + * @param e + * @param set + * @return + */ + protected boolean isInBoundsX(Entry e, IBarLineScatterCandleBubbleDataSet set) { + + if (e == null) + return false; + + float entryIndex = set.getEntryIndex(e); + + if (e == null || entryIndex >= set.getEntryCount() * mAnimator.getPhaseX()) { + return false; + } else { + return true; + } + } + + /** + * Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. + */ + protected class XBounds { + + /** + * minimum visible entry index + */ + public int min; + + /** + * maximum visible entry index + */ + public int max; + + /** + * range of visible entry indices + */ + public int range; + + /** + * Calculates the minimum and maximum x values as well as the range between them. + * + * @param chart + * @param dataSet + */ + public void set(BarLineScatterCandleBubbleDataProvider chart, IBarLineScatterCandleBubbleDataSet dataSet) { + float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + + float low = chart.getLowestVisibleX(); + float high = chart.getHighestVisibleX(); + + Entry entryFrom = dataSet.getEntryForXValue(low, Float.NaN, DataSet.Rounding.DOWN); + Entry entryTo = dataSet.getEntryForXValue(high, Float.NaN, DataSet.Rounding.UP); + + min = entryFrom == null ? 0 : dataSet.getEntryIndex(entryFrom); + max = entryTo == null ? 0 : dataSet.getEntryIndex(entryTo); + range = (int) ((max - min) * phaseX); + } + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java similarity index 65% rename from MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java index 45329aae9f..5ce19c2c9f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BubbleChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/BubbleChartRenderer.java @@ -4,6 +4,7 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.BubbleData; @@ -11,6 +12,7 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -21,7 +23,7 @@ * Bubble chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed * under Apache License 2.0 Ported by Daniel Cohen Gindi */ -public class BubbleChartRenderer extends DataRenderer { +public class BubbleChartRenderer extends BarLineScatterCandleBubbleRenderer { protected BubbleDataProvider mChart; @@ -48,7 +50,7 @@ public void drawData(Canvas c) { for (IBubbleDataSet set : bubbleData.getDataSets()) { - if (set.isVisible() && set.getEntryCount() > 0) + if (set.isVisible()) drawDataSet(c, set); } } @@ -56,44 +58,45 @@ public void drawData(Canvas c) { private float[] sizeBuffer = new float[4]; private float[] pointBuffer = new float[2]; - protected float getShapeSize(float entrySize, float maxSize, float reference) { - final float factor = (maxSize == 0f) ? 1f : (float) Math.sqrt(entrySize / maxSize); + protected float getShapeSize(float entrySize, float maxSize, float reference, boolean normalizeSize) { + final float factor = normalizeSize ? ((maxSize == 0f) ? 1f : (float) Math.sqrt(entrySize / maxSize)) : + entrySize; final float shapeSize = reference * factor; return shapeSize; } protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { + if (dataSet.getEntryCount() < 1) + return; + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryIndex(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + mXBounds.set(mChart, dataSet); sizeBuffer[0] = 0f; sizeBuffer[2] = 1f; trans.pointValuesToPixel(sizeBuffer); + boolean normalizeSize = dataSet.isNormalizeSizeEnabled(); + // calcualte the full width of 1 step on the x-axis final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); final float maxBubbleHeight = Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - for (int j = minx; j < maxx; j++) { + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { final BubbleEntry entry = dataSet.getEntryForIndex(j); - pointBuffer[0] = (float) (entry.getXIndex() - minx) * phaseX + (float) minx; - pointBuffer[1] = (float) (entry.getVal()) * phaseY; + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; trans.pointValuesToPixel(pointBuffer); - float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize) / 2f; + float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize, normalizeSize) / 2f; if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) @@ -105,7 +108,7 @@ protected void drawDataSet(Canvas c, IBubbleDataSet dataSet) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - final int color = dataSet.getColor(entry.getXIndex()); + final int color = dataSet.getColor(j); mRenderPaint.setColor(color); c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mRenderPaint); @@ -121,8 +124,7 @@ public void drawValues(Canvas c) { return; // if values are drawn - if (bubbleData.getYValCount() < (int) (Math.ceil((float) (mChart.getMaxVisibleCount()) - * mViewPortHandler.getScaleX()))) { + if (isDrawingValuesAllowed(mChart)) { final List dataSets = bubbleData.getDataSets(); @@ -132,29 +134,29 @@ public void drawValues(Canvas c) { IBubbleDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet applyValueTextStyle(dataSet); - final float phaseX = mAnimator.getPhaseX(); + final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); final float phaseY = mAnimator.getPhaseY(); - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + mXBounds.set(mChart, dataSet); final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesBubble(dataSet, phaseX, phaseY, minx, maxx); + .generateTransformedValuesBubble(dataSet, phaseY, mXBounds.min, mXBounds.max); final float alpha = phaseX == 1 ? phaseY : phaseX; + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { - int valueTextColor = dataSet.getValueTextColor(j / 2 + minx); + int valueTextColor = dataSet.getValueTextColor(j / 2 + mXBounds.min); valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), Color.green(valueTextColor), Color.blue(valueTextColor)); @@ -167,11 +169,28 @@ public void drawValues(Canvas c) { if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) continue; - BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + minx); + BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, + y + (0.5f * lineHeight), valueTextColor); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { - drawValue(c, dataSet.getValueFormatter(), entry.getSize(), entry, i, x, - y + (0.5f * lineHeight), valueTextColor); + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } @@ -187,43 +206,48 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { BubbleData bubbleData = mChart.getBubbleData(); - float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); - for (Highlight indice : indices) { + for (Highlight high : indices) { - IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(indice.getDataSetIndex()); + IBubbleDataSet set = bubbleData.getDataSetByIndex(high.getDataSetIndex()); - if (dataSet == null || !dataSet.isHighlightEnabled()) + if (set == null || !set.isHighlightEnabled()) continue; - BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX); - BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX); + final BubbleEntry entry = set.getEntryForXValue(high.getX(), high.getY()); - int minx = dataSet.getEntryIndex(entryFrom); - int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount()); + if (entry.getY() != high.getY()) + continue; - final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice); - if (entry == null || entry.getXIndex() != indice.getXIndex()) + if (!isInBoundsX(entry, set)) continue; - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + Transformer trans = mChart.getTransformer(set.getAxisDependency()); sizeBuffer[0] = 0f; sizeBuffer[2] = 1f; trans.pointValuesToPixel(sizeBuffer); + boolean normalizeSize = set.isNormalizeSizeEnabled(); + // calcualte the full width of 1 step on the x-axis final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]); - final float maxBubbleHeight = Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); + final float maxBubbleHeight = Math.abs( + mViewPortHandler.contentBottom() - mViewPortHandler.contentTop()); final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth); - pointBuffer[0] = (float) (entry.getXIndex() - minx) * phaseX + (float) minx; - pointBuffer[1] = (float) (entry.getVal()) * phaseY; + pointBuffer[0] = entry.getX(); + pointBuffer[1] = (entry.getY()) * phaseY; trans.pointValuesToPixel(pointBuffer); - float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize) / 2f; + high.setDraw(pointBuffer[0], pointBuffer[1]); + + float shapeHalf = getShapeSize(entry.getSize(), + set.getMaxSize(), + referenceSize, + normalizeSize) / 2f; if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf) || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) @@ -235,10 +259,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break; - if (indice.getXIndex() < minx || indice.getXIndex() >= maxx) - continue; - - final int originalColor = dataSet.getColor(entry.getXIndex()); + final int originalColor = set.getColor((int) entry.getX()); Color.RGBToHSV(Color.red(originalColor), Color.green(originalColor), Color.blue(originalColor), _hsvBuffer); @@ -246,7 +267,7 @@ public void drawHighlighted(Canvas c, Highlight[] indices) { final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer); mHighlightPaint.setColor(color); - mHighlightPaint.setStrokeWidth(dataSet.getHighlightCircleWidth()); + mHighlightPaint.setStrokeWidth(set.getHighlightCircleWidth()); c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint); } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java similarity index 76% rename from MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java index 2ef6c0962c..991b702117 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -3,6 +3,7 @@ import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.CandleData; @@ -11,6 +12,8 @@ import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -45,39 +48,35 @@ public void drawData(Canvas c) { for (ICandleDataSet set : candleData.getDataSets()) { - if (set.isVisible() && set.getEntryCount() > 0) + if (set.isVisible()) drawDataSet(c, set); } } + @SuppressWarnings("ResourceAsColor") protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - float phaseX = mAnimator.getPhaseX(); float phaseY = mAnimator.getPhaseY(); float barSpace = dataSet.getBarSpace(); boolean showCandleBar = dataSet.getShowCandleBar(); - int minx = Math.max(mMinX, 0); - int maxx = Math.min(mMaxX + 1, dataSet.getEntryCount()); + mXBounds.set(mChart, dataSet); mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); // draw the body - for (int j = minx, - count = (int) Math.ceil((maxx - minx) * phaseX + (float)minx); - j < count; - j++) { + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { // get the entry CandleEntry e = dataSet.getEntryForIndex(j); - final int xIndex = e.getXIndex(); - - if (xIndex < minx || xIndex >= maxx) + if (e == null) continue; + final float xPos = e.getX(); + final float open = e.getOpen(); final float close = e.getClose(); final float high = e.getHigh(); @@ -86,10 +85,10 @@ protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { if (showCandleBar) { // calculate the shadow - mShadowBuffers[0] = xIndex; - mShadowBuffers[2] = xIndex; - mShadowBuffers[4] = xIndex; - mShadowBuffers[6] = xIndex; + mShadowBuffers[0] = xPos; + mShadowBuffers[2] = xPos; + mShadowBuffers[4] = xPos; + mShadowBuffers[6] = xPos; if (open > close) { mShadowBuffers[1] = high * phaseY; @@ -149,9 +148,9 @@ else if (open < close) // calculate the body - mBodyBuffers[0] = xIndex - 0.5f + barSpace; + mBodyBuffers[0] = xPos - 0.5f + barSpace; mBodyBuffers[1] = close * phaseY; - mBodyBuffers[2] = (xIndex + 0.5f - barSpace); + mBodyBuffers[2] = (xPos + 0.5f - barSpace); mBodyBuffers[3] = open * phaseY; trans.pointValuesToPixel(mBodyBuffers); @@ -201,19 +200,19 @@ else if (open < close) } } else { - mRangeBuffers[0] = xIndex; + mRangeBuffers[0] = xPos; mRangeBuffers[1] = high * phaseY; - mRangeBuffers[2] = xIndex; + mRangeBuffers[2] = xPos; mRangeBuffers[3] = low * phaseY; - mOpenBuffers[0] = xIndex - 0.5f + barSpace; + mOpenBuffers[0] = xPos - 0.5f + barSpace; mOpenBuffers[1] = open * phaseY; - mOpenBuffers[2] = xIndex; + mOpenBuffers[2] = xPos; mOpenBuffers[3] = open * phaseY; - mCloseBuffers[0] = xIndex + 0.5f - barSpace; + mCloseBuffers[0] = xPos + 0.5f - barSpace; mCloseBuffers[1] = close * phaseY; - mCloseBuffers[2] = xIndex; + mCloseBuffers[2] = xPos; mCloseBuffers[3] = close * phaseY; trans.pointValuesToPixel(mRangeBuffers); @@ -249,7 +248,6 @@ else if (open < close) mCloseBuffers[0], mCloseBuffers[1], mCloseBuffers[2], mCloseBuffers[3], mRenderPaint); - } } } @@ -258,8 +256,7 @@ else if (open < close) public void drawValues(Canvas c) { // if values are drawn - if (mChart.getCandleData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { + if (isDrawingValuesAllowed(mChart)) { List dataSets = mChart.getCandleData().getDataSets(); @@ -267,7 +264,7 @@ public void drawValues(Canvas c) { ICandleDataSet dataSet = dataSets.get(i); - if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) continue; // apply the text-styling defined by the DataSet @@ -275,14 +272,17 @@ public void drawValues(Canvas c) { Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - int minx = Math.max(mMinX, 0); - int maxx = Math.min(mMaxX + 1, dataSet.getEntryCount()); + mXBounds.set(mChart, dataSet); float[] positions = trans.generateTransformedValuesCandle( - dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); + dataSet, mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); float yOffset = Utils.convertDpToPixel(5f); + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + for (int j = 0; j < positions.length; j += 2) { float x = positions[j]; @@ -294,10 +294,35 @@ public void drawValues(Canvas c) { if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) continue; - CandleEntry entry = dataSet.getEntryForIndex(j / 2 + minx); + CandleEntry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getHigh(), + entry, + i, + x, + y - yOffset, + dataSet + .getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); - drawValue(c, dataSet.getValueFormatter(), entry.getHigh(), entry, i, x, y - yOffset, dataSet.getValueTextColor(j / 2)); + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } } @@ -309,39 +334,30 @@ public void drawExtras(Canvas c) { @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (int i = 0; i < indices.length; i++) { + CandleData candleData = mChart.getCandleData(); - int xIndex = indices[i].getXIndex(); // get the - // x-position + for (Highlight high : indices) { - ICandleDataSet set = mChart.getCandleData().getDataSetByIndex( - indices[i].getDataSetIndex()); + ICandleDataSet set = candleData.getDataSetByIndex(high.getDataSetIndex()); if (set == null || !set.isHighlightEnabled()) continue; - CandleEntry e = set.getEntryForXIndex(xIndex); + CandleEntry e = set.getEntryForXValue(high.getX(), high.getY()); - if (e == null || e.getXIndex() != xIndex) + if (!isInBoundsX(e, set)) continue; - float low = e.getLow() * mAnimator.getPhaseY(); - float high = e.getHigh() * mAnimator.getPhaseY(); - float y = (low + high) / 2f; + float lowValue = e.getLow() * mAnimator.getPhaseY(); + float highValue = e.getHigh() * mAnimator.getPhaseY(); + float y = (lowValue + highValue) / 2f; - float min = mChart.getYChartMin(); - float max = mChart.getYChartMax(); + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), y); - - float[] pts = new float[]{ - xIndex, y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); + high.setDraw((float) pix.x, (float) pix.y); // draw the lines - drawHighlightLines(c, pts, set); + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); } } - } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java similarity index 61% rename from MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java index 6a1b2e518a..6d0d4d3da0 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -3,12 +3,16 @@ import android.graphics.Canvas; import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider; import com.github.mikephil.charting.utils.ViewPortHandler; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -20,25 +24,27 @@ public class CombinedChartRenderer extends DataRenderer { /** * all rederers for the different kinds of data this combined-renderer can draw */ - protected List mRenderers; + protected List mRenderers = new ArrayList(5); + + protected WeakReference mChart; public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); - - createRenderers(chart, animator, viewPortHandler); + mChart = new WeakReference(chart); + createRenderers(); } /** * Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into * consideration. - * - * @param chart - * @param animator - * @param viewPortHandler */ - protected void createRenderers(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + public void createRenderers() { - mRenderers = new ArrayList(); + mRenderers.clear(); + + CombinedChart chart = (CombinedChart)mChart.get(); + if (chart == null) + return; DrawOrder[] orders = chart.getDrawOrder(); @@ -47,23 +53,23 @@ protected void createRenderers(CombinedChart chart, ChartAnimator animator, View switch (order) { case BAR: if (chart.getBarData() != null) - mRenderers.add(new BarChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new BarChartRenderer(chart, mAnimator, mViewPortHandler)); break; case BUBBLE: if (chart.getBubbleData() != null) - mRenderers.add(new BubbleChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new BubbleChartRenderer(chart, mAnimator, mViewPortHandler)); break; case LINE: if (chart.getLineData() != null) - mRenderers.add(new LineChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new LineChartRenderer(chart, mAnimator, mViewPortHandler)); break; case CANDLE: if (chart.getCandleData() != null) - mRenderers.add(new CandleStickChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new CandleStickChartRenderer(chart, mAnimator, mViewPortHandler)); break; case SCATTER: if (chart.getScatterData() != null) - mRenderers.add(new ScatterChartRenderer(chart, animator, viewPortHandler)); + mRenderers.add(new ScatterChartRenderer(chart, mAnimator, mViewPortHandler)); break; } } @@ -97,16 +103,40 @@ public void drawExtras(Canvas c) { renderer.drawExtras(c); } + protected List mHighlightBuffer = new ArrayList(); + @Override public void drawHighlighted(Canvas c, Highlight[] indices) { - for (DataRenderer renderer : mRenderers) - renderer.drawHighlighted(c, indices); - } - @Override - public void calcXBounds(BarLineScatterCandleBubbleDataProvider chart, int xAxisModulus) { - for (DataRenderer renderer : mRenderers) - renderer.calcXBounds(chart, xAxisModulus); + Chart chart = mChart.get(); + if (chart == null) return; + + for (DataRenderer renderer : mRenderers) { + ChartData data = null; + + if (renderer instanceof BarChartRenderer) + data = ((BarChartRenderer)renderer).mChart.getBarData(); + else if (renderer instanceof LineChartRenderer) + data = ((LineChartRenderer)renderer).mChart.getLineData(); + else if (renderer instanceof CandleStickChartRenderer) + data = ((CandleStickChartRenderer)renderer).mChart.getCandleData(); + else if (renderer instanceof ScatterChartRenderer) + data = ((ScatterChartRenderer)renderer).mChart.getScatterData(); + else if (renderer instanceof BubbleChartRenderer) + data = ((BubbleChartRenderer)renderer).mChart.getBubbleData(); + + int dataIndex = data == null ? -1 + : ((CombinedData)chart.getData()).getAllData().indexOf(data); + + mHighlightBuffer.clear(); + + for (Highlight h : indices) { + if (h.getDataIndex() == dataIndex || h.getDataIndex() == -1) + mHighlightBuffer.add(h); + } + + renderer.drawHighlighted(c, mHighlightBuffer.toArray(new Highlight[mHighlightBuffer.size()])); + } } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java similarity index 88% rename from MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java index f4a88fe08d..e8e5446f4d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/DataRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/DataRenderer.java @@ -6,12 +6,15 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; import com.github.mikephil.charting.animation.ChartAnimator; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -65,6 +68,11 @@ public DataRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { mHighlightPaint.setColor(Color.rgb(255, 187, 115)); } + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() + * mViewPortHandler.getScaleX(); + } + /** * Returns the Paint object this renderer uses for drawing the values * (value-text). @@ -128,7 +136,7 @@ protected void applyValueTextStyle(IDataSet set) { public abstract void drawValues(Canvas c); /** - * Draws the value of the given entry by using the provided ValueFormatter. + * Draws the value of the given entry by using the provided IValueFormatter. * * @param c canvas * @param formatter formatter for custom value-formatting @@ -139,7 +147,7 @@ protected void applyValueTextStyle(IDataSet set) { * @param y position * @param color */ - public void drawValue(Canvas c, ValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { + public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) { mValuePaint.setColor(color); c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java new file mode 100644 index 0000000000..0cd72345fb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java @@ -0,0 +1,443 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint.Align; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.buffer.BarBuffer; +import com.github.mikephil.charting.buffer.HorizontalBarBuffer; +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.ChartInterface; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Fill; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +/** + * Renderer for the HorizontalBarChart. + * + * @author Philipp Jahoda + */ +public class HorizontalBarChartRenderer extends BarChartRenderer { + + public HorizontalBarChartRenderer(BarDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(chart, animator, viewPortHandler); + + mValuePaint.setTextAlign(Align.LEFT); + } + + @Override + public void initBuffers() { + + BarData barData = mChart.getBarData(); + mBarBuffers = new HorizontalBarBuffer[barData.getDataSetCount()]; + + for (int i = 0; i < mBarBuffers.length; i++) { + IBarDataSet set = barData.getDataSetByIndex(i); + mBarBuffers[i] = new HorizontalBarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1), + barData.getDataSetCount(), set.isStacked()); + } + } + + private RectF mBarShadowRectBuffer = new RectF(); + + @Override + protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mBarBorderPaint.setColor(dataSet.getBarBorderColor()); + mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth())); + + final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + // draw the bar shadow before the values + if (mChart.isDrawBarShadowEnabled()) { + mShadowPaint.setColor(dataSet.getBarShadowColor()); + + BarData barData = mChart.getBarData(); + + final float barWidth = barData.getBarWidth(); + final float barWidthHalf = barWidth / 2.0f; + float x; + + for (int i = 0, count = Math.min((int)(Math.ceil((float)(dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount()); + i < count; + i++) { + + BarEntry e = dataSet.getEntryForIndex(i); + + x = e.getX(); + + mBarShadowRectBuffer.top = x - barWidthHalf; + mBarShadowRectBuffer.bottom = x + barWidthHalf; + + trans.rectValueToPixel(mBarShadowRectBuffer); + + if (!mViewPortHandler.isInBoundsTop(mBarShadowRectBuffer.bottom)) + continue; + + if (!mViewPortHandler.isInBoundsBottom(mBarShadowRectBuffer.top)) + break; + + mBarShadowRectBuffer.left = mViewPortHandler.contentLeft(); + mBarShadowRectBuffer.right = mViewPortHandler.contentRight(); + + c.drawRect(mBarShadowRectBuffer, mShadowPaint); + } + } + + // initialize the buffer + BarBuffer buffer = mBarBuffers[index]; + buffer.setPhases(phaseX, phaseY); + buffer.setDataSet(index); + buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency())); + buffer.setBarWidth(mChart.getBarData().getBarWidth()); + + buffer.feed(dataSet); + + trans.pointValuesToPixel(buffer.buffer); + + final boolean isCustomFill = dataSet.getFills() != null && !dataSet.getFills().isEmpty(); + final boolean isSingleColor = dataSet.getColors().size() == 1; + final boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + if (isSingleColor) { + mRenderPaint.setColor(dataSet.getColor()); + } + + for (int j = 0, pos = 0; j < buffer.size(); j += 4, pos++) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 3])) + break; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) + continue; + + if (!isSingleColor) { + // Set the color for the currently drawn value. If the index + // is out of bounds, reuse colors. + mRenderPaint.setColor(dataSet.getColor(j / 4)); + } + + if (isCustomFill) { + dataSet.getFill(pos) + .fillRect( + c, mRenderPaint, + buffer.buffer[j], + buffer.buffer[j + 1], + buffer.buffer[j + 2], + buffer.buffer[j + 3], + isInverted ? Fill.Direction.LEFT : Fill.Direction.RIGHT); + } + else { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mRenderPaint); + } + + if (drawBorder) { + c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2], + buffer.buffer[j + 3], mBarBorderPaint); + } + } + } + + @Override + public void drawValues(Canvas c) { + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getBarData().getDataSets(); + + final float valueOffsetPlus = Utils.convertDpToPixel(5f); + float posOffset = 0f; + float negOffset = 0f; + final boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); + + for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { + + IBarDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet)) + continue; + + boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + final float halfTextHeight = Utils.calcTextHeight(mValuePaint, "10") / 2f; + + IValueFormatter formatter = dataSet.getValueFormatter(); + + // get the buffer + BarBuffer buffer = mBarBuffers[i]; + + final float phaseY = mAnimator.getPhaseY(); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + // if only single values are drawn (sum) + if (!dataSet.isStacked()) { + + for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) { + + float y = (buffer.buffer[j + 1] + buffer.buffer[j + 3]) / 2f; + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[j + 1])) + break; + + if (!mViewPortHandler.isInBoundsX(buffer.buffer[j])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[j + 1])) + continue; + + BarEntry entry = dataSet.getEntryForIndex(j / 4); + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) + - (buffer.buffer[j + 2] - buffer.buffer[j]); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + formattedValue, + buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset), + y + halfTextHeight, + dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[j + 2] + (val >= 0 ? posOffset : negOffset); + float py = y; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + // if each value of a potential stack should be drawn + } else { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + int bufferIndex = 0; + int index = 0; + + while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) { + + BarEntry entry = dataSet.getEntryForIndex(index); + + int color = dataSet.getValueTextColor(index); + float[] vals = entry.getYVals(); + + // we still draw stacked bars, but there is one + // non-stacked + // in between + if (vals == null) { + + if (!mViewPortHandler.isInBoundsTop(buffer.buffer[bufferIndex + 1])) + break; + + if (!mViewPortHandler.isInBoundsX(buffer.buffer[bufferIndex])) + continue; + + if (!mViewPortHandler.isInBoundsBottom(buffer.buffer[bufferIndex + 1])) + continue; + + float val = entry.getY(); + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, + buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset), + buffer.buffer[bufferIndex + 1] + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float px = buffer.buffer[bufferIndex + 2] + + (entry.getY() >= 0 ? posOffset : negOffset); + float py = buffer.buffer[bufferIndex + 1]; + + px += iconsOffset.x; + py += iconsOffset.y; + + Utils.drawImage( + c, + icon, + (int)px, + (int)py, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + } else { + + float[] transformed = new float[vals.length * 2]; + + float posY = 0f; + float negY = -entry.getNegativeSum(); + + for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) { + + float value = vals[idx]; + float y; + + 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; + } else if (value >= 0.0f) { + posY += value; + y = posY; + } else { + y = negY; + negY -= value; + } + + transformed[k] = y * phaseY; + } + + trans.pointValuesToPixel(transformed); + + for (int k = 0; k < transformed.length; k += 2) { + + final float val = vals[k / 2]; + String formattedValue = formatter.getFormattedValue(val, + entry, i, mViewPortHandler); + + // calculate the correct offset depending on the draw position of the value + float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue); + posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); + negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); + + if (isInverted) { + posOffset = -posOffset - valueTextWidth; + negOffset = -negOffset - valueTextWidth; + } + + final boolean drawBelow = + (val == 0.0f && negY == 0.0f && posY > 0.0f) || + val < 0.0f; + + float x = transformed[k] + + (drawBelow ? negOffset : posOffset); + float y = (buffer.buffer[bufferIndex + 1] + buffer.buffer[bufferIndex + 3]) / 2f; + + if (!mViewPortHandler.isInBoundsTop(y)) + break; + + if (!mViewPortHandler.isInBoundsX(x)) + continue; + + if (!mViewPortHandler.isInBoundsBottom(y)) + continue; + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, formattedValue, x, y + halfTextHeight, color); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + } + + bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length; + index++; + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + protected void drawValue(Canvas c, String valueText, float x, float y, int color) { + mValuePaint.setColor(color); + c.drawText(valueText, x, y, mValuePaint); + } + + @Override + protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) { + + float top = x - barWidthHalf; + float bottom = x + barWidthHalf; + float left = y1; + float right = y2; + + mBarRect.set(left, top, right, bottom); + + trans.rectToPixelPhaseHorizontal(mBarRect, mAnimator.getPhaseY()); + } + + @Override + protected void setHighlightDrawPos(Highlight high, RectF bar) { + high.setDraw(bar.centerY(), bar.right); + } + + @Override + protected boolean isDrawingValuesAllowed(ChartInterface chart) { + return chart.getData().getEntryCount() < chart.getMaxVisibleCount() + * mViewPortHandler.getScaleY(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java new file mode 100644 index 0000000000..5d49580561 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LegendRenderer.java @@ -0,0 +1,570 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.LegendEntry; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +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.Collections; +import java.util.List; + +public class LegendRenderer extends Renderer { + + /** + * paint for the legend labels + */ + protected Paint mLegendLabelPaint; + + /** + * paint used for the legend forms + */ + protected Paint mLegendFormPaint; + + /** + * the legend object this renderer renders + */ + protected Legend mLegend; + + public LegendRenderer(ViewPortHandler viewPortHandler, Legend legend) { + super(viewPortHandler); + + this.mLegend = legend; + + mLegendLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLegendLabelPaint.setTextSize(Utils.convertDpToPixel(9f)); + mLegendLabelPaint.setTextAlign(Align.LEFT); + + mLegendFormPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mLegendFormPaint.setStyle(Paint.Style.FILL); + } + + /** + * Returns the Paint object used for drawing the Legend labels. + * + * @return + */ + public Paint getLabelPaint() { + return mLegendLabelPaint; + } + + /** + * Returns the Paint object used for drawing the Legend forms. + * + * @return + */ + public Paint getFormPaint() { + return mLegendFormPaint; + } + + + protected List computedEntries = new ArrayList<>(16); + + /** + * Prepares the legend and calculates all needed forms, labels and colors. + * + * @param data + */ + public void computeLegend(ChartData data) { + + if (!mLegend.isLegendCustom()) { + + computedEntries.clear(); + + // loop for building up the colors and labels used in the legend + for (int i = 0; i < data.getDataSetCount(); i++) { + + IDataSet dataSet = data.getDataSetByIndex(i); + if (dataSet == null) continue; + + List clrs = dataSet.getColors(); + int entryCount = dataSet.getEntryCount(); + + // if we have a barchart with stacked bars + if (dataSet instanceof IBarDataSet && ((IBarDataSet) dataSet).isStacked()) { + + IBarDataSet bds = (IBarDataSet) dataSet; + String[] sLabels = bds.getStackLabels(); + + int minEntries = Math.min(clrs.size(), bds.getStackSize()); + + for (int j = 0; j < minEntries; j++) { + String label; + if (sLabels.length > 0) { + int labelIndex = j % minEntries; + label = labelIndex < sLabels.length ? sLabels[labelIndex] : null; + } else { + label = null; + } + + computedEntries.add(new LegendEntry( + label, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + + if (bds.getLabel() != null) { + // add the legend description label + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + null, + ColorTemplate.COLOR_NONE + )); + } + + } else if (dataSet instanceof IPieDataSet) { + + IPieDataSet pds = (IPieDataSet) dataSet; + + for (int j = 0; j < clrs.size() && j < entryCount; j++) { + + computedEntries.add(new LegendEntry( + pds.getEntryForIndex(j).getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + + if (pds.getLabel() != null) { + // add the legend description label + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + Legend.LegendForm.NONE, + Float.NaN, + Float.NaN, + null, + ColorTemplate.COLOR_NONE + )); + } + + } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() != + ColorTemplate.COLOR_NONE) { + + int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor(); + int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor(); + + computedEntries.add(new LegendEntry( + null, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + decreasingColor + )); + + computedEntries.add(new LegendEntry( + dataSet.getLabel(), + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + increasingColor + )); + + } else { // all others + + for (int j = 0; j < clrs.size() && j < entryCount; j++) { + + String label; + + // if multiple colors are set for a DataSet, group them + if (j < clrs.size() - 1 && j < entryCount - 1) { + label = null; + } else { // add label to the last entry + label = data.getDataSetByIndex(i).getLabel(); + } + + computedEntries.add(new LegendEntry( + label, + dataSet.getForm(), + dataSet.getFormSize(), + dataSet.getFormLineWidth(), + dataSet.getFormLineDashEffect(), + clrs.get(j) + )); + } + } + } + + if (mLegend.getExtraEntries() != null) { + Collections.addAll(computedEntries, mLegend.getExtraEntries()); + } + + mLegend.setEntries(computedEntries); + } + + Typeface tf = mLegend.getTypeface(); + + if (tf != null) + mLegendLabelPaint.setTypeface(tf); + + mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setColor(mLegend.getTextColor()); + + // calculate all dimensions of the mLegend + mLegend.calculateDimensions(mLegendLabelPaint, mViewPortHandler); + } + + protected Paint.FontMetrics legendFontMetrics = new Paint.FontMetrics(); + + public void renderLegend(Canvas c) { + + if (!mLegend.isEnabled()) + return; + + Typeface tf = mLegend.getTypeface(); + + if (tf != null) + mLegendLabelPaint.setTypeface(tf); + + mLegendLabelPaint.setTextSize(mLegend.getTextSize()); + mLegendLabelPaint.setColor(mLegend.getTextColor()); + + float labelLineHeight = Utils.getLineHeight(mLegendLabelPaint, legendFontMetrics); + float labelLineSpacing = Utils.getLineSpacing(mLegendLabelPaint, legendFontMetrics) + + Utils.convertDpToPixel(mLegend.getYEntrySpace()); + float formYOffset = labelLineHeight - Utils.calcTextHeight(mLegendLabelPaint, "ABC") / 2.f; + + LegendEntry[] entries = mLegend.getEntries(); + + float formToTextSpace = Utils.convertDpToPixel(mLegend.getFormToTextSpace()); + float xEntrySpace = Utils.convertDpToPixel(mLegend.getXEntrySpace()); + Legend.LegendOrientation orientation = mLegend.getOrientation(); + Legend.LegendHorizontalAlignment horizontalAlignment = mLegend.getHorizontalAlignment(); + Legend.LegendVerticalAlignment verticalAlignment = mLegend.getVerticalAlignment(); + Legend.LegendDirection direction = mLegend.getDirection(); + float defaultFormSize = Utils.convertDpToPixel(mLegend.getFormSize()); + + // space between the entries + float stackSpace = Utils.convertDpToPixel(mLegend.getStackSpace()); + + float yoffset = mLegend.getYOffset(); + float xoffset = mLegend.getXOffset(); + float originPosX = 0.f; + + switch (horizontalAlignment) { + case LEFT: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = xoffset; + else + originPosX = mViewPortHandler.contentLeft() + xoffset; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + originPosX += mLegend.mNeededWidth; + + break; + + case RIGHT: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() - xoffset; + else + originPosX = mViewPortHandler.contentRight() - xoffset; + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + originPosX -= mLegend.mNeededWidth; + + break; + + case CENTER: + + if (orientation == Legend.LegendOrientation.VERTICAL) + originPosX = mViewPortHandler.getChartWidth() / 2.f; + else + originPosX = mViewPortHandler.contentLeft() + + mViewPortHandler.contentWidth() / 2.f; + + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? +xoffset + : -xoffset); + + // Horizontally layed out legends do the center offset on a line basis, + // So here we offset the vertical ones only. + if (orientation == Legend.LegendOrientation.VERTICAL) { + originPosX += (direction == Legend.LegendDirection.LEFT_TO_RIGHT + ? -mLegend.mNeededWidth / 2.0 + xoffset + : mLegend.mNeededWidth / 2.0 - xoffset); + } + + break; + } + + switch (orientation) { + case HORIZONTAL: { + + List calculatedLineSizes = mLegend.getCalculatedLineSizes(); + List calculatedLabelSizes = mLegend.getCalculatedLabelSizes(); + List calculatedLabelBreakPoints = mLegend.getCalculatedLabelBreakPoints(); + + float posX = originPosX; + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = yoffset; + break; + + case BOTTOM: + posY = mViewPortHandler.getChartHeight() - yoffset - mLegend.mNeededHeight; + break; + + case CENTER: + posY = (mViewPortHandler.getChartHeight() - mLegend.mNeededHeight) / 2.f + yoffset; + break; + } + + int lineIndex = 0; + + for (int i = 0, count = entries.length; i < count; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); + + if (i < calculatedLabelBreakPoints.size() && calculatedLabelBreakPoints.get(i)) { + posX = originPosX; + posY += labelLineHeight + labelLineSpacing; + } + + if (posX == originPosX && + horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER && + lineIndex < calculatedLineSizes.size()) { + posX += (direction == Legend.LegendDirection.RIGHT_TO_LEFT + ? calculatedLineSizes.get(lineIndex).width + : -calculatedLineSizes.get(lineIndex).width) / 2.f; + lineIndex++; + } + + boolean isStacked = e.label == null; // grouped forms have null labels + + if (drawingForm) { + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= formSize; + + drawForm(c, posX, posY + formYOffset, e, mLegend); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += formSize; + } + + if (!isStacked) { + if (drawingForm) + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -formToTextSpace : + formToTextSpace; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= calculatedLabelSizes.get(i).width; + + drawLabel(c, posX, posY + labelLineHeight, e.label); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += calculatedLabelSizes.get(i).width; + + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -xEntrySpace : xEntrySpace; + } else + posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; + } + + break; + } + + case VERTICAL: { + // contains the stacked legend size in pixels + float stack = 0f; + boolean wasStacked = false; + float posY = 0.f; + + switch (verticalAlignment) { + case TOP: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? 0.f + : mViewPortHandler.contentTop()); + posY += yoffset; + break; + + case BOTTOM: + posY = (horizontalAlignment == Legend.LegendHorizontalAlignment.CENTER + ? mViewPortHandler.getChartHeight() + : mViewPortHandler.contentBottom()); + posY -= mLegend.mNeededHeight + yoffset; + break; + + case CENTER: + posY = mViewPortHandler.getChartHeight() / 2.f + - mLegend.mNeededHeight / 2.f + + mLegend.getYOffset(); + break; + } + + for (int i = 0; i < entries.length; i++) { + + LegendEntry e = entries[i]; + boolean drawingForm = e.form != Legend.LegendForm.NONE; + float formSize = Float.isNaN(e.formSize) ? defaultFormSize : Utils.convertDpToPixel(e.formSize); + + float posX = originPosX; + + if (drawingForm) { + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += stack; + else + posX -= formSize - stack; + + drawForm(c, posX, posY + formYOffset, e, mLegend); + + if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) + posX += formSize; + } + + if (e.label != null) { + + if (drawingForm && !wasStacked) + posX += direction == Legend.LegendDirection.LEFT_TO_RIGHT ? formToTextSpace + : -formToTextSpace; + else if (wasStacked) + posX = originPosX; + + if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) + posX -= Utils.calcTextWidth(mLegendLabelPaint, e.label); + + if (!wasStacked) { + drawLabel(c, posX, posY + labelLineHeight, e.label); + } else { + posY += labelLineHeight + labelLineSpacing; + drawLabel(c, posX, posY + labelLineHeight, e.label); + } + + // make a step down + posY += labelLineHeight + labelLineSpacing; + stack = 0f; + } else { + stack += formSize + stackSpace; + wasStacked = true; + } + } + + break; + + } + } + } + + private Path mLineFormPath = new Path(); + + /** + * Draws the Legend-form at the given position with the color at the given + * index. + * + * @param c canvas to draw with + * @param x position + * @param y position + * @param entry the entry to render + * @param legend the legend context + */ + protected void drawForm( + Canvas c, + float x, float y, + LegendEntry entry, + Legend legend) { + + if (entry.formColor == ColorTemplate.COLOR_SKIP || + entry.formColor == ColorTemplate.COLOR_NONE || + entry.formColor == 0) + return; + + int restoreCount = c.save(); + + Legend.LegendForm form = entry.form; + if (form == Legend.LegendForm.DEFAULT) + form = legend.getForm(); + + mLegendFormPaint.setColor(entry.formColor); + + final float formSize = Utils.convertDpToPixel( + Float.isNaN(entry.formSize) + ? legend.getFormSize() + : entry.formSize); + final float half = formSize / 2f; + + switch (form) { + case NONE: + // Do nothing + break; + + case EMPTY: + // Do not draw, but keep space for the form + break; + + case DEFAULT: + case CIRCLE: + mLegendFormPaint.setStyle(Paint.Style.FILL); + c.drawCircle(x + half, y, half, mLegendFormPaint); + break; + + case SQUARE: + mLegendFormPaint.setStyle(Paint.Style.FILL); + c.drawRect(x, y - half, x + formSize, y + half, mLegendFormPaint); + break; + + case LINE: + { + final float formLineWidth = Utils.convertDpToPixel( + Float.isNaN(entry.formLineWidth) + ? legend.getFormLineWidth() + : entry.formLineWidth); + final DashPathEffect formLineDashEffect = entry.formLineDashEffect == null + ? legend.getFormLineDashEffect() + : entry.formLineDashEffect; + mLegendFormPaint.setStyle(Paint.Style.STROKE); + mLegendFormPaint.setStrokeWidth(formLineWidth); + mLegendFormPaint.setPathEffect(formLineDashEffect); + + mLineFormPath.reset(); + mLineFormPath.moveTo(x, y); + mLineFormPath.lineTo(x + formSize, y); + c.drawPath(mLineFormPath, mLegendFormPaint); + } + break; + } + + c.restoreToCount(restoreCount); + } + + /** + * Draws the provided label at the given position. + * + * @param c canvas to draw with + * @param x + * @param y + * @param label the label to draw + */ + protected void drawLabel(Canvas c, float x, float y, String label) { + c.drawText(label, x, y, mLegendLabelPaint); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java new file mode 100644 index 0000000000..a86c16f76b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineChartRenderer.java @@ -0,0 +1,863 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; + +public class LineChartRenderer extends LineRadarRenderer { + + protected LineDataProvider mChart; + + /** + * paint for the inner circle of the value indicators + */ + protected Paint mCirclePaintInner; + + /** + * Bitmap object used for drawing the paths (otherwise they are too long if + * rendered directly on the canvas) + */ + protected WeakReference mDrawBitmap; + + /** + * on this canvas, the paths are rendered, it is initialized with the + * pathBitmap + */ + protected Canvas mBitmapCanvas; + + /** + * the bitmap configuration to be used + */ + protected Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; + + protected Path cubicPath = new Path(); + protected Path cubicFillPath = new Path(); + + public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mCirclePaintInner = new Paint(Paint.ANTI_ALIAS_FLAG); + mCirclePaintInner.setStyle(Paint.Style.FILL); + mCirclePaintInner.setColor(Color.WHITE); + } + + @Override + public void initBuffers() { + } + + @Override + public void drawData(Canvas c) { + + int width = (int) mViewPortHandler.getChartWidth(); + int height = (int) mViewPortHandler.getChartHeight(); + + Bitmap drawBitmap = mDrawBitmap == null ? null : mDrawBitmap.get(); + + if (drawBitmap == null + || (drawBitmap.getWidth() != width) + || (drawBitmap.getHeight() != height)) { + + if (width > 0 && height > 0) { + drawBitmap = Bitmap.createBitmap(width, height, mBitmapConfig); + mDrawBitmap = new WeakReference<>(drawBitmap); + mBitmapCanvas = new Canvas(drawBitmap); + } else + return; + } + + drawBitmap.eraseColor(Color.TRANSPARENT); + + LineData lineData = mChart.getLineData(); + + for (ILineDataSet set : lineData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + + c.drawBitmap(drawBitmap, 0, 0, mRenderPaint); + } + + protected void drawDataSet(Canvas c, ILineDataSet dataSet) { + + if (dataSet.getEntryCount() < 1) + return; + + mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); + mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); + + switch (dataSet.getMode()) { + default: + case LINEAR: + case STEPPED: + drawLinear(c, dataSet); + break; + + case CUBIC_BEZIER: + drawCubicBezier(dataSet); + break; + + case HORIZONTAL_BEZIER: + drawHorizontalBezier(dataSet); + break; + } + + mRenderPaint.setPathEffect(null); + } + + protected void drawHorizontalBezier(ILineDataSet dataSet) { + + float phaseY = mAnimator.getPhaseY(); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + cubicPath.reset(); + + if (mXBounds.range >= 1) { + + Entry prev = dataSet.getEntryForIndex(mXBounds.min); + Entry cur = prev; + + // let the spline start + cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); + + for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { + + prev = cur; + cur = dataSet.getEntryForIndex(j); + + final float cpx = (prev.getX()) + + (cur.getX() - prev.getX()) / 2.0f; + + cubicPath.cubicTo( + cpx, prev.getY() * phaseY, + cpx, cur.getY() * phaseY, + cur.getX(), cur.getY() * phaseY); + } + } + + // if filled is enabled, close the path + if (dataSet.isDrawFilledEnabled()) { + + cubicFillPath.reset(); + cubicFillPath.addPath(cubicPath); + // create a new path, this is bad for performance + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); + } + + mRenderPaint.setColor(dataSet.getColor()); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + trans.pathValueToPixel(cubicPath); + + mBitmapCanvas.drawPath(cubicPath, mRenderPaint); + + mRenderPaint.setPathEffect(null); + } + + protected void drawCubicBezier(ILineDataSet dataSet) { + + float phaseY = mAnimator.getPhaseY(); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + float intensity = dataSet.getCubicIntensity(); + + cubicPath.reset(); + + if (mXBounds.range >= 1) { + + float prevDx = 0f; + float prevDy = 0f; + float curDx = 0f; + float curDy = 0f; + + // Take an extra point from the left, and an extra from the right. + // That's because we need 4 points for a cubic bezier (cubic=4), otherwise we get lines moving and doing weird stuff on the edges of the chart. + // So in the starting `prev` and `cur`, go -2, -1 + // And in the `lastIndex`, add +1 + + final int firstIndex = mXBounds.min + 1; + final int lastIndex = mXBounds.min + mXBounds.range; + + Entry prevPrev; + Entry prev = dataSet.getEntryForIndex(Math.max(firstIndex - 2, 0)); + Entry cur = dataSet.getEntryForIndex(Math.max(firstIndex - 1, 0)); + Entry next = cur; + int nextIndex = -1; + + if (cur == null) return; + + // let the spline start + cubicPath.moveTo(cur.getX(), cur.getY() * phaseY); + + for (int j = mXBounds.min + 1; j <= mXBounds.range + mXBounds.min; j++) { + + prevPrev = prev; + prev = cur; + cur = nextIndex == j ? next : dataSet.getEntryForIndex(j); + + nextIndex = j + 1 < dataSet.getEntryCount() ? j + 1 : j; + next = dataSet.getEntryForIndex(nextIndex); + + prevDx = (cur.getX() - prevPrev.getX()) * intensity; + prevDy = (cur.getY() - prevPrev.getY()) * intensity; + curDx = (next.getX() - prev.getX()) * intensity; + curDy = (next.getY() - prev.getY()) * intensity; + + cubicPath.cubicTo(prev.getX() + prevDx, (prev.getY() + prevDy) * phaseY, + cur.getX() - curDx, + (cur.getY() - curDy) * phaseY, cur.getX(), cur.getY() * phaseY); + } + } + + // if filled is enabled, close the path + if (dataSet.isDrawFilledEnabled()) { + + cubicFillPath.reset(); + cubicFillPath.addPath(cubicPath); + + drawCubicFill(mBitmapCanvas, dataSet, cubicFillPath, trans, mXBounds); + } + + mRenderPaint.setColor(dataSet.getColor()); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + trans.pathValueToPixel(cubicPath); + + mBitmapCanvas.drawPath(cubicPath, mRenderPaint); + + mRenderPaint.setPathEffect(null); + } + + protected void drawCubicFill(Canvas c, ILineDataSet dataSet, Path spline, Transformer trans, XBounds bounds) { + + float fillMin = dataSet.getFillFormatter() + .getFillLinePosition(dataSet, mChart); + + spline.lineTo(dataSet.getEntryForIndex(bounds.min + bounds.range).getX(), fillMin); + spline.lineTo(dataSet.getEntryForIndex(bounds.min).getX(), fillMin); + spline.close(); + + trans.pathValueToPixel(spline); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, spline, drawable); + } else { + + drawFilledPath(c, spline, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + private float[] mLineBuffer = new float[4]; + + /** + * Draws a normal line. + * + * @param c + * @param dataSet + */ + protected void drawLinear(Canvas c, ILineDataSet dataSet) { + + int entryCount = dataSet.getEntryCount(); + + final boolean isDrawSteppedEnabled = dataSet.isDrawSteppedEnabled(); + final int pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2; + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + + mRenderPaint.setStyle(Paint.Style.STROKE); + + Canvas canvas = null; + + // if the data-set is dashed, draw on bitmap-canvas + if (dataSet.isDashedLineEnabled()) { + canvas = mBitmapCanvas; + } else { + canvas = c; + } + + mXBounds.set(mChart, dataSet); + + // if drawing filled is enabled + if (dataSet.isDrawFilledEnabled() && entryCount > 0) { + drawLinearFill(c, dataSet, trans, mXBounds); + } + + // more than 1 color + if (dataSet.getColors().size() > 1) { + + int numberOfFloats = pointsPerEntryPair * 2; + + if (mLineBuffer.length <= numberOfFloats) + mLineBuffer = new float[numberOfFloats * 2]; + + int max = mXBounds.min + mXBounds.range; + + for (int j = mXBounds.min; j < max; j++) { + + Entry e = dataSet.getEntryForIndex(j); + if (e == null) continue; + + mLineBuffer[0] = e.getX(); + mLineBuffer[1] = e.getY() * phaseY; + + if (j < mXBounds.max) { + + e = dataSet.getEntryForIndex(j + 1); + + if (e == null) break; + + if (isDrawSteppedEnabled) { + mLineBuffer[2] = e.getX(); + mLineBuffer[3] = mLineBuffer[1]; + mLineBuffer[4] = mLineBuffer[2]; + mLineBuffer[5] = mLineBuffer[3]; + mLineBuffer[6] = e.getX(); + mLineBuffer[7] = e.getY() * phaseY; + } else { + mLineBuffer[2] = e.getX(); + mLineBuffer[3] = e.getY() * phaseY; + } + + } else { + mLineBuffer[2] = mLineBuffer[0]; + mLineBuffer[3] = mLineBuffer[1]; + } + + // Determine the start and end coordinates of the line, and make sure they differ. + float firstCoordinateX = mLineBuffer[0]; + float firstCoordinateY = mLineBuffer[1]; + float lastCoordinateX = mLineBuffer[numberOfFloats - 2]; + float lastCoordinateY = mLineBuffer[numberOfFloats - 1]; + + if (firstCoordinateX == lastCoordinateX && + firstCoordinateY == lastCoordinateY) + continue; + + trans.pointValuesToPixel(mLineBuffer); + + if (!mViewPortHandler.isInBoundsRight(firstCoordinateX)) + break; + + // make sure the lines don't do shitty things outside + // bounds + if (!mViewPortHandler.isInBoundsLeft(lastCoordinateX) || + !mViewPortHandler.isInBoundsTop(Math.max(firstCoordinateY, lastCoordinateY)) || + !mViewPortHandler.isInBoundsBottom(Math.min(firstCoordinateY, lastCoordinateY))) + continue; + + // get the color that is set for this line-segment + mRenderPaint.setColor(dataSet.getColor(j)); + + canvas.drawLines(mLineBuffer, 0, pointsPerEntryPair * 2, mRenderPaint); + } + + } else { // only one color per dataset + + if (mLineBuffer.length < Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 2) + mLineBuffer = new float[Math.max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) * 4]; + + Entry e1, e2; + + e1 = dataSet.getEntryForIndex(mXBounds.min); + + if (e1 != null) { + + int j = 0; + for (int x = mXBounds.min; x <= mXBounds.range + mXBounds.min; x++) { + + e1 = dataSet.getEntryForIndex(x == 0 ? 0 : (x - 1)); + e2 = dataSet.getEntryForIndex(x); + + if (e1 == null || e2 == null) continue; + + mLineBuffer[j++] = e1.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + + if (isDrawSteppedEnabled) { + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e1.getY() * phaseY; + } + + mLineBuffer[j++] = e2.getX(); + mLineBuffer[j++] = e2.getY() * phaseY; + } + + if (j > 0) { + trans.pointValuesToPixel(mLineBuffer); + + final int size = Math.max((mXBounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) * 2; + + mRenderPaint.setColor(dataSet.getColor()); + + canvas.drawLines(mLineBuffer, 0, size, mRenderPaint); + } + } + } + + mRenderPaint.setPathEffect(null); + } + + protected Path mGenerateFilledPathBuffer = new Path(); + + /** + * Draws a filled linear path on the canvas. + * + * @param c + * @param dataSet + * @param trans + * @param bounds + */ + protected void drawLinearFill(Canvas c, ILineDataSet dataSet, Transformer trans, XBounds bounds) { + + final Path filled = mGenerateFilledPathBuffer; + + final int startingIndex = bounds.min; + final int endingIndex = bounds.range + bounds.min; + final int indexInterval = 128; + + int currentStartIndex = 0; + int currentEndIndex = indexInterval; + int iterations = 0; + + // Doing this iteratively in order to avoid OutOfMemory errors that can happen on large bounds sets. + do { + currentStartIndex = startingIndex + (iterations * indexInterval); + currentEndIndex = currentStartIndex + indexInterval; + currentEndIndex = currentEndIndex > endingIndex ? endingIndex : currentEndIndex; + + if (currentStartIndex <= currentEndIndex) { + generateFilledPath(dataSet, currentStartIndex, currentEndIndex, filled); + + trans.pathValueToPixel(filled); + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, filled, drawable); + } else { + + drawFilledPath(c, filled, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + iterations++; + + } while (currentStartIndex <= currentEndIndex); + + } + + /** + * Generates a path that is used for filled drawing. + * + * @param dataSet The dataset from which to read the entries. + * @param startIndex The index from which to start reading the dataset + * @param endIndex The index from which to stop reading the dataset + * @param outputPath The path object that will be assigned the chart data. + * @return + */ + private void generateFilledPath(final ILineDataSet dataSet, final int startIndex, final int endIndex, final Path outputPath) { + + final float fillMin = dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart); + final float phaseY = mAnimator.getPhaseY(); + final boolean isDrawSteppedEnabled = dataSet.getMode() == LineDataSet.Mode.STEPPED; + + final Path filled = outputPath; + filled.reset(); + + final Entry entry = dataSet.getEntryForIndex(startIndex); + + filled.moveTo(entry.getX(), fillMin); + filled.lineTo(entry.getX(), entry.getY() * phaseY); + + // create a new path + Entry currentEntry = null; + Entry previousEntry = entry; + for (int x = startIndex + 1; x <= endIndex; x++) { + + currentEntry = dataSet.getEntryForIndex(x); + + if (isDrawSteppedEnabled) { + filled.lineTo(currentEntry.getX(), previousEntry.getY() * phaseY); + } + + filled.lineTo(currentEntry.getX(), currentEntry.getY() * phaseY); + + previousEntry = currentEntry; + } + + // close up + if (currentEntry != null) { + filled.lineTo(currentEntry.getX(), fillMin); + } + + filled.close(); + } + + @Override + public void drawValues(Canvas c) { + + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getLineData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ILineDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + // make sure the values do not interfear with the circles + int valOffset = (int) (dataSet.getCircleRadius() * 1.75f); + + if (!dataSet.isDrawCirclesEnabled()) + valOffset = valOffset / 2; + + mXBounds.set(mChart, dataSet); + + float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator + .getPhaseY(), mXBounds.min, mXBounds.max); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + float x = positions[j]; + float y = positions[j + 1]; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) + continue; + + Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x, + y - valOffset, dataSet.getValueTextColor(j / 2)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + drawCircles(c); + } + + /** + * cache for the circle bitmaps of all datasets + */ + private HashMap mImageCaches = new HashMap<>(); + + /** + * buffer for drawing the circles + */ + private float[] mCirclesBuffer = new float[2]; + + protected void drawCircles(Canvas c) { + + mRenderPaint.setStyle(Paint.Style.FILL); + + float phaseY = mAnimator.getPhaseY(); + + mCirclesBuffer[0] = 0; + mCirclesBuffer[1] = 0; + + List dataSets = mChart.getLineData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ILineDataSet dataSet = dataSets.get(i); + + if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled() || + dataSet.getEntryCount() == 0) + continue; + + mCirclePaintInner.setColor(dataSet.getCircleHoleColor()); + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + mXBounds.set(mChart, dataSet); + + float circleRadius = dataSet.getCircleRadius(); + float circleHoleRadius = dataSet.getCircleHoleRadius(); + boolean drawCircleHole = dataSet.isDrawCircleHoleEnabled() && + circleHoleRadius < circleRadius && + circleHoleRadius > 0.f; + boolean drawTransparentCircleHole = drawCircleHole && + dataSet.getCircleHoleColor() == ColorTemplate.COLOR_NONE; + + DataSetImageCache imageCache; + + if (mImageCaches.containsKey(dataSet)) { + imageCache = mImageCaches.get(dataSet); + } else { + imageCache = new DataSetImageCache(); + mImageCaches.put(dataSet, imageCache); + } + + boolean changeRequired = imageCache.init(dataSet); + + // only fill the cache with new bitmaps if a change is required + if (changeRequired) { + imageCache.fill(dataSet, drawCircleHole, drawTransparentCircleHole); + } + + int boundsRangeCount = mXBounds.range + mXBounds.min; + + for (int j = mXBounds.min; j <= boundsRangeCount; j++) { + + Entry e = dataSet.getEntryForIndex(j); + + if (e == null) break; + + mCirclesBuffer[0] = e.getX(); + mCirclesBuffer[1] = e.getY() * phaseY; + + trans.pointValuesToPixel(mCirclesBuffer); + + if (!mViewPortHandler.isInBoundsRight(mCirclesBuffer[0])) + break; + + if (!mViewPortHandler.isInBoundsLeft(mCirclesBuffer[0]) || + !mViewPortHandler.isInBoundsY(mCirclesBuffer[1])) + continue; + + Bitmap circleBitmap = imageCache.getBitmap(j); + + if (circleBitmap != null) { + c.drawBitmap(circleBitmap, mCirclesBuffer[0] - circleRadius, mCirclesBuffer[1] - circleRadius, null); + } + } + } + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + LineData lineData = mChart.getLineData(); + + for (Highlight high : indices) { + + ILineDataSet set = lineData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + Entry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); + + high.setDraw((float) pix.x, (float) pix.y); + + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); + } + } + + /** + * Sets the Bitmap.Config to be used by this renderer. + * Default: Bitmap.Config.ARGB_8888 + * Use Bitmap.Config.ARGB_4444 to consume less memory. + * + * @param config + */ + public void setBitmapConfig(Bitmap.Config config) { + mBitmapConfig = config; + releaseBitmap(); + } + + /** + * Returns the Bitmap.Config that is used by this renderer. + * + * @return + */ + public Bitmap.Config getBitmapConfig() { + return mBitmapConfig; + } + + /** + * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. + */ + public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } + if (mDrawBitmap != null) { + Bitmap drawBitmap = mDrawBitmap.get(); + if (drawBitmap != null) { + drawBitmap.recycle(); + } + mDrawBitmap.clear(); + mDrawBitmap = null; + } + } + + private class DataSetImageCache { + + private Path mCirclePathBuffer = new Path(); + + private Bitmap[] circleBitmaps; + + /** + * Sets up the cache, returns true if a change of cache was required. + * + * @param set + * @return + */ + protected boolean init(ILineDataSet set) { + + int size = set.getCircleColorCount(); + boolean changeRequired = false; + + if (circleBitmaps == null) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } else if (circleBitmaps.length != size) { + circleBitmaps = new Bitmap[size]; + changeRequired = true; + } + + return changeRequired; + } + + /** + * Fills the cache with bitmaps for the given dataset. + * + * @param set + * @param drawCircleHole + * @param drawTransparentCircleHole + */ + protected void fill(ILineDataSet set, boolean drawCircleHole, boolean drawTransparentCircleHole) { + + int colorCount = set.getCircleColorCount(); + float circleRadius = set.getCircleRadius(); + float circleHoleRadius = set.getCircleHoleRadius(); + + for (int i = 0; i < colorCount; i++) { + + Bitmap.Config conf = Bitmap.Config.ARGB_4444; + Bitmap circleBitmap = Bitmap.createBitmap((int) (circleRadius * 2.1), (int) (circleRadius * 2.1), conf); + + Canvas canvas = new Canvas(circleBitmap); + circleBitmaps[i] = circleBitmap; + mRenderPaint.setColor(set.getCircleColor(i)); + + if (drawTransparentCircleHole) { + // Begin path for circle with hole + mCirclePathBuffer.reset(); + + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleRadius, + Path.Direction.CW); + + // Cut hole in path + mCirclePathBuffer.addCircle( + circleRadius, + circleRadius, + circleHoleRadius, + Path.Direction.CCW); + + // Fill in-between + canvas.drawPath(mCirclePathBuffer, mRenderPaint); + } else { + + canvas.drawCircle( + circleRadius, + circleRadius, + circleRadius, + mRenderPaint); + + if (drawCircleHole) { + canvas.drawCircle( + circleRadius, + circleRadius, + circleHoleRadius, + mCirclePaintInner); + } + } + } + } + + /** + * Returns the cached Bitmap at the given index. + * + * @param index + * @return + */ + protected Bitmap getBitmap(int index) { + return circleBitmaps[index % circleBitmaps.length]; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java new file mode 100644 index 0000000000..288eff64bf --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineRadarRenderer.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 25/01/16. + */ +public abstract class LineRadarRenderer extends LineScatterCandleRadarRenderer { + + public LineRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Draws the provided path in filled mode with the provided drawable. + * + * @param c + * @param filledPath + * @param drawable + */ + protected void drawFilledPath(Canvas c, Path filledPath, Drawable drawable) { + + if (clipPathSupported()) { + + int save = c.save(); + c.clipPath(filledPath); + + drawable.setBounds((int) mViewPortHandler.contentLeft(), + (int) mViewPortHandler.contentTop(), + (int) mViewPortHandler.contentRight(), + (int) mViewPortHandler.contentBottom()); + drawable.draw(c); + + c.restoreToCount(save); + } else { + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + + "this code was run on API level " + Utils.getSDKInt() + "."); + } + } + + /** + * Draws the provided path in filled mode with the provided color and alpha. + * Special thanks to Angelo Suzuki (https://github.com/tinsukE) for this. + * + * @param c + * @param filledPath + * @param fillColor + * @param fillAlpha + */ + protected void drawFilledPath(Canvas c, Path filledPath, int fillColor, int fillAlpha) { + + int color = (fillAlpha << 24) | (fillColor & 0xffffff); + + if (clipPathSupported()) { + + int save = c.save(); + + c.clipPath(filledPath); + + c.drawColor(color); + c.restoreToCount(save); + } else { + + // save + Paint.Style previous = mRenderPaint.getStyle(); + int previousColor = mRenderPaint.getColor(); + + // set + mRenderPaint.setStyle(Paint.Style.FILL); + mRenderPaint.setColor(color); + + c.drawPath(filledPath, mRenderPaint); + + // restore + mRenderPaint.setColor(previousColor); + mRenderPaint.setStyle(previous); + } + } + + /** + * Clip path with hardware acceleration only working properly on API level 18 and above. + * + * @return + */ + private boolean clipPathSupported() { + return Utils.getSDKInt() >= 18; + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java similarity index 78% rename from MPChartLib/src/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java index c262953aa3..53f54cbfd9 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -10,7 +10,7 @@ /** * Created by Philipp Jahoda on 11/07/15. */ -public abstract class LineScatterCandleRadarRenderer extends DataRenderer { +public abstract class LineScatterCandleRadarRenderer extends BarLineScatterCandleBubbleRenderer { /** * path that is used for drawing highlight-lines (drawLines(...) cannot be used because of dashes) @@ -25,10 +25,11 @@ public LineScatterCandleRadarRenderer(ChartAnimator animator, ViewPortHandler vi * Draws vertical & horizontal highlight-lines if enabled. * * @param c - * @param pts the transformed x- and y-position of the lines + * @param x x-position of the highlight line intersection + * @param y y-position of the highlight line intersection * @param set the currently drawn dataset */ - protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadarDataSet set) { + protected void drawHighlightLines(Canvas c, float x, float y, ILineScatterCandleRadarDataSet set) { // set color and stroke-width mHighlightPaint.setColor(set.getHighLightColor()); @@ -42,8 +43,8 @@ protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadar // create vertical path mHighlightLinePath.reset(); - mHighlightLinePath.moveTo(pts[0], mViewPortHandler.contentTop()); - mHighlightLinePath.lineTo(pts[0], mViewPortHandler.contentBottom()); + mHighlightLinePath.moveTo(x, mViewPortHandler.contentTop()); + mHighlightLinePath.lineTo(x, mViewPortHandler.contentBottom()); c.drawPath(mHighlightLinePath, mHighlightPaint); } @@ -53,8 +54,8 @@ protected void drawHighlightLines(Canvas c, float[] pts, ILineScatterCandleRadar // create horizontal path mHighlightLinePath.reset(); - mHighlightLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); - mHighlightLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + mHighlightLinePath.moveTo(mViewPortHandler.contentLeft(), y); + mHighlightLinePath.lineTo(mViewPortHandler.contentRight(), y); c.drawPath(mHighlightLinePath, mHighlightPaint); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java new file mode 100644 index 0000000000..f35c775d45 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/PieChartRenderer.java @@ -0,0 +1,1070 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.data.PieDataSet; +import com.github.mikephil.charting.data.PieEntry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.lang.ref.WeakReference; +import java.util.List; + +public class PieChartRenderer extends DataRenderer { + + protected PieChart mChart; + + /** + * paint for the hole in the center of the pie chart and the transparent + * circle + */ + protected Paint mHolePaint; + protected Paint mTransparentCirclePaint; + protected Paint mValueLinePaint; + + /** + * paint object for the text that can be displayed in the center of the + * chart + */ + private TextPaint mCenterTextPaint; + + /** + * paint object used for drwing the slice-text + */ + private Paint mEntryLabelsPaint; + + private StaticLayout mCenterTextLayout; + private CharSequence mCenterTextLastValue; + private RectF mCenterTextLastBounds = new RectF(); + private RectF[] mRectBuffer = {new RectF(), new RectF(), new RectF()}; + + /** + * Bitmap for drawing the center hole + */ + protected WeakReference mDrawBitmap; + + protected Canvas mBitmapCanvas; + + public PieChartRenderer(PieChart chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHolePaint.setColor(Color.WHITE); + mHolePaint.setStyle(Style.FILL); + + mTransparentCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTransparentCirclePaint.setColor(Color.WHITE); + mTransparentCirclePaint.setStyle(Style.FILL); + mTransparentCirclePaint.setAlpha(105); + + mCenterTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + mCenterTextPaint.setColor(Color.BLACK); + mCenterTextPaint.setTextSize(Utils.convertDpToPixel(12f)); + + mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); + mValuePaint.setColor(Color.WHITE); + mValuePaint.setTextAlign(Align.CENTER); + + mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mEntryLabelsPaint.setColor(Color.WHITE); + mEntryLabelsPaint.setTextAlign(Align.CENTER); + mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f)); + + mValueLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mValueLinePaint.setStyle(Style.STROKE); + } + + public Paint getPaintHole() { + return mHolePaint; + } + + public Paint getPaintTransparentCircle() { + return mTransparentCirclePaint; + } + + public TextPaint getPaintCenterText() { + return mCenterTextPaint; + } + + public Paint getPaintEntryLabels() { + return mEntryLabelsPaint; + } + + @Override + public void initBuffers() { + // TODO Auto-generated method stub + } + + @Override + public void drawData(Canvas c) { + + int width = (int) mViewPortHandler.getChartWidth(); + int height = (int) mViewPortHandler.getChartHeight(); + + Bitmap drawBitmap = mDrawBitmap == null ? null : mDrawBitmap.get(); + + if (drawBitmap == null + || (drawBitmap.getWidth() != width) + || (drawBitmap.getHeight() != height)) { + + if (width > 0 && height > 0) { + drawBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); + mDrawBitmap = new WeakReference<>(drawBitmap); + mBitmapCanvas = new Canvas(drawBitmap); + } else + return; + } + + drawBitmap.eraseColor(Color.TRANSPARENT); + + PieData pieData = mChart.getData(); + + for (IPieDataSet set : pieData.getDataSets()) { + + if (set.isVisible() && set.getEntryCount() > 0) + drawDataSet(c, set); + } + } + + private Path mPathBuffer = new Path(); + private RectF mInnerRectBuffer = new RectF(); + + protected float calculateMinimumRadiusForSpacedSlice( + MPPointF center, + float radius, + float angle, + float arcStartPointX, + float arcStartPointY, + float startAngle, + float sweepAngle) { + final float angleMiddle = startAngle + sweepAngle / 2.f; + + // Other point of the arc + float arcEndPointX = center.x + radius * (float) Math.cos((startAngle + sweepAngle) * Utils.FDEG2RAD); + float arcEndPointY = center.y + radius * (float) Math.sin((startAngle + sweepAngle) * Utils.FDEG2RAD); + + // Middle point on the arc + float arcMidPointX = center.x + radius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + float arcMidPointY = center.y + radius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + // This is the base of the contained triangle + double basePointsDistance = Math.sqrt( + Math.pow(arcEndPointX - arcStartPointX, 2) + + Math.pow(arcEndPointY - arcStartPointY, 2)); + + // After reducing space from both sides of the "slice", + // the angle of the contained triangle should stay the same. + // So let's find out the height of that triangle. + float containedTriangleHeight = (float) (basePointsDistance / 2.0 * + Math.tan((180.0 - angle) / 2.0 * Utils.DEG2RAD)); + + // Now we subtract that from the radius + float spacedRadius = radius - containedTriangleHeight; + + // And now subtract the height of the arc that's between the triangle and the outer circle + spacedRadius -= Math.sqrt( + Math.pow(arcMidPointX - (arcEndPointX + arcStartPointX) / 2.f, 2) + + Math.pow(arcMidPointY - (arcEndPointY + arcStartPointY) / 2.f, 2)); + + return spacedRadius; + } + + /** + * Calculates the sliceSpace to use based on visible values and their size compared to the set sliceSpace. + * + * @param dataSet + * @return + */ + protected float getSliceSpace(IPieDataSet dataSet) { + + if (!dataSet.isAutomaticallyDisableSliceSpacingEnabled()) + return dataSet.getSliceSpace(); + + float spaceSizeRatio = dataSet.getSliceSpace() / mViewPortHandler.getSmallestContentExtension(); + float minValueRatio = dataSet.getYMin() / mChart.getData().getYValueSum() * 2; + + float sliceSpace = spaceSizeRatio > minValueRatio ? 0f : dataSet.getSliceSpace(); + + return sliceSpace; + } + + protected void drawDataSet(Canvas c, IPieDataSet dataSet) { + + float angle = 0; + float rotationAngle = mChart.getRotationAngle(); + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + final RectF circleBox = mChart.getCircleBox(); + + final int entryCount = dataSet.getEntryCount(); + final float[] drawAngles = mChart.getDrawAngles(); + final MPPointF center = mChart.getCenterCircleBox(); + final float radius = mChart.getRadius(); + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + final float userInnerRadius = drawInnerArc + ? radius * (mChart.getHoleRadius() / 100.f) + : 0.f; + final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f; + final RectF roundedCircleBox = new RectF(); + final boolean drawRoundedSlices = drawInnerArc && mChart.isDrawRoundedSlicesEnabled(); + + int visibleAngleCount = 0; + for (int j = 0; j < entryCount; j++) { + // draw only if the value is greater than zero + if ((Math.abs(dataSet.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { + visibleAngleCount++; + } + } + + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : getSliceSpace(dataSet); + + for (int j = 0; j < entryCount; j++) { + + float sliceAngle = drawAngles[j]; + float innerRadius = userInnerRadius; + + Entry e = dataSet.getEntryForIndex(j); + + // draw only if the value is greater than zero + if (!(Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + angle += sliceAngle * phaseX; + continue; + } + + // Don't draw if it's highlighted, unless the chart uses rounded slices + if (dataSet.isHighlightEnabled() && mChart.needsHighlight(j) && !drawRoundedSlices) { + angle += sliceAngle * phaseX; + continue; + } + + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + + mRenderPaint.setColor(dataSet.getColor(j)); + + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } + + mPathBuffer.reset(); + + if (drawRoundedSlices) { + float x = center.x + (radius - roundedRadius) * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float y = center.y + (radius - roundedRadius) * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); + roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius); + } + + float arcStartPointX = center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD); + float arcStartPointY = center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD); + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW); + } else { + + if (drawRoundedSlices) { + mPathBuffer.arcTo(roundedCircleBox, startAngleOuter + 180, -180); + } + + mPathBuffer.arcTo( + circleBox, + startAngleOuter, + sweepAngleOuter + ); + } + + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); + + if (drawInnerArc && (innerRadius > 0.f || accountForSliceSpacing)) { + + if (accountForSliceSpacing) { + float minSpacedRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + arcStartPointX, arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + if (drawRoundedSlices) { + float x = center.x + (radius - roundedRadius) * (float) Math.cos(endAngleInner * Utils.FDEG2RAD); + float y = center.y + (radius - roundedRadius) * (float) Math.sin(endAngleInner * Utils.FDEG2RAD); + roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius); + mPathBuffer.arcTo(roundedCircleBox, endAngleInner, 180); + } else + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + if (accountForSliceSpacing) { + + float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + float sliceSpaceOffset = + calculateMinimumRadiusForSpacedSlice( + center, + radius, + sliceAngle * phaseY, + arcStartPointX, + arcStartPointY, + startAngleOuter, + sweepAngleOuter); + + float arcEndPointX = center.x + + sliceSpaceOffset * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + float arcEndPointY = center.y + + sliceSpaceOffset * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + mPathBuffer.lineTo( + center.x, + center.y); + } + } + + } + + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + + angle += sliceAngle * phaseX; + } + + MPPointF.recycleInstance(center); + } + + @Override + public void drawValues(Canvas c) { + + MPPointF center = mChart.getCenterCircleBox(); + + // get whole the radius + float radius = mChart.getRadius(); + float rotationAngle = mChart.getRotationAngle(); + float[] drawAngles = mChart.getDrawAngles(); + float[] absoluteAngles = mChart.getAbsoluteAngles(); + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + final float roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f; + final float holeRadiusPercent = mChart.getHoleRadius() / 100.f; + float labelRadiusOffset = radius / 10f * 3.6f; + + if (mChart.isDrawHoleEnabled()) { + labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f; + + if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) { + // Add curved circle slice and spacing to rotation angle, so that it sits nicely inside + rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius); + } + } + + final float labelRadius = radius - labelRadiusOffset; + + PieData data = mChart.getData(); + List dataSets = data.getDataSets(); + + float yValueSum = data.getYValueSum(); + + boolean drawEntryLabels = mChart.isDrawEntryLabelsEnabled(); + + float angle; + int xIndex = 0; + + c.save(); + + float offset = Utils.convertDpToPixel(5.f); + + for (int i = 0; i < dataSets.size(); i++) { + + IPieDataSet dataSet = dataSets.get(i); + + final boolean drawValues = dataSet.isDrawValuesEnabled(); + + if (!drawValues && !drawEntryLabels) + continue; + + final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition(); + final PieDataSet.ValuePosition yValuePosition = dataSet.getYValuePosition(); + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + + Utils.convertDpToPixel(4f); + + IValueFormatter formatter = dataSet.getValueFormatter(); + + int entryCount = dataSet.getEntryCount(); + + boolean isUseValueColorForLineEnabled = dataSet.isUseValueColorForLineEnabled(); + int valueLineColor = dataSet.getValueLineColor(); + + mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); + + final float sliceSpace = getSliceSpace(dataSet); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < entryCount; j++) { + + PieEntry entry = dataSet.getEntryForIndex(j); + + if (xIndex == 0) + angle = 0.f; + else + angle = absoluteAngles[xIndex - 1] * phaseX; + + final float sliceAngle = drawAngles[xIndex]; + final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius); + + // offset needed to center the drawn text in the slice + final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f; + + angle = angle + angleOffset; + + final float transformedAngle = rotationAngle + angle * phaseY; + + float value = mChart.isUsePercentValuesEnabled() ? entry.getY() + / yValueSum * 100f : entry.getY(); + String entryLabel = entry.getLabel(); + + final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); + final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); + + final boolean drawXOutside = drawEntryLabels && + xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawYOutside = drawValues && + yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE; + final boolean drawXInside = drawEntryLabels && + xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + final boolean drawYInside = drawValues && + yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE; + + if (drawXOutside || drawYOutside) { + + final float valueLineLength1 = dataSet.getValueLinePart1Length(); + final float valueLineLength2 = dataSet.getValueLinePart2Length(); + final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f; + + float pt2x, pt2y; + float labelPtx, labelPty; + + float line1Radius; + + if (mChart.isDrawHoleEnabled()) + line1Radius = (radius - (radius * holeRadiusPercent)) + * valueLinePart1OffsetPercentage + + (radius * holeRadiusPercent); + else + line1Radius = radius * valueLinePart1OffsetPercentage; + + final float polyline2Width = dataSet.isValueLineVariableLength() + ? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin( + transformedAngle * Utils.FDEG2RAD)) + : labelRadius * valueLineLength2; + + final float pt0x = line1Radius * sliceXBase + center.x; + final float pt0y = line1Radius * sliceYBase + center.y; + + final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x; + final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y; + + if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) { + pt2x = pt1x - polyline2Width; + pt2y = pt1y; + + mValuePaint.setTextAlign(Align.RIGHT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.RIGHT); + + labelPtx = pt2x - offset; + labelPty = pt2y; + } else { + pt2x = pt1x + polyline2Width; + pt2y = pt1y; + mValuePaint.setTextAlign(Align.LEFT); + + if(drawXOutside) + mEntryLabelsPaint.setTextAlign(Align.LEFT); + + labelPtx = pt2x + offset; + labelPty = pt2y; + } + + int lineColor = ColorTemplate.COLOR_NONE; + + if (isUseValueColorForLineEnabled) + lineColor = dataSet.getColor(j); + else if (valueLineColor != ColorTemplate.COLOR_NONE) + lineColor = valueLineColor; + + if (lineColor != ColorTemplate.COLOR_NONE) { + mValueLinePaint.setColor(lineColor); + c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); + c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); + } + + // draw everything, depending on settings + if (drawXOutside && drawYOutside) { + + drawValue(c, + formatter, + value, + entry, + 0, + labelPtx, + labelPty, + dataSet.getValueTextColor(j)); + + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight); + } + + } else if (drawXOutside) { + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight / 2.f); + } + } else if (drawYOutside) { + + drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet + .getValueTextColor(j)); + } + } + + if (drawXInside || drawYInside) { + // calculate the text position + float x = labelRadius * sliceXBase + center.x; + float y = labelRadius * sliceYBase + center.y; + + mValuePaint.setTextAlign(Align.CENTER); + + // draw everything, depending on settings + if (drawXInside && drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j)); + + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, x, y + lineHeight); + } + + } else if (drawXInside) { + if (j < data.getEntryCount() && entryLabel != null) { + drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f); + } + } else if (drawYInside) { + + drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j)); + } + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + float x = (labelRadius + iconsOffset.y) * sliceXBase + center.x; + float y = (labelRadius + iconsOffset.y) * sliceYBase + center.y; + y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)x, + (int)y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + + xIndex++; + } + + MPPointF.recycleInstance(iconsOffset); + } + MPPointF.recycleInstance(center); + c.restore(); + } + + /** + * Draws an entry label at the specified position. + * + * @param c + * @param label + * @param x + * @param y + */ + protected void drawEntryLabel(Canvas c, String label, float x, float y) { + c.drawText(label, x, y, mEntryLabelsPaint); + } + + @Override + public void drawExtras(Canvas c) { + drawHole(c); + c.drawBitmap(mDrawBitmap.get(), 0, 0, null); + drawCenterText(c); + } + + private Path mHoleCirclePath = new Path(); + + /** + * draws the hole in the center of the chart and the transparent circle / + * hole + */ + protected void drawHole(Canvas c) { + + if (mChart.isDrawHoleEnabled() && mBitmapCanvas != null) { + + float radius = mChart.getRadius(); + float holeRadius = radius * (mChart.getHoleRadius() / 100); + MPPointF center = mChart.getCenterCircleBox(); + + if (Color.alpha(mHolePaint.getColor()) > 0) { + // draw the hole-circle + mBitmapCanvas.drawCircle( + center.x, center.y, + holeRadius, mHolePaint); + } + + // only draw the circle if it can be seen (not covered by the hole) + if (Color.alpha(mTransparentCirclePaint.getColor()) > 0 && + mChart.getTransparentCircleRadius() > mChart.getHoleRadius()) { + + int alpha = mTransparentCirclePaint.getAlpha(); + float secondHoleRadius = radius * (mChart.getTransparentCircleRadius() / 100); + + mTransparentCirclePaint.setAlpha((int) ((float) alpha * mAnimator.getPhaseX() * mAnimator.getPhaseY())); + + // draw the transparent-circle + mHoleCirclePath.reset(); + mHoleCirclePath.addCircle(center.x, center.y, secondHoleRadius, Path.Direction.CW); + mHoleCirclePath.addCircle(center.x, center.y, holeRadius, Path.Direction.CCW); + mBitmapCanvas.drawPath(mHoleCirclePath, mTransparentCirclePaint); + + // reset alpha + mTransparentCirclePaint.setAlpha(alpha); + } + MPPointF.recycleInstance(center); + } + } + + protected Path mDrawCenterTextPathBuffer = new Path(); + /** + * draws the description text in the center of the pie chart makes most + * sense when center-hole is enabled + */ + protected void drawCenterText(Canvas c) { + + CharSequence centerText = mChart.getCenterText(); + + if (mChart.isDrawCenterTextEnabled() && centerText != null) { + + MPPointF center = mChart.getCenterCircleBox(); + MPPointF offset = mChart.getCenterTextOffset(); + + float x = center.x + offset.x; + float y = center.y + offset.y; + + float innerRadius = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled() + ? mChart.getRadius() * (mChart.getHoleRadius() / 100f) + : mChart.getRadius(); + + RectF holeRect = mRectBuffer[0]; + holeRect.left = x - innerRadius; + holeRect.top = y - innerRadius; + holeRect.right = x + innerRadius; + holeRect.bottom = y + innerRadius; + RectF boundingRect = mRectBuffer[1]; + boundingRect.set(holeRect); + + float radiusPercent = mChart.getCenterTextRadiusPercent() / 100f; + if (radiusPercent > 0.0) { + boundingRect.inset( + (boundingRect.width() - boundingRect.width() * radiusPercent) / 2.f, + (boundingRect.height() - boundingRect.height() * radiusPercent) / 2.f + ); + } + + if (!centerText.equals(mCenterTextLastValue) || !boundingRect.equals(mCenterTextLastBounds)) { + + // Next time we won't recalculate StaticLayout... + mCenterTextLastBounds.set(boundingRect); + mCenterTextLastValue = centerText; + + float width = mCenterTextLastBounds.width(); + + // If width is 0, it will crash. Always have a minimum of 1 + mCenterTextLayout = new StaticLayout(centerText, 0, centerText.length(), + mCenterTextPaint, + (int) Math.max(Math.ceil(width), 1.f), + Layout.Alignment.ALIGN_CENTER, 1.f, 0.f, false); + } + + //float layoutWidth = Utils.getStaticLayoutMaxWidth(mCenterTextLayout); + float layoutHeight = mCenterTextLayout.getHeight(); + + c.save(); + if (Build.VERSION.SDK_INT >= 18) { + Path path = mDrawCenterTextPathBuffer; + path.reset(); + path.addOval(holeRect, Path.Direction.CW); + c.clipPath(path); + } + + c.translate(boundingRect.left, boundingRect.top + (boundingRect.height() - layoutHeight) / 2.f); + mCenterTextLayout.draw(c); + + c.restore(); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(offset); + } + } + + protected RectF mDrawHighlightedRectF = new RectF(); + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + /* Skip entirely if using rounded circle slices, because it doesn't make sense to highlight + * in this way. + * TODO: add support for changing slice color with highlighting rather than only shifting the slice + */ + + final boolean drawInnerArc = mChart.isDrawHoleEnabled() && !mChart.isDrawSlicesUnderHoleEnabled(); + if (drawInnerArc && mChart.isDrawRoundedSlicesEnabled()) + return; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float angle; + float rotationAngle = mChart.getRotationAngle(); + + float[] drawAngles = mChart.getDrawAngles(); + float[] absoluteAngles = mChart.getAbsoluteAngles(); + final MPPointF center = mChart.getCenterCircleBox(); + final float radius = mChart.getRadius(); + final float userInnerRadius = drawInnerArc + ? radius * (mChart.getHoleRadius() / 100.f) + : 0.f; + + final RectF highlightedCircleBox = mDrawHighlightedRectF; + highlightedCircleBox.set(0,0,0,0); + + for (int i = 0; i < indices.length; i++) { + + // get the index to highlight + int index = (int) indices[i].getX(); + + if (index >= drawAngles.length) + continue; + + IPieDataSet set = mChart.getData() + .getDataSetByIndex(indices[i].getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final int entryCount = set.getEntryCount(); + int visibleAngleCount = 0; + for (int j = 0; j < entryCount; j++) { + // draw only if the value is greater than zero + if ((Math.abs(set.getEntryForIndex(j).getY()) > Utils.FLOAT_EPSILON)) { + visibleAngleCount++; + } + } + + if (index == 0) + angle = 0.f; + else + angle = absoluteAngles[index - 1] * phaseX; + + final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace(); + + float sliceAngle = drawAngles[index]; + float innerRadius = userInnerRadius; + + float shift = set.getSelectionShift(); + final float highlightedRadius = radius + shift; + highlightedCircleBox.set(mChart.getCircleBox()); + highlightedCircleBox.inset(-shift, -shift); + + final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f; + + Integer highlightColor = set.getHighlightColor(); + if (highlightColor == null) + highlightColor = set.getColor(index); + mRenderPaint.setColor(highlightColor); + + final float sliceSpaceAngleOuter = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * radius); + + final float sliceSpaceAngleShifted = visibleAngleCount == 1 ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * highlightedRadius); + + final float startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.f) * phaseY; + float sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY; + if (sweepAngleOuter < 0.f) { + sweepAngleOuter = 0.f; + } + + final float startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.f) * phaseY; + float sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY; + if (sweepAngleShifted < 0.f) { + sweepAngleShifted = 0.f; + } + + mPathBuffer.reset(); + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, highlightedRadius, Path.Direction.CW); + } else { + + mPathBuffer.moveTo( + center.x + highlightedRadius * (float) Math.cos(startAngleShifted * Utils.FDEG2RAD), + center.y + highlightedRadius * (float) Math.sin(startAngleShifted * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + highlightedCircleBox, + startAngleShifted, + sweepAngleShifted + ); + } + + float sliceSpaceRadius = 0.f; + if (accountForSliceSpacing) { + sliceSpaceRadius = + calculateMinimumRadiusForSpacedSlice( + center, radius, + sliceAngle * phaseY, + center.x + radius * (float) Math.cos(startAngleOuter * Utils.FDEG2RAD), + center.y + radius * (float) Math.sin(startAngleOuter * Utils.FDEG2RAD), + startAngleOuter, + sweepAngleOuter); + } + + // API < 21 does not receive floats in addArc, but a RectF + mInnerRectBuffer.set( + center.x - innerRadius, + center.y - innerRadius, + center.x + innerRadius, + center.y + innerRadius); + + if (drawInnerArc && + (innerRadius > 0.f || accountForSliceSpacing)) { + + if (accountForSliceSpacing) { + float minSpacedRadius = sliceSpaceRadius; + + if (minSpacedRadius < 0.f) + minSpacedRadius = -minSpacedRadius; + + innerRadius = Math.max(innerRadius, minSpacedRadius); + } + + final float sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.f ? + 0.f : + sliceSpace / (Utils.FDEG2RAD * innerRadius); + final float startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.f) * phaseY; + float sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY; + if (sweepAngleInner < 0.f) { + sweepAngleInner = 0.f; + } + final float endAngleInner = startAngleInner + sweepAngleInner; + + if (sweepAngleOuter >= 360.f && sweepAngleOuter % 360f <= Utils.FLOAT_EPSILON) { + // Android is doing "mod 360" + mPathBuffer.addCircle(center.x, center.y, innerRadius, Path.Direction.CCW); + } else { + + mPathBuffer.lineTo( + center.x + innerRadius * (float) Math.cos(endAngleInner * Utils.FDEG2RAD), + center.y + innerRadius * (float) Math.sin(endAngleInner * Utils.FDEG2RAD)); + + mPathBuffer.arcTo( + mInnerRectBuffer, + endAngleInner, + -sweepAngleInner + ); + } + } else { + + if (sweepAngleOuter % 360f > Utils.FLOAT_EPSILON) { + + if (accountForSliceSpacing) { + final float angleMiddle = startAngleOuter + sweepAngleOuter / 2.f; + + final float arcEndPointX = center.x + + sliceSpaceRadius * (float) Math.cos(angleMiddle * Utils.FDEG2RAD); + final float arcEndPointY = center.y + + sliceSpaceRadius * (float) Math.sin(angleMiddle * Utils.FDEG2RAD); + + mPathBuffer.lineTo( + arcEndPointX, + arcEndPointY); + + } else { + + mPathBuffer.lineTo( + center.x, + center.y); + } + + } + + } + + mPathBuffer.close(); + + mBitmapCanvas.drawPath(mPathBuffer, mRenderPaint); + } + + MPPointF.recycleInstance(center); + } + + /** + * This gives all pie-slices a rounded edge. + * + * @param c + */ + protected void drawRoundedSlices(Canvas c) { + + if (!mChart.isDrawRoundedSlicesEnabled()) + return; + + IPieDataSet dataSet = mChart.getData().getDataSet(); + + if (!dataSet.isVisible()) + return; + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + MPPointF center = mChart.getCenterCircleBox(); + float r = mChart.getRadius(); + + // calculate the radius of the "slice-circle" + float circleRadius = (r - (r * mChart.getHoleRadius() / 100f)) / 2f; + + float[] drawAngles = mChart.getDrawAngles(); + float angle = mChart.getRotationAngle(); + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + float sliceAngle = drawAngles[j]; + + Entry e = dataSet.getEntryForIndex(j); + + // draw only if the value is greater than zero + if ((Math.abs(e.getY()) > Utils.FLOAT_EPSILON)) { + + float x = (float) ((r - circleRadius) + * Math.cos(Math.toRadians((angle + sliceAngle) + * phaseY)) + center.x); + float y = (float) ((r - circleRadius) + * Math.sin(Math.toRadians((angle + sliceAngle) + * phaseY)) + center.y); + + mRenderPaint.setColor(dataSet.getColor(j)); + mBitmapCanvas.drawCircle(x, y, circleRadius, mRenderPaint); + } + + angle += sliceAngle * phaseX; + } + MPPointF.recycleInstance(center); + } + + /** + * Releases the drawing bitmap. This should be called when {@link LineChart#onDetachedFromWindow()}. + */ + public void releaseBitmap() { + if (mBitmapCanvas != null) { + mBitmapCanvas.setBitmap(null); + mBitmapCanvas = null; + } + if (mDrawBitmap != null) { + Bitmap drawBitmap = mDrawBitmap.get(); + if (drawBitmap != null) { + drawBitmap.recycle(); + } + mDrawBitmap.clear(); + mDrawBitmap = null; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java new file mode 100644 index 0000000000..dbf0e8f807 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/RadarChartRenderer.java @@ -0,0 +1,398 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.data.RadarData; +import com.github.mikephil.charting.data.RadarEntry; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +public class RadarChartRenderer extends LineRadarRenderer { + + protected RadarChart mChart; + + /** + * paint for drawing the web + */ + protected Paint mWebPaint; + protected Paint mHighlightCirclePaint; + + public RadarChartRenderer(RadarChart chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setStyle(Paint.Style.STROKE); + mHighlightPaint.setStrokeWidth(2f); + mHighlightPaint.setColor(Color.rgb(255, 187, 115)); + + mWebPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mWebPaint.setStyle(Paint.Style.STROKE); + + mHighlightCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + + public Paint getWebPaint() { + return mWebPaint; + } + + @Override + public void initBuffers() { + // TODO Auto-generated method stub + + } + + @Override + public void drawData(Canvas c) { + + RadarData radarData = mChart.getData(); + + int mostEntries = radarData.getMaxEntryCountSet().getEntryCount(); + + for (IRadarDataSet set : radarData.getDataSets()) { + + if (set.isVisible()) { + drawDataSet(c, set, mostEntries); + } + } + } + + protected Path mDrawDataSetSurfacePathBuffer = new Path(); + /** + * Draws the RadarDataSet + * + * @param c + * @param dataSet + * @param mostEntries the entry count of the dataset with the most entries + */ + protected void drawDataSet(Canvas c, IRadarDataSet dataSet, int mostEntries) { + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + Path surface = mDrawDataSetSurfacePathBuffer; + surface.reset(); + + boolean hasMovedToPoint = false; + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + mRenderPaint.setColor(dataSet.getColor(j)); + + RadarEntry e = dataSet.getEntryForIndex(j); + + Utils.getPosition( + center, + (e.getY() - mChart.getYChartMin()) * factor * phaseY, + sliceangle * j * phaseX + mChart.getRotationAngle(), pOut); + + if (Float.isNaN(pOut.x)) + continue; + + if (!hasMovedToPoint) { + surface.moveTo(pOut.x, pOut.y); + hasMovedToPoint = true; + } else + surface.lineTo(pOut.x, pOut.y); + } + + if (dataSet.getEntryCount() > mostEntries) { + // if this is not the largest set, draw a line to the center before closing + surface.lineTo(center.x, center.y); + } + + surface.close(); + + if (dataSet.isDrawFilledEnabled()) { + + final Drawable drawable = dataSet.getFillDrawable(); + if (drawable != null) { + + drawFilledPath(c, surface, drawable); + } else { + + drawFilledPath(c, surface, dataSet.getFillColor(), dataSet.getFillAlpha()); + } + } + + mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); + mRenderPaint.setStyle(Paint.Style.STROKE); + + // draw the line (only if filled is disabled or alpha is below 255) + if (!dataSet.isDrawFilledEnabled() || dataSet.getFillAlpha() < 255) + c.drawPath(surface, mRenderPaint); + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + @Override + public void drawValues(Canvas c) { + + float phaseX = mAnimator.getPhaseX(); + float phaseY = mAnimator.getPhaseY(); + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + MPPointF pIcon = MPPointF.getInstance(0,0); + + float yoffset = Utils.convertDpToPixel(5f); + + for (int i = 0; i < mChart.getData().getDataSetCount(); i++) { + + IRadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); + + if (!shouldDrawValues(dataSet)) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < dataSet.getEntryCount(); j++) { + + RadarEntry entry = dataSet.getEntryForIndex(j); + + Utils.getPosition( + center, + (entry.getY() - mChart.getYChartMin()) * factor * phaseY, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pOut); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + pOut.x, + pOut.y - yoffset, + dataSet.getValueTextColor + (j)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.getPosition( + center, + (entry.getY()) * factor * phaseY + iconsOffset.y, + sliceangle * j * phaseX + mChart.getRotationAngle(), + pIcon); + + //noinspection SuspiciousNameCombination + pIcon.y += iconsOffset.x; + + Utils.drawImage( + c, + icon, + (int)pIcon.x, + (int)pIcon.y, + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(pIcon); + } + + @Override + public void drawExtras(Canvas c) { + drawWeb(c); + } + + protected void drawWeb(Canvas c) { + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + float rotationangle = mChart.getRotationAngle(); + + MPPointF center = mChart.getCenterOffsets(); + + // draw the web lines that come from the center + mWebPaint.setStrokeWidth(mChart.getWebLineWidth()); + mWebPaint.setColor(mChart.getWebColor()); + mWebPaint.setAlpha(mChart.getWebAlpha()); + + final int xIncrements = 1 + mChart.getSkipWebLineCount(); + int maxEntryCount = mChart.getData().getMaxEntryCountSet().getEntryCount(); + + MPPointF p = MPPointF.getInstance(0,0); + for (int i = 0; i < maxEntryCount; i += xIncrements) { + + Utils.getPosition( + center, + mChart.getYRange() * factor, + sliceangle * i + rotationangle, + p); + + c.drawLine(center.x, center.y, p.x, p.y, mWebPaint); + } + MPPointF.recycleInstance(p); + + // draw the inner-web + mWebPaint.setStrokeWidth(mChart.getWebLineWidthInner()); + mWebPaint.setColor(mChart.getWebColorInner()); + mWebPaint.setAlpha(mChart.getWebAlpha()); + + int labelCount = mChart.getYAxis().mEntryCount; + + MPPointF p1out = MPPointF.getInstance(0,0); + MPPointF p2out = MPPointF.getInstance(0,0); + for (int j = 0; j < labelCount; j++) { + + for (int i = 0; i < mChart.getData().getEntryCount(); i++) { + + float r = (mChart.getYAxis().mEntries[j] - mChart.getYChartMin()) * factor; + + Utils.getPosition(center, r, sliceangle * i + rotationangle, p1out); + Utils.getPosition(center, r, sliceangle * (i + 1) + rotationangle, p2out); + + c.drawLine(p1out.x, p1out.y, p2out.x, p2out.y, mWebPaint); + + + } + } + MPPointF.recycleInstance(p1out); + MPPointF.recycleInstance(p2out); + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + + RadarData radarData = mChart.getData(); + + for (Highlight high : indices) { + + IRadarDataSet set = radarData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + RadarEntry e = set.getEntryForIndex((int) high.getX()); + + if (!isInBoundsX(e, set)) + continue; + + float y = (e.getY() - mChart.getYChartMin()); + + Utils.getPosition(center, + y * factor * mAnimator.getPhaseY(), + sliceangle * high.getX() * mAnimator.getPhaseX() + mChart.getRotationAngle(), + pOut); + + high.setDraw(pOut.x, pOut.y); + + // draw the lines + drawHighlightLines(c, pOut.x, pOut.y, set); + + if (set.isDrawHighlightCircleEnabled()) { + + if (!Float.isNaN(pOut.x) && !Float.isNaN(pOut.y)) { + + int strokeColor = set.getHighlightCircleStrokeColor(); + if (strokeColor == ColorTemplate.COLOR_NONE) { + strokeColor = set.getColor(0); + } + + if (set.getHighlightCircleStrokeAlpha() < 255) { + strokeColor = ColorTemplate.colorWithAlpha(strokeColor, set.getHighlightCircleStrokeAlpha()); + } + + drawHighlightCircle(c, + pOut, + set.getHighlightCircleInnerRadius(), + set.getHighlightCircleOuterRadius(), + set.getHighlightCircleFillColor(), + strokeColor, + set.getHighlightCircleStrokeWidth()); + } + } + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + protected Path mDrawHighlightCirclePathBuffer = new Path(); + public void drawHighlightCircle(Canvas c, + MPPointF point, + float innerRadius, + float outerRadius, + int fillColor, + int strokeColor, + float strokeWidth) { + c.save(); + + outerRadius = Utils.convertDpToPixel(outerRadius); + innerRadius = Utils.convertDpToPixel(innerRadius); + + if (fillColor != ColorTemplate.COLOR_NONE) { + Path p = mDrawHighlightCirclePathBuffer; + p.reset(); + p.addCircle(point.x, point.y, outerRadius, Path.Direction.CW); + if (innerRadius > 0.f) { + p.addCircle(point.x, point.y, innerRadius, Path.Direction.CCW); + } + mHighlightCirclePaint.setColor(fillColor); + mHighlightCirclePaint.setStyle(Paint.Style.FILL); + c.drawPath(p, mHighlightCirclePaint); + } + + if (strokeColor != ColorTemplate.COLOR_NONE) { + mHighlightCirclePaint.setColor(strokeColor); + mHighlightCirclePaint.setStyle(Paint.Style.STROKE); + mHighlightCirclePaint.setStrokeWidth(Utils.convertDpToPixel(strokeWidth)); + c.drawCircle(point.x, point.y, outerRadius, mHighlightCirclePaint); + } + + c.restore(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java new file mode 100644 index 0000000000..90f49683ae --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/Renderer.java @@ -0,0 +1,21 @@ + +package com.github.mikephil.charting.renderer; + +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Abstract baseclass of all Renderers. + * + * @author Philipp Jahoda + */ +public abstract class Renderer { + + /** + * the component that handles the drawing area of the chart and it's offsets + */ + protected ViewPortHandler mViewPortHandler; + + public Renderer(ViewPortHandler viewPortHandler) { + this.mViewPortHandler = viewPortHandler; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java new file mode 100644 index 0000000000..ccd077e55c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/ScatterChartRenderer.java @@ -0,0 +1,197 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Transformer; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class ScatterChartRenderer extends LineScatterCandleRadarRenderer { + + protected ScatterDataProvider mChart; + + public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + } + + @Override + public void initBuffers() { + } + + @Override + public void drawData(Canvas c) { + + ScatterData scatterData = mChart.getScatterData(); + + for (IScatterDataSet set : scatterData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + } + + float[] mPixelBuffer = new float[2]; + + protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { + + if (dataSet.getEntryCount() < 1) + return; + + ViewPortHandler viewPortHandler = mViewPortHandler; + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + + IShapeRenderer renderer = dataSet.getShapeRenderer(); + if (renderer == null) { + Log.i("MISSING", "There's no IShapeRenderer specified for ScatterDataSet"); + return; + } + + int max = (int)(Math.min( + Math.ceil((float)dataSet.getEntryCount() * mAnimator.getPhaseX()), + (float)dataSet.getEntryCount())); + + for (int i = 0; i < max; i++) { + + Entry e = dataSet.getEntryForIndex(i); + + mPixelBuffer[0] = e.getX(); + mPixelBuffer[1] = e.getY() * phaseY; + + trans.pointValuesToPixel(mPixelBuffer); + + if (!viewPortHandler.isInBoundsRight(mPixelBuffer[0])) + break; + + if (!viewPortHandler.isInBoundsLeft(mPixelBuffer[0]) + || !viewPortHandler.isInBoundsY(mPixelBuffer[1])) + continue; + + mRenderPaint.setColor(dataSet.getColor(i / 2)); + renderer.renderShape( + c, dataSet, mViewPortHandler, + mPixelBuffer[0], mPixelBuffer[1], + mRenderPaint); + } + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getScatterData().getDataSets(); + + for (int i = 0; i < mChart.getScatterData().getDataSetCount(); i++) { + + IScatterDataSet dataSet = dataSets.get(i); + + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) + continue; + + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); + + mXBounds.set(mChart, dataSet); + + float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) + .generateTransformedValuesScatter(dataSet, + mAnimator.getPhaseX(), mAnimator.getPhaseY(), mXBounds.min, mXBounds.max); + + float shapeSize = Utils.convertDpToPixel(dataSet.getScatterShapeSize()); + + MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset()); + iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x); + iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y); + + for (int j = 0; j < positions.length; j += 2) { + + if (!mViewPortHandler.isInBoundsRight(positions[j])) + break; + + // make sure the lines don't do shitty things outside bounds + if ((!mViewPortHandler.isInBoundsLeft(positions[j]) + || !mViewPortHandler.isInBoundsY(positions[j + 1]))) + continue; + + Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min); + + if (dataSet.isDrawValuesEnabled()) { + drawValue(c, + dataSet.getValueFormatter(), + entry.getY(), + entry, + i, + positions[j], + positions[j + 1] - shapeSize, + dataSet.getValueTextColor(j / 2 + mXBounds.min)); + } + + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { + + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(positions[j] + iconsOffset.x), + (int)(positions[j + 1] + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } + } + + MPPointF.recycleInstance(iconsOffset); + } + } + } + + @Override + public void drawExtras(Canvas c) { + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + ScatterData scatterData = mChart.getScatterData(); + + for (Highlight high : indices) { + + IScatterDataSet set = scatterData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + final Entry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), e.getY() * mAnimator + .getPhaseY()); + + high.setDraw((float) pix.x, (float) pix.y); + + // draw the lines + drawHighlightLines(c, (float) pix.x, (float) pix.y, set); + } + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java similarity index 55% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index a0a2bff642..8adb56c73a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -6,13 +6,14 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; -import android.graphics.PointF; -import android.util.Size; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointD; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -24,7 +25,7 @@ public class XAxisRenderer extends AxisRenderer { protected XAxis mXAxis; public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { - super(viewPortHandler, trans); + super(viewPortHandler, trans, xAxis); this.mXAxis = xAxis; @@ -33,20 +34,54 @@ public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer t mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); } - public void computeAxis(float xValMaximumLength, List xValues) { + protected void setupGridPaint() { + mGridPaint.setColor(mXAxis.getGridColor()); + mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); + mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); + } - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); - StringBuilder widthText = new StringBuilder(); + if (inverted) { - int xValChars = Math.round(xValMaximumLength); + min = (float) p2.x; + max = (float) p1.x; + } else { + + min = (float) p1.x; + max = (float) p2.x; + } - for (int i = 0; i < xValChars; i++) { - widthText.append('h'); + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } - final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, widthText.toString()); + computeAxisValues(min, max); + } + + @Override + protected void computeAxisValues(float min, float max) { + super.computeAxisValues(min, max); + + computeSize(); + } + + protected void computeSize() { + + String longest = mXAxis.getLongestLabel(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + + final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); final float labelWidth = labelSize.width; final float labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); @@ -56,21 +91,14 @@ public void computeAxis(float xValMaximumLength, List xValues) { labelHeight, mXAxis.getLabelRotationAngle()); - StringBuilder space = new StringBuilder(); - int xValSpaceChars = mXAxis.getSpaceBetweenLabels(); - for (int i = 0; i < xValSpaceChars; i++) { - space.append('h'); - } - - final FSize spaceSize = Utils.calcTextSize(mAxisLabelPaint, space.toString()); - - mXAxis.mLabelWidth = Math.round(labelWidth + spaceSize.width); + mXAxis.mLabelWidth = Math.round(labelWidth); mXAxis.mLabelHeight = Math.round(labelHeight); - mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width + spaceSize.width); + mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); - mXAxis.setValues(xValues); + FSize.recycleInstance(labelRotatedSize); + FSize.recycleInstance(labelSize); } @Override @@ -85,33 +113,36 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); + MPPointF pointF = MPPointF.getInstance(0,0); if (mXAxis.getPosition() == XAxisPosition.TOP) { - - drawLabels(c, mViewPortHandler.contentTop() - yoffset, - new PointF(0.5f, 1.0f)); + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, - new PointF(0.5f, 1.0f)); + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentBottom() + yoffset, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF); } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentTop() - yoffset, - new PointF(0.5f, 1.0f)); - drawLabels(c, mViewPortHandler.contentBottom() + yoffset, - new PointF(0.5f, 0.0f)); + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); } + MPPointF.recycleInstance(pointF); } @Override @@ -122,6 +153,7 @@ public void renderAxisLine(Canvas c) { mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect()); if (mXAxis.getPosition() == XAxisPosition.TOP || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE @@ -145,111 +177,154 @@ public void renderAxisLine(Canvas c) { * * @param pos */ - protected void drawLabels(Canvas c, float pos, PointF anchor) { + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; + float[] positions = new float[mXAxis.mEntryCount * 2]; - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + for (int i = 0; i < positions.length; i += 2) { - position[0] = i; + // only fill x values + if (centeringEnabled) { + positions[i] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i] = mXAxis.mEntries[i / 2]; + } + } - mTrans.pointValuesToPixel(position); + mTrans.pointValuesToPixel(positions); - if (mViewPortHandler.isInBoundsX(position[0])) { + for (int i = 0; i < positions.length; i += 2) { - String label = mXAxis.getValues().get(i); + float x = positions[i]; + + if (mViewPortHandler.isInBoundsX(x)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); if (mXAxis.isAvoidFirstLastClippingEnabled()) { // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1 && mXAxis.getValues().size() > 1) { + if (i / 2 == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); if (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; + && x + width > mViewPortHandler.getChartWidth()) + x -= width / 2; // avoid clipping of the first } else if (i == 0) { float width = Utils.calcTextWidth(mAxisLabelPaint, label); - position[0] += width / 2; + x += width / 2; } } - drawLabel(c, label, i, position[0], pos, anchor, labelRotationAngleDegrees); + drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees); } } } - protected void drawLabel(Canvas c, String label, int xIndex, float x, float y, PointF anchor, float angleDegrees) { - String formattedLabel = mXAxis.getValueFormatter().getXValue(label, xIndex, mViewPortHandler); - Utils.drawText(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); + protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) { + Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); } - + protected Path mRenderGridLinesPath = new Path(); + protected float[] mRenderGridLinesBuffer = new float[2]; @Override public void renderGridLines(Canvas c) { if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) return; - // pre alloc - float[] position = new float[] { - 0f, 0f - }; + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); - - Path gridLinePath = new Path(); + if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){ + mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mRenderGridLinesBuffer; - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + for (int i = 0; i < positions.length; i += 2) { + positions[i] = mXAxis.mEntries[i / 2]; + positions[i + 1] = mXAxis.mEntries[i / 2]; + } - position[0] = i; - mTrans.pointValuesToPixel(position); + mTrans.pointValuesToPixel(positions); - if (position[0] >= mViewPortHandler.offsetLeft() - && position[0] <= mViewPortHandler.getChartWidth()) { + setupGridPaint(); - gridLinePath.moveTo(position[0], mViewPortHandler.contentBottom()); - gridLinePath.lineTo(position[0], mViewPortHandler.contentTop()); + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); - // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - } + for (int i = 0; i < positions.length; i += 2) { - gridLinePath.reset(); + drawGridLine(c, positions[i], positions[i + 1], gridLinePath); } + + c.restoreToCount(clipRestoreCount); } - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { + protected RectF mGridClippingRect = new RectF(); - List limitLines = mXAxis.getLimitLines(); + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; + } - if (limitLines == null || limitLines.size() <= 0) - return; + /** + * Draws the grid line at the specified position using the provided path. + * + * @param c + * @param x + * @param y + * @param gridLinePath + */ + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - float[] position = new float[2]; + gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); + gridLinePath.lineTo(x, mViewPortHandler.contentTop()); - for (int i = 0; i < limitLines.size(); i++) { + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); + + gridLinePath.reset(); + } - LimitLine l = limitLines.get(i); + protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); - if(!l.isEnabled()) + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mXAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] position = mRenderLimitLinesBuffer; + position[0] = 0; + position[1] = 0; + + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); + position[0] = l.getLimit(); position[1] = 0.f; @@ -257,14 +332,15 @@ public void renderLimitLines(Canvas c) { renderLimitLineLine(c, l, position); renderLimitLineLabel(c, l, position, 2.f + l.getYOffset()); - } - } + + c.restoreToCount(clipRestoreCount); + } + } float[] mLimitLineSegmentsBuffer = new float[4]; private Path mLimitLinePath = new Path(); - public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) - { + public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) { mLimitLineSegmentsBuffer[0] = position[0]; mLimitLineSegmentsBuffer[1] = mViewPortHandler.contentTop(); mLimitLineSegmentsBuffer[2] = position[0]; @@ -282,8 +358,7 @@ public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) c.drawPath(mLimitLinePath, mLimitLinePaint); } - public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) - { + public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { String label = limitLine.getLabel(); // if drawing the limit-value label is enabled @@ -295,6 +370,7 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(limitLine.getTextSize()); + float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); final LimitLine.LimitLabelPosition labelPosition = limitLine.getLabelPosition(); @@ -303,7 +379,8 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { mLimitLinePaint.setTextAlign(Align.LEFT); @@ -312,7 +389,8 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position mLimitLinePaint.setTextAlign(Align.RIGHT); final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); } else { mLimitLinePaint.setTextAlign(Align.RIGHT); @@ -320,5 +398,4 @@ public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position } } } - } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java similarity index 65% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index e862263520..86047cf1b8 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -5,33 +5,64 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; -import android.graphics.PointF; +import android.graphics.RectF; import com.github.mikephil.charting.charts.BarChart; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.utils.FSize; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; import java.util.List; -public class XAxisRendererHorizontalBarChart extends XAxisRendererBarChart { +public class XAxisRendererHorizontalBarChart extends XAxisRenderer { + + protected BarChart mChart; public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans, BarChart chart) { - super(viewPortHandler, xAxis, trans, chart); + super(viewPortHandler, xAxis, trans); + + this.mChart = chart; + } + + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + + if (inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisValues(min, max); } @Override - public void computeAxis(float xValAverageLength, List xValues) { + protected void computeSize() { mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mXAxis.setValues(xValues); String longest = mXAxis.getLongestLabel(); @@ -49,6 +80,8 @@ public void computeAxis(float xValAverageLength, List xValues) { mXAxis.mLabelHeight = Math.round(labelHeight); mXAxis.mLabelRotatedWidth = (int)(labelRotatedSize.width + mXAxis.getXOffset() * 3.5f); mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); } @Override @@ -63,102 +96,89 @@ public void renderAxisLabels(Canvas c) { mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); - if (mXAxis.getPosition() == XAxisPosition.TOP) { + MPPointF pointF = MPPointF.getInstance(0,0); - drawLabels(c, mViewPortHandler.contentRight() + xoffset, - new PointF(0.0f, 0.5f)); + if (mXAxis.getPosition() == XAxisPosition.TOP) { + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.contentRight() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentLeft() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentLeft() + xoffset, - new PointF(0.0f, 0.5f)); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF); } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentRight() + xoffset, - new PointF(0.0f, 0.5f)); - drawLabels(c, mViewPortHandler.contentLeft() - xoffset, - new PointF(1.0f, 0.5f)); + pointF.x = 0.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() + xoffset, pointF); + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); } + + MPPointF.recycleInstance(pointF); } - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ @Override - protected void drawLabels(Canvas c, float pos, PointF anchor) { + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - BarData bd = mChart.getData(); - int step = bd.getDataSetCount(); + float[] positions = new float[mXAxis.mEntryCount * 2]; - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + for (int i = 0; i < positions.length; i += 2) { - position[1] = i * step + i * bd.getGroupSpace() - + bd.getGroupSpace() / 2f; - - // consider groups (center label for each group) - if (step > 1) { - position[1] += ((float) step - 1f) / 2f; + // only fill x values + if (centeringEnabled) { + positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i + 1] = mXAxis.mEntries[i / 2]; } + } + + mTrans.pointValuesToPixel(positions); - mTrans.pointValuesToPixel(position); + for (int i = 0; i < positions.length; i += 2) { - if (mViewPortHandler.isInBoundsY(position[1])) { + float y = positions[i + 1]; - String label = mXAxis.getValues().get(i); - drawLabel(c, label, i, pos, position[1], anchor, labelRotationAngleDegrees); + if (mViewPortHandler.isInBoundsY(y)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); } } } @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - float[] position = new float[] { - 0f, 0f - }; - - mGridPaint.setColor(mXAxis.getGridColor()); - mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); - - BarData bd = mChart.getData(); - // take into consideration that multiple DataSets increase mDeltaX - int step = bd.getDataSetCount(); - - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; + } - position[1] = i * step + i * bd.getGroupSpace() - 0.5f; + @Override + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - mTrans.pointValuesToPixel(position); + gridLinePath.moveTo(mViewPortHandler.contentRight(), y); + gridLinePath.lineTo(mViewPortHandler.contentLeft(), y); - if (mViewPortHandler.isInBoundsY(position[1])) { + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); - c.drawLine(mViewPortHandler.contentLeft(), position[1], - mViewPortHandler.contentRight(), position[1], mGridPaint); - } - } + gridLinePath.reset(); } @Override @@ -187,7 +207,8 @@ public void renderAxisLine(Canvas c) { } } - /** + protected Path mRenderLimitLinesPathBuffer = new Path(); + /** * Draws the LimitLines associated with this axis to the screen. * This is the standard YAxis renderer using the XAxis limit lines. * @@ -201,8 +222,12 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[2]; - Path limitLinePath = new Path(); + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { @@ -211,6 +236,11 @@ public void renderLimitLines(Canvas c) { if(!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + mLimitLinePaint.setStyle(Paint.Style.STROKE); mLimitLinePaint.setColor(l.getLineColor()); mLimitLinePaint.setStrokeWidth(l.getLineWidth()); @@ -273,6 +303,8 @@ public void renderLimitLines(Canvas c) { pts[1] + yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java similarity index 67% rename from MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java index 50a2fc7dd4..956e8c7d5c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java @@ -6,6 +6,7 @@ import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -26,7 +27,7 @@ public void renderAxisLabels(Canvas c) { return; final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - final PointF drawLabelAnchor = new PointF(0.5f, 0.0f); + final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f); mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); @@ -38,20 +39,24 @@ public void renderAxisLabels(Canvas c) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { - int mod = mXAxis.mAxisLabelModulus; - for (int i = 0; i < mXAxis.getValues().size(); i += mod) { - String label = mXAxis.getValues().get(i); + String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; - PointF p = Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelRotatedWidth / 2f, angle); + Utils.getPosition(center, mChart.getYRange() * factor + + mXAxis.mLabelRotatedWidth / 2f, angle, pOut); - drawLabel(c, label, i, p.x, p.y - mXAxis.mLabelRotatedHeight / 2.f, + drawLabel(c, label, pOut.x, pOut.y - mXAxis.mLabelRotatedHeight / 2.f, drawLabelAnchor, labelRotationAngleDegrees); } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + MPPointF.recycleInstance(drawLabelAnchor); } /** diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java similarity index 56% rename from MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index cfd46fed07..53cca7ee03 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -5,12 +5,13 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -24,140 +25,19 @@ public class YAxisRenderer extends AxisRenderer { protected Paint mZeroLinePaint; public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { - super(viewPortHandler, trans); + super(viewPortHandler, trans, yAxis); this.mYAxis = yAxis; - mAxisLabelPaint.setColor(Color.BLACK); - mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + if(mViewPortHandler != null) { - mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mZeroLinePaint.setColor(Color.GRAY); - mZeroLinePaint.setStrokeWidth(1f); - mZeroLinePaint.setStyle(Paint.Style.STROKE); - } - - /** - * Computes the axis values. - * - * @param yMin - the minimum y-value in the data object for this axis - * @param yMax - the maximum y-value in the data object for this axis - */ - public void computeAxis(float yMin, float yMax) { - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - - if (!mYAxis.isInverted()) { - yMin = (float) p2.y; - yMax = (float) p1.y; - } else { - - yMin = (float) p1.y; - yMax = (float) p2.y; - } - } - - computeAxisValues(yMin, yMax); - } - - /** - * Sets up the y-axis labels. Computes the desired number of labels between the two given extremes. Unlike the - * papareXLabels() method, this method needs to be called upon every refresh of the view. - * - * @return - */ - protected void computeAxisValues(float min, float max) { - - float yMin = min; - float yMax = max; - - int labelCount = mYAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0) { - mYAxis.mEntries = new float[]{}; - mYAxis.mEntryCount = 0; - return; - } - - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - double intervalMagnitude = Math.pow(10, (int) Math.log10(interval)); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or - // 90 - interval = Math.floor(10 * intervalMagnitude); - } - - // force label count - if (mYAxis.isForceLabelsEnabled()) { - - float step = (float) range / (float) (labelCount - 1); - mYAxis.mEntryCount = labelCount; - - if (mYAxis.mEntries.length < labelCount) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[labelCount]; - } - - float v = min; - - for (int i = 0; i < labelCount; i++) { - mYAxis.mEntries[i] = v; - v += step; - } - - // no forced count - } else { - - // if the labels should only show min and max - if (mYAxis.isShowOnlyMinMaxEnabled()) { - - mYAxis.mEntryCount = 2; - mYAxis.mEntries = new float[2]; - mYAxis.mEntries[0] = yMin; - mYAxis.mEntries[1] = yMax; - - } else { - - double first = Math.ceil(yMin / interval) * interval; - double last = Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - int n = 0; - for (f = first; f <= last; f += interval) { - ++n; - } - - mYAxis.mEntryCount = n; - - if (mYAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mYAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - - if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) - f = 0.0; - - mYAxis.mEntries[i] = (float) f; - } - } - } + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); - // set decimals - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; + mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mZeroLinePaint.setColor(Color.GRAY); + mZeroLinePaint.setStrokeWidth(1f); + mZeroLinePaint.setStyle(Paint.Style.STROKE); } } @@ -170,16 +50,7 @@ public void renderAxisLabels(Canvas c) { if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) return; - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels - // are - // static on the x-axis - positions[i + 1] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); + float[] positions = getTransformedPositions(); mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); @@ -243,84 +114,143 @@ public void renderAxisLine(Canvas c) { */ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + float xOffset = mYAxis.getLabelXOffset(); + // draw - for (int i = 0; i < mYAxis.mEntryCount; i++) { + for (int i = from; i < to; i++) { String text = mYAxis.getFormattedLabel(i); - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; - - c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint); + c.drawText(text, + fixedPosition + xOffset, + positions[i * 2 + 1] + offset, + mAxisLabelPaint); } } + protected Path mRenderGridLinesPath = new Path(); @Override public void renderGridLines(Canvas c) { if (!mYAxis.isEnabled()) return; - // pre alloc - float[] position = new float[2]; - if (mYAxis.isDrawGridLinesEnabled()) { + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + + float[] positions = getTransformedPositions(); + mGridPaint.setColor(mYAxis.getGridColor()); mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); mGridPaint.setPathEffect(mYAxis.getGridDashPathEffect()); - Path gridLinePath = new Path(); - - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - position[1] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); - gridLinePath.moveTo(mViewPortHandler.offsetLeft(), position[1]); - gridLinePath.lineTo(mViewPortHandler.contentRight(), position[1]); + // draw the grid + for (int i = 0; i < positions.length; i += 2) { // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - + c.drawPath(linePath(gridLinePath, i, positions), mGridPaint); gridLinePath.reset(); } + + c.restoreToCount(clipRestoreCount); } if (mYAxis.isDrawZeroLineEnabled()) { + drawZeroLine(c); + } + } - // draw zero line - position[1] = 0f; - mTrans.pointValuesToPixel(position); + protected RectF mGridClippingRect = new RectF(); - drawZeroLine(c, mViewPortHandler.offsetLeft(), mViewPortHandler.contentRight(), position[1] - 1, position[1] - 1); - } + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; } /** - * Draws the zero line at the specified position. + * Calculates the path for a grid line. * - * @param c - * @param x1 - * @param x2 - * @param y1 - * @param y2 + * @param p + * @param i + * @param positions + * @return + */ + protected Path linePath(Path p, int i, float[] positions) { + + p.moveTo(mViewPortHandler.offsetLeft(), positions[i + 1]); + p.lineTo(mViewPortHandler.contentRight(), positions[i + 1]); + + return p; + } + + protected float[] mGetTransformedPositionsBuffer = new float[2]; + /** + * Transforms the values contained in the axis entries to screen pixels and returns them in form of a float array + * of x- and y-coordinates. + * + * @return */ - protected void drawZeroLine(Canvas c, float x1, float x2, float y1, float y2) { + protected float[] getTransformedPositions() { + + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2){ + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; + + for (int i = 0; i < positions.length; i += 2) { + // only fill y values, x values are not needed for y-labels + positions[i + 1] = mYAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + return positions; + } + + protected Path mDrawZeroLinePath = new Path(); + protected RectF mZeroLineClippingRect = new RectF(); + + /** + * Draws the zero line. + */ + protected void drawZeroLine(Canvas c) { + + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(0.f, -mYAxis.getZeroLineWidth()); + c.clipRect(mZeroLineClippingRect); + + // draw zero line + MPPointD pos = mTrans.getPixelForValues(0f, 0f); mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - Path zeroLinePath = new Path(); + Path zeroLinePath = mDrawZeroLinePath; + zeroLinePath.reset(); - zeroLinePath.moveTo(x1, y1); - zeroLinePath.lineTo(x2, y2); + zeroLinePath.moveTo(mViewPortHandler.contentLeft(), (float) pos.y); + zeroLinePath.lineTo(mViewPortHandler.contentRight(), (float) pos.y); // draw a path because lines don't support dashing on lower android versions c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); } + protected Path mRenderLimitLines = new Path(); + protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); /** * Draws the LimitLines associated with this axis to the screen. * @@ -334,8 +264,11 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[2]; - Path limitLinePath = new Path(); + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + Path limitLinePath = mRenderLimitLines; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { @@ -344,6 +277,11 @@ public void renderLimitLines(Canvas c) { if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + mLimitLinePaint.setStyle(Paint.Style.STROKE); mLimitLinePaint.setColor(l.getLineColor()); mLimitLinePaint.setStrokeWidth(l.getLineWidth()); @@ -407,6 +345,8 @@ public void renderLimitLines(Canvas c) { pts[1] + yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java similarity index 69% rename from MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index bcd63778a0..fedf8054a1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -5,13 +5,13 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Path; +import android.graphics.RectF; import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition; -import com.github.mikephil.charting.utils.PointD; +import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -33,24 +33,28 @@ public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yA * @param yMin - the minimum y-value in the data object for this axis * @param yMax - the maximum y-value in the data object for this axis */ - public void computeAxis(float yMin, float yMax) { + @Override + public void computeAxis(float yMin, float yMax, boolean inverted) { // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) if (mViewPortHandler.contentHeight() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { - PointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - PointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); - if (!mYAxis.isInverted()) { + if (!inverted) { yMin = (float) p1.x; yMax = (float) p2.x; } else { yMin = (float) p2.x; yMax = (float) p1.x; } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); } computeAxisValues(yMin, yMax); @@ -65,16 +69,7 @@ public void renderAxisLabels(Canvas c) { if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) return; - float[] positions = new float[mYAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - // only fill y values, x values are not needed since the y-labels - // are - // static on the x-axis - positions[i] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); + float[] positions = getTransformedPositions(); mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); @@ -142,53 +137,87 @@ protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, flo mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); - for (int i = 0; i < mYAxis.mEntryCount; i++) { + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + float xOffset = mYAxis.getLabelXOffset(); + + for (int i = from; i < to; i++) { String text = mYAxis.getFormattedLabel(i); - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; + c.drawText(text, + positions[i * 2], + fixedPosition - offset + xOffset, + mAxisLabelPaint); + } + } + + @Override + protected float[] getTransformedPositions() { + + if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2) { + mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; + } + float[] positions = mGetTransformedPositionsBuffer; - c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisLabelPaint); + for (int i = 0; i < positions.length; i += 2) { + // only fill x values, y values are not needed for x-labels + positions[i] = mYAxis.mEntries[i / 2]; } + + mTrans.pointValuesToPixel(positions); + return positions; } @Override - public void renderGridLines(Canvas c) { + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; + } - if (!mYAxis.isEnabled()) - return; + @Override + protected Path linePath(Path p, int i, float[] positions) { + + p.moveTo(positions[i], mViewPortHandler.contentTop()); + p.lineTo(positions[i], mViewPortHandler.contentBottom()); - // pre alloc - float[] position = new float[2]; + return p; + } - if (mYAxis.isDrawGridLinesEnabled()) { + protected Path mDrawZeroLinePathBuffer = new Path(); - mGridPaint.setColor(mYAxis.getGridColor()); - mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); + @Override + protected void drawZeroLine(Canvas c) { - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); - position[0] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); + // draw zero line + MPPointD pos = mTrans.getPixelForValues(0f, 0f); - c.drawLine(position[0], mViewPortHandler.contentTop(), position[0], - mViewPortHandler.contentBottom(), - mGridPaint); - } - } + mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); + mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - if (mYAxis.isDrawZeroLineEnabled()) { + Path zeroLinePath = mDrawZeroLinePathBuffer; + zeroLinePath.reset(); - // draw zero line - position[0] = 0f; - mTrans.pointValuesToPixel(position); + zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop()); + zeroLinePath.lineTo((float) pos.x - 1, mViewPortHandler.contentBottom()); - drawZeroLine(c, position[0]+1, position[0]+1, mViewPortHandler.contentTop(), mViewPortHandler.contentBottom()); - } + // draw a path because lines don't support dashing on lower android versions + c.drawPath(zeroLinePath, mZeroLinePaint); + + c.restoreToCount(clipRestoreCount); } + protected Path mRenderLimitLinesPathBuffer = new Path(); + protected float[] mRenderLimitLinesBuffer = new float[4]; /** * Draws the LimitLines associated with this axis to the screen. * This is the standard XAxis renderer using the YAxis limit lines. @@ -203,8 +232,13 @@ public void renderLimitLines(Canvas c) { if (limitLines == null || limitLines.size() <= 0) return; - float[] pts = new float[4]; - Path limitLinePath = new Path(); + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + pts[2] = 0; + pts[3] = 0; + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); for (int i = 0; i < limitLines.size(); i++) { @@ -213,6 +247,11 @@ public void renderLimitLines(Canvas c) { if (!l.isEnabled()) continue; + int clipRestoreCount = c.save(); + mLimitLineClippingRect.set(mViewPortHandler.getContentRect()); + mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); + pts[0] = l.getLimit(); pts[2] = l.getLimit(); @@ -269,6 +308,8 @@ public void renderLimitLines(Canvas c) { c.drawText(label, pts[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); } } + + c.restoreToCount(clipRestoreCount); } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java new file mode 100644 index 0000000000..f7b1ad9e87 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -0,0 +1,232 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.PointF; + +import com.github.mikephil.charting.charts.RadarChart; +import com.github.mikephil.charting.components.LimitLine; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.util.List; + +public class YAxisRendererRadarChart extends YAxisRenderer { + + private RadarChart mChart; + + public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, RadarChart chart) { + super(viewPortHandler, yAxis, null); + + this.mChart = chart; + } + + @Override + protected void computeAxisValues(float min, float max) { + + float yMin = min; + float yMax = max; + + int labelCount = mAxis.getLabelCount(); + double range = Math.abs(yMax - yMin); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mAxis.mEntries = new float[]{}; + mAxis.mCenteredEntries = new float[]{}; + mAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mAxis.isGranularityEnabled()) + interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); + } + + boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled(); + int n = centeringEnabled ? 1 : 0; + + // force label count + if (mAxis.isForceLabelsEnabled()) { + + float step = (float) range / (float) (labelCount - 1); + mAxis.mEntryCount = labelCount; + + if (mAxis.mEntries.length < labelCount) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[labelCount]; + } + + float v = min; + + for (int i = 0; i < labelCount; i++) { + mAxis.mEntries[i] = v; + v += step; + } + + n = labelCount; + + // no forced count + } else { + + double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + if (centeringEnabled) { + first -= interval; + } + + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + + double f; + int i; + + if (interval != 0.0) { + for (f = first; f <= last; f += interval) { + ++n; + } + } + + n++; + + mAxis.mEntryCount = n; + + if (mAxis.mEntries.length < n) { + // Ensure stops contains at least numStops elements. + mAxis.mEntries = new float[n]; + } + + for (f = first, i = 0; i < n; f += interval, ++i) { + + if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0; + + mAxis.mEntries[i] = (float) f; + } + } + + // set decimals + if (interval < 1) { + mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); + } else { + mAxis.mDecimals = 0; + } + + if (centeringEnabled) { + + if (mAxis.mCenteredEntries.length < n) { + mAxis.mCenteredEntries = new float[n]; + } + + float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; + + for (int i = 0; i < n; i++) { + mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; + } + } + + mAxis.mAxisMinimum = mAxis.mEntries[0]; + mAxis.mAxisMaximum = mAxis.mEntries[n-1]; + mAxis.mAxisRange = Math.abs(mAxis.mAxisMaximum - mAxis.mAxisMinimum); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) + return; + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + float factor = mChart.getFactor(); + + final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; + final int to = mYAxis.isDrawTopYLabelEntryEnabled() + ? mYAxis.mEntryCount + : (mYAxis.mEntryCount - 1); + + float xOffset = mYAxis.getLabelXOffset(); + + for (int j = from; j < to; j++) { + + float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; + + Utils.getPosition(center, r, mChart.getRotationAngle(), pOut); + + String label = mYAxis.getFormattedLabel(j); + + c.drawText(label, pOut.x + xOffset, pOut.y, mAxisLabelPaint); + } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } + + private Path mRenderLimitLinesPathBuffer = new Path(); + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mYAxis.getLimitLines(); + + if (limitLines == null) + return; + + float sliceangle = mChart.getSliceAngle(); + + // calculate the factor that is needed for transforming the value to + // pixels + float factor = mChart.getFactor(); + + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < limitLines.size(); i++) { + + LimitLine l = limitLines.get(i); + + if (!l.isEnabled()) + continue; + + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + + float r = (l.getLimit() - mChart.getYChartMin()) * factor; + + Path limitPath = mRenderLimitLinesPathBuffer; + limitPath.reset(); + + + for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { + + Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle(), pOut); + + if (j == 0) + limitPath.moveTo(pOut.x, pOut.y); + else + limitPath.lineTo(pOut.x, pOut.y); + } + limitPath.close(); + + c.drawPath(limitPath, mLimitLinePaint); + } + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(pOut); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java new file mode 100644 index 0000000000..9328b276c4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronDownShapeRenderer.java @@ -0,0 +1,41 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronDownShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY + (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java new file mode 100644 index 0000000000..6dea0abf7b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/ChevronUpShapeRenderer.java @@ -0,0 +1,42 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class ChevronUpShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX + (2 * shapeHalf), + posY, + renderPaint); + + c.drawLine( + posX, + posY - (2 * shapeHalf), + posX - (2 * shapeHalf), + posY, + renderPaint); + + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java new file mode 100644 index 0000000000..ac7abb92de --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CircleShapeRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CircleShapeRenderer implements IShapeRenderer +{ + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); + + c.drawCircle( + posX, + posY, + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + renderPaint.setStyle(Paint.Style.FILL); + + renderPaint.setColor(shapeHoleColor); + c.drawCircle( + posX, + posY, + shapeHoleSizeHalf, + renderPaint); + } + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawCircle( + posX, + posY, + shapeHalf, + renderPaint); + } + + } + +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java new file mode 100644 index 0000000000..202670d6ba --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/CrossShapeRenderer.java @@ -0,0 +1,41 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class CrossShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX - shapeHalf, + posY, + posX + shapeHalf, + posY, + renderPaint); + c.drawLine( + posX, + posY - shapeHalf, + posX, + posY + shapeHalf, + renderPaint); + + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java new file mode 100644 index 0000000000..20b57a900d --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/IShapeRenderer.java @@ -0,0 +1,28 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:07 + */ +public interface IShapeRenderer +{ + + /** + * Renders the provided ScatterDataSet with a shape. + * + * @param c Canvas object for drawing the shape + * @param dataSet The DataSet to be drawn + * @param viewPortHandler Contains information about the current state of the view + * @param posX Position to draw the shape at + * @param posY Position to draw the shape at + * @param renderPaint Paint object used for styling and drawing + */ + void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java new file mode 100644 index 0000000000..ac98679233 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/SquareShapeRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class SquareShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + final float shapeStrokeSizeHalf = shapeStrokeSize / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + if (shapeSize > 0.0) { + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(shapeStrokeSize); + + c.drawRect(posX - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posY - shapeHoleSizeHalf - shapeStrokeSizeHalf, + posX + shapeHoleSizeHalf + shapeStrokeSizeHalf, + posY + shapeHoleSizeHalf + shapeStrokeSizeHalf, + renderPaint); + + if (shapeHoleColor != ColorTemplate.COLOR_NONE) { + renderPaint.setStyle(Paint.Style.FILL); + + renderPaint.setColor(shapeHoleColor); + c.drawRect(posX - shapeHoleSizeHalf, + posY - shapeHoleSizeHalf, + posX + shapeHoleSizeHalf, + posY + shapeHoleSizeHalf, + renderPaint); + } + + } else { + renderPaint.setStyle(Paint.Style.FILL); + + c.drawRect(posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java new file mode 100644 index 0000000000..5343454bbb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/TriangleShapeRenderer.java @@ -0,0 +1,80 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class TriangleShapeRenderer implements IShapeRenderer +{ + + protected Path mTrianglePathBuffer = new Path(); + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeSize = dataSet.getScatterShapeSize(); + final float shapeHalf = shapeSize / 2f; + final float shapeHoleSizeHalf = Utils.convertDpToPixel(dataSet.getScatterShapeHoleRadius()); + final float shapeHoleSize = shapeHoleSizeHalf * 2.f; + final float shapeStrokeSize = (shapeSize - shapeHoleSize) / 2.f; + + final int shapeHoleColor = dataSet.getScatterShapeHoleColor(); + + renderPaint.setStyle(Paint.Style.FILL); + + // create a triangle path + Path tri = mTrianglePathBuffer; + tri.reset(); + + tri.moveTo(posX, posY - shapeHalf); + tri.lineTo(posX + shapeHalf, posY + shapeHalf); + tri.lineTo(posX - shapeHalf, posY + shapeHalf); + + if (shapeSize > 0.0) { + tri.lineTo(posX, posY - shapeHalf); + + tri.moveTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + } + + tri.close(); + + c.drawPath(tri, renderPaint); + tri.reset(); + + if (shapeSize > 0.0 && + shapeHoleColor != ColorTemplate.COLOR_NONE) { + + renderPaint.setColor(shapeHoleColor); + + tri.moveTo(posX, + posY - shapeHalf + shapeStrokeSize); + tri.lineTo(posX + shapeHalf - shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.lineTo(posX - shapeHalf + shapeStrokeSize, + posY + shapeHalf - shapeStrokeSize); + tri.close(); + + c.drawPath(tri, renderPaint); + tri.reset(); + } + + } + +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java new file mode 100644 index 0000000000..225640ec8e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/scatter/XShapeRenderer.java @@ -0,0 +1,42 @@ +package com.github.mikephil.charting.renderer.scatter; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; +import com.github.mikephil.charting.utils.Utils; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by wajdic on 15/06/2016. + * Created at Time 09:08 + */ +public class XShapeRenderer implements IShapeRenderer +{ + + + @Override + public void renderShape(Canvas c, IScatterDataSet dataSet, ViewPortHandler viewPortHandler, + float posX, float posY, Paint renderPaint) { + + final float shapeHalf = dataSet.getScatterShapeSize() / 2f; + + renderPaint.setStyle(Paint.Style.STROKE); + renderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); + + c.drawLine( + posX - shapeHalf, + posY - shapeHalf, + posX + shapeHalf, + posY + shapeHalf, + renderPaint); + c.drawLine( + posX + shapeHalf, + posY - shapeHalf, + posX - shapeHalf, + posY + shapeHalf, + renderPaint); + + } + +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java similarity index 86% rename from MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java index 441d77b484..4d9c1de790 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java @@ -19,13 +19,13 @@ public class ColorTemplate { /** * an "invalid" color that indicates that no color is set */ - public static final int COLOR_NONE = -1; + public static final int COLOR_NONE = 0x00112233; /** * this "color" is used for the Legend creation and indicates that the next * form should be skipped */ - public static final int COLOR_SKIP = -2; + public static final int COLOR_SKIP = 0x00112234; /** * THE COLOR THEMES ARE PREDEFINED (predefined color integer arrays), FEEL @@ -51,6 +51,9 @@ public class ColorTemplate { Color.rgb(192, 255, 140), Color.rgb(255, 247, 140), Color.rgb(255, 208, 140), Color.rgb(140, 234, 255), Color.rgb(255, 140, 157) }; + public static final int[] MATERIAL_COLORS = { + rgb("#2ecc71"), rgb("#f1c40f"), rgb("#e74c3c"), rgb("#3498db") + }; /** * Converts the given hex-color-string to rgb. @@ -75,6 +78,17 @@ public static int getHoloBlue() { return Color.rgb(51, 181, 229); } + /** + * Sets the alpha component of the given color. + * + * @param color + * @param alpha 0 - 255 + * @return + */ + public static int colorWithAlpha(int color, int alpha) { + return (color & 0xffffff) | ((alpha & 0xff) << 24); + } + /** * turn an array of resource-colors (contains resource-id integers) into an * array list of actual color integers diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java new file mode 100644 index 0000000000..8f59c12d07 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/EntryXComparator.java @@ -0,0 +1,22 @@ +package com.github.mikephil.charting.utils; + +import com.github.mikephil.charting.data.Entry; + +import java.util.Comparator; + +/** + * Comparator for comparing Entry-objects by their x-value. + * Created by philipp on 17/06/15. + */ +public class EntryXComparator implements Comparator { + @Override + public int compare(Entry entry1, Entry entry2) { + float diff = entry1.getX() - entry2.getX(); + + if (diff == 0f) return 0; + else { + if (diff > 0f) return 1; + else return -1; + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java new file mode 100644 index 0000000000..a12bc45918 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FSize.java @@ -0,0 +1,79 @@ + +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * Class for describing width and height dimensions in some arbitrary + * unit. Replacement for the android.Util.SizeF which is available only on API >= 21. + */ +public final class FSize extends ObjectPool.Poolable{ + + // TODO : Encapsulate width & height + + public float width; + public float height; + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(256, new FSize(0,0)); + pool.setReplenishPercentage(0.5f); + } + + + protected ObjectPool.Poolable instantiate(){ + return new FSize(0,0); + } + + public static FSize getInstance(final float width, final float height){ + FSize result = pool.get(); + result.width = width; + result.height = height; + return result; + } + + public static void recycleInstance(FSize instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public FSize() { + } + + public FSize(final float width, final float height) { + this.width = width; + this.height = height; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof FSize) { + final FSize other = (FSize) obj; + return width == other.width && height == other.height; + } + return false; + } + + @Override + public String toString() { + return width + "x" + height; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Float.floatToIntBits(width) ^ Float.floatToIntBits(height); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java similarity index 96% rename from MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java index ebbf76a918..5aff51ff84 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FileUtils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/FileUtils.java @@ -61,7 +61,7 @@ public static List loadEntriesFromFile(String path) { vals[i] = Float.parseFloat(split[i]); } - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } } } catch (IOException e) { @@ -122,7 +122,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { String[] split = line.split("#"); if (split.length <= 2) { - entries.add(new Entry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); + entries.add(new Entry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); } else { float[] vals = new float[split.length - 1]; @@ -131,7 +131,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { vals[i] = Float.parseFloat(split[i]); } - entries.add(new BarEntry(vals, Integer.parseInt(split[split.length - 1]))); + entries.add(new BarEntry(Integer.parseInt(split[split.length - 1]), vals)); } line = reader.readLine(); } @@ -191,7 +191,7 @@ public static List loadEntriesFromAssets(AssetManager am, String path) { /** * Saves an Array of Entries to the specified location on the sdcard * - * @param ds + * @param entries * @param path */ public static void saveToSdCard(List entries, String path) { @@ -216,7 +216,7 @@ public static void saveToSdCard(List entries, String path) { for (Entry e : entries) { - buf.append(e.getVal() + "#" + e.getXIndex()); + buf.append(e.getY() + "#" + e.getX()); buf.newLine(); } @@ -242,7 +242,7 @@ public static List loadBarEntriesFromAssets(AssetManager am, String pa // process line String[] split = line.split("#"); - entries.add(new BarEntry(Float.parseFloat(split[0]), Integer.parseInt(split[1]))); + entries.add(new BarEntry(Float.parseFloat(split[1]), Float.parseFloat(split[0]))); line = reader.readLine(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java new file mode 100644 index 0000000000..d12e1fb8d7 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Fill.java @@ -0,0 +1,342 @@ +package com.github.mikephil.charting.utils; + +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class Fill +{ + public enum Type + { + EMPTY, COLOR, LINEAR_GRADIENT, DRAWABLE + } + + public enum Direction + { + DOWN, UP, RIGHT, LEFT + } + + /** + * the type of fill + */ + private Type mType = Type.EMPTY; + + /** + * the color that is used for filling + */ + @Nullable + private Integer mColor = null; + + private Integer mFinalColor = null; + + /** + * the drawable to be used for filling + */ + @Nullable + protected Drawable mDrawable; + + @Nullable + private int[] mGradientColors; + + @Nullable + private float[] mGradientPositions; + + /** + * transparency used for filling + */ + private int mAlpha = 255; + + public Fill() + { + } + + public Fill(int color) + { + this.mType = Type.COLOR; + this.mColor = color; + calculateFinalColor(); + } + + public Fill(int startColor, int endColor) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = new int[]{startColor, endColor}; + } + + public Fill(@NonNull int[] gradientColors) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = gradientColors; + } + + public Fill(@NonNull int[] gradientColors, @NonNull float[] gradientPositions) + { + this.mType = Type.LINEAR_GRADIENT; + this.mGradientColors = gradientColors; + this.mGradientPositions = gradientPositions; + } + + public Fill(@NonNull Drawable drawable) + { + this.mType = Type.DRAWABLE; + this.mDrawable = drawable; + } + + public Type getType() + { + return mType; + } + + public void setType(Type type) + { + this.mType = type; + } + + @Nullable + public Integer getColor() + { + return mColor; + } + + public void setColor(int color) + { + this.mColor = color; + calculateFinalColor(); + } + + public int[] getGradientColors() + { + return mGradientColors; + } + + public void setGradientColors(int[] colors) + { + this.mGradientColors = colors; + } + + public float[] getGradientPositions() + { + return mGradientPositions; + } + + public void setGradientPositions(float[] positions) + { + this.mGradientPositions = positions; + } + + public void setGradientColors(int startColor, int endColor) + { + this.mGradientColors = new int[]{startColor, endColor}; + } + + public int getAlpha() + { + return mAlpha; + } + + public void setAlpha(int alpha) + { + this.mAlpha = alpha; + calculateFinalColor(); + } + + private void calculateFinalColor() + { + if (mColor == null) + { + mFinalColor = null; + } else + { + int alpha = (int) Math.floor(((mColor >> 24) / 255.0) * (mAlpha / 255.0) * 255.0); + mFinalColor = (alpha << 24) | (mColor & 0xffffff); + } + } + + public void fillRect(Canvas c, Paint paint, + float left, float top, float right, float bottom, + Direction gradientDirection) + { + switch (mType) + { + case EMPTY: + return; + + case COLOR: + { + if (mFinalColor == null) return; + + if (isClipPathSupported()) + { + int save = c.save(); + + c.clipRect(left, top, right, bottom); + c.drawColor(mFinalColor); + + c.restoreToCount(save); + } + else + { + // save + Paint.Style previous = paint.getStyle(); + int previousColor = paint.getColor(); + + // set + paint.setStyle(Paint.Style.FILL); + paint.setColor(mFinalColor); + + c.drawRect(left, top, right, bottom, paint); + + // restore + paint.setColor(previousColor); + paint.setStyle(previous); + } + } + break; + + case LINEAR_GRADIENT: + { + if (mGradientColors == null) return; + + LinearGradient gradient = new LinearGradient( + (int) (gradientDirection == Direction.RIGHT + ? right + : gradientDirection == Direction.LEFT + ? left + : left), + (int) (gradientDirection == Direction.UP + ? bottom + : gradientDirection == Direction.DOWN + ? top + : top), + (int) (gradientDirection == Direction.RIGHT + ? left + : gradientDirection == Direction.LEFT + ? right + : left), + (int) (gradientDirection == Direction.UP + ? top + : gradientDirection == Direction.DOWN + ? bottom + : top), + mGradientColors, + mGradientPositions, + android.graphics.Shader.TileMode.MIRROR); + + paint.setShader(gradient); + + c.drawRect(left, top, right, bottom, paint); + } + break; + + case DRAWABLE: + { + if (mDrawable == null) return; + + mDrawable.setBounds((int) left, (int) top, (int) right, (int) bottom); + mDrawable.draw(c); + } + break; + } + } + + public void fillPath(Canvas c, Path path, Paint paint, + @Nullable RectF clipRect) + { + switch (mType) + { + case EMPTY: + return; + + case COLOR: + { + if (mFinalColor == null) return; + + if (clipRect != null && isClipPathSupported()) + { + int save = c.save(); + + c.clipPath(path); + c.drawColor(mFinalColor); + + c.restoreToCount(save); + } + else + { + // save + Paint.Style previous = paint.getStyle(); + int previousColor = paint.getColor(); + + // set + paint.setStyle(Paint.Style.FILL); + paint.setColor(mFinalColor); + + c.drawPath(path, paint); + + // restore + paint.setColor(previousColor); + paint.setStyle(previous); + } + } + break; + + case LINEAR_GRADIENT: + { + if (mGradientColors == null) return; + + LinearGradient gradient = new LinearGradient( + 0, + 0, + c.getWidth(), + c.getHeight(), + mGradientColors, + mGradientPositions, + android.graphics.Shader.TileMode.MIRROR); + + paint.setShader(gradient); + + c.drawPath(path, paint); + } + break; + + case DRAWABLE: + { + if (mDrawable == null) return; + + ensureClipPathSupported(); + + int save = c.save(); + c.clipPath(path); + + mDrawable.setBounds( + clipRect == null ? 0 : (int) clipRect.left, + clipRect == null ? 0 : (int) clipRect.top, + clipRect == null ? c.getWidth() : (int) clipRect.right, + clipRect == null ? c.getHeight() : (int) clipRect.bottom); + mDrawable.draw(c); + + c.restoreToCount(save); + } + break; + } + } + + private boolean isClipPathSupported() + { + return Utils.getSDKInt() >= 18; + } + + private void ensureClipPathSupported() + { + if (Utils.getSDKInt() < 18) + { + throw new RuntimeException("Fill-drawables not (yet) supported below API level 18, " + + "this code was run on API level " + Utils.getSDKInt() + "."); + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java new file mode 100644 index 0000000000..5a415b0477 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/HorizontalViewPortHandler.java @@ -0,0 +1,29 @@ + +package com.github.mikephil.charting.utils; + +/** + * ViewPortHandler for HorizontalBarChart. + */ +public class HorizontalViewPortHandler extends ViewPortHandler { + + +// @Override +// public void setMinimumScaleX(float xScale) { +// setMinimumScaleY(xScale); +// } +// +// @Override +// public void setMinimumScaleY(float yScale) { +// setMinimumScaleX(yScale); +// } +// +// @Override +// public void setMinMaxScaleX(float minScaleX, float maxScaleX) { +// setMinMaxScaleY(minScaleX, maxScaleX); +// } +// +// @Override +// public void setMinMaxScaleY(float minScaleY, float maxScaleY) { +// setMinMaxScaleX(minScaleY, maxScaleY); +// } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java new file mode 100644 index 0000000000..f6220a72e9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointD.java @@ -0,0 +1,53 @@ + +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * Point encapsulating two double values. + * + * @author Philipp Jahoda + */ +public class MPPointD extends ObjectPool.Poolable { + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(64, new MPPointD(0,0)); + pool.setReplenishPercentage(0.5f); + } + + public static MPPointD getInstance(double x, double y){ + MPPointD result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static void recycleInstance(MPPointD instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public double x; + public double y; + + protected ObjectPool.Poolable instantiate(){ + return new MPPointD(0,0); + } + + private MPPointD(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns a string representation of the object + */ + public String toString() { + return "MPPointD, x: " + x + ", y: " + y; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java new file mode 100644 index 0000000000..fb5a00f508 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/MPPointF.java @@ -0,0 +1,99 @@ +package com.github.mikephil.charting.utils; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * Created by Tony Patino on 6/24/16. + */ +public class MPPointF extends ObjectPool.Poolable { + + private static ObjectPool pool; + + public float x; + public float y; + + static { + pool = ObjectPool.create(32, new MPPointF(0,0)); + pool.setReplenishPercentage(0.5f); + } + + public MPPointF() { + } + + public MPPointF(float x, float y) { + this.x = x; + this.y = y; + } + + public static MPPointF getInstance(float x, float y) { + MPPointF result = pool.get(); + result.x = x; + result.y = y; + return result; + } + + public static MPPointF getInstance() { + return pool.get(); + } + + public static MPPointF getInstance(MPPointF copy) { + MPPointF result = pool.get(); + result.x = copy.x; + result.y = copy.y; + return result; + } + + public static void recycleInstance(MPPointF instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + /** + * Return a new point from the data in the specified parcel. + */ + public MPPointF createFromParcel(Parcel in) { + MPPointF r = new MPPointF(0,0); + r.my_readFromParcel(in); + return r; + } + + /** + * Return an array of rectangles of the specified size. + */ + public MPPointF[] newArray(int size) { + return new MPPointF[size]; + } + }; + + /** + * Set the point's coordinates from the data stored in the specified + * parcel. To write a point to a parcel, call writeToParcel(). + * Provided to support older Android devices. + * + * @param in The parcel to read the point's coordinates from + */ + public void my_readFromParcel(Parcel in) { + x = in.readFloat(); + y = in.readFloat(); + } + + public float getX(){ + return this.x; + } + + public float getY(){ + return this.y; + } + + @Override + protected ObjectPool.Poolable instantiate() { + return new MPPointF(0,0); + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java new file mode 100644 index 0000000000..d1d54371f9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ObjectPool.java @@ -0,0 +1,218 @@ +package com.github.mikephil.charting.utils; + +import java.util.List; + +/** + * An object pool for recycling of object instances extending Poolable. + * + * + * Cost/Benefit : + * Cost - The pool can only contain objects extending Poolable. + * Benefit - The pool can very quickly determine if an object is elligable for storage without iteration. + * Benefit - The pool can also know if an instance of Poolable is already stored in a different pool instance. + * Benefit - The pool can grow as needed, if it is empty + * Cost - However, refilling the pool when it is empty might incur a time cost with sufficiently large capacity. Set the replenishPercentage to a lower number if this is a concern. + * + * Created by Tony Patino on 6/20/16. + */ +public class ObjectPool { + + private static int ids = 0; + + private int poolId; + private int desiredCapacity; + private Object[] objects; + private int objectsPointer; + private T modelObject; + private float replenishPercentage; + + + /** + * Returns the id of the given pool instance. + * + * @return an integer ID belonging to this pool instance. + */ + public int getPoolId(){ + return poolId; + } + + /** + * Returns an ObjectPool instance, of a given starting capacity, that recycles instances of a given Poolable object. + * + * @param withCapacity A positive integer value. + * @param object An instance of the object that the pool should recycle. + * @return + */ + public static synchronized ObjectPool create(int withCapacity, Poolable object){ + ObjectPool result = new ObjectPool(withCapacity, object); + result.poolId = ids; + ids++; + + return result; + } + + private ObjectPool(int withCapacity, T object){ + if(withCapacity <= 0){ + throw new IllegalArgumentException("Object Pool must be instantiated with a capacity greater than 0!"); + } + this.desiredCapacity = withCapacity; + this.objects = new Object[this.desiredCapacity]; + this.objectsPointer = 0; + this.modelObject = object; + this.replenishPercentage = 1.0f; + this.refillPool(); + } + + /** + * Set the percentage of the pool to replenish on empty. Valid values are between + * 0.00f and 1.00f + * + * @param percentage a value between 0 and 1, representing the percentage of the pool to replenish. + */ + public void setReplenishPercentage(float percentage){ + float p = percentage; + if(p > 1){ + p = 1; + } + else if(p < 0f){ + p = 0f; + } + this.replenishPercentage = p; + } + + public float getReplenishPercentage(){ + return replenishPercentage; + } + + private void refillPool(){ + this.refillPool(this.replenishPercentage); + } + + private void refillPool(float percentage){ + int portionOfCapacity = (int) (desiredCapacity * percentage); + + if(portionOfCapacity < 1){ + portionOfCapacity = 1; + }else if(portionOfCapacity > desiredCapacity){ + portionOfCapacity = desiredCapacity; + } + + for(int i = 0 ; i < portionOfCapacity ; i++){ + this.objects[i] = modelObject.instantiate(); + } + objectsPointer = portionOfCapacity - 1; + } + + /** + * Returns an instance of Poolable. If get() is called with an empty pool, the pool will be + * replenished. If the pool capacity is sufficiently large, this could come at a performance + * cost. + * + * @return An instance of Poolable object T + */ + public synchronized T get(){ + + if(this.objectsPointer == -1 && this.replenishPercentage > 0.0f){ + this.refillPool(); + } + + T result = (T)objects[this.objectsPointer]; + result.currentOwnerId = Poolable.NO_OWNER; + this.objectsPointer--; + + return result; + } + + /** + * Recycle an instance of Poolable that this pool is capable of generating. + * The T instance passed must not already exist inside this or any other ObjectPool instance. + * + * @param object An object of type T to recycle + */ + public synchronized void recycle(T object){ + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + + this.objectsPointer++; + if(this.objectsPointer >= objects.length){ + this.resizePool(); + } + + object.currentOwnerId = this.poolId; + objects[this.objectsPointer] = object; + + } + + /** + * Recycle a List of Poolables that this pool is capable of generating. + * The T instances passed must not already exist inside this or any other ObjectPool instance. + * + * @param objects A list of objects of type T to recycle + */ + public synchronized void recycle(List objects){ + while(objects.size() + this.objectsPointer + 1 > this.desiredCapacity){ + this.resizePool(); + } + final int objectsListSize = objects.size(); + + // Not relying on recycle(T object) because this is more performant. + for(int i = 0 ; i < objectsListSize ; i++){ + T object = objects.get(i); + if(object.currentOwnerId != Poolable.NO_OWNER){ + if(object.currentOwnerId == this.poolId){ + throw new IllegalArgumentException("The object passed is already stored in this pool!"); + }else { + throw new IllegalArgumentException("The object to recycle already belongs to poolId " + object.currentOwnerId + ". Object cannot belong to two different pool instances simultaneously!"); + } + } + object.currentOwnerId = this.poolId; + this.objects[this.objectsPointer + 1 + i] = object; + } + this.objectsPointer += objectsListSize; + } + + private void resizePool() { + final int oldCapacity = this.desiredCapacity; + this.desiredCapacity *= 2; + Object[] temp = new Object[this.desiredCapacity]; + for(int i = 0 ; i < oldCapacity ; i++){ + temp[i] = this.objects[i]; + } + this.objects = temp; + } + + /** + * Returns the capacity of this object pool. Note : The pool will automatically resize + * to contain additional objects if the user tries to add more objects than the pool's + * capacity allows, but this comes at a performance cost. + * + * @return The capacity of the pool. + */ + public int getPoolCapacity(){ + return this.objects.length; + } + + /** + * Returns the number of objects remaining in the pool, for diagnostic purposes. + * + * @return The number of objects remaining in the pool. + */ + public int getPoolCount(){ + return this.objectsPointer + 1; + } + + + public static abstract class Poolable{ + + public static int NO_OWNER = -1; + int currentOwnerId = NO_OWNER; + + protected abstract Poolable instantiate(); + + } +} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java similarity index 67% rename from MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java index c8f15a5de1..0496bd0673 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -5,10 +5,8 @@ import android.graphics.Path; import android.graphics.RectF; -import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; @@ -54,12 +52,10 @@ public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, fl float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); - if (Float.isInfinite(scaleX)) - { + if (Float.isInfinite(scaleX)) { scaleX = 0; } - if (Float.isInfinite(scaleY)) - { + if (Float.isInfinite(scaleY)) { scaleY = 0; } @@ -88,15 +84,10 @@ public void prepareMatrixOffset(boolean inverted) { .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); mMatrixOffset.postScale(1.0f, -1.0f); } - - // mMatrixOffset.set(offset); - - // mMatrixOffset.reset(); - // - // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - - // mOffsetBottom); } + protected float[] valuePointsForGenerateTransformedValuesScatter = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the SCATTERCHART. @@ -104,18 +95,26 @@ public void prepareMatrixOffset(boolean inverted) { * @param data * @return */ - public float[] generateTransformedValuesScatter(IScatterDataSet data, - float phaseY) { + public float[] generateTransformedValuesScatter(IScatterDataSet data, float phaseX, + float phaseY, int from, int to) { - float[] valuePoints = new float[data.getEntryCount() * 2]; + final int count = (int) ((to - from) * phaseX + 1) * 2; - for (int j = 0; j < valuePoints.length; j += 2) { + if (valuePointsForGenerateTransformedValuesScatter.length != count) { + valuePointsForGenerateTransformedValuesScatter = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesScatter; - Entry e = data.getEntryForIndex(j / 2); + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + from); if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -124,6 +123,8 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesBubble = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BUBBLECHART. @@ -131,20 +132,25 @@ public float[] generateTransformedValuesScatter(IScatterDataSet data, * @param data * @return */ - public float[] generateTransformedValuesBubble(IBubbleDataSet data, - float phaseX, float phaseY, int from, int to) { + public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) { - final int count = (int) Math.ceil(to - from) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; + final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; - float[] valuePoints = new float[count]; + if (valuePointsForGenerateTransformedValuesBubble.length != count) { + valuePointsForGenerateTransformedValuesBubble = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesBubble; for (int j = 0; j < count; j += 2) { Entry e = data.getEntryForIndex(j / 2 + from); if (e != null) { - valuePoints[j] = (float) (e.getXIndex() - from) * phaseX + from; - valuePoints[j + 1] = e.getVal() * phaseY; + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -153,6 +159,8 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesLine = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the LINECHART. @@ -161,19 +169,26 @@ public float[] generateTransformedValuesBubble(IBubbleDataSet data, * @return */ public float[] generateTransformedValuesLine(ILineDataSet data, - float phaseX, float phaseY, int from, int to) { + float phaseX, float phaseY, + int min, int max) { - final int count = (int) Math.ceil((to - from) * phaseX) * 2; + final int count = ((int) ((max - min) * phaseX) + 1) * 2; - float[] valuePoints = new float[count]; + if (valuePointsForGenerateTransformedValuesLine.length != count) { + valuePointsForGenerateTransformedValuesLine = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesLine; for (int j = 0; j < count; j += 2) { - Entry e = data.getEntryForIndex(j / 2 + from); + Entry e = data.getEntryForIndex(j / 2 + min); if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -182,6 +197,8 @@ public float[] generateTransformedValuesLine(ILineDataSet data, return valuePoints; } + protected float[] valuePointsForGenerateTransformedValuesCandle = new float[1]; + /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the CANDLESTICKCHART. @@ -192,17 +209,23 @@ public float[] generateTransformedValuesLine(ILineDataSet data, public float[] generateTransformedValuesCandle(ICandleDataSet data, float phaseX, float phaseY, int from, int to) { - final int count = (int) Math.ceil((to - from) * phaseX) * 2; + final int count = (int) ((to - from) * phaseX + 1) * 2; - float[] valuePoints = new float[count]; + if (valuePointsForGenerateTransformedValuesCandle.length != count) { + valuePointsForGenerateTransformedValuesCandle = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesCandle; for (int j = 0; j < count; j += 2) { CandleEntry e = data.getEntryForIndex(j / 2 + from); if (e != null) { - valuePoints[j] = e.getXIndex(); + valuePoints[j] = e.getX(); valuePoints[j + 1] = e.getHigh() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; } } @@ -211,78 +234,6 @@ public float[] generateTransformedValuesCandle(ICandleDataSet data, return valuePoints; } - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param data - * @param dataSetIndex the dataset index - * @param bd - * @param phaseY - * @return - */ - public float[] generateTransformedValuesBarChart(IBarDataSet data, - int dataSetIndex, BarData bd, float phaseY) { - - float[] valuePoints = new float[data.getEntryCount() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = data.getEntryForIndex(j / 2); - int i = e.getXIndex(); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + i * (setCount - 1) + dataSetIndex + space * i - + space / 2f; - float y = e.getVal(); - - valuePoints[j] = x; - valuePoints[j + 1] = y * phaseY; - } - - getValueToPixelMatrix().mapPoints(valuePoints); - - return valuePoints; - } - - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BARCHART. - * - * @param data - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesHorizontalBarChart(IBarDataSet data, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[data.getEntryCount() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = data.getEntryForIndex(j / 2); - int i = e.getXIndex(); - - // calculate the x-position, depending on datasetcount - float x = i + i * (setCount - 1) + dataSet + space * i - + space / 2f; - float y = e.getVal(); - - valuePoints[j] = y * phaseY; - valuePoints[j + 1] = x; - } - - getValueToPixelMatrix().mapPoints(valuePoints); - - return valuePoints; - } - /** * transform a path with all the given matrices VERY IMPORTANT: keep order * to value-touch-offset @@ -339,7 +290,7 @@ public void rectValueToPixel(RectF r) { * @param r * @param phaseY */ - public void rectValueToPixel(RectF r, float phaseY) { + public void rectToPixelPhase(RectF r, float phaseY) { // multiply the height of the rect with the phase r.top *= phaseY; @@ -350,6 +301,17 @@ public void rectValueToPixel(RectF r, float phaseY) { mMatrixOffset.mapRect(r); } + public void rectToPixelPhaseHorizontal(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.left *= phaseY; + r.right *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + /** * Transform a rectangle with all matrices with potential animation phases. * @@ -392,6 +354,8 @@ public void rectValuesToPixel(List rects) { m.mapRect(rects.get(i)); } + protected Matrix mPixelToValueMatrixBuffer = new Matrix(); + /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. @@ -400,7 +364,8 @@ public void rectValuesToPixel(List rects) { */ public void pixelsToValue(float[] pixels) { - Matrix tmp = new Matrix(); + Matrix tmp = mPixelToValueMatrixBuffer; + tmp.reset(); // invert all matrixes to convert back to the original value mMatrixOffset.invert(tmp); @@ -414,28 +379,58 @@ public void pixelsToValue(float[] pixels) { } /** - * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to + * buffer for performance + */ + float[] ptsBuffer = new float[2]; + + /** + * Returns a recyclable MPPointD instance. + * returns the x and y values in the chart at the given touch point + * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). + * getPixelForValues(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getValuesByTouchPoint(float x, float y) { + + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, result); + return result; + } + + public void getValuesByTouchPoint(float x, float y, MPPointD outputPoint) { + + ptsBuffer[0] = x; + ptsBuffer[1] = y; + + pixelsToValue(ptsBuffer); + + outputPoint.x = ptsBuffer[0]; + outputPoint.y = ptsBuffer[1]; + } + + /** + * Returns a recyclable MPPointD instance. + * Returns the x and y coordinates (pixels) for a given x and y value in the chart. * * @param x * @param y * @return */ - public PointD getValuesByTouchPoint(float x, float y) { + public MPPointD getPixelForValues(float x, float y) { - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; + ptsBuffer[0] = x; + ptsBuffer[1] = y; - pixelsToValue(pts); + pointValuesToPixel(ptsBuffer); - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; + double xPx = ptsBuffer[0]; + double yPx = ptsBuffer[1]; - return new PointD(xTouchVal, yTouchVal); + return MPPointD.getInstance(xPx, yPx); } public Matrix getValueMatrix() { diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java similarity index 98% rename from MPChartLib/src/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java index aee132cca2..05fa82ae29 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/TransformerHorizontalBarChart.java @@ -15,7 +15,7 @@ public TransformerHorizontalBarChart(ViewPortHandler viewPortHandler) { /** * Prepares the matrix that contains all offsets. * - * @param chart + * @param inverted */ public void prepareMatrixOffset(boolean inverted) { diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java similarity index 71% rename from MPChartLib/src/com/github/mikephil/charting/utils/Utils.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java index b8e6cd7964..c302673919 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -6,22 +6,23 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.DisplayMetrics; import android.util.Log; +import android.util.SizeF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; -import com.github.mikephil.charting.components.YAxis.AxisDependency; import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.ValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; import java.util.List; @@ -39,7 +40,13 @@ public abstract class Utils { private static int mMinimumFlingVelocity = 50; private static int mMaximumFlingVelocity = 8000; public final static double DEG2RAD = (Math.PI / 180.0); - public final static float FDEG2RAD = ((float)Math.PI / 180.f); + public final static float FDEG2RAD = ((float) Math.PI / 180.f); + + @SuppressWarnings("unused") + public final static double DOUBLE_EPSILON = Double.longBitsToDouble(1); + + @SuppressWarnings("unused") + public final static float FLOAT_EPSILON = Float.intBitsToFloat(1); /** * initialize method, called inside the Chart.init() method. @@ -99,15 +106,13 @@ public static float convertDpToPixel(float dp) { if (mMetrics == null) { Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertDpToPixel(...). Otherwise conversion does not take place."); + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " calling Utils.convertDpToPixel(...). Otherwise conversion does not " + + "take place."); return dp; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertDpToPixel(...)."); } - DisplayMetrics metrics = mMetrics; - float px = dp * (metrics.densityDpi / 160f); - return px; + return dp * mMetrics.density; } /** @@ -122,15 +127,13 @@ public static float convertPixelsToDp(float px) { if (mMetrics == null) { Log.e("MPChartLib-Utils", - "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertPixelsToDp(...). Otherwise conversion does not take place."); + "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before" + + " calling Utils.convertPixelsToDp(...). Otherwise conversion does not" + + " take place."); return px; - // throw new IllegalStateException( - // "Utils NOT INITIALIZED. You need to call Utils.init(...) at least once before calling Utils.convertPixelsToDp(...)."); } - DisplayMetrics metrics = mMetrics; - float dp = px / (metrics.densityDpi / 160f); - return dp; + return px / mMetrics.density; } /** @@ -145,6 +148,7 @@ public static int calcTextWidth(Paint paint, String demoText) { return (int) paint.measureText(demoText); } + private static Rect mCalcTextHeightRect = new Rect(); /** * calculates the approximate height of a text, depending on a demo text * avoid repeated calls (e.g. inside drawing methods) @@ -155,36 +159,68 @@ public static int calcTextWidth(Paint paint, String demoText) { */ public static int calcTextHeight(Paint paint, String demoText) { - Rect r = new Rect(); + Rect r = mCalcTextHeightRect; + r.set(0,0,0,0); paint.getTextBounds(demoText, 0, demoText.length(), r); return r.height(); } + private static Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); + public static float getLineHeight(Paint paint) { - Paint.FontMetrics metrics = paint.getFontMetrics(); - return metrics.descent - metrics.ascent; + return getLineHeight(paint, mFontMetrics); + } + + public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.descent - fontMetrics.ascent; } public static float getLineSpacing(Paint paint) { - Paint.FontMetrics metrics = paint.getFontMetrics(); - return metrics.ascent - metrics.top + metrics.bottom; + return getLineSpacing(paint, mFontMetrics); + } + + public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics){ + paint.getFontMetrics(fontMetrics); + return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom; } /** + * Returns a recyclable FSize instance. * calculates the approximate size of a text, depending on a demo text * avoid repeated calls (e.g. inside drawing methods) * * @param paint * @param demoText - * @return + * @return A Recyclable FSize instance */ public static FSize calcTextSize(Paint paint, String demoText) { - Rect r = new Rect(); + FSize result = FSize.getInstance(0,0); + calcTextSize(paint, demoText, result); + return result; + } + + private static Rect mCalcTextSizeRect = new Rect(); + /** + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @param outputFSize An output variable, modified by the function. + */ + public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) { + + Rect r = mCalcTextSizeRect; + r.set(0,0,0,0); paint.getTextBounds(demoText, 0, demoText.length(), r); - return new FSize(r.width(), r.height()); + outputFSize.width = r.width(); + outputFSize.height = r.height(); + } + /** * Math.pow(...) is very expensive, so avoid calling it and create it * yourself. @@ -193,9 +229,23 @@ public static FSize calcTextSize(Paint paint, String demoText) { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + private static IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); + + private static IValueFormatter generateDefaultValueFormatter() { + final DefaultValueFormatter formatter = new DefaultValueFormatter(1); + return formatter; + } + + /// - returns: The default value formatter used for all chart components that needs a default + public static IValueFormatter getDefaultValueFormatter() + { + return mDefaultValueFormatter; + } + /** * Formats the given number to the given number of decimals, and returns the - * number as a string, maximum 35 characters. If thousands are separated, the separating character is a dot ("."). + * number as a string, maximum 35 characters. If thousands are separated, the separating + * character is a dot ("."). * * @param number * @param digitCount @@ -216,7 +266,8 @@ public static String formatNumber(float number, int digitCount, boolean separate * @param separateChar a caracter to be paced between the "thousands" * @return */ - public static String formatNumber(float number, int digitCount, boolean separateThousands, char separateChar) { + public static String formatNumber(float number, int digitCount, boolean separateThousands, + char separateChar) { char[] out = new char[35]; @@ -302,6 +353,11 @@ public static String formatNumber(float number, int digitCount, boolean separate * @return */ public static float roundToNextSignificant(double number) { + if (Double.isInfinite(number) || + Double.isNaN(number) || + number == 0.0) + return 0; + final float d = (float) Math.ceil((float) Math.log10(number < 0 ? -number : number)); final int pw = 1 - (int) d; final float magnitude = (float) Math.pow(10, pw); @@ -319,6 +375,10 @@ public static float roundToNextSignificant(double number) { public static int getDecimals(float number) { float i = roundToNextSignificant(number); + + if (Float.isInfinite(i)) + return 0; + return (int) Math.ceil(-Math.log10(i)) + 2; } @@ -332,13 +392,18 @@ public static int[] convertIntegers(List integers) { int[] ret = new int[integers.size()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = integers.get(i).intValue(); - } + copyIntegers(integers, ret); return ret; } + public static void copyIntegers(List from, int[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + /** * Converts the provided String List to a String array. * @@ -356,6 +421,13 @@ public static String[] convertStrings(List strings) { return ret; } + public static void copyStrings(List from, String[] to){ + int count = to.length < from.size() ? to.length : from.size(); + for(int i = 0 ; i < count ; i++){ + to[i] = from.get(i); + } + } + /** * Replacement for the Math.nextUp(...) method that is only available in * HONEYCOMB and higher. Dat's some seeeeek sheeet. @@ -374,81 +446,7 @@ public static double nextUp(double d) { } /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. This will return -Integer.MAX_VALUE if failure. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static int getClosestDataSetIndex(List valsAtIndex, float val, - AxisDependency axis) { - - int index = -Integer.MAX_VALUE; - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelectionDetail sel = valsAtIndex.get(i); - - if (axis == null || sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs((float) sel.val - val); - if (cdistance < distance) { - index = valsAtIndex.get(i).dataSetIndex; - distance = cdistance; - } - } - } - - return index; - } - - /** - * Returns the minimum distance from a touch-y-value (in pixels) to the - * closest y-value (in pixels) that is displayed in the chart. - * - * @param valsAtIndex - * @param val - * @param axis - * @return - */ - public static float getMinimumDistance(List valsAtIndex, float val, - AxisDependency axis) { - - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelectionDetail sel = valsAtIndex.get(i); - - if (sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs(sel.val - val); - if (cdistance < distance) { - distance = cdistance; - } - } - } - - return distance; - } - - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public static boolean needsDefaultFormatter(ValueFormatter formatter) { - if (formatter == null) - return true; - if (formatter instanceof DefaultValueFormatter) - return true; - - return false; - } - - /** + * 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. * @@ -457,13 +455,18 @@ public static boolean needsDefaultFormatter(ValueFormatter formatter) { * @param angle in degrees, converted to radians internally * @return */ - public static PointF getPosition(PointF center, float dist, float angle) { + public static MPPointF getPosition(MPPointF center, float dist, float angle) { - PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))), - (float) (center.y + dist * Math.sin(Math.toRadians(angle)))); + MPPointF p = MPPointF.getInstance(0,0); + getPosition(center, dist, angle, p); return p; } + public static 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))); + } + public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, VelocityTracker tracker) { @@ -522,27 +525,51 @@ public static float getNormalizedAngle(float angle) { return angle % 360.f; } + private static Rect mDrawableBoundsCache = new Rect(); + + public static void drawImage(Canvas canvas, + Drawable drawable, + int x, int y, + int width, int height) { + + MPPointF drawOffset = MPPointF.getInstance(); + drawOffset.x = x - (width / 2); + drawOffset.y = y - (height / 2); + + drawable.copyBounds(mDrawableBoundsCache); + drawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + width, + mDrawableBoundsCache.top + width); + + int saveId = canvas.save(); + // translate to the correct position and draw + canvas.translate(drawOffset.x, drawOffset.y); + drawable.draw(canvas); + canvas.restoreToCount(saveId); + } + private static Rect mDrawTextRectBuffer = new Rect(); private static Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); - public static void drawText(Canvas c, String text, float x, float y, - Paint paint, - PointF anchor, float angleDegrees) { + public static void drawXAxisValue(Canvas c, String text, float x, float y, + Paint paint, + MPPointF anchor, float angleDegrees) { float drawOffsetX = 0.f; float drawOffsetY = 0.f; + final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); - final float lineHeight = mDrawTextRectBuffer.height(); - - // Android sometimes has pre-padding + // Android sometimes has pre-padding drawOffsetX -= mDrawTextRectBuffer.left; // Android does not snap the bounds to line boundaries, // and draws from bottom to top. // And we want to normalize it. - drawOffsetY += lineHeight; + drawOffsetY += -mFontMetricsBuffer.ascent; // To have a consistent point of reference, we always draw left-aligned Paint.Align originalTextAlign = paint.getTextAlign(); @@ -566,6 +593,7 @@ public static void drawText(Canvas c, String text, float x, float y, translateX -= rotatedSize.width * (anchor.x - 0.5f); translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); } c.save(); @@ -575,8 +603,7 @@ public static void drawText(Canvas c, String text, float x, float y, c.drawText(text, drawOffsetX, drawOffsetY, paint); c.restore(); - } - else { + } else { if (anchor.x != 0.f || anchor.y != 0.f) { drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x; @@ -595,7 +622,7 @@ public static void drawText(Canvas c, String text, float x, float y, public static void drawMultilineText(Canvas c, StaticLayout textLayout, float x, float y, TextPaint paint, - PointF anchor, float angleDegrees) { + MPPointF anchor, float angleDegrees) { float drawOffsetX = 0.f; float drawOffsetY = 0.f; @@ -637,6 +664,7 @@ public static void drawMultilineText(Canvas c, StaticLayout textLayout, translateX -= rotatedSize.width * (anchor.x - 0.5f); translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); } c.save(); @@ -647,8 +675,7 @@ public static void drawMultilineText(Canvas c, StaticLayout textLayout, textLayout.draw(c); c.restore(); - } - else { + } else { if (anchor.x != 0.f || anchor.y != 0.f) { drawOffsetX -= drawWidth * anchor.x; @@ -673,7 +700,7 @@ public static void drawMultilineText(Canvas c, String text, float x, float y, TextPaint paint, FSize constrainedToSize, - PointF anchor, float angleDegrees) { + MPPointF anchor, float angleDegrees) { StaticLayout textLayout = new StaticLayout( text, 0, text.length(), @@ -685,29 +712,68 @@ public static void drawMultilineText(Canvas c, String text, drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees); } - public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) - { + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleSize + * @param degrees + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByDegrees(FSize rectangleSize, float degrees) { final float radians = degrees * FDEG2RAD; - return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); } - public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) - { - return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, radians); + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleSize + * @param radians + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByRadians(FSize rectangleSize, float radians) { + return getSizeOfRotatedRectangleByRadians(rectangleSize.width, rectangleSize.height, + radians); } - public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float rectangleHeight, float degrees) - { + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleWidth + * @param rectangleHeight + * @param degrees + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float + rectangleHeight, float degrees) { final float radians = degrees * FDEG2RAD; return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians); } - public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float rectangleHeight, float radians) - { - return new FSize( - Math.abs(rectangleWidth * (float)Math.cos(radians)) + Math.abs(rectangleHeight * (float)Math.sin(radians)), - Math.abs(rectangleWidth * (float)Math.sin(radians)) + Math.abs(rectangleHeight * (float)Math.cos(radians)) + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleWidth + * @param rectangleHeight + * @param radians + * @return A Recyclable FSize instance + */ + public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float + rectangleHeight, float radians) { + return FSize.getInstance( + Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * + (float) Math.sin(radians)), + Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * + (float) Math.cos(radians)) ); } + public static int getSDKInt() { + return android.os.Build.VERSION.SDK_INT; + } } diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java similarity index 71% rename from MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java index 0a220cf508..71b4b9bf79 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -2,13 +2,12 @@ package com.github.mikephil.charting.utils; import android.graphics.Matrix; -import android.graphics.PointF; import android.graphics.RectF; -import android.util.Log; import android.view.View; /** - * Class that contains information about the charts current viewport settings, including offsets, scale & translation levels, ... + * Class that contains information about the charts current viewport settings, including offsets, scale & translation + * levels, ... * * @author Philipp Jahoda */ @@ -102,7 +101,6 @@ public void setChartDimens(float width, float height) { mChartWidth = width; restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); - } public boolean hasChartDimens() { @@ -162,8 +160,8 @@ public RectF getContentRect() { return mContentRect; } - public PointF getContentCenter() { - return new PointF(mContentRect.centerX(), mContentRect.centerY()); + public MPPointF getContentCenter() { + return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY()); } public float getChartHeight() { @@ -174,6 +172,15 @@ public float getChartWidth() { return mChartWidth; } + /** + * Returns the smallest extension of the content rect (width or height). + * + * @return + */ + public float getSmallestContentExtension() { + return Math.min(mContentRect.width(), mContentRect.height()); + } + /** * ################ ################ ################ ################ */ @@ -189,13 +196,16 @@ public float getChartWidth() { public Matrix zoomIn(float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(1.4f, 1.4f, x, y); - + zoomIn(x, y, save); return save; } + public void zoomIn(float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.4f, 1.4f, x, y); + } + /** * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom * center. @@ -203,11 +213,24 @@ public Matrix zoomIn(float x, float y) { public Matrix zoomOut(float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); + zoomOut(x, y, save); + return save; + } - save.postScale(0.7f, 0.7f, x, y); + public void zoomOut(float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(0.7f, 0.7f, x, y); + } - return save; + /** + * Zooms out to original size. + * @param outputMatrix + */ + public void resetZoom(Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(1.0f, 1.0f, 0.0f, 0.0f); } /** @@ -220,13 +243,16 @@ public Matrix zoomOut(float x, float y) { public Matrix zoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(scaleX, scaleY); - + zoom(scaleX, scaleY, save); return save; } + public void zoom(float scaleX, float scaleY, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX, scaleY); + } + /** * Post-scales by the specified scale factors. x and y is pivot. * @@ -239,13 +265,16 @@ public Matrix zoom(float scaleX, float scaleY) { public Matrix zoom(float scaleX, float scaleY, float x, float y) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(scaleX, scaleY, x, y); - + zoom(scaleX, scaleY, x, y, save); return save; } + public void zoom(float scaleX, float scaleY, float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(scaleX, scaleY, x, y); + } + /** * Sets the scale factor to the specified values. * @@ -256,13 +285,16 @@ public Matrix zoom(float scaleX, float scaleY, float x, float y) { public Matrix setZoom(float scaleX, float scaleY) { Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.setScale(scaleX, scaleY); - + setZoom(scaleX, scaleY, save); return save; } + public void setZoom(float scaleX, float scaleY, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.setScale(scaleX, scaleY); + } + /** * Sets the scale factor to the specified values. x and y is pivot. * @@ -282,21 +314,35 @@ public Matrix setZoom(float scaleX, float scaleY, float x, float y) { return save; } + protected float[] valsBufferForFitScreen = new float[9]; + /** * Resets all zooming and dragging and makes the chart fit exactly it's * bounds. */ public Matrix fitScreen() { + Matrix save = new Matrix(); + fitScreen(save); + return save; + } + + /** + * Resets all zooming and dragging and makes the chart fit exactly it's + * bounds. Output Matrix is available for those who wish to cache the object. + */ + public void fitScreen(Matrix outputMatrix) { mMinScaleX = 1f; mMinScaleY = 1f; - Matrix save = new Matrix(); - save.set(mMatrixTouch); + outputMatrix.set(mMatrixTouch); - float[] vals = new float[9]; + float[] vals = valsBufferForFitScreen; + for (int i = 0; i < 9; i++) { + vals[i] = 0; + } - save.getValues(vals); + outputMatrix.getValues(vals); // reset all translations and scaling vals[Matrix.MTRANS_X] = 0f; @@ -304,13 +350,11 @@ public Matrix fitScreen() { vals[Matrix.MSCALE_X] = 1f; vals[Matrix.MSCALE_Y] = 1f; - save.setValues(vals); - - return save; + outputMatrix.setValues(vals); } /** - * Post-translates to the specified points. + * Post-translates to the specified points. Less Performant. * * @param transformedPts * @return @@ -318,16 +362,26 @@ public Matrix fitScreen() { public Matrix translate(final float[] transformedPts) { Matrix save = new Matrix(); - save.set(mMatrixTouch); + translate(transformedPts, save); + return save; + } + /** + * Post-translates to the specified points. Output matrix allows for caching objects. + * + * @param transformedPts + * @return + */ + public void translate(final float[] transformedPts, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); final float x = transformedPts[0] - offsetLeft(); final float y = transformedPts[1] - offsetTop(); - - save.postTranslate(-x, -y); - - return save; + outputMatrix.postTranslate(-x, -y); } + protected Matrix mCenterViewPortMatrixBuffer = new Matrix(); + /** * Centers the viewport around the specified position (x-index and y-value) * in the chart. Centering the viewport outside the bounds of the chart is @@ -340,7 +394,8 @@ public Matrix translate(final float[] transformedPts) { */ public void centerViewPort(final float[] transformedPts, final View view) { - Matrix save = new Matrix(); + Matrix save = mCenterViewPortMatrixBuffer; + save.reset(); save.set(mMatrixTouch); final float x = transformedPts[0] - offsetLeft(); @@ -352,7 +407,7 @@ public void centerViewPort(final float[] transformedPts, final View view) { } /** - * buffer for storing matrix values + * buffer for storing the 9 matrix values of a 3x3 matrix */ protected final float[] matrixBuffer = new float[9]; @@ -391,7 +446,7 @@ public void limitTransAndScale(Matrix matrix, RectF content) { float curTransY = matrixBuffer[Matrix.MTRANS_Y]; float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; - // min scale-x is 1f, max is the max float + // min scale-x is 1f mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); // min scale-y is 1f @@ -406,12 +461,10 @@ public void limitTransAndScale(Matrix matrix, RectF content) { } float maxTransX = -width * (mScaleX - 1f); - float newTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); - mTransX = newTransX; + mTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); float maxTransY = height * (mScaleY - 1f); - float newTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); - mTransY = newTransY; + mTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); matrixBuffer[Matrix.MTRANS_X] = mTransX; matrixBuffer[Matrix.MSCALE_X] = mScaleX; @@ -444,6 +497,9 @@ public void setMinimumScaleX(float xScale) { */ public void setMaximumScaleX(float xScale) { + if (xScale == 0.f) + xScale = Float.MAX_VALUE; + mMaxScaleX = xScale; limitTransAndScale(mMatrixTouch, mContentRect); @@ -460,6 +516,9 @@ public void setMinMaxScaleX(float minScaleX, float maxScaleX) { if (minScaleX < 1f) minScaleX = 1f; + if (maxScaleX == 0.f) + maxScaleX = Float.MAX_VALUE; + mMinScaleX = minScaleX; mMaxScaleX = maxScaleX; @@ -488,11 +547,28 @@ public void setMinimumScaleY(float yScale) { */ public void setMaximumScaleY(float yScale) { + if (yScale == 0.f) + yScale = Float.MAX_VALUE; + mMaxScaleY = yScale; limitTransAndScale(mMatrixTouch, mContentRect); } + public void setMinMaxScaleY(float minScaleY, float maxScaleY) { + + if (minScaleY < 1f) + minScaleY = 1f; + + if (maxScaleY == 0.f) + maxScaleY = Float.MAX_VALUE; + + mMinScaleY = minScaleY; + mMaxScaleY = maxScaleY; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + /** * Returns the charts-touch matrix used for translation and scale on touch. * @@ -510,42 +586,33 @@ public Matrix getMatrixTouch() { */ public boolean isInBoundsX(float x) { - if (isInBoundsLeft(x) && isInBoundsRight(x)) - return true; - else - return false; + return isInBoundsLeft(x) && isInBoundsRight(x); } public boolean isInBoundsY(float y) { - if (isInBoundsTop(y) && isInBoundsBottom(y)) - return true; - else - return false; + return isInBoundsTop(y) && isInBoundsBottom(y); } public boolean isInBounds(float x, float y) { - if (isInBoundsX(x) && isInBoundsY(y)) - return true; - else - return false; + return isInBoundsX(x) && isInBoundsY(y); } public boolean isInBoundsLeft(float x) { - return mContentRect.left <= x ? true : false; + return mContentRect.left <= x + 1; } public boolean isInBoundsRight(float x) { x = (float) ((int) (x * 100.f)) / 100.f; - return mContentRect.right >= x ? true : false; + return mContentRect.right >= x - 1; } public boolean isInBoundsTop(float y) { - return mContentRect.top <= y ? true : false; + return mContentRect.top <= y; } public boolean isInBoundsBottom(float y) { y = (float) ((int) (y * 100.f)) / 100.f; - return mContentRect.bottom >= y ? true : false; + return mContentRect.bottom >= y; } /** @@ -562,6 +629,22 @@ public float getScaleY() { return mScaleY; } + public float getMinScaleX() { + return mMinScaleX; + } + + public float getMaxScaleX() { + return mMaxScaleX; + } + + public float getMinScaleY() { + return mMinScaleY; + } + + public float getMaxScaleY() { + return mMaxScaleY; + } + /** * Returns the translation (drag / pan) distance on the x-axis * @@ -587,10 +670,7 @@ public float getTransY() { */ public boolean isFullyZoomedOut() { - if (isFullyZoomedOutX() && isFullyZoomedOutY()) - return true; - else - return false; + return isFullyZoomedOutX() && isFullyZoomedOutY(); } /** @@ -599,10 +679,7 @@ public boolean isFullyZoomedOut() { * @return */ public boolean isFullyZoomedOutY() { - if (mScaleY > mMinScaleY || mMinScaleY > 1f) - return false; - else - return true; + return !(mScaleY > mMinScaleY || mMinScaleY > 1f); } /** @@ -612,10 +689,7 @@ public boolean isFullyZoomedOutY() { * @return */ public boolean isFullyZoomedOutX() { - if (mScaleX > mMinScaleX || mMinScaleX > 1f) - return false; - else - return true; + return !(mScaleX > mMinScaleX || mMinScaleX > 1f); } /** @@ -644,7 +718,7 @@ public void setDragOffsetY(float offset) { * @return */ public boolean hasNoDragOffset() { - return mTransOffsetX <= 0 && mTransOffsetY <= 0 ? true : false; + return mTransOffsetX <= 0 && mTransOffsetY <= 0; } /** @@ -653,7 +727,7 @@ public boolean hasNoDragOffset() { * @return */ public boolean canZoomOutMoreX() { - return (mScaleX > mMinScaleX); + return mScaleX > mMinScaleX; } /** @@ -662,7 +736,24 @@ public boolean canZoomOutMoreX() { * @return */ public boolean canZoomInMoreX() { - return (mScaleX < mMaxScaleX); + return mScaleX < mMaxScaleX; } + /** + * Returns true if the chart is not yet fully zoomed out on the y-axis + * + * @return + */ + public boolean canZoomOutMoreY() { + return mScaleY > mMinScaleY; + } + + /** + * Returns true if the chart is not yet fully zoomed in on the y-axis + * + * @return + */ + public boolean canZoomInMoreY() { + return mScaleY < mMaxScaleY; + } } diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java new file mode 100644 index 0000000000..784c290ceb --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ApproximatorTest.java @@ -0,0 +1,43 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.filter.Approximator; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 07/06/16. + */ +public class ApproximatorTest { + + @Test + public void testApproximation() { + + float[] points = new float[]{ + 10, 20, + 20, 30, + 25, 25, + 30, 28, + 31, 31, + 33, 33, + 40, 40, + 44, 40, + 48, 23, + 50, 20, + 55, 20, + 60, 25}; + + assertEquals(24, points.length); + + Approximator a = new Approximator(); + + float[] reduced = a.reduceWithDouglasPeucker(points, 2); + + assertEquals(18, reduced.length); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java new file mode 100644 index 0000000000..05cb2f3592 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/AxisRendererTest.java @@ -0,0 +1,105 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.renderer.AxisRenderer; +import com.github.mikephil.charting.renderer.YAxisRenderer; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 31/05/16. + */ +public class AxisRendererTest { + + + @Test + public void testComputeAxisValues() { + + YAxis yAxis = new YAxis(); + yAxis.setLabelCount(6); + AxisRenderer renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + float[] entries = yAxis.mEntries; + + assertEquals(6, entries.length); + assertEquals(20, entries[1] - entries[0], 0.01); // interval 20 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + yAxis.setGranularity(50f); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(3, entries.length); + assertEquals(50, entries[1] - entries[0], 0.01); // interval 50 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(25, entries[1] - entries[0], 0.01); // interval 25 + assertEquals(0, entries[0], 0.01); + assertEquals(100, entries[entries.length - 1], 0.01); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, true); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0025, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.01, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(5, false); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(0, 0.01f, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(0.0020, entries[1] - entries[0], 0.0001); + assertEquals(0, entries[0], 0.0001); + assertEquals(0.0080, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 50, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-40, entries[0], 0.0001); + assertEquals(0, entries[2], 0.0001); + assertEquals(40, entries[entries.length - 1], 0.0001); + + yAxis = new YAxis(); + yAxis.setLabelCount(6); + renderer = new YAxisRenderer(null, yAxis, null); + + renderer.computeAxis(-50, 100, false); + entries = yAxis.mEntries; + + assertEquals(5, entries.length); + assertEquals(-30, entries[0], 0.0001); + assertEquals(30, entries[2], 0.0001); + assertEquals(90, entries[entries.length - 1], 0.0001); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java new file mode 100644 index 0000000000..99954bce27 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/BarDataTest.java @@ -0,0 +1,72 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.BarData; +import com.github.mikephil.charting.data.BarDataSet; +import com.github.mikephil.charting.data.BarEntry; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class BarDataTest { + + @Test + public void testGroupBars() { + + float groupSpace = 5f; + float barSpace = 1f; + + List values1 = new ArrayList<>(); + List values2 = new ArrayList<>(); + + for(int i = 0; i < 5; i++) { + values1.add(new BarEntry(i, 50)); + values2.add(new BarEntry(i, 60)); + } + + BarDataSet barDataSet1 = new BarDataSet(values1, "Set1"); + BarDataSet barDataSet2 = new BarDataSet(values2, "Set2"); + + BarData data = new BarData(barDataSet1, barDataSet2); + data.setBarWidth(10f); + + float groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(27f, groupWidth, 0.01f); + + assertEquals(0f, values1.get(0).getX(), 0.01f); + assertEquals(1f, values1.get(1).getX(), 0.01f); + + data.groupBars(1000, groupSpace, barSpace); + + // 1000 + 2.5 + 0.5 + 5 + assertEquals(1008f, values1.get(0).getX(), 0.01f); + assertEquals(1019f, values2.get(0).getX(), 0.01f); + assertEquals(1035f, values1.get(1).getX(), 0.01f); + assertEquals(1046f, values2.get(1).getX(), 0.01f); + + data.groupBars(-1000, groupSpace, barSpace); + + assertEquals(-992f, values1.get(0).getX(), 0.01f); + assertEquals(-981f, values2.get(0).getX(), 0.01f); + assertEquals(-965f, values1.get(1).getX(), 0.01f); + assertEquals(-954f, values2.get(1).getX(), 0.01f); + + data.setBarWidth(20f); + groupWidth = data.getGroupWidth(groupSpace, barSpace); + assertEquals(47f, groupWidth, 0.01f); + + data.setBarWidth(10f); + data.groupBars(-20, groupSpace, barSpace); + + assertEquals(-12f, values1.get(0).getX(), 0.01f); + assertEquals(-1f, values2.get(0).getX(), 0.01f); + assertEquals(15f, values1.get(1).getX(), 0.01f); + assertEquals(26f, values2.get(1).getX(), 0.01f); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java new file mode 100644 index 0000000000..ae464eefd0 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ChartDataTest.java @@ -0,0 +1,211 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +/** + * Created by philipp on 06/06/16. + */ +public class ChartDataTest { + + @Test + public void testDynamicChartData() { + + List entries1 = new ArrayList(); + entries1.add(new Entry(10, 10)); + entries1.add(new Entry(15, -2)); + entries1.add(new Entry(21, 50)); + + ScatterDataSet set1 = new ScatterDataSet(entries1, ""); + + List entries2 = new ArrayList(); + entries2.add(new Entry(-1, 10)); + entries2.add(new Entry(10, 2)); + entries2.add(new Entry(20, 5)); + + ScatterDataSet set2 = new ScatterDataSet(entries2, ""); + + ScatterData data = new ScatterData(set1, set2); + + assertEquals(-2, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(6, data.getEntryCount()); + + assertEquals(-1f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-2f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + assertEquals(3, data.getMaxEntryCountSet().getEntryCount()); + + // now add and remove values + data.addEntry(new Entry(-10, -10), 0); + + assertEquals(set1, data.getMaxEntryCountSet()); + assertEquals(4, data.getMaxEntryCountSet().getEntryCount()); + + assertEquals(-10f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(50f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-10f, data.getXMin(), 0.01f); + assertEquals(21f, data.getXMax(), 0.01f); + + assertEquals(-10f, data.getYMin(), 0.01f); + assertEquals(50f, data.getYMax(), 0.01f); + + data.addEntry(new Entry(-100, 100), 0); + data.addEntry(new Entry(0, -100), 0); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + // right axis will adapt left axis values if no right axis values are present + assertEquals(-100, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List entries3 = new ArrayList(); + entries3.add(new Entry(0, 200)); + entries3.add(new Entry(0, -50)); + + ScatterDataSet set3 = new ScatterDataSet(entries3, ""); + set3.setAxisDependency(YAxis.AxisDependency.RIGHT); + + data.addDataSet(set3); + + assertEquals(3, data.getDataSetCount()); + + assertEquals(-100f, data.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(100f, data.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-50f, data.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(200f, data.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + LineData lineData = new LineData(); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(0, lineData.getDataSetCount()); + + List lineEntries1 = new ArrayList(); + lineEntries1.add(new Entry(10, 90)); + lineEntries1.add(new Entry(1000, 1000)); + + LineDataSet lineSet1 = new LineDataSet(lineEntries1, ""); + + lineData.addDataSet(lineSet1); + + assertEquals(1, lineData.getDataSetCount()); + assertEquals(2, lineSet1.getEntryCount()); + assertEquals(2, lineData.getEntryCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000f, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + List lineEntries2 = new ArrayList(); + lineEntries2.add(new Entry(-1000, 2000)); + lineEntries2.add(new Entry(2000, -3000)); + + Entry e = new Entry(-1000, 2500); + lineEntries2.add(e); + + LineDataSet lineSet2 = new LineDataSet(lineEntries2, ""); + lineSet2.setAxisDependency(YAxis.AxisDependency.RIGHT); + + lineData.addDataSet(lineSet2); + + assertEquals(2, lineData.getDataSetCount()); + assertEquals(3, lineSet2.getEntryCount()); + assertEquals(5, lineData.getEntryCount()); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2500, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2500, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeEntry(e, 1)); + + assertEquals(-1000, lineData.getXMin(), 0.01f); + assertEquals(2000, lineData.getXMax(), 0.01f); + + assertEquals(-3000, lineData.getYMin(), 0.01f); + assertEquals(2000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(-3000, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(2000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertEquals(2, lineData.getDataSetCount()); + assertTrue(lineData.removeDataSet(lineSet2)); + assertEquals(1, lineData.getDataSetCount()); + + assertEquals(10, lineData.getXMin(), 0.01f); + assertEquals(1000, lineData.getXMax(), 0.01f); + + assertEquals(90, lineData.getYMin(), 0.01f); + assertEquals(1000, lineData.getYMax(), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(1000f, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(90, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(1000, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertTrue(lineData.removeDataSet(lineSet1)); + assertEquals(0, lineData.getDataSetCount()); + + assertEquals(Float.MAX_VALUE, lineData.getXMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getXMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.LEFT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.LEFT), 0.01f); + + assertEquals(Float.MAX_VALUE, lineData.getYMin(YAxis.AxisDependency.RIGHT), 0.01f); + assertEquals(-Float.MAX_VALUE, lineData.getYMax(YAxis.AxisDependency.RIGHT), 0.01f); + + assertFalse(lineData.removeDataSet(lineSet1)); + assertFalse(lineData.removeDataSet(lineSet2)); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java new file mode 100644 index 0000000000..3be28d2a57 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/DataSetTest.java @@ -0,0 +1,238 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterDataSet; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +/** + * Created by philipp on 31/05/16. + */ +public class DataSetTest { + + @Test + public void testCalcMinMax() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21f, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(3, set.getEntryCount()); + + set.addEntry(new Entry(25, 1)); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(25f, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(4, set.getEntryCount()); + + set.removeEntry(3); + + assertEquals(10f, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(2f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + } + + @Test + public void testAddRemoveEntry() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 2)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + assertEquals(3, set.getEntryCount()); + + set.addEntryOrdered(new Entry(5, 1)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(5, set.getXMin(), 0.01f); + assertEquals(21, set.getXMax(), 0.01f); + + assertEquals(1f, set.getYMin(), 0.01f); + assertEquals(10f, set.getYMax(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(20, 50)); + + assertEquals(5, set.getEntryCount()); + + assertEquals(20, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(50, set.getEntryForIndex(3).getY(), 0.01f); + + assertTrue(set.removeEntry(3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertEquals(5, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(1, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeFirst()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + set.addEntryOrdered(new Entry(15, 3)); + + assertEquals(4, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(1).getX(), 0.01f); + assertEquals(3, set.getEntryForIndex(1).getY(), 0.01f); + + assertEquals(21, set.getEntryForIndex(3).getX(), 0.01f); + assertEquals(5, set.getEntryForIndex(3).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(3, set.getEntryCount()); + + assertEquals(15, set.getEntryForIndex(2).getX(), 0.01f); + assertEquals(2, set.getEntryForIndex(2).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(2, set.getEntryCount()); + + assertTrue(set.removeLast()); + + assertEquals(1, set.getEntryCount()); + + assertEquals(10, set.getEntryForIndex(0).getX(), 0.01f); + assertEquals(10, set.getEntryForIndex(0).getY(), 0.01f); + + assertTrue(set.removeLast()); + + assertEquals(0, set.getEntryCount()); + + assertFalse(set.removeLast()); + assertFalse(set.removeFirst()); + } + + @Test + public void testGetEntryForXValue() { + + List entries = new ArrayList(); + entries.add(new Entry(10, 10)); + entries.add(new Entry(15, 5)); + entries.add(new Entry(21, 5)); + + ScatterDataSet set = new ScatterDataSet(entries, ""); + + Entry closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(15, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(15, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(14, Float.NaN, DataSet.Rounding.DOWN); + assertEquals(10, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(17, Float.NaN, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.UP); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(21, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(21, closest.getX(), 0.01f); + assertEquals(5, closest.getY(), 0.01f); + } + + @Test + public void testGetEntryForXValueWithDuplicates() { + + // sorted list of values (by x position) + List values = new ArrayList(); + values.add(new Entry(0, 10)); + values.add(new Entry(1, 20)); + values.add(new Entry(2, 30)); + values.add(new Entry(3, 40)); + values.add(new Entry(3, 50)); // duplicate + values.add(new Entry(4, 60)); + values.add(new Entry(4, 70)); // duplicate + values.add(new Entry(5, 80)); + values.add(new Entry(6, 90)); + values.add(new Entry(7, 100)); + values.add(new Entry(8, 110)); + values.add(new Entry(8, 120)); // duplicate + + ScatterDataSet set = new ScatterDataSet(values, ""); + + Entry closest = set.getEntryForXValue(0, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(0, closest.getX(), 0.01f); + assertEquals(10, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(5.4f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4.6f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(5, closest.getX(), 0.01f); + assertEquals(80, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(7, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(7, closest.getX(), 0.01f); + assertEquals(100, closest.getY(), 0.01f); + + closest = set.getEntryForXValue(4f, Float.NaN, DataSet.Rounding.CLOSEST); + assertEquals(4, closest.getX(), 0.01f); + assertEquals(60, closest.getY(), 0.01f); + + List entries = set.getEntriesForXValue(4f); + assertEquals(2, entries.size()); + assertEquals(60, entries.get(0).getY(), 0.01f); + assertEquals(70, entries.get(1).getY(), 0.01f); + + entries = set.getEntriesForXValue(3.5f); + assertEquals(0, entries.size()); + + entries = set.getEntriesForXValue(2f); + assertEquals(1, entries.size()); + assertEquals(30, entries.get(0).getY(), 0.01f); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java new file mode 100644 index 0000000000..f1e1e0279e --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/LargeValueFormatterTest.java @@ -0,0 +1,95 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.formatter.LargeValueFormatter; + +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; + +/** + * Created by philipp on 06/06/16. + */ +public class LargeValueFormatterTest { + + @Test + public void test() { + + LargeValueFormatter formatter = new LargeValueFormatter(); + + String result = formatter.getFormattedValue(5f, null); + assertEquals("5", result); + + result = formatter.getFormattedValue(5.5f, null); + assertEquals("5.5", result); + + result = formatter.getFormattedValue(50f, null); + assertEquals("50", result); + + result = formatter.getFormattedValue(50.5f, null); + assertEquals("50.5", result); + + result = formatter.getFormattedValue(500f, null); + assertEquals("500", result); + + result = formatter.getFormattedValue(1100f, null); + assertEquals("1.1k", result); + + result = formatter.getFormattedValue(10000f, null); + assertEquals("10k", result); + + result = formatter.getFormattedValue(10500f, null); + assertEquals("10.5k", result); + + result = formatter.getFormattedValue(100000f, null); + assertEquals("100k", result); + + result = formatter.getFormattedValue(1000000f, null); + assertEquals("1m", result); + + result = formatter.getFormattedValue(1500000f, null); + assertEquals("1.5m", result); + + result = formatter.getFormattedValue(9500000f, null); + assertEquals("9.5m", result); + + result = formatter.getFormattedValue(22200000f, null); + assertEquals("22.2m", result); + + result = formatter.getFormattedValue(222000000f, null); + assertEquals("222m", result); + + result = formatter.getFormattedValue(1000000000f, null); + assertEquals("1b", result); + + result = formatter.getFormattedValue(9900000000f, null); + assertEquals("9.9b", result); + + result = formatter.getFormattedValue(99000000000f, null); + assertEquals("99b", result); + + result = formatter.getFormattedValue(99500000000f, null); + assertEquals("99.5b", result); + + result = formatter.getFormattedValue(999000000000f, null); + assertEquals("999b", result); + + result = formatter.getFormattedValue(1000000000000f, null); + assertEquals("1t", result); + + formatter.setSuffix(new String[]{"", "k", "m", "b", "t", "q"}); // quadrillion support + result = formatter.getFormattedValue(1000000000000000f, null); + assertEquals("1q", result); + + result = formatter.getFormattedValue(1100000000000000f, null); + assertEquals("1.1q", result); + + result = formatter.getFormattedValue(10000000000000000f, null); + assertEquals("10q", result); + + result = formatter.getFormattedValue(13300000000000000f, null); + assertEquals("13.3q", result); + + result = formatter.getFormattedValue(100000000000000000f, null); + assertEquals("100q", result); + } +} diff --git a/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java new file mode 100644 index 0000000000..e1dbe81be9 --- /dev/null +++ b/MPChartLib/src/test/java/com/github/mikephil/charting/test/ObjectPoolTest.java @@ -0,0 +1,240 @@ +package com.github.mikephil.charting.test; + +import com.github.mikephil.charting.utils.ObjectPool; + +import junit.framework.Assert; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by otheruser on 6/28/16. + */ +public class ObjectPoolTest { + + static class TestPoolable extends ObjectPool.Poolable{ + + private static ObjectPool pool; + + static { + pool = ObjectPool.create(4, new TestPoolable(0,0)); + } + + public int foo = 0; + public int bar = 0; + + protected ObjectPool.Poolable instantiate(){ + return new TestPoolable(0,0); + } + + private TestPoolable(int foo, int bar){ + this.foo = foo; + this.bar = bar; + } + + public static TestPoolable getInstance(int foo, int bar){ + TestPoolable result = pool.get(); + result.foo = foo; + result.bar = bar; + return result; + } + + public static void recycleInstance(TestPoolable instance){ + pool.recycle(instance); + } + + public static void recycleInstances(List instances){ + pool.recycle(instances); + } + + public static ObjectPool getPool(){ + return pool; + } + + } + + @Test + public void testObjectPool(){ + + int poolCapacity = TestPoolable.getPool().getPoolCapacity(); + int poolCount = TestPoolable.getPool().getPoolCount(); + TestPoolable testPoolable; + ArrayList testPoolables = new ArrayList<>(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolable = TestPoolable.getInstance(6,7); + Assert.assertEquals(6, testPoolable.foo); + Assert.assertEquals(7, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + + testPoolable = TestPoolable.getInstance(20,30); + Assert.assertEquals(20, testPoolable.foo); + Assert.assertEquals(30, testPoolable.bar); + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(0, poolCount); + + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(4, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(8,9)); + Assert.assertEquals(12, testPoolables.get(0).foo); + Assert.assertEquals(24, testPoolables.get(0).bar); + Assert.assertEquals(1, testPoolables.get(1).foo); + Assert.assertEquals(2, testPoolables.get(1).bar); + Assert.assertEquals(3, testPoolables.get(2).foo); + Assert.assertEquals(5, testPoolables.get(2).bar); + Assert.assertEquals(6, testPoolables.get(3).foo); + Assert.assertEquals(8, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(4, poolCapacity); + Assert.assertEquals(3, poolCount); + + TestPoolable.recycleInstances(testPoolables); + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(8, poolCapacity); + Assert.assertEquals(8, poolCount); + + testPoolables.clear(); + + + testPoolables.add(TestPoolable.getInstance(0,0)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(1,2)); + testPoolables.add(TestPoolable.getInstance(3,5)); + testPoolables.add(TestPoolable.getInstance(8,9)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(12,24)); + testPoolables.add(TestPoolable.getInstance(6,8)); + testPoolables.add(TestPoolable.getInstance(6,8)); + Assert.assertEquals(0, testPoolables.get(0).foo); + Assert.assertEquals(0, testPoolables.get(0).bar); + Assert.assertEquals(6, testPoolables.get(1).foo); + Assert.assertEquals(8, testPoolables.get(1).bar); + Assert.assertEquals(1, testPoolables.get(2).foo); + Assert.assertEquals(2, testPoolables.get(2).bar); + Assert.assertEquals(3, testPoolables.get(3).foo); + Assert.assertEquals(5, testPoolables.get(3).bar); + Assert.assertEquals(8, testPoolables.get(4).foo); + Assert.assertEquals(9, testPoolables.get(4).bar); + Assert.assertEquals(12, testPoolables.get(5).foo); + Assert.assertEquals(24, testPoolables.get(5).bar); + Assert.assertEquals(12, testPoolables.get(6).foo); + Assert.assertEquals(24, testPoolables.get(6).bar); + Assert.assertEquals(12, testPoolables.get(7).foo); + Assert.assertEquals(24, testPoolables.get(7).bar); + Assert.assertEquals(6, testPoolables.get(8).foo); + Assert.assertEquals(8, testPoolables.get(8).bar); + Assert.assertEquals(6, testPoolables.get(9).foo); + Assert.assertEquals(8, testPoolables.get(9).bar); + + for(TestPoolable p : testPoolables){ + TestPoolable.recycleInstance(p); + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + testPoolable = TestPoolable.getInstance(9001,9001); + Assert.assertEquals(9001, testPoolable.foo); + Assert.assertEquals(9001, testPoolable.bar); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(15, poolCount); + + + TestPoolable.recycleInstance(testPoolable); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(16, poolCount); + + Exception e = null; + try{ + // expect an exception. + TestPoolable.recycleInstance(testPoolable); + }catch (Exception ex){ + e = ex; + }finally{ + Assert.assertEquals(e.getMessage(), true, e != null); + } + + testPoolables.clear(); + + TestPoolable.getPool().setReplenishPercentage(0.5f); + int i = 16; + while(i > 0){ + testPoolables.add(TestPoolable.getInstance(0,0)); + i--; + } + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(0, poolCount); + + testPoolables.add(TestPoolable.getInstance(0,0)); + + poolCapacity = TestPoolable.getPool().getPoolCapacity(); + poolCount = TestPoolable.getPool().getPoolCount(); + Assert.assertEquals(16, poolCapacity); + Assert.assertEquals(7, poolCount); + + + } + +} diff --git a/README.md b/README.md index 50da145069..9a31698504 100644 --- a/README.md +++ b/README.md @@ -1,205 +1,199 @@ -[![Twitter](https://img.shields.io/badge/Twitter-@PhilippJahoda-blue.svg?style=flat)](http://twitter.com/philippjahoda) -[![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) +![banner](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic_smaller.png) + +[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?style=flat)](https://jitpack.io/#PhilJay/MPAndroidChart) +[![API](https://img.shields.io/badge/API-14%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=14) [![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-orange.svg?style=flat)](http://android-arsenal.com/details/1/741) -[![Release](https://img.shields.io/github/release/PhilJay/MPAndroidChart.svg?label=maven central)](https://jitpack.io/#PhilJay/MPAndroidChart) [![API](https://img.shields.io/badge/API-8%2B-green.svg?style=flat)](https://android-arsenal.com/api?level=8) +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) +[![Twitter](https://img.shields.io/badge/Twitter-@mpandroidchart-blue.svg?style=flat)](http://twitter.com/mpandroidchart) + +:zap: A powerful & easy to use chart library for Android :zap: + +[**Charts**](https://github.com/danielgindi/Charts) is the iOS version of this library + +## Table of Contents +1. [Quick Start](#quick-start) + 1. [Gradle](#gradle-setup) + 1. [Maven](#maven-setup) +1. [Documentation](#documentation) +1. [Examples](#examples) +1. [Questions](#report) +1. [Donate](#donate) +1. [Social Media](#social) +1. [More Examples](#more-examples) +1. [License](#licence) +1. [Creators](#creators) + +### Gradle Setup + +```gradle +repositories { + maven { url 'https://jitpack.io' } +} + +dependencies { + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' +} +``` + +### Maven Setup + +```xml + + + jitpack.io + https://jitpack.io + -Remember: *It's all about the looks.* + + + com.github.PhilJay + MPAndroidChart + v3.1.0 + +``` +
-![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) +

Documentation :notebook_with_decorative_cover:

-[**MPAndroidChart**](https://github.com/PhilJay/MPAndroidChart) :zap: is a powerful & easy to use chart library for Android. It runs on [API level 8](http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels) and upwards. +See the [**documentation**](https://weeklycoding.com/mpandroidchart/) for examples and general use of MPAndroidChart. -As an additional feature, this library allows cross-platform development between Android and iOS as an iOS version of this library is also available: [**ios-charts**](https://github.com/danielgindi/ios-charts) :zap:. +See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0/javadoc/) for more advanced documentation. -Are you using this library? Let me know about it and I will add your project to the [**references**](https://github.com/PhilJay/MPAndroidChart/wiki/References). +
-Spread the word ------ +

Examples :eyes:

-If you like this library, please tell others about it :two_hearts: +Download the [MPAndroidChart Example App](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) or look at the [source code](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample). + +[![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI) - - - +
- - []()Follow me on **Twitter**: [**@PhilippJahoda**](https://twitter.com/PhilippJahoda) - - Contact me on **LinkedIn**: [**PhilippJahoda**](https://www.linkedin.com/in/philippjahoda/en) - - Look me up on **StackOverflow**: [**Philipp Jahoda**](http://stackoverflow.com/users/1590502/philipp-jahoda) +

Questions & Issues :thinking:

-Donations ------ +This repository's issue tracker is only for bugs and feature requests. The maintainers ask that you refrain from asking questions about how to use MPAndroidChart through the issue tracker. + +Please read the [**documentation**](https://weeklycoding.com/mpandroidchart/) first, then ask all your questions on [stackoverflow.com](https://stackoverflow.com/questions/tagged/mpandroidchart) for the fastest answer. + +
+ + **This project needs you!** If you would like to support this project's further development, the creator of this project or the continuous maintenance of this project, **feel free to donate**. Your donation is highly appreciated (and I love food, coffee and beer). Thank you! -**PayPal** +**My Bitcoin Wallet** (Bitcoin only) - - [**Donate 5 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! - - [**Donate 10 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! - - [**Donate 15 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! - - [**Donate 25 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! - - [**Donate 50 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ZPQVJ2XRBSBYY): I really really want to support this project, great job! - - [**Donate 100 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=KY7F59RYPCYCQ): You are the man! This project saved me hours (if not days) of struggle and hard work, simply awesome! - - Of course, you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! +1G8G6tqQ3oh38BvDH3xq8o6gGVMvBTkcUg +**My Ethereum Wallet** (Ethereum only) -Demo ------ +0x04ef098bf9f91871391363e3caf791afa3adc39b -For a brief overview of the most important features, please download the **PlayStore Demo** [**MPAndroidChart Example.apk**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample) and try it out. The corresponding code for the demo-application is also included in this repository inside the **MPChartExample folder**. +[**Lightning Network (tippin.me)**](https://tippin.me/@PhilippJahoda) -[![ScreenShot](https://github.com/PhilJay/MPAndroidChart/blob/master/design/video_thumbnail.png)](https://www.youtube.com/watch?v=ufaK_Hd6BpI) -Questions & Issues ------ - -If you are having questions or problems, you should: - - - Make sure you are using the latest version of the library. Check the [**release-section**](https://github.com/PhilJay/MPAndroidChart/releases). - - Study the [**Documentation-Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v2.2.3/javadoc/) - - Search or open questions on [**stackoverflow**](https://stackoverflow.com/search?q=mpandroidchart) with the `mpandroidchart` tag - - Search [**known issues**](https://github.com/PhilJay/MPAndroidChart/issues) for your problem (open and closed) - - Create new issues (please :fire: **search known issues before** :fire:, do not create duplicate issues) - -Please do not expect answers to your questions if you have not considered all above mentioned points in advance. - -Features -======= - -**Core features:** - - 8 different chart types - - Scaling on both axes (with touch-gesture, axes separately or pinch-zoom) - - Dragging / Panning (with touch-gesture) - - Combined-Charts (line-, bar-, scatter-, candle-data) - - Dual (separate) Axes - - Customizeable Axes (both x- and y-axis) - - Highlighting values (with customizeable popup-views) - - Save chart to SD-Card (as image, or as .txt file) - - Predefined color templates - - Legends (generated automatically, customizeable) - - Animations (build up animations, on both x- and y-axis) - - Limit lines (providing additional information, maximums, ...) - - Fully customizeable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...) - - Smooth zooming and scrolling for up to 30.000 data points in Line- and BarChart - - Gradle support - - Plotting data directly from [**Realm.io**](https://realm.io) mobile database - -**Chart types:** - - - **LineChart (with legend, simple design)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) - - **LineChart (with legend, simple design)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) +**PayPal** - - **LineChart (cubic lines)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) +- [**Donate 5 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7G52RA87ED8NY): Thank's for creating this project, here's a coffee (or some beer) for you! +- [**Donate 10 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4C9TPE67F5PUQ): Wow, I am stunned. Let me take you to the movies! +- [**Donate 15 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YKMPTFMVK3JMC): I really appreciate your work, let's grab some lunch! +- [**Donate 25 $**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H9JA4QX7UHXCY): That's some awesome stuff you did right there, dinner is on me! +- Or you can also [**choose what you want to donate**](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EGBENAC5XBCKS), all donations are awesome! - - **LineChart (gradient fill)** -![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) +
- - **Combined-Chart (bar- and linechart in this case)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +

Social Media :fire:

- - **BarChart (with legend, simple design)** +If you like this library, please tell others about it :two_hearts: :two_hearts: -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) +[![Share on Twitter](https://github.com/PhilJay/MPAndroidChart/blob/master/design/twitter_icon.png)](https://twitter.com/intent/tweet?text=Check%20out%20the%20awesome%20MPAndroidChart%20library%20on%20Github:%20https://github.com/PhilJay/MPAndroidChart) +[![Share on Google+](https://github.com/PhilJay/MPAndroidChart/blob/master/design/googleplus_icon.png)](https://plus.google.com/share?url=https://github.com/PhilJay/MPAndroidChart) +[![Share on Facebook](https://github.com/PhilJay/MPAndroidChart/blob/master/design/facebook_icon.png)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/PhilJay/MPAndroidChart) - - **BarChart (grouped DataSets)** +If you like, you can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda). -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) +
- - **Horizontal-BarChart** +

More Examples :+1:

-![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) +
+**LineChart (with legend, simple design)** - - **PieChart (with selection, ...)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart4.png) +

-![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) +**LineChart (with legend, simple design)** - - **ScatterChart** (with squares, triangles, circles, ... and more) +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_linechart3.png) +

-![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) +**LineChart (cubic lines)** - - **CandleStickChart** (for financial data) +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) +

-![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) +**LineChart (gradient fill)** - - **BubbleChart** (area covered by bubbles indicates the value) +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/line_chart_gradient.png) +

-![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) +**BarChart (with legend, simple design)** - - **RadarChart** (spider web chart) +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) +

-![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) +**BarChart (grouped DataSets)** +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) +

+**Horizontal-BarChart** -Usage -======= +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) +

-In order to use the library, there are 4 different options: +**Combined-Chart (bar- and linechart in this case)** -**1. Gradle dependency** (recommended) +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +

- - Add the following to your `build.gradle`: - ```gradle -repositories { - maven { url "https://jitpack.io" } -} +**PieChart (with selection, ...)** -dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' -} -``` +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) +

-**2. Maven** -- Add the following to your `pom.xml`: - ```xml - - jitpack.io - https://jitpack.io - +**ScatterChart** (with squares, triangles, circles, ... and more) - - com.github.PhilJay - MPAndroidChart - v2.2.3 - -``` +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) +

-**3. jar file only** - - Download the [**latest .jar file**](https://github.com/PhilJay/MPAndroidChart/releases) from the releases section - - Copy the **mpandroidchartlibrary-version.jar** file into the `libs` folder of your Android application project - - Start using the library - -**4. clone whole repository** - - Open your **commandline-input** and navigate to the desired destination folder on your machine (where you want to place the library) - - Use the command `git clone https://github.com/PhilJay/MPAndroidChart.git` to download the full MPAndroidChart repository to your computer (this includes the folder of the library as well as the folder of the example project) - - Import the library folder (`MPChartLib`) into Android Studio (recommended) or your Eclipse workspace - - Add it as a reference to your project: - - [referencing library projects in Eclipse](http://developer.android.com/tools/projects/projects-eclipse.html#ReferencingLibraryProject) - - [managing projects from Android Studio](https://developer.android.com/sdk/installing/create-project.html) +**CandleStickChart** (for financial data) +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) +

-Documentation -======= -For a **detailed documentation** :notebook_with_decorative_cover:, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki) or the [javadocs](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v2.2.3/javadoc/). +**BubbleChart** (area covered by bubbles indicates the yValue) -Furthermore, you can also rely on the [**MPChartExample**](https://github.com/PhilJay/MPAndroidChart/tree/master/MPChartExample) folder and check out the example code in that project. The corresponding application to the example project is also [**available in the Google PlayStore**](https://play.google.com/store/apps/details?id=com.xxmassdeveloper.mpchartexample). +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/bubblechart.png) +

+ +**RadarChart** (spider web chart) + +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/radarchart.png) -You have a problem that cannot be solved by having a look at the example project and documentation? -No problem, let's talk: [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/PhilJay/MPAndroidChart?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) +
-License -======= -Copyright 2016 Philipp Jahoda +

License :page_facing_up:

+ +Copyright 2020 Philipp Jahoda 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 +> 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, @@ -207,4 +201,14 @@ 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. -**Special thanks** to [danielgindi](https://github.com/danielgindi), [mikegr](https://github.com/mikegr), [ph1lb4](https://github.com/ph1lb4) and [jitpack.io](https://github.com/jitpack-io) for their contributions to this project. +
+ +

Special Thanks :heart:

+ +These people rock! + +- [danielgindi](https://github.com/danielgindi) - Daniel Gindi +- [mikegr](https://github.com/mikegr) - Michael Greifeneder +- [tony](https://github.com/tonypatino-monoclesociety) - Tony +- [almic](https://github.com/almic) - Mick A. +- [jitpack.io](https://github.com/jitpack-io) - JitPack.io diff --git a/build.gradle b/build.gradle index 1dbeb8610c..cee7a83e80 100644 --- a/build.gradle +++ b/build.gradle @@ -1,19 +1,17 @@ -task wrapper(type: Wrapper) { - gradleVersion = '2.9' -} - buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' + classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } } allprojects { repositories { jcenter() + google() } } diff --git a/design/feature_graphic_smaller.png b/design/feature_graphic_smaller.png new file mode 100644 index 0000000000..c1e5db5799 Binary files /dev/null and b/design/feature_graphic_smaller.png differ diff --git a/design/other/bottom.png b/design/other/bottom.png new file mode 100644 index 0000000000..368f30987b Binary files /dev/null and b/design/other/bottom.png differ diff --git a/design/other/left.png b/design/other/left.png new file mode 100644 index 0000000000..03e7404c55 Binary files /dev/null and b/design/other/left.png differ diff --git a/design/other/right.png b/design/other/right.png new file mode 100644 index 0000000000..9c9ea43605 Binary files /dev/null and b/design/other/right.png differ diff --git a/design/video_thumbnail.psd b/design/video_thumbnail.psd deleted file mode 100644 index 4695da6729..0000000000 Binary files a/design/video_thumbnail.psd and /dev/null differ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..0c0632ee2f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +android.enableJetifier=true +android.useAndroidX=true +org.gradle.jvmargs=-Xmx2048M diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af18b88f2..0f8bc4e375 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Nov 19 21:53:09 CST 2015 +#Sat Apr 27 15:48:29 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/gradlew b/gradlew index 9d82f78915..f6890acbdf 100755 --- a/gradlew +++ b/gradlew @@ -12,7 +12,7 @@ DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` -# Use the maximum available, or set MAX_FD != -1 to use that value. +# Use the maximum available, or set MAX_FD != -1 to use that yValue. MAX_FD="maximum" warn ( ) { diff --git a/gradlew.bat b/gradlew.bat index aec99730b4..b411ee1fa1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -57,7 +57,7 @@ set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp -if "x%~1" == "x" goto execute +if "xPx%~1" == "xPx" goto execute set CMD_LINE_ARGS=%* goto execute diff --git a/screenshots/grouped_barchart_wiki.png b/screenshots/grouped_barchart_wiki.png new file mode 100644 index 0000000000..c4d9e66836 Binary files /dev/null and b/screenshots/grouped_barchart_wiki.png differ diff --git a/screenshots/linechart_wiki.png b/screenshots/linechart_wiki.png new file mode 100644 index 0000000000..b81f5baf02 Binary files /dev/null and b/screenshots/linechart_wiki.png differ diff --git a/screenshots/normal_barchart_wiki.png b/screenshots/normal_barchart_wiki.png new file mode 100644 index 0000000000..93479eb37d Binary files /dev/null and b/screenshots/normal_barchart_wiki.png differ diff --git a/screenshots/piechart_wiki.png b/screenshots/piechart_wiki.png new file mode 100644 index 0000000000..deaed36137 Binary files /dev/null and b/screenshots/piechart_wiki.png differ diff --git a/screenshots/simpledesign_barchart1.png b/screenshots/simpledesign_barchart1.png deleted file mode 100644 index e1c3f14155..0000000000 Binary files a/screenshots/simpledesign_barchart1.png and /dev/null differ diff --git a/settings.gradle b/settings.gradle index 6d793f8d10..32efe09de1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,7 @@ include 'MPChartLib' //include 'MPAndroidChart-Realm' include 'MPChartExample' +//include ':MPChartLib-Realm' +//project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm')