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/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/AndroidManifest.xml b/MPChartExample/AndroidManifest.xml deleted file mode 100644 index ab53a45c0a..0000000000 --- a/MPChartExample/AndroidManifest.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MPChartExample/build.gradle b/MPChartExample/build.gradle index baaf9d0c91..2d607e9991 100644 --- a/MPChartExample/build.gradle +++ b/MPChartExample/build.gradle @@ -1,57 +1,26 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 21 - buildToolsVersion '21.1.1' + compileSdkVersion 28 defaultConfig { + applicationId "com.xxmassdeveloper.mpchartexample" minSdkVersion 16 - targetSdkVersion 21 - versionCode 40 - versionName '2.1.0' - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - assets.srcDirs = ['assets'] - manifest.srcFile 'AndroidManifest.xml' - } - } + 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.1.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -repositories { - maven { url "https://jitpack.io/" } } 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:21.0.3' - //compile 'com.github.PhilJay:MPAndroidChart:v2.0.9' + 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 07fc2a4c2f..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-22 -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/menu/bar.xml b/MPChartExample/res/menu/bar.xml deleted file mode 100644 index 52eadc6458..0000000000 --- a/MPChartExample/res/menu/bar.xml +++ /dev/null @@ -1,49 +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 3020bdb786..0000000000 --- a/MPChartExample/res/menu/bubble.xml +++ /dev/null @@ -1,45 +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 972e172826..0000000000 --- a/MPChartExample/res/menu/candle.xml +++ /dev/null @@ -1,37 +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/draw.xml b/MPChartExample/res/menu/draw.xml deleted file mode 100644 index e519fd1cff..0000000000 --- a/MPChartExample/res/menu/draw.xml +++ /dev/null @@ -1,40 +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 d58bcd309e..0000000000 --- a/MPChartExample/res/menu/line.xml +++ /dev/null @@ -1,56 +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 3013da3f2c..0000000000 --- a/MPChartExample/res/menu/pie.xml +++ /dev/null @@ -1,41 +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 cbde056f4f..0000000000 --- a/MPChartExample/res/menu/radar.xml +++ /dev/null @@ -1,37 +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 a90337fd3c..0000000000 --- a/MPChartExample/res/menu/realtime.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/MPChartExample/res/menu/scatter.xml b/MPChartExample/res/menu/scatter.xml deleted file mode 100644 index 3020bdb786..0000000000 --- a/MPChartExample/res/menu/scatter.xml +++ /dev/null @@ -1,45 +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 cf6fbe9070..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/AnotherBarActivity.java +++ /dev/null @@ -1,219 +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.DataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - 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 fbf1edb7ed..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivity.java +++ /dev/null @@ -1,284 +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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.ValueFormatter; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; -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); - - // 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.setSpaceBetweenLabels(2); - - ValueFormatter custom = new MyValueFormatter(); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(8); - leftAxis.setValueFormatter(custom); - leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART); - leftAxis.setSpaceTop(15f); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(8); - rightAxis.setValueFormatter(custom); - rightAxis.setSpaceTop(15f); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.BELOW_CHART_LEFT); - l.setForm(LegendForm.SQUARE); - l.setFormSize(9f); - l.setTextSize(11f); - l.setXEntrySpace(4f); - - 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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.notifyDataSetChanged(); - 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.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - 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.setValueFormatter(new MyValueFormatter()); - 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()); - } - - 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 d994831bb4..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivityMultiDataset.java +++ /dev/null @@ -1,252 +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.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.LargeValueFormatter; -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); - - XAxis xl = mChart.getXAxis(); - xl.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - leftAxis.setValueFormatter(new LargeValueFormatter()); - leftAxis.setDrawGridLines(false); - leftAxis.setSpaceTop(25f); - - 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 (DataSet 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.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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 da02dee99b..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BarChartActivitySinus.java +++ /dev/null @@ -1,245 +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.DataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -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); - leftAxis.setStartAtZero(false); - leftAxis.setAxisMinValue(-2.5f); - leftAxis.setAxisMaxValue(2.5f); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setDrawGridLines(false); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(6); - rightAxis.setStartAtZero(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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.notifyDataSetChanged(); - 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.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - 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/BubbleChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java deleted file mode 100644 index a4b4f70154..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/BubbleChartActivity.java +++ /dev/null @@ -1,265 +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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -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); - mChart.setHighlightEnabled(true); - - // enable scaling and dragging - mChart.setDragEnabled(true); - mChart.setScaleEnabled(true); - - mChart.setMaxVisibleValueCount(200); - mChart.setPinchZoom(true); - - mChart.getAxisLeft().setStartAtZero(false); - mChart.getAxisRight().setStartAtZero(false); - - 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.setStartAtZero(false); - yl.setSpaceBottom(30f); - - 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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - 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) { - - 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 3dc3faf480..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CandleStickChartActivity.java +++ /dev/null @@ -1,212 +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.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.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); - leftAxis.setDrawGridLines(false); - leftAxis.setDrawAxisLine(false); - leftAxis.setStartAtZero(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.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.STROKE); - set1.setIncreasingColor(Color.rgb(122, 242, 84)); - set1.setIncreasingPaintStyle(Paint.Style.FILL); - - 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 3d98460374..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CombinedChartActivity.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 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.DataSet; -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.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); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setDrawGridLines(false); - - 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.setCircleSize(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.setBodySpace(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 (DataSet set : mChart.getData().getDataSets()) { - if (set instanceof LineDataSet) - set.setDrawValues(!set.isDrawValuesEnabled()); - } - - mChart.invalidate(); - break; - } - case R.id.actionToggleBarValues: { - for (DataSet 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 4714b37895..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/CubicLineChartActivity.java +++ /dev/null @@ -1,283 +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.DataSet; -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.utils.ColorTemplate; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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); - // if enabled, the chart will always start at zero on the y-axis - - // no description text - mChart.setDescription(""); - - // enable value highlighting - mChart.setHighlightEnabled(true); - - // 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.setTypeface(tf); - x.setEnabled(false); - - YAxis y = mChart.getAxisLeft(); - y.setTypeface(tf); - y.setLabelCount(5); - y.setEnabled(false); - - 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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(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.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - 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(); - - // 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(2f); - set1.setCircleSize(5f); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setColor(Color.rgb(104, 241, 175)); - set1.setFillColor(ColorTemplate.getHoloBlue()); - - // 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 314bb02cf8..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/DynamicalAddingActivity.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 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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.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) { - - LineDataSet 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.setVisibleXRange(6); - mChart.setVisibleYRange(10, AxisDependency.LEFT); -// -// // this automatically refreshes the chart (calls invalidate()) - mChart.moveViewTo(data.getXValCount()-7, 50f, AxisDependency.LEFT); - - // redraw the chart - mChart.invalidate(); - } - } - - private void removeLastEntry() { - - LineData data = mChart.getData(); - - if(data != null) { - - LineDataSet 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.setCircleSize(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.setCircleSize(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 77db08da4a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/HorizontalBarChartActivity.java +++ /dev/null @@ -1,278 +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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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.setInverted(true); - - YAxis yr = mChart.getAxisRight(); - yr.setTypeface(tf); - yr.setDrawAxisLine(true); - yr.setDrawGridLines(false); -// 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: { - for (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - 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(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/InvertedLineChartActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java deleted file mode 100644 index 7555cf7180..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java +++ /dev/null @@ -1,295 +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.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.data.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -public class InvertedLineChartActivity 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); - mChart.setDrawGridBackground(false); - - // no description text - mChart.setDescription(""); - - // enable value highlighting - mChart.setHighlightEnabled(true); - - // 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(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); - - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(false); - - XAxis xl = mChart.getXAxis(); - xl.setAvoidFirstLastClipping(true); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setInverted(true); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setEnabled(false); - - // add data - setData(25, 50); - - // // restrain the maximum scale-out factor - // mChart.setScaleMinima(3f, 3f); - // - // // center the view to a specific position inside the chart - // mChart.centerViewPort(10, 50); - - // 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 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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - 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(); - - // 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 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 - - } - - 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(); - - 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.setCircleSize(4f); - - // create a data object with the datasets - LineData data = new LineData(xVals, set1); - - // set data - mChart.setData(data); - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java deleted file mode 100644 index 43ddd89a4e..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity1.java +++ /dev/null @@ -1,407 +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.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.YAxis; -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.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.data.filter.Approximator.ApproximatorType; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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 value highlighting - mChart.setHighlightEnabled(true); - - // 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); - - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(false); - - // x-axis limit line -// LimitLine llXAxis = new LimitLine(10f, "Index 10"); -// llXAxis.setLineWidth(4f); -// llXAxis.enableDashedLine(10f, 10f, 0f); -// llXAxis.setLabelPosition(LimitLabelPosition.POS_RIGHT); -// llXAxis.setTextSize(10f); -// -// XAxis xAxis = mChart.getXAxis(); -// xAxis.addLimitLine(llXAxis); - - LimitLine ll1 = new LimitLine(130f, "Upper Limit"); - ll1.setLineWidth(4f); - ll1.enableDashedLine(10f, 10f, 0f); - ll1.setLabelPosition(LimitLabelPosition.POS_RIGHT); - ll1.setTextSize(10f); - - LimitLine ll2 = new LimitLine(-30f, "Lower Limit"); - ll2.setLineWidth(4f); - ll2.enableDashedLine(10f, 10f, 0f); - ll2.setLabelPosition(LimitLabelPosition.POS_RIGHT); - ll2.setTextSize(10f); - - 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.setStartAtZero(false); - leftAxis.enableGridDashedLine(10f, 10f, 0f); - - // limit lines are drawn behind data (and not on top) - leftAxis.setDrawLimitLinesBehindData(true); - - mChart.getAxisRight().setEnabled(false); - - // 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: { - for (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - 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.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - - // - // for(int i = 0; i < 10; i++) { - // mChart.addEntry(new Entry((float) (Math.random() * 100), - // i+2), 0); - // mChart.invalidate(); - // } - // - // Toast.makeText(getApplicationContext(), "valcount: " + - // mChart.getDataOriginal().getYValCount() + ", valsum: " + - // mChart.getDataOriginal().getYValueSum(), - // Toast.LENGTH_SHORT).show(); - // - 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.setColor(Color.BLACK); - set1.setCircleColor(Color.BLACK); - set1.setLineWidth(1f); - set1.setCircleSize(3f); - set1.setDrawCircleHole(false); - set1.setValueTextSize(9f); - set1.setFillAlpha(65); - set1.setFillColor(Color.BLACK); -// set1.setDrawFilled(true); - // set1.setShader(new LinearGradient(0, 0, 0, mChart.getHeight(), - // Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR)); - - 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 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("", "low: " + mChart.getLowestVisibleXIndex() + ", high: " + mChart.getHighestVisibleXIndex()); - } - - @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 a7fcafe51d..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/LineChartActivity2.java +++ /dev/null @@ -1,347 +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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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 value highlighting - mChart.setHighlightEnabled(true); - - // enable touch gestures - mChart.setTouchEnabled(true); - - mChart.setDragDecelerationFrictionCoef(0.95f); - - // 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); - - 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.setDrawGridLines(true); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(tf); - rightAxis.setTextColor(Color.RED); - rightAxis.setAxisMaxValue(900); - rightAxis.setStartAtZero(false); - rightAxis.setAxisMinValue(-200); - rightAxis.setDrawGridLines(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: { - for (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCubic: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCubicEnabled()) - set.setDrawCubic(false); - else - set.setDrawCubic(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(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.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - 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(); - - // 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.setCircleSize(3f); - set1.setFillAlpha(65); - set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setHighLightColor(Color.rgb(244, 117, 117)); - set1.setDrawCircleHole(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.setCircleSize(3f); - set2.setFillAlpha(65); - set2.setFillColor(Color.RED); - set2.setDrawCircleHole(false); - set2.setHighLightColor(Color.rgb(244, 117, 117)); - - 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()); - } - - @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 928ee21e09..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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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.setStartAtZero(true); - - // enable value highlighting - mChart.setHighlightEnabled(true); - - // 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: { - for (DataSet 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.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilled: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawFilledEnabled()) - set.setDrawFilled(false); - else - set.setDrawFilled(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleCircles: { - ArrayList sets = (ArrayList) mChart.getData() - .getDataSets(); - - for (LineDataSet set : sets) { - if (set.isDrawCirclesEnabled()) - set.setDrawCircles(false); - else - set.setDrawCircles(true); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - // the angle of filtering is 35° - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 35); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.setCircleSize(4f); - - int color = mColors[z % mColors.length]; - d.setColor(color); - d.setCircleColor(color); - dataSets.add(d); - } - - // make the first DataSet dashed - dataSets.get(0).enableDashedLine(10, 10, 0); - dataSets.get(0).setColors(ColorTemplate.VORDIPLOM_COLORS); - 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 ed91daf4f4..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PerformanceLineChart.java +++ /dev/null @@ -1,137 +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."); - - // disable value highlighting - mChart.setHighlightEnabled(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); - - // enable/disable highlight indicators (the lines that indicate the - // highlighted Entry) - mChart.setHighlightIndicatorEnabled(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 940e4f8590..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/PieChartActivity.java +++ /dev/null @@ -1,300 +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.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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.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.setDragDecelerationFrictionCoef(0.95f); - - tf = Typeface.createFromAsset(getAssets(), "OpenSans-Regular.ttf"); - - mChart.setCenterTextTypeface(Typeface.createFromAsset(getAssets(), "OpenSans-Light.ttf")); - - mChart.setDrawHoleEnabled(true); - mChart.setHoleColorTransparent(true); - - mChart.setTransparentCircleColor(Color.WHITE); - - mChart.setHoleRadius(58f); - mChart.setTransparentCircleRadius(61f); - - mChart.setDrawCenterText(true); - - mChart.setRotationAngle(0); - // enable rotation of the chart by touch - mChart.setRotationEnabled(true); - - // mChart.setUnit(" €"); - // mChart.setDrawUnitsInChart(true); - - // add a selection listener - mChart.setOnChartValueSelectedListener(this); - - mChart.setCenterText("MPAndroidChart\nby Philipp Jahoda"); - - setData(3, 100); - - mChart.animateY(1500, Easing.EasingOption.EaseInOutQuad); - // mChart.spin(2000, 0, 360); - - Legend l = mChart.getLegend(); - l.setPosition(LegendPosition.RIGHT_OF_CHART); - l.setXEntrySpace(7f); - l.setYEntrySpace(5f); - } - - @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 (DataSet 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(1800); - break; - } - case R.id.animateY: { - mChart.animateY(1800); - break; - } - case R.id.animateXY: { - mChart.animateXY(1800, 1800); - 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); - - 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(); - } - - @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 - - } - - // private void removeLastEntry() { - // - // PieData data = mChart.getDataOriginal(); - // - // if (data != null) { - // - // PieDataSet set = data.getDataSet(); - // - // 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 addEntry() { - // - // PieData data = mChart.getDataOriginal(); - // - // if (data != null) { - // - // PieDataSet set = data.getDataSet(); - // // set.addEntry(...); - // - // data.addEntry(new Entry((float) (Math.random() * 25) + 20f, - // set.getEntryCount()), 0); - // - // // let the chart know it's data has changed - // mChart.notifyDataSetChanged(); - // - // // redraw the chart - // mChart.invalidate(); - // } - // } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java deleted file mode 100644 index b7f07a1abc..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RadarChartActivitry.java +++ /dev/null @@ -1,202 +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.DataSet; -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.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(); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setTypeface(tf); - xAxis.setTextSize(9f); - - YAxis yAxis = mChart.getYAxis(); - yAxis.setTypeface(tf); - yAxis.setLabelCount(5); - yAxis.setTextSize(9f); - yAxis.setStartAtZero(true); - - 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 (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - 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 (RadarDataSet 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.invalidate(); - break; - } - case R.id.actionToggleYLabels: { - - mChart.getYAxis().setEnabled(!mChart.getYAxis().isEnabled()); - mChart.invalidate(); - 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.setDrawFilled(true); - set1.setLineWidth(2f); - - RadarDataSet set2 = new RadarDataSet(yVals2, "Set 2"); - set2.setColor(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 f2a03502d7..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/ScatterChartActivity.java +++ /dev/null @@ -1,250 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample; - -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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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); - mChart.setHighlightEnabled(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); - - 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: { - for (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - mChart.invalidate(); - break; - } - case R.id.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - 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() + 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.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/StackedBarActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java deleted file mode 100644 index a0be9f34d1..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/StackedBarActivity.java +++ /dev/null @@ -1,270 +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.DataSet; -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.listener.OnChartValueSelectedListener; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -import com.xxmassdeveloper.mpchartexample.custom.MyValueFormatter; -import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; - -import java.util.ArrayList; - -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); - - // if false values are only drawn for the stack sum, else each value is - // drawn - mChart.setDrawValuesForWholeStack(true); - // scaling can now only be done on x- and y-axis separately - mChart.setPinchZoom(false); - - mChart.setDrawBarShadow(false); - - mChart.setDrawValueAboveBar(false); - - // change the position of the y-labels - YAxis yLabels = mChart.getAxisLeft(); - // yLabels.setPosition(YLabelPosition.BOTH_SIDED); -// yLabels.setLabelCount(5); - yLabels.setValueFormatter(new MyValueFormatter()); - - mChart.getAxisRight().setValueFormatter(new MyValueFormatter()); - mChart.getAxisRight().setDrawGridLines(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: { - for (DataSet set : mChart.getData().getDataSets()) - set.setDrawValues(!set.isDrawValuesEnabled()); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionTogglePinch: { - if (mChart.isPinchZoomEnabled()) - mChart.setPinchZoom(false); - else - mChart.setPinchZoom(true); - - mChart.invalidate(); - break; - } - case R.id.actionToggleHighlightArrow: { - if (mChart.isDrawHighlightArrowEnabled()) - mChart.setDrawHighlightArrow(false); - else - mChart.setDrawHighlightArrow(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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.actionToggleFilter: { - - Approximator a = new Approximator(ApproximatorType.DOUGLAS_PEUCKER, 25); - - if (!mChart.isFilteringEnabled()) { - mChart.enableFiltering(a); - } else { - mChart.disableFiltering(); - } - mChart.invalidate(); - 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; - Log.i("VAL SELECTED", - "Value: " + entry.getVals()[h.getStackIndex()]); - } - - @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/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/MyValueFormatter.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java deleted file mode 100644 index c71b1ac1b8..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.xxmassdeveloper.mpchartexample.custom; - -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.text.DecimalFormat; - -public class MyValueFormatter implements ValueFormatter { - - private DecimalFormat mFormat; - - public MyValueFormatter() { - mFormat = new DecimalFormat("###,###,###,##0.0"); - } - - @Override - public String getFormattedValue(float value) { - return mFormat.format(value) + " $"; - } - -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java deleted file mode 100644 index 6968d09b1f..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java +++ /dev/null @@ -1,56 +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 ComplexityFragment extends SimpleFragment { - - public static Fragment newInstance() { - return new ComplexityFragment(); - } - - 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.setHighlightIndicatorEnabled(false); - mChart.setDrawGridBackground(false); - - mChart.setData(getComplexity()); - mChart.animateX(3000); - -// mChart.setScaleMinima(3f, 3f); -// mChart.centerViewPort(300, 0); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); - l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setEnabled(false); - - return v; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java deleted file mode 100644 index b06ab32b84..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java +++ /dev/null @@ -1,48 +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.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 { - - public static Fragment newInstance() { - return new PieChartFrag(); - } - - private PieChart mChart; - - @Override - public View onCreateView(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("Revenues"); - mChart.setCenterTextSize(22f); - mChart.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()); - - return v; - } -} diff --git a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java deleted file mode 100644 index 74253f750a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java +++ /dev/null @@ -1,58 +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.ScatterChart; -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; -import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; - - -public class ScatterChartFrag extends SimpleFragment { - - public static Fragment newInstance() { - return new ScatterChartFrag(); - } - - private ScatterChart mChart; - - @Override - public View onCreateView(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"); - - MyMarkerView mv = new MyMarkerView(getActivity(), R.layout.custom_marker_view); - - mChart.setMarkerView(mv); - - mChart.setHighlightIndicatorEnabled(false); -// mChart.setBorderStyles(new BorderStyle[] { BorderStyle.LEFT }); - mChart.setDrawGridBackground(false); - mChart.setData(generateScatterData(2, 10000, 400)); - - XAxis xAxis = mChart.getXAxis(); - xAxis.setEnabled(false); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - - YAxis rightAxis = mChart.getAxisRight(); - rightAxis.setTypeface(tf); - rightAxis.setDrawGridLines(false); - - Legend l = mChart.getLegend(); - l.setTypeface(tf); - - return v; - } -} 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 35f459eff4..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SineCosineFragment.java +++ /dev/null @@ -1,60 +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.setCircleSize(5f); - - mChart.setHighlightIndicatorEnabled(false); - mChart.setDrawGridBackground(false); - - mChart.setData(generateLineData()); - mChart.animateX(3000); - -// mChart.setScaleMinima(3f, 3f); -// mChart.centerViewPort(300, 0); - - Typeface tf = Typeface.createFromAsset(getActivity().getAssets(),"OpenSans-Light.ttf"); - - Legend l = mChart.getLegend(); - l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); - leftAxis.setTypeface(tf); - leftAxis.setStartAtZero(false); - 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/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/MainActivity.java b/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java deleted file mode 100644 index b800acce7a..0000000000 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java +++ /dev/null @@ -1,336 +0,0 @@ - -package com.xxmassdeveloper.mpchartexample.notimportant; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -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.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import com.github.mikephil.charting.utils.Utils; -import com.xxmassdeveloper.mpchartexample.AnotherBarActivity; -import com.xxmassdeveloper.mpchartexample.BarChartActivity; -import com.xxmassdeveloper.mpchartexample.BarChartActivityMultiDataset; -import com.xxmassdeveloper.mpchartexample.BarChartActivitySinus; -import com.xxmassdeveloper.mpchartexample.BubbleChartActivity; -import com.xxmassdeveloper.mpchartexample.CandleStickChartActivity; -import com.xxmassdeveloper.mpchartexample.CombinedChartActivity; -import com.xxmassdeveloper.mpchartexample.CubicLineChartActivity; -import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity; -import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity; -import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity; -import com.xxmassdeveloper.mpchartexample.LineChartActivity1; -import com.xxmassdeveloper.mpchartexample.LineChartActivity2; -import com.xxmassdeveloper.mpchartexample.LineChartActivityColored; -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.R; -import com.xxmassdeveloper.mpchartexample.RadarChartActivitry; -import com.xxmassdeveloper.mpchartexample.RealtimeLineChartActivity; -import com.xxmassdeveloper.mpchartexample.ScatterChartActivity; -import com.xxmassdeveloper.mpchartexample.StackedBarActivity; -import com.xxmassdeveloper.mpchartexample.fragments.SimpleChartDemo; - -import java.util.ArrayList; -import java.util.List; - -public class MainActivity extends Activity implements 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); - - // 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("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("Draw Chart", - "Demonstration of drawing values into the chart per touch-gesture. With callbacks.")); - 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.")); - - 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, LineChartActivity1.class); - startActivity(i); - break; - case 1: - i = new Intent(this, LineChartActivity2.class); - startActivity(i); - break; - case 2: - i = new Intent(this, BarChartActivity.class); - startActivity(i); - break; - case 3: - i = new Intent(this, HorizontalBarChartActivity.class); - startActivity(i); - break; - case 4: - i = new Intent(this, CombinedChartActivity.class); - startActivity(i); - break; - case 5: - i = new Intent(this, PieChartActivity.class); - startActivity(i); - break; - case 6: - i = new Intent(this, ScatterChartActivity.class); - startActivity(i); - break; - case 7: - i = new Intent(this, BubbleChartActivity.class); - startActivity(i); - break; - case 8: - i = new Intent(this, StackedBarActivity.class); - startActivity(i); - break; - case 9: - i = new Intent(this, AnotherBarActivity.class); - startActivity(i); - break; - case 10: - i = new Intent(this, MultiLineChartActivity.class); - startActivity(i); - break; - case 11: - i = new Intent(this, BarChartActivityMultiDataset.class); - startActivity(i); - break; - case 12: - // i = new Intent(this, DrawChartActivity.class); - // startActivity(i); - - AlertDialog.Builder b = new AlertDialog.Builder(this); - b.setTitle("Feature not available"); - b.setMessage("Due to recent changes to the data model of the library, this feature is temporarily not available."); - b.setPositiveButton("OK", null); - b.create().show(); - break; - case 13: - i = new Intent(this, SimpleChartDemo.class); - startActivity(i); - break; - case 14: - i = new Intent(this, ListViewBarChartActivity.class); - startActivity(i); - break; - case 15: - i = new Intent(this, ListViewMultiChartActivity.class); - startActivity(i); - break; - case 16: - i = new Intent(this, InvertedLineChartActivity.class); - startActivity(i); - break; - case 17: - i = new Intent(this, CandleStickChartActivity.class); - startActivity(i); - break; - case 18: - i = new Intent(this, CubicLineChartActivity.class); - startActivity(i); - break; - case 19: - i = new Intent(this, RadarChartActivitry.class); - startActivity(i); - break; - case 20: - i = new Intent(this, LineChartActivityColored.class); - startActivity(i); - break; - case 21: - i = new Intent(this, RealtimeLineChartActivity.class); - startActivity(i); - break; - case 22: - i = new Intent(this, DynamicalAddingActivity.class); - startActivity(i); - break; - case 23: - i = new Intent(this, PerformanceLineChart.class); - startActivity(i); - break; - case 24: - i = new Intent(this, BarChartActivitySinus.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.main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - Intent i = null; - - switch (item.getItemId()) { - case R.id.viewGithub: - i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart")); - startActivity(i); - break; - case R.id.report: - i = new Intent(Intent.ACTION_SENDTO, Uri.fromParts( - "mailto", "philjay.librarysup@gmail.com", null)); - i.putExtra(Intent.EXTRA_SUBJECT, "MPAndroidChart Issue"); - 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")); - startActivity(i); - break; - } - - return true; - } - - private class ContentItem { - String name; - String desc; - - public ContentItem(String n, String d) { - name = n; - desc = d; - } - } - - private class MyAdapter extends ArrayAdapter { - - public MyAdapter(Context context, List objects) { - super(context, 0, objects); - } - - @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); - - convertView.setTag(holder); - - } else { - holder = (ViewHolder) convertView.getTag(); - } - - holder.tvName.setText(c.name); - holder.tvDesc.setText(c.desc); - - return convertView; - } - - private class ViewHolder { - - TextView tvName, tvDesc; - } - } -} 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 53% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/DrawChartActivity.java index 021eb60ca5..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,23 +18,25 @@ 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.utils.Highlight; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; import java.util.ArrayList; +import java.util.List; /** * 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) { @@ -41,64 +45,49 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.activity_draw_chart); - mChart = (LineChart) findViewById(R.id.chart1); - - // listener for selecting and drawing - mChart.setOnChartValueSelectedListener(this); - mChart.setOnDrawListener(this); + setTitle("DrawChartActivity"); - // enable drawing with the finger - // mChart.setDrawingEnabled(true); + chart = findViewById(R.id.chart1); - // mChart.setLineWidth(5f); - // mChart.setCircleSize(5f); - - mChart.setHighlightEnabled(true); + // listener for selecting and drawing + 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.setCircleSize(5f); + 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 @@ -112,38 +101,45 @@ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.actionToggleValues: { - for (DataSet set : mChart.getData().getDataSets()) + List sets = chart.getData() + .getDataSets(); + + for (ILineDataSet iSet : sets) { + + LineDataSet set = (LineDataSet) iSet; set.setDrawValues(!set.isDrawValuesEnabled()); + } - mChart.invalidate(); + chart.invalidate(); break; } case R.id.actionToggleHighlight: { - if (mChart.isHighlightEnabled()) - mChart.setHighlightEnabled(false); - else - mChart.setHighlightEnabled(true); - mChart.invalidate(); - break; - } - case R.id.actionToggleStartzero: { - mChart.getAxisLeft().setStartAtZero(!mChart.getAxisLeft().isStartAtZeroEnabled()); - mChart.getAxisRight().setStartAtZero(!mChart.getAxisRight().isStartAtZeroEnabled()); - 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: { + 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; } } @@ -151,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 @@ -173,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/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java new file mode 100644 index 0000000000..08983710f2 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/InvertedLineChartActivity.java @@ -0,0 +1,283 @@ + +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.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.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.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 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("InvertedLineChartActivity"); + + 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.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(true); + + // set an alternative background color + // 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 + + XAxis xl = chart.getXAxis(); + xl.setAvoidFirstLastClipping(true); + xl.setAxisMinimum(0f); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setInverted(true); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setEnabled(false); + + // add data + seekBarX.setProgress(25); + seekBarY.setProgress(50); + + // // restrain the maximum scale-out factor + // chart.setScaleMinima(3f, 3f); + // + // // center the view to a specific position inside the chart + // chart.centerViewPort(10, 50); + + // get the legend (only possible after setting data) + Legend l = chart.getLegend(); + + // modify the legend ... + l.setForm(LegendForm.LINE); + + // 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 + 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/InvertedLineChartActivity.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.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.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; + } + } + 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, "InvertedLineChartActivity"); + } + + @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/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 c66ba99b40..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; @@ -15,10 +19,10 @@ 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) { @@ -27,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); @@ -72,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); @@ -84,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); @@ -91,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.setCircleSize(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 58% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewBarChartActivity.java index f2a66d1942..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; @@ -20,6 +24,7 @@ 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.ColorTemplate; import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase; @@ -28,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 { @@ -40,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++) { @@ -56,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) { @@ -77,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); @@ -86,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.setLabelCount(5); + leftAxis.setTypeface(tfLight); + leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(15f); - + YAxis rightAxis = holder.chart.getAxisRight(); - rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + 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; } @@ -124,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 58% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/ListViewMultiChartActivity.java index 4a47000f90..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,8 @@ 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; import com.xxmassdeveloper.mpchartexample.listviewitems.ChartItem; @@ -30,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 { @@ -42,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())); } } @@ -65,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.setCircleSize(4.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.setCircleSize(4.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 51% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java index 1cb994d822..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,15 +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.utils.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) { @@ -36,123 +41,68 @@ 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"); - // no description text - mChart.setDescription(""); - mChart.setNoDataTextDescription("You need to provide data for the chart."); + chart = findViewById(R.id.chart1); + chart.setOnChartValueSelectedListener(this); - // enable value highlighting - mChart.setHighlightEnabled(true); + // 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(false); + 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.setStartAtZero(false); + leftAxis.setAxisMaximum(100f); + leftAxis.setAxisMinimum(0f); leftAxis.setDrawGridLines(true); - YAxis rightAxis = mChart.getAxisRight(); + YAxis rightAxis = chart.getAxisRight(); rightAxis.setEnabled(false); - -// new Thread(new Runnable() { -// -// @Override -// public void run() { -// for(int i = 0; i < 5000; i++) { -// -// runOnUiThread(new Runnable() { -// -// @Override -// public void run() { -// addEntry(); -// } -// }); -// -// try { -// Thread.sleep(35); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// } -// } -// }).start(); - } - @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; - } - } - return true; - } - - private int year = 2015; - private void addEntry() { - LineData data = mChart.getData(); + LineData data = chart.getData(); if (data != null) { - LineDataSet set = data.getDataSetByIndex(0); + ILineDataSet set = data.getDataSetByIndex(0); // set.addEntry(...); // can be called as well if (set == null) { @@ -160,27 +110,22 @@ 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.setVisibleXRange(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); - - // redraw the chart - // mChart.invalidate(); } } @@ -191,7 +136,7 @@ private LineDataSet createSet() { set.setColor(ColorTemplate.getHoloBlue()); set.setCircleColor(Color.WHITE); set.setLineWidth(2f); - set.setCircleSize(4f); + set.setCircleRadius(4f); set.setFillAlpha(65); set.setFillColor(ColorTemplate.getHoloBlue()); set.setHighLightColor(Color.rgb(244, 117, 117)); @@ -201,8 +146,90 @@ private LineDataSet createSet() { return set; } + private Thread thread; + + private void feedMultiple() { + + if (thread != null) + thread.interrupt(); + + final Runnable runnable = new Runnable() { + + @Override + public void run() { + addEntry(); + } + }; + + thread = new Thread(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < 1000; i++) { + + // Don't generate garbage runnables inside the loop. + runOnUiThread(runnable); + + try { + Thread.sleep(25); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }); + + thread.start(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.realtime, menu); + return true; + } + @Override - public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { + 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()); } @@ -210,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/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java new file mode 100644 index 0000000000..2ca87b2f0f --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyFillFormatter.java @@ -0,0 +1,25 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +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. + */ +@SuppressWarnings("unused") +public class MyFillFormatter implements IFillFormatter +{ + + private float fillPos; + + public MyFillFormatter(float fillPos) { + this.fillPos = fillPos; + } + + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + // your logic could be here + 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 53% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java index 0f21a17df9..2c1da9b4e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyMarkerView.java @@ -1,55 +1,54 @@ 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.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, int dataSetIndex) { + public void refreshContent(Entry e, Highlight highlight) { if (e instanceof CandleEntry) { 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() { - // this will center the marker-view horizontally - return -(getWidth() / 2); + super.refreshContent(e, highlight); } @Override - public int getYOffset() { - // 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/MyValueFormatter.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java new file mode 100644 index 0000000000..ec1c119818 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/MyValueFormatter.java @@ -0,0 +1,22 @@ +package com.xxmassdeveloper.mpchartexample.custom; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +public class MyValueFormatter implements IValueFormatter +{ + + private final DecimalFormat mFormat; + + public MyValueFormatter() { + mFormat = new DecimalFormat("###,###,###,##0.0"); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value) + " $"; + } +} 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/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java new file mode 100644 index 0000000000..554ef2fe27 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/custom/StackedBarsMarkerView.java @@ -0,0 +1,61 @@ + +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.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; + + public StackedBarsMarkerView(Context context, int layoutResource) { + super(context, layoutResource); + + tvContent = findViewById(R.id.tvContent); + } + + // runs every time the MarkerView is redrawn, can be used to update the + // content (user-interface) + @Override + public void refreshContent(Entry e, Highlight highlight) { + + if (e instanceof BarEntry) { + + BarEntry be = (BarEntry) e; + + if(be.getYVals() != null) { + + // draw the stack value + tvContent.setText(Utils.formatNumber(be.getYVals()[highlight.getStackIndex()], 0, true)); + } else { + tvContent.setText(Utils.formatNumber(be.getY(), 0, true)); + } + } else { + + tvContent.setText(Utils.formatNumber(e.getY(), 0, true)); + } + + 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/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 54% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/BarChartFrag.java index e1c5aa4309..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; @@ -13,6 +14,7 @@ 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.listener.ChartTouchListener; import com.github.mikephil.charting.listener.OnChartGestureListener; import com.xxmassdeveloper.mpchartexample.R; import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; @@ -20,55 +22,66 @@ 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); - - mChart.setHighlightIndicatorEnabled(false); - - 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(); + chart.setData(generateBarData(1, 20000, 12)); + + Legend l = chart.getLegend(); l.setTypeface(tf); - - YAxis leftAxis = mChart.getAxisLeft(); + + YAxis leftAxis = chart.getAxisLeft(); leftAxis.setTypeface(tf); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) - mChart.getAxisRight().setEnabled(false); - - XAxis xAxis = mChart.getXAxis(); + chart.getAxisRight().setEnabled(false); + + 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; } + @Override + public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "START"); + } + + @Override + public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) { + Log.i("Gesture", "END"); + chart.highlightValues(null); + } + @Override public void onChartLongPressed(MotionEvent me) { - Log.i("LongPress", "Chart longpressed."); + Log.i("LongPress", "Chart long pressed."); } @Override @@ -83,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/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java new file mode 100644 index 0000000000..9edee8bdfb --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ComplexityFragment.java @@ -0,0 +1,55 @@ +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 ComplexityFragment extends SimpleFragment { + + @NonNull + public static Fragment newInstance() { + return new ComplexityFragment(); + } + + @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(getComplexity()); + 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); + + chart.getAxisRight().setEnabled(false); + + XAxis xAxis = chart.getXAxis(); + xAxis.setEnabled(false); + + return v; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java new file mode 100644 index 0000000000..5de9a46ea3 --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/PieChartFrag.java @@ -0,0 +1,64 @@ +package com.xxmassdeveloper.mpchartexample.fragments; +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.Legend; +import com.xxmassdeveloper.mpchartexample.R; + + +public class PieChartFrag extends SimpleFragment { + + @NonNull + public static Fragment newInstance() { + return new PieChartFrag(); + } + + @SuppressWarnings("FieldCanBeLocal") + private PieChart chart; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.frag_simple_pie, container, false); + + 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 + 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; + } + + private SpannableString generateCenterText() { + SpannableString s = new SpannableString("Revenues\nQuarters 2015"); + s.setSpan(new RelativeSizeSpan(2f), 0, 8, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 8, s.length(), 0); + return s; + } +} diff --git a/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java new file mode 100644 index 0000000000..d5d292bf0b --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/ScatterChartFrag.java @@ -0,0 +1,69 @@ +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.ScatterChart; +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.xxmassdeveloper.mpchartexample.R; +import com.xxmassdeveloper.mpchartexample.custom.MyMarkerView; + + +public class ScatterChartFrag extends SimpleFragment { + + @NonNull + public static Fragment newInstance() { + return new ScatterChartFrag(); + } + + @SuppressWarnings("FieldCanBeLocal") + private ScatterChart chart; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.frag_simple_scatter, container, false); + + 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); + + chart.setDrawGridBackground(false); + chart.setData(generateScatterData(6, 10000, 200)); + + XAxis xAxis = chart.getXAxis(); + xAxis.setEnabled(true); + xAxis.setPosition(XAxisPosition.BOTTOM); + + YAxis leftAxis = chart.getAxisLeft(); + leftAxis.setTypeface(tf); + + YAxis rightAxis = chart.getAxisRight(); + rightAxis.setTypeface(tf); + rightAxis.setDrawGridLines(false); + + 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); + 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 50% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java index ce9a49b719..ab70041e60 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/fragments/SimpleFragment.java @@ -1,83 +1,93 @@ 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; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.FileUtils; 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]); @@ -85,126 +95,104 @@ 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 2014"); + + 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.setCircleSize(3f); + ds1.setCircleRadius(3f); ds2.setLineWidth(2.5f); - ds2.setCircleSize(3f); + ds2.setCircleRadius(3f); ds3.setLineWidth(2.5f); - ds3.setCircleSize(3f); + ds3.setCircleRadius(3f); ds4.setLineWidth(2.5f); - ds4.setCircleSize(3f); - - - // load DataSets from textfiles in assets folders - sets.add(ds1); + ds4.setCircleRadius(3f); + + + // 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 80% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/BarChartItem.java index d886c9d485..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,29 +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); + leftAxis.setLabelCount(5, false); leftAxis.setSpaceTop(20f); - + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + rightAxis.setLabelCount(5, false); rightAxis.setSpaceTop(20f); + 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 81% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/LineChartItem.java index 16346251dd..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(); @@ -61,12 +63,14 @@ public View getView(int position, View convertView, Context c) { YAxis leftAxis = holder.chart.getAxisLeft(); leftAxis.setTypeface(mTf); - leftAxis.setLabelCount(5); - + leftAxis.setLabelCount(5, false); + leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true) + YAxis rightAxis = holder.chart.getAxisRight(); rightAxis.setTypeface(mTf); - rightAxis.setLabelCount(5); + rightAxis.setLabelCount(5, false); rightAxis.setDrawGridLines(false); + 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 53% rename from MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java rename to MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java index 5578c146d4..916f8dc5e9 100644 --- a/MPChartExample/src/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/listviewitems/PieChartItem.java @@ -1,28 +1,34 @@ package com.xxmassdeveloper.mpchartexample.listviewitems; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; import android.view.LayoutInflater; import android.view.View; 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.utils.PercentFormatter; +import com.github.mikephil.charting.formatter.PercentFormatter; +import com.github.mikephil.charting.utils.ColorTemplate; import com.xxmassdeveloper.mpchartexample.R; public class PieChartItem extends ChartItem { - private Typeface mTf; + private final Typeface mTf; + private final SpannableString mCenterText; public PieChartItem(ChartData cd, Context c) { super(cd); mTf = Typeface.createFromAsset(c.getAssets(), "OpenSans-Regular.ttf"); + mCenterText = generateCenterText(); } @Override @@ -30,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) { @@ -41,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); @@ -50,13 +57,14 @@ 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("MPChart\nAndroid"); + holder.chart.setCenterText(mCenterText); holder.chart.setCenterTextTypeface(mTf); - holder.chart.setCenterTextSize(18f); + holder.chart.setCenterTextSize(9f); holder.chart.setUsePercentValues(true); + holder.chart.setExtraOffsets(5, 10, 50, 10); mChartData.setValueFormatter(new PercentFormatter()); mChartData.setValueTypeface(mTf); @@ -66,15 +74,31 @@ 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); // do not forget to refresh the chart // holder.chart.invalidate(); - holder.chart.animateXY(900, 900); + holder.chart.animateY(900); return convertView; } + private SpannableString generateCenterText() { + SpannableString s = new SpannableString("MPAndroidChart\ncreated by\nPhilipp Jahoda"); + s.setSpan(new RelativeSizeSpan(1.6f), 0, 14, 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.VORDIPLOM_COLORS[0]), 0, 14, 0); + s.setSpan(new RelativeSizeSpan(.9f), 14, 25, 0); + s.setSpan(new ForegroundColorSpan(Color.GRAY), 14, 25, 0); + s.setSpan(new RelativeSizeSpan(1.4f), 25, s.length(), 0); + s.setSpan(new ForegroundColorSpan(ColorTemplate.getHoloBlue()), 25, s.length(), 0); + return s; + } + private static class ViewHolder { PieChart chart; } 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/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java new file mode 100644 index 0000000000..88e5dc8d8b --- /dev/null +++ b/MPChartExample/src/main/java/com/xxmassdeveloper/mpchartexample/notimportant/MainActivity.java @@ -0,0 +1,280 @@ + +package com.xxmassdeveloper.mpchartexample.notimportant; + +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.AdapterView.OnItemClickListener; +import android.widget.ListView; + +import com.github.mikephil.charting.utils.Utils; +import com.xxmassdeveloper.mpchartexample.AnotherBarActivity; +import com.xxmassdeveloper.mpchartexample.BarChartActivity; +import com.xxmassdeveloper.mpchartexample.BarChartActivityMultiDataset; +import com.xxmassdeveloper.mpchartexample.BarChartActivitySinus; +import com.xxmassdeveloper.mpchartexample.BarChartPositiveNegative; +import com.xxmassdeveloper.mpchartexample.BubbleChartActivity; +import com.xxmassdeveloper.mpchartexample.CandleStickChartActivity; +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.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 java.util.ArrayList; + +import androidx.appcompat.app.AppCompatActivity; + +public class MainActivity extends AppCompatActivity implements 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("MPAndroidChart Example"); + + // initialize the utilities + Utils.init(this); + + 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 = findViewById(R.id.listView1); + lv.setAdapter(adapter); + + lv.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView av, View v, int pos, long arg3) { + + Intent i = null; + + switch (pos) { + case 1: + i = new Intent(this, LineChartActivity1.class); + break; + case 2: + i = new Intent(this, MultiLineChartActivity.class); + break; + case 3: + i = new Intent(this, LineChartActivity2.class); + break; + case 4: + i = new Intent(this, InvertedLineChartActivity.class); + break; + case 5: + i = new Intent(this, CubicLineChartActivity.class); + break; + case 6: + i = new Intent(this, LineChartActivityColored.class); + break; + case 7: + i = new Intent(this, PerformanceLineChart.class); + break; + case 8: + i = new Intent(this, FilledLineActivity.class); + break; + case 10: + i = new Intent(this, BarChartActivity.class); + break; + case 11: + i = new Intent(this, AnotherBarActivity.class); + break; + case 12: + i = new Intent(this, BarChartActivityMultiDataset.class); + break; + case 13: + i = new Intent(this, HorizontalBarChartActivity.class); + break; + case 14: + i = new Intent(this, StackedBarActivity.class); + break; + case 15: + i = new Intent(this, BarChartPositiveNegative.class); + break; + case 16: + i = new Intent(this, HorizontalBarNegativeChartActivity.class); + break; + case 17: + i = new Intent(this, StackedBarActivityNegative.class); + break; + case 18: + i = new Intent(this, BarChartActivitySinus.class); + break; + case 20: + i = new Intent(this, PieChartActivity.class); + break; + case 21: + i = new Intent(this, PiePolylineChartActivity.class); + break; + case 22: + i = new Intent(this, HalfPieChartActivity.class); + break; + case 24: + i = new Intent(this, CombinedChartActivity.class); + break; + case 25: + i = new Intent(this, ScatterChartActivity.class); + break; + case 26: + i = new Intent(this, BubbleChartActivity.class); + break; + case 27: + 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); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + Intent i; + + switch (item.getItemId()) { + case R.id.viewGithub: + i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("https://github.com/PhilJay/MPAndroidChart")); + startActivity(i); + break; + case R.id.report: + i = new Intent(Intent.ACTION_SENDTO, Uri.fromParts( + "mailto", "philjay.librarysup@gmail.com", null)); + i.putExtra(Intent.EXTRA_SUBJECT, "MPAndroidChart Issue"); + i.putExtra(Intent.EXTRA_TEXT, "Your error report here..."); + startActivity(Intent.createChooser(i, "Report Problem")); + break; + case R.id.website: + i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse("http://at.linkedin.com/in/philippjahoda")); + startActivity(i); + break; + } + + return true; + } +} 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/src/main/res/drawable/fade_red.xml b/MPChartExample/src/main/res/drawable/fade_red.xml new file mode 100644 index 0000000000..54ac10ba03 --- /dev/null +++ b/MPChartExample/src/main/res/drawable/fade_red.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/MPChartExample/src/main/res/layout/activity_age_distribution.xml b/MPChartExample/src/main/res/layout/activity_age_distribution.xml new file mode 100644 index 0000000000..574510fa0b --- /dev/null +++ b/MPChartExample/src/main/res/layout/activity_age_distribution.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/MPChartExample/res/layout/activity_awesomedesign.xml b/MPChartExample/src/main/res/layout/activity_awesomedesign.xml similarity index 82% rename from MPChartExample/res/layout/activity_awesomedesign.xml rename to MPChartExample/src/main/res/layout/activity_awesomedesign.xml index 16b293b149..647f0252f2 100644 --- a/MPChartExample/res/layout/activity_awesomedesign.xml +++ b/MPChartExample/src/main/res/layout/activity_awesomedesign.xml @@ -2,9 +2,9 @@ - - + + + android:layout_height="match_parent"> - + + android:textAppearance="?android:attr/textAppearanceSmall" /> + android:textAppearance="?android:attr/textAppearanceSmall" /> diff --git a/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml b/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml new file mode 100644 index 0000000000..90a8c0f521 --- /dev/null +++ b/MPChartExample/src/main/res/layout/activity_barchart_noseekbar.xml @@ -0,0 +1,11 @@ + + + + + + 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"> - + + + + + + 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"> - + + + + + + + + + + + + + + + + + + + + + + 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 78% rename from MPChartExample/res/layout/frag_simple_line.xml rename to MPChartExample/src/main/res/layout/frag_simple_line.xml index ef1b69a35d..7a2757476b 100644 --- a/MPChartExample/res/layout/frag_simple_line.xml +++ b/MPChartExample/src/main/res/layout/frag_simple_line.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/frag_simple_pie.xml b/MPChartExample/src/main/res/layout/frag_simple_pie.xml similarity index 78% rename from MPChartExample/res/layout/frag_simple_pie.xml rename to MPChartExample/src/main/res/layout/frag_simple_pie.xml index ef63ff3382..ed490ddbe6 100644 --- a/MPChartExample/res/layout/frag_simple_pie.xml +++ b/MPChartExample/src/main/res/layout/frag_simple_pie.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/frag_simple_scatter.xml b/MPChartExample/src/main/res/layout/frag_simple_scatter.xml similarity index 78% rename from MPChartExample/res/layout/frag_simple_scatter.xml rename to MPChartExample/src/main/res/layout/frag_simple_scatter.xml index 53c3391526..2e42332db3 100644 --- a/MPChartExample/res/layout/frag_simple_scatter.xml +++ b/MPChartExample/src/main/res/layout/frag_simple_scatter.xml @@ -6,7 +6,7 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/MPChartExample/res/layout/list_item.xml b/MPChartExample/src/main/res/layout/list_item.xml similarity index 84% rename from MPChartExample/res/layout/list_item.xml rename to MPChartExample/src/main/res/layout/list_item.xml index eb95ab8fc0..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/src/main/res/menu/bubble.xml b/MPChartExample/src/main/res/menu/bubble.xml new file mode 100644 index 0000000000..7b9ab5cd11 --- /dev/null +++ b/MPChartExample/src/main/res/menu/bubble.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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/src/main/res/menu/draw.xml b/MPChartExample/src/main/res/menu/draw.xml new file mode 100644 index 0000000000..36383db54f --- /dev/null +++ b/MPChartExample/src/main/res/menu/draw.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + 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/src/main/res/menu/only_github.xml b/MPChartExample/src/main/res/menu/only_github.xml new file mode 100644 index 0000000000..c0a9b66934 --- /dev/null +++ b/MPChartExample/src/main/res/menu/only_github.xml @@ -0,0 +1,7 @@ + + + + + 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/src/main/res/menu/scatter.xml b/MPChartExample/src/main/res/menu/scatter.xml new file mode 100644 index 0000000000..eb8e0efa67 --- /dev/null +++ b/MPChartExample/src/main/res/menu/scatter.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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/build.gradle b/MPChartLib/build.gradle index 2b10d4db7c..16d3e3ce10 100644 --- a/MPChartLib/build.gradle +++ b/MPChartLib/build.gradle @@ -1,24 +1,15 @@ apply plugin: 'com.android.library' -apply plugin: 'android-maven' + +group='com.github.philjay' android { - compileSdkVersion 21 - buildToolsVersion '21.1.1' - // resourcePrefix 'mpcht' + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { - minSdkVersion 8 - targetSdkVersion 21 - 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 { @@ -26,23 +17,14 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - lintOptions { - abortOnError false + testOptions { + unitTests.returnDefaultValues = true // this prevents "not mocked" error } } dependencies { - //compile fileTree(dir: 'libs', include: ['*.jar']) - //compile 'com.android.support:support-v4:19.+' - //compile 'com.nineoldandroids:library:2.4.+' -} - -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) { @@ -50,6 +32,19 @@ task sourcesJar(type: Jar) { classifier = 'sources' } +task javadoc(type: Javadoc) { + options.charSet = 'UTF-8' + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + artifacts { archives sourcesJar + archives javadocJar } 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 362a0a3082..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-22 -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 54202984d0..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/animation/EasingFunction.java +++ /dev/null @@ -1,16 +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 - public float getInterpolation(float input); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java deleted file mode 100644 index da20fce995..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/BarBuffer.java +++ /dev/null @@ -1,152 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.BarEntry; - -import java.util.List; - -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) { - super(size); - this.mGroupSpace = groupspace; - this.mDataSetCount = dataSetCount; - this.mContainsStacks = containsStacks; - } - - public void setBarSpace(float barspace) { - this.mBarSpace = barspace; - } - - public void setDataSet(int index) { - this.mDataSetIndex = index; - } - - public void setInverted(boolean inverted) { - this.mInverted = inverted; - } - - protected void addBar(float left, float top, float right, float bottom) { - - buffer[index++] = left; - buffer[index++] = top; - buffer[index++] = right; - buffer[index++] = bottom; - } - - @Override - public void feed(List entries) { - - float size = entries.size() * phaseX; - - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; - - for (int i = 0; i < size; i++) { - - BarEntry e = entries.get(i); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + i * dataSetOffset + mDataSetIndex - + mGroupSpace * i + groupSpaceHalf; - float y = e.getVal(); - float [] vals = e.getVals(); - - if(mInverted) { // inverted axis, here, I chose performance over readability - - if(!mContainsStacks || vals == null) { - - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; - float bottom = y >= 0 ? y : 0; - float top = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (top > 0) - top *= phaseY; - else - bottom *= phaseY; - - addBar(left, top, right, bottom); - - } else { - - float all = e.getVal(); - - // fill the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - y = vals[k] + all; - - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; - float bottom = y >= 0 ? y : 0; - float top = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (top > 0) - top *= phaseY; - else - bottom *= phaseY; - - addBar(left, top, right, bottom); - } - } - - } else { // non inverted axis - - if(!mContainsStacks || vals == null) { - - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (top > 0) - top *= phaseY; - else - bottom *= phaseY; - - addBar(left, top, right, bottom); - - } else { - - float all = e.getVal(); - - // fill the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - y = vals[k] + all; - - float left = x - barWidth + barSpaceHalf; - float right = x + barWidth - barSpaceHalf; - float top = y >= 0 ? y : 0; - float bottom = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (top > 0) - top *= phaseY; - else - bottom *= phaseY; - - addBar(left, top, right, bottom); - } - } - } - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/CandleBodyBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/CandleBodyBuffer.java deleted file mode 100644 index 46164a0add..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/CandleBodyBuffer.java +++ /dev/null @@ -1,41 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.CandleEntry; - -import java.util.List; - -public class CandleBodyBuffer extends AbstractBuffer { - - private float mBodySpace = 0f; - - public CandleBodyBuffer(int size) { - super(size); - } - - public void setBodySpace(float bodySpace) { - this.mBodySpace = bodySpace; - } - - private void addBody(float left, float top, float right, float bottom) { - - buffer[index++] = left; - buffer[index++] = top; - buffer[index++] = right; - buffer[index++] = bottom; - } - - @Override - public void feed(List entries) { - - int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom); - - for (int i = mFrom; i < size; i++) { - - CandleEntry e = entries.get(i); - addBody(e.getXIndex() - 0.5f + mBodySpace, e.getClose() * phaseY, e.getXIndex() + 0.5f - mBodySpace, e.getOpen() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/CandleShadowBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/CandleShadowBuffer.java deleted file mode 100644 index 85b1766d66..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/CandleShadowBuffer.java +++ /dev/null @@ -1,35 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.CandleEntry; - -import java.util.List; - -public class CandleShadowBuffer extends AbstractBuffer { - - public CandleShadowBuffer(int size) { - super(size); - } - - private void addShadow(float x1, float y1, float x2, float y2) { - - buffer[index++] = x1; - buffer[index++] = y1; - buffer[index++] = x2; - buffer[index++] = y2; - } - - @Override - public void feed(List entries) { - - int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom); - - for (int i = mFrom; i < size; i++) { - - CandleEntry e = entries.get(i); - addShadow(e.getXIndex(), e.getHigh() * phaseY, e.getXIndex(), e.getLow() * phaseY); - } - - reset(); - } -} 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 1b37525942..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/CircleBuffer.java +++ /dev/null @@ -1,32 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; - -import java.util.List; - -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(List entries) { - - int size = (int)Math.ceil((mTo - mFrom) * phaseX + mFrom); - - for (int i = mFrom; i < size; i++) { - - Entry e = entries.get(i); - addCircle(e.getXIndex(), e.getVal() * phaseY); - } - - reset(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java b/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java deleted file mode 100644 index 15dde45436..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/HorizontalBarBuffer.java +++ /dev/null @@ -1,122 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.BarEntry; - -import java.util.List; - -public class HorizontalBarBuffer extends BarBuffer { - - public HorizontalBarBuffer(int size, float groupspace, int dataSetCount, boolean containsStacks) { - super(size, groupspace, dataSetCount, containsStacks); - } - - @Override - public void feed(List entries) { - - float size = entries.size() * phaseX; - - int dataSetOffset = (mDataSetCount - 1); - float barSpaceHalf = mBarSpace / 2f; - float groupSpaceHalf = mGroupSpace / 2f; - float barWidth = 0.5f; - - for (int i = 0; i < size; i++) { - - BarEntry e = entries.get(i); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + i * dataSetOffset + mDataSetIndex - + mGroupSpace * i + groupSpaceHalf; - float y = e.getVal(); - float[] vals = e.getVals(); - - if(mInverted) { // inverted axis - - if (!mContainsStacks || vals == null) { - - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (right > 0) - right *= phaseY; - else - left *= phaseY; - - addBar(left, top, right, bottom); - - } else { - - float all = e.getVal(); - - // fill the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - y = vals[k] + all; - - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; - float left = y >= 0 ? y : 0; - float right = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (right > 0) - right *= phaseY; - else - left *= phaseY; - - addBar(left, top, right, bottom); - } - } - - } else { // not inverted - - if (!mContainsStacks || vals == null) { - - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; - float right = y >= 0 ? y : 0; - float left = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (right > 0) - right *= phaseY; - else - left *= phaseY; - - addBar(left, top, right, bottom); - - } else { - - float all = e.getVal(); - - // fill the stack - for (int k = 0; k < vals.length; k++) { - - all -= vals[k]; - y = vals[k] + all; - - float bottom = x - barWidth + barSpaceHalf; - float top = x + barWidth - barSpaceHalf; - float right = y >= 0 ? y : 0; - float left = y <= 0 ? y : 0; - - // multiply the height of the rect with the phase - if (right > 0) - right *= phaseY; - else - left *= phaseY; - - addBar(left, top, right, bottom); - } - } - } - } - - 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 173ac166f3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/LineBuffer.java +++ /dev/null @@ -1,58 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; - -import java.util.List; - -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(List entries) { - moveTo(entries.get(mFrom).getXIndex(), entries.get(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 = entries.get(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 11dcb66779..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/buffer/ScatterBuffer.java +++ /dev/null @@ -1,32 +0,0 @@ - -package com.github.mikephil.charting.buffer; - -import com.github.mikephil.charting.data.Entry; - -import java.util.List; - -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(List entries) { - - float size = entries.size() * phaseX; - - for (int i = 0; i < size; i++) { - - Entry e = entries.get(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 d890f42557..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarChart.java +++ /dev/null @@ -1,369 +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.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.renderer.BarChartRenderer; -import com.github.mikephil.charting.renderer.XAxisRendererBarChart; -import com.github.mikephil.charting.utils.Highlight; - -/** - * 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, all values of a stack are drawn individually, and not - * just their sum - */ - private boolean mDrawValuesForWholeStack = 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); - - 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(); - - int maxEntry = 0; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet set = mData.getDataSetByIndex(i); - - if (maxEntry < set.getEntryCount()) - maxEntry = set.getEntryCount(); - } - - float groupSpace = mData.getGroupSpace(); - mDeltaX += maxEntry * 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 (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - mLeftAxisTransformer.pixelsToValue(pts); - - if (pts[0] < mXChartMin || pts[0] > mXChartMax) - return null; - - return getHighlight(pts[0], pts[1]); - } - - /** - * Returns the correct Highlight object (including xIndex and dataSet-index) - * for the specified touch position. - * - * @param xPosition - * @return - */ - protected Highlight getHighlight(double xPosition, double yPosition) { - - int setCount = mData.getDataSetCount(); - int valCount = mData.getXValCount(); - int dataSetIndex = 0; - int xIndex = 0; - - // only one dataset exists - if (!mData.isGrouped()) { - - xIndex = (int) Math.round(xPosition); - - // check bounds - if (xIndex < 0) { - xIndex = 0; - } else if (xIndex >= valCount) { - xIndex = valCount - 1; - } - - // if this bardata is grouped into more datasets - } else { - - // calculate how often the group-space appears - int steps = (int) ((float) xPosition / ((float) setCount + mData.getGroupSpace())); - - float groupSpaceSum = mData.getGroupSpace() * (float) steps; - - float baseNoSpace = (float) xPosition - groupSpaceSum; - - if (mLogEnabled) - Log.i(LOG_TAG, "base: " + xPosition + ", steps: " + steps + ", groupSpaceSum: " - + groupSpaceSum - + ", baseNoSpace: " + baseNoSpace); - - dataSetIndex = (int) baseNoSpace % setCount; - xIndex = (int) baseNoSpace / setCount; - - if (mLogEnabled) - Log.i(LOG_TAG, "xIndex: " + xIndex + ", dataSet: " + dataSetIndex); - - // check bounds - if (xIndex < 0) { - xIndex = 0; - dataSetIndex = 0; - } else if (xIndex >= valCount) { - xIndex = valCount - 1; - dataSetIndex = setCount - 1; - } - - // check bounds - if (dataSetIndex < 0) - dataSetIndex = 0; - else if (dataSetIndex >= setCount) - dataSetIndex = setCount - 1; - } - - if (!mData.getDataSetByIndex(dataSetIndex).isStacked()) - return new Highlight(xIndex, dataSetIndex); - else - return getStackedHighlight(xIndex, dataSetIndex, yPosition); - } - - /** - * This method creates the Highlight object that also indicates which value - * of a stacked BarEntry has been selected. - * - * @param xIndex - * @param dataSet - * @param yValue - * @return - */ - protected Highlight getStackedHighlight(int xIndex, int dataSet, double yValue) { - - BarEntry entry = mData.getDataSetByIndex(dataSet).getEntryForXIndex(xIndex); - - if (entry != null) { - int stackIndex = entry.getClosestIndexAbove((float) yValue); - Highlight h = new Highlight(xIndex, dataSet, stackIndex); - return h; - } else - return null; - } - - /** - * 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 - * @param dataSetIndex - * @return - */ - public RectF getBarBounds(BarEntry e) { - - BarDataSet 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, all values of a stack are drawn individually, and not - * just their sum - * - * @param enabled - */ - public void setDrawValuesForWholeStack(boolean enabled) { - mDrawValuesForWholeStack = enabled; - } - - /** - * returns true if all values of a stack are drawn, and not just their sum - * - * @return - */ - public boolean isDrawValuesForWholeStackEnabled() { - return mDrawValuesForWholeStack; - } - - /** - * 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] <= 0) ? 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/BarLineChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java deleted file mode 100644 index addddfff9e..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/BarLineChartBase.java +++ /dev/null @@ -1,1443 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.PointF; -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.BarLineScatterCandleData; -import com.github.mikephil.charting.data.BarLineScatterCandleDataSet; -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.data.LineDataSet; -import com.github.mikephil.charting.data.filter.Approximator; -import com.github.mikephil.charting.interfaces.BarLineScatterCandleDataProvider; -import com.github.mikephil.charting.jobs.MoveViewJob; -import com.github.mikephil.charting.listener.BarLineChartTouchListener; -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.FillFormatter; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.PointD; -import com.github.mikephil.charting.utils.SelInfo; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Base-class of LineChart, BarChart, ScatterChart and CandleStickChart. - * - * @author Philipp Jahoda - */ -@SuppressLint("RtlHardcoded") -public abstract class BarLineChartBase>> - extends Chart implements BarLineScatterCandleDataProvider { - - /** the maximum number of entried to which values will be drawn */ - protected int mMaxVisibleCount = 100; - - /** - * flag that indicates if pinch-zoom is enabled. if true, both x and y axis - * can be scaled with 2 fingers, if false, x and y axis can be scaled - * separately - */ - protected boolean mPinchZoomEnabled = false; - - /** flag that indicates if double tap zoom is enabled or not */ - protected boolean mDoubleTapToZoomEnabled = true; - - /** - * flag that indicates if highlighting per dragging over a fully zoomed out - * chart is enabled - */ - protected boolean mHighlightPerDragEnabled = true; - - /** - * if set to true, the highlight indicator (lines for linechart, dark bar - * for barchart) will be drawn upon selecting values. - */ - protected boolean mHighLightIndicatorEnabled = true; - - /** if true, dragging is enabled for the chart */ - private boolean mDragEnabled = true; - - private boolean mScaleXEnabled = true; - private boolean mScaleYEnabled = true; - - /** if true, data filtering is enabled */ - protected boolean mFilterData = false; - - /** paint object for the (by default) lightgrey background of the grid */ - protected Paint mGridBackgroundPaint; - - protected Paint mBorderPaint; - - /** flag indicating if the grid background should be drawn or not */ - protected boolean mDrawGridBackground = true; - - protected boolean mDrawBorders = false; - - /** the listener for user drawing on the chart */ - protected OnDrawListener mDrawListener; - - /** - * the object representing the labels on the y-axis, this object is prepared - * in the pepareYLabels() method - */ - protected YAxis mAxisLeft; - protected YAxis mAxisRight; - - /** the object representing the labels on the x-axis */ - protected XAxis mXAxis; - - protected YAxisRenderer mAxisRendererLeft; - protected YAxisRenderer mAxisRendererRight; - - protected Transformer mLeftAxisTransformer; - protected Transformer mRightAxisTransformer; - - protected XAxisRenderer mXAxisRenderer; - - // /** the approximator object used for data filtering */ - // private Approximator mApproximator; - - public BarLineChartBase(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public BarLineChartBase(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BarLineChartBase(Context context) { - super(context); - } - - @Override - protected void init() { - super.init(); - - mAxisLeft = new YAxis(AxisDependency.LEFT); - mAxisRight = new YAxis(AxisDependency.RIGHT); - - mXAxis = new XAxis(); - - mLeftAxisTransformer = new Transformer(mViewPortHandler); - mRightAxisTransformer = new Transformer(mViewPortHandler); - - mAxisRendererLeft = new YAxisRenderer(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); - mAxisRendererRight = new YAxisRenderer(mViewPortHandler, mAxisRight, mRightAxisTransformer); - - mXAxisRenderer = new XAxisRenderer(mViewPortHandler, mXAxis, mLeftAxisTransformer); - - mListener = new BarLineChartTouchListener>>>( - this, mViewPortHandler.getMatrixTouch()); - - mGridBackgroundPaint = new Paint(); - mGridBackgroundPaint.setStyle(Style.FILL); - // mGridBackgroundPaint.setColor(Color.WHITE); - mGridBackgroundPaint.setColor(Color.rgb(240, 240, 240)); // light - // grey - - mBorderPaint = new Paint(); - mBorderPaint.setStyle(Style.STROKE); - mBorderPaint.setColor(Color.BLACK); - mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - } - - // for performance tracking - private long totalTime = 0; - private long drawCycles = 0; - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mDataNotSet) - return; - - long starttime = System.currentTimeMillis(); - calcModulus(); - - mXAxisRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus); - mRenderer.calcXBounds(this, mXAxis.mAxisLabelModulus); - - // execute all drawing commands - drawGridBackground(canvas); - - if (mAxisLeft.isEnabled()) - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); - if (mAxisRight.isEnabled()) - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); - - mXAxisRenderer.renderAxisLine(canvas); - mAxisRendererLeft.renderAxisLine(canvas); - mAxisRendererRight.renderAxisLine(canvas); - - // make sure the graph values and grid cannot be drawn outside the - // content-rect - int clipRestoreCount = canvas.save(); - canvas.clipRect(mViewPortHandler.getContentRect()); - - mXAxisRenderer.renderGridLines(canvas); - mAxisRendererLeft.renderGridLines(canvas); - mAxisRendererRight.renderGridLines(canvas); - - if (mXAxis.isDrawLimitLinesBehindDataEnabled()) - mXAxisRenderer.renderLimitLines(canvas); - - if (mAxisLeft.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererLeft.renderLimitLines(canvas); - - if (mAxisRight.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererRight.renderLimitLines(canvas); - - mRenderer.drawData(canvas); - - if (!mXAxis.isDrawLimitLinesBehindDataEnabled()) - mXAxisRenderer.renderLimitLines(canvas); - - if (!mAxisLeft.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererLeft.renderLimitLines(canvas); - - if (!mAxisRight.isDrawLimitLinesBehindDataEnabled()) - mAxisRendererRight.renderLimitLines(canvas); - - // if highlighting is enabled - if (mHighlightEnabled && mHighLightIndicatorEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(canvas, mIndicesToHightlight); - - // Removes clipping rectangle - canvas.restoreToCount(clipRestoreCount); - - mRenderer.drawExtras(canvas); - - mXAxisRenderer.renderAxisLabels(canvas); - mAxisRendererLeft.renderAxisLabels(canvas); - mAxisRendererRight.renderAxisLabels(canvas); - - mRenderer.drawValues(canvas); - - mLegendRenderer.renderLegend(canvas); - - drawMarkers(canvas); - - drawDescription(canvas); - - if (mLogEnabled) { - long drawtime = (System.currentTimeMillis() - starttime); - totalTime += drawtime; - drawCycles += 1; - long average = totalTime / drawCycles; - Log.i(LOG_TAG, "Drawtime: " + drawtime + " ms, average: " + average + " ms, cycles: " - + drawCycles); - } - } - - /** - * RESET PERFORMANCE TRACKING FIELDS - */ - public void resetTracking() { - totalTime = 0; - drawCycles = 0; - } - - protected void prepareValuePxMatrix() { - - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing Value-Px Matrix, xmin: " + mXChartMin + ", xmax: " - + mXChartMax + ", xdelta: " + mDeltaX); - - mRightAxisTransformer.prepareMatrixValuePx(mXChartMin, mDeltaX, mAxisRight.mAxisRange, - mAxisRight.mAxisMinimum); - mLeftAxisTransformer.prepareMatrixValuePx(mXChartMin, mDeltaX, mAxisLeft.mAxisRange, - mAxisLeft.mAxisMinimum); - } - - protected void prepareOffsetMatrix() { - - mRightAxisTransformer.prepareMatrixOffset(mAxisRight.isInverted()); - mLeftAxisTransformer.prepareMatrixOffset(mAxisLeft.isInverted()); - } - - @Override - public void notifyDataSetChanged() { - - if (mDataNotSet) { - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing... DATA NOT SET."); - return; - } else { - if (mLogEnabled) - Log.i(LOG_TAG, "Preparing..."); - } - - if (mRenderer != null) - mRenderer.initBuffers(); - - calcMinMax(); - - if (mAxisLeft.needsDefaultFormatter()) - mAxisLeft.setValueFormatter(mDefaultFormatter); - if (mAxisRight.needsDefaultFormatter()) - mAxisRight.setValueFormatter(mDefaultFormatter); - - mAxisRendererLeft.computeAxis(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisMaximum); - mAxisRendererRight.computeAxis(mAxisRight.mAxisMinimum, mAxisRight.mAxisMaximum); - - mXAxisRenderer.computeAxis(mData.getXValAverageLength(), mData.getXVals()); - - mLegendRenderer.computeLegend(mData); - - calculateOffsets(); - } - - @Override - protected void calcMinMax() { - - float minLeft = mData.getYMin(AxisDependency.LEFT); - float maxLeft = mData.getYMax(AxisDependency.LEFT); - float minRight = mData.getYMin(AxisDependency.RIGHT); - float maxRight = mData.getYMax(AxisDependency.RIGHT); - - float leftRange = Math.abs(maxLeft - (mAxisLeft.isStartAtZeroEnabled() ? 0 : minLeft)); - float rightRange = Math.abs(maxRight - (mAxisRight.isStartAtZeroEnabled() ? 0 : minRight)); - - // in case all values are equal - if (leftRange == 0f) { - maxLeft = maxLeft + 1f; - if (!mAxisLeft.isStartAtZeroEnabled()) - minLeft = minLeft - 1f; - } - - if (rightRange == 0f) { - maxRight = maxRight + 1f; - if (!mAxisRight.isStartAtZeroEnabled()) - minRight = minRight - 1f; - } - - float topSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceTop(); - float topSpaceRight = rightRange / 100f * mAxisRight.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mAxisLeft.getSpaceBottom(); - float bottomSpaceRight = rightRange / 100f * mAxisRight.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - mAxisLeft.mAxisMaximum = !Float.isNaN(mAxisLeft.getAxisMaxValue()) ? mAxisLeft - .getAxisMaxValue() : maxLeft + topSpaceLeft; - mAxisRight.mAxisMaximum = !Float.isNaN(mAxisRight.getAxisMaxValue()) ? mAxisRight - .getAxisMaxValue() : maxRight + topSpaceRight; - mAxisLeft.mAxisMinimum = !Float.isNaN(mAxisLeft.getAxisMinValue()) ? mAxisLeft - .getAxisMinValue() : minLeft - bottomSpaceLeft; - mAxisRight.mAxisMinimum = !Float.isNaN(mAxisRight.getAxisMinValue()) ? mAxisRight - .getAxisMinValue() : minRight - bottomSpaceRight; - - // consider starting at zero (0) - if (mAxisLeft.isStartAtZeroEnabled()) - mAxisLeft.mAxisMinimum = 0f; - - if (mAxisRight.isStartAtZeroEnabled()) - mAxisRight.mAxisMinimum = 0f; - - mAxisLeft.mAxisRange = Math.abs(mAxisLeft.mAxisMaximum - mAxisLeft.mAxisMinimum); - mAxisRight.mAxisRange = Math.abs(mAxisRight.mAxisMaximum - mAxisRight.mAxisMinimum); - } - - @Override - protected void calculateOffsets() { - - if (!mCustomViewPortEnabled) { - - 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 += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART - || mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - offsetLeft += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - offsetBottom += mLegend.mTextHeightMax * 3f; - } - } - - // offsets for y-labels - if (mAxisLeft.needsOffset()) { - offsetLeft += mAxisLeft.getRequiredWidthSpace(mAxisRendererLeft - .getPaintAxisLabels()); - } - - if (mAxisRight.needsOffset()) { - offsetRight += mAxisRight.getRequiredWidthSpace(mAxisRendererRight - .getPaintAxisLabels()); - } - - float xlabelheight = mXAxis.mLabelHeight * 2f; - - if (mXAxis.isEnabled()) { - - // offsets for x-labels - if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - offsetBottom += xlabelheight; - - } else if (mXAxis.getPosition() == XAxisPosition.TOP) { - - offsetTop += xlabelheight; - - } else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - - offsetBottom += xlabelheight; - offsetTop += xlabelheight; - } - } - - float min = Utils.convertDpToPixel(10f); - - mViewPortHandler.restrainViewPort(Math.max(min, offsetLeft), Math.max(min, offsetTop), - Math.max(min, offsetRight), Math.max(min, 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(); - } - - /** - * calculates the modulus for x-labels and grid - */ - protected void calcModulus() { - - if (mXAxis == null || !mXAxis.isEnabled()) - return; - - if (!mXAxis.isAxisModulusCustom()) { - - float[] values = new float[9]; - mViewPortHandler.getMatrixTouch().getValues(values); - - mXAxis.mAxisLabelModulus = (int) Math - .ceil((mData.getXValCount() * mXAxis.mLabelWidth) - / (mViewPortHandler.contentWidth() * values[Matrix.MSCALE_X])); - - } - - if (mLogEnabled) - Log.i(LOG_TAG, "X-Axis modulus: " + mXAxis.mAxisLabelModulus + ", x-axis label width: " - + mXAxis.mLabelWidth + ", content width: " + mViewPortHandler.contentWidth()); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; - } - - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - - float xPos = e.getXIndex(); - - if (this instanceof BarChart) { - - BarData bd = (BarData) mData; - float space = bd.getGroupSpace(); - float j = mData.getDataSetByIndex(dataSetIndex) - .getEntryPosition(e); - - float x = (j * (mData.getDataSetCount() - 1)) + dataSetIndex + space * j + space - / 2f; - - xPos += x; - } - - // position of the marker depends on selected value index and value - float[] pts = new float[] { - xPos, e.getVal() * mAnimator.getPhaseY() - }; - - getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency()) - .pointValuesToPixel(pts); - - return pts; - } - - /** - * draws the grid background - */ - protected void drawGridBackground(Canvas c) { - - if (mDrawGridBackground) { - - // draw the grid background - c.drawRect(mViewPortHandler.getContentRect(), mGridBackgroundPaint); - } - - if (mDrawBorders) { - c.drawRect(mViewPortHandler.getContentRect(), mBorderPaint); - } - } - - /** - * Returns the Transformer class that contains all matrices and is - * responsible for transforming values into pixels on the screen and - * backwards. - * - * @return - */ - public Transformer getTransformer(AxisDependency which) { - if (which == AxisDependency.LEFT) - return mLeftAxisTransformer; - else - return mRightAxisTransformer; - } - - /** touchlistener that handles touches and gestures on the chart */ - protected OnTouchListener mListener; - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - - if (mListener == null || mDataNotSet) - return false; - - // check if touch gestures are enabled - if (!mTouchEnabled) - return false; - else - return mListener.onTouch(this, event); - } - - @Override - public void computeScroll() { - - if (mListener instanceof BarLineChartTouchListener) - ((BarLineChartTouchListener) mListener).computeScroll(); - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ - - /** - * Zooms in by 1.4f, into the charts center. center. - */ - public void zoomIn() { - Matrix save = mViewPortHandler.zoomIn(getWidth() / 2f, -(getHeight() / 2f)); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Zooms out by 0.7f, from the charts center. center. - */ - public void zoomOut() { - Matrix save = mViewPortHandler.zoomOut(getWidth() / 2f, -(getHeight() / 2f)); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Zooms in or out by the given scale factor. x and y are the coordinates - * (in pixels) of the zoom center. - * - * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in - * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in - * @param x - * @param y - */ - public void zoom(float scaleX, float scaleY, float x, float y) { - Matrix save = mViewPortHandler.zoom(scaleX, scaleY, x, -y); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Resets all zooming and dragging and makes the chart fit exactly it's - * bounds. - */ - public void fitScreen() { - Matrix save = mViewPortHandler.fitScreen(); - mViewPortHandler.refresh(save, this, true); - } - - /** - * Sets the minimum scale value to which can be zoomed out. 1f = fitScreen - * - * @param scaleX - * @param scaleY - */ - public void setScaleMinima(float scaleX, float scaleY) { - mViewPortHandler.setMinimumScaleX(scaleX); - mViewPortHandler.setMinimumScaleY(scaleY); - } - - /** - * Sets the size of the area (range on the x-axis) that should be maximum - * visible at once. If this is e.g. set to 10, no more than 10 values on the - * x-axis can be viewed at once without scrolling. - * - * @param xRange - */ - public void setVisibleXRange(float xRange) { - float xScale = mDeltaX / (xRange); - mViewPortHandler.setMinimumScaleX(xScale); - } - - /** - * Sets the size of the area (range on the y-axis) that should be maximum - * visible at once. - * - * @param yRange - * @param axis - the axis for which this limit should apply - */ - public void setVisibleYRange(float yRange, AxisDependency axis) { - float yScale = getDeltaY(axis) / yRange; - mViewPortHandler.setMinimumScaleY(yScale); - } - - /** - * Moves the left side of the current viewport to the specified x-index. - * - * @param xIndex - */ - public void moveViewToX(float xIndex) { - - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, 0f, - getTransformer(AxisDependency.LEFT), this); - - if (mViewPortHandler.hasChartDimens()) { - post(job); - } else { - mJobs.add(job); - } - - // float[] pts = new float[] { - // xIndex, 0f - // }; - // - // getTransformer(AxisDependency.LEFT).pointValuesToPixel(pts); - // - // mViewPortHandler.centerViewPort(pts, this); - } - - /** - * Centers the viewport to the specified y-value on the y-axis. - * - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis - */ - public void moveViewToY(float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - - Runnable job = new MoveViewJob(mViewPortHandler, 0f, yValue + valsInView / 2f, - getTransformer(axis), this); - - if (mViewPortHandler.hasChartDimens()) { - post(job); - } else { - mJobs.add(job); - } - } - - /** - * This will move the left side of the current viewport to the specified - * x-index on the x-axis, and center the viewport to the specified y-value - * on the y-axis. - * - * @param xIndex - * @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) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - - Runnable job = new MoveViewJob(mViewPortHandler, xIndex, yValue + valsInView / 2f, - getTransformer(axis), this); - - if (mViewPortHandler.hasChartDimens()) { - post(job); - } else { - mJobs.add(job); - } - } - - /** - * This will move the center of the current viewport to the specified - * x-index and y-value. - * - * @param xIndex - * @param yValue - * @param axis - which axis should be used as a reference for the y-axis - */ - public void centerViewTo(int xIndex, float yValue, AxisDependency axis) { - - float valsInView = getDeltaY(axis) / mViewPortHandler.getScaleY(); - float xsInView = getXAxis().getValues().size() / mViewPortHandler.getScaleX(); - - Runnable job = new MoveViewJob(mViewPortHandler, - xIndex - xsInView / 2f, yValue + valsInView / 2f, - getTransformer(axis), this); - - if (mViewPortHandler.hasChartDimens()) { - post(job); - } else { - mJobs.add(job); - } - } - - /** flag that indicates if a custom viewport offset has been set */ - private boolean mCustomViewPortEnabled = false; - - /** - * Sets custom offsets for the current ViewPort (the offsets on the sides of - * the actual chart window). Setting this will prevent the chart from - * automatically calculating it's offsets. Use resetViewPortOffsets() to - * undo this. - * - * @param left - * @param top - * @param right - * @param bottom - */ - public void setViewPortOffsets(final float left, final float top, - final float right, final float bottom) { - - mCustomViewPortEnabled = true; - post(new Runnable() { - - @Override - public void run() { - - mViewPortHandler.restrainViewPort(left, top, right, bottom); - prepareOffsetMatrix(); - prepareValuePxMatrix(); - } - }); - } - - /** - * Resets all custom offsets set via setViewPortOffsets(...) method. Allows - * the chart to again calculate all offsets automatically. - */ - public void resetViewPortOffsets() { - mCustomViewPortEnabled = false; - calculateOffsets(); - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW IS GETTERS AND SETTERS */ - - /** - * Returns the delta-y value (y-value range) of the specified axis. - * - * @param axis - * @return - */ - public float getDeltaY(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mAxisLeft.mAxisRange; - else - return mAxisRight.mAxisRange; - } - - /** - * set a new (e.g. custom) charttouchlistener NOTE: make sure to - * setTouchEnabled(true); if you need touch gestures on the chart - * - * @param l - */ - public void setOnTouchListener(OnTouchListener l) { - this.mListener = l; - } - - /** - * Sets the OnDrawListener - * - * @param drawListener - */ - public void setOnDrawListener(OnDrawListener drawListener) { - this.mDrawListener = drawListener; - } - - /** - * Gets the OnDrawListener. May be null. - * - * @return - */ - public OnDrawListener getDrawListener() { - return mDrawListener; - } - - /** - * 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) { - - if (e == null) - return null; - - float[] vals = new float[] { - e.getXIndex(), e.getVal() - }; - - getTransformer(axis).pointValuesToPixel(vals); - - return new PointF(vals[0], vals[1]); - } - - /** - * sets the number of maximum visible drawn values on the chart only active - * when setDrawValues() is enabled - * - * @param count - */ - public void setMaxVisibleValueCount(int count) { - this.mMaxVisibleCount = count; - } - - public int getMaxVisibleCount() { - return mMaxVisibleCount; - } - - /** - * If set to true, the highlight indicator (vertical line for LineChart and - * ScatterChart, dark bar overlay for BarChart) that gives visual indication - * that an Entry has been selected will be drawn upon selecting values. This - * does not depend on the MarkerView. Default: true - * - * @param enabled - */ - public void setHighlightIndicatorEnabled(boolean enabled) { - mHighLightIndicatorEnabled = enabled; - } - - /** - * Set this to true to allow highlighting per dragging over the chart - * surface when it is fully zoomed out. Default: true - * - * @param enabled - */ - public void setHighlightPerDragEnabled(boolean enabled) { - mHighlightPerDragEnabled = enabled; - } - - public boolean isHighlightPerDragEnabled() { - return mHighlightPerDragEnabled; - } - - /** - * Sets the color for the background of the chart-drawing area (everything - * behind the grid lines). - * - * @param color - */ - public void setGridBackgroundColor(int color) { - mGridBackgroundPaint.setColor(color); - } - - /** - * Set this to true to enable dragging (moving the chart with the finger) - * for the chart (this does not effect scaling). - * - * @param enabled - */ - public void setDragEnabled(boolean enabled) { - this.mDragEnabled = enabled; - } - - /** - * Returns true if dragging is enabled for the chart, false if not. - * - * @return - */ - public boolean isDragEnabled() { - return mDragEnabled; - } - - /** - * Set this to true to enable scaling (zooming in and out by gesture) for - * the chart (this does not effect dragging) on both X- and Y-Axis. - * - * @param enabled - */ - public void setScaleEnabled(boolean enabled) { - this.mScaleXEnabled = enabled; - this.mScaleYEnabled = enabled; - } - - public void setScaleXEnabled(boolean enabled) { - mScaleXEnabled = enabled; - } - - public void setScaleYEnabled(boolean enabled) { - mScaleYEnabled = enabled; - } - - public boolean isScaleXEnabled() { - return mScaleXEnabled; - } - - public boolean isScaleYEnabled() { - return mScaleYEnabled; - } - - /** - * Set this to true to enable zooming in by double-tap on the chart. - * Default: enabled - * - * @param enabled - */ - public void setDoubleTapToZoomEnabled(boolean enabled) { - mDoubleTapToZoomEnabled = enabled; - } - - /** - * Returns true if zooming via double-tap is enabled false if not. - * - * @return - */ - public boolean isDoubleTapToZoomEnabled() { - return mDoubleTapToZoomEnabled; - } - - /** - * set this to true to draw the grid background, false if not - * - * @param enabled - */ - public void setDrawGridBackground(boolean enabled) { - mDrawGridBackground = 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. - * - * @param enabled - */ - public void setDrawBorders(boolean enabled) { - mDrawBorders = enabled; - } - - /** - * Sets the width of the border lines in dp. - * - * @param width - */ - public void setBorderWidth(float width) { - mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(width)); - } - - /** - * Sets the color of the chart border lines. - * - * @param color - */ - public void setBorderColor(int color) { - mBorderPaint.setColor(color); - } - - /** - * 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 - */ - public Highlight getHighlightByTouchPoint(float x, float y) { - - if (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - - // take any transformer to determine the x-axis value - mLeftAxisTransformer.pixelsToValue(pts); - - double xTouchVal = pts[0]; - double base = Math.floor(xTouchVal); - - double touchOffset = mDeltaX * 0.025; - - // touch out of chart - if (xTouchVal < -touchOffset || xTouchVal > mDeltaX + touchOffset) - return null; - - if (base < 0) - base = 0; - if (base >= mDeltaX) - base = mDeltaX - 1; - - int xIndex = (int) base; - - // check if we are more than half of a x-value or not - if (xTouchVal - base > 0.5) { - xIndex = (int) base + 1; - } - - List valsAtIndex = getYValsAtIndex(xIndex); - - float leftdist = Utils.getMinimumDistance(valsAtIndex, y, AxisDependency.LEFT); - float rightdist = Utils.getMinimumDistance(valsAtIndex, y, AxisDependency.RIGHT); - - if (mData.getFirstRight() == null) - rightdist = Float.MAX_VALUE; - if (mData.getFirstLeft() == null) - leftdist = Float.MAX_VALUE; - - AxisDependency axis = leftdist < rightdist ? AxisDependency.LEFT : AxisDependency.RIGHT; - - int dataSetIndex = Utils.getClosestDataSetIndex(valsAtIndex, y, axis); - - if (dataSetIndex == -1) - return null; - - return new Highlight(xIndex, dataSetIndex); - } - - /** - * Returns an array of SelInfo objects for the given x-index. The SelInfo - * 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 getYValsAtIndex(int xIndex) { - - List vals = new ArrayList(); - - float[] pts = new float[2]; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - float yVal = dataSet.getYValForXIndex(xIndex); - pts[1] = yVal; - - getTransformer(dataSet.getAxisDependency()).pointValuesToPixel(pts); - - if (!Float.isNaN(pts[1])) { - vals.add(new SelInfo(pts[1], i, dataSet)); - } - } - - return vals; - } - - /** - * Returns the x and y values in the chart at the given touch point - * (encapsulated in a PointD). This method transforms pixel coordinates to - * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). - * - * @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]; - - return new PointD(xTouchVal, yTouchVal); - } - - /** - * Transforms the given chart values into pixels. This is the opposite - * method to getValuesByTouchPoint(...). - * - * @param x - * @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; - } - - /** - * returns the Entry object displayed at the touched position of the chart - * - * @param x - * @param y - * @return - */ - public Entry getEntryByTouchPoint(float x, float y) { - Highlight h = getHighlightByTouchPoint(x, y); - if (h != null) { - return mData.getEntryForHighlight(h); - } - return null; - } - - /** - * returns the DataSet object displayed at the touched position of the chart - * - * @param x - * @param y - * @return - */ - public BarLineScatterCandleDataSet getDataSetByTouchPoint(float x, float y) { - Highlight h = getHighlightByTouchPoint(x, y); - if (h != null) { - return mData.getDataSetByIndex(h.getDataSetIndex()); - } - return null; - } - - /** - * Returns the lowest x-index (value on the x-axis) that is still visible on - * the chart. - * - * @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); - } - - /** - * Returns the highest x-index (value on the x-axis) that is still visible - * on the chart. - * - * @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]; - } - - /** - * returns the current x-scale factor - */ - public float getScaleX() { - if (mViewPortHandler == null) - return 1f; - else - return mViewPortHandler.getScaleX(); - } - - /** - * returns the current y-scale factor - */ - public float getScaleY() { - if (mViewPortHandler == null) - return 1f; - else - return mViewPortHandler.getScaleY(); - } - - /** - * if the chart is fully zoomed out, return true - * - * @return - */ - public boolean isFullyZoomedOut() { - return mViewPortHandler.isFullyZoomedOut(); - } - - /** - * Returns the left y-axis object. In the horizontal bar-chart, this is the - * top axis. - * - * @return - */ - public YAxis getAxisLeft() { - return mAxisLeft; - } - - /** - * Returns the right y-axis object. In the horizontal bar-chart, this is the - * bottom axis. - * - * @return - */ - public YAxis getAxisRight() { - return mAxisRight; - } - - /** - * Returns the y-axis object to the corresponding AxisDependency. In the - * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM - * - * @param axis - * @return - */ - public YAxis getAxis(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mAxisLeft; - else - return mAxisRight; - } - - @Override - 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; - } - - /** - * Enables data filtering for the chart data, filtering will use the user - * customized Approximator handed over to this method. - * - * @param a - */ - public void enableFiltering(Approximator a) { - mFilterData = true; - // mApproximator = a; - } - - /** - * Disables data filtering for the chart. - */ - public void disableFiltering() { - mFilterData = false; - } - - /** - * returns true if data filtering is enabled, false if not - * - * @return - */ - public boolean isFilteringEnabled() { - return mFilterData; - } - - /** - * if set to true, both x and y axis can be scaled with 2 fingers, if false, - * x and y axis can be scaled separately. default: false - * - * @param enabled - */ - public void setPinchZoom(boolean enabled) { - mPinchZoomEnabled = enabled; - } - - /** - * returns true if pinch-zoom is enabled, false if not - * - * @return - */ - public boolean isPinchZoomEnabled() { - return mPinchZoomEnabled; - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. - * - * @param offset - */ - public void setDragOffsetX(float offset) { - mViewPortHandler.setDragOffsetX(offset); - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. - * - * @param offset - */ - public void setDragOffsetY(float offset) { - mViewPortHandler.setDragOffsetY(offset); - } - - /** - * Returns true if both drag offsets (x and y) are zero or smaller. - * - * @return - */ - public boolean hasNoDragOffset() { - return mViewPortHandler.hasNoDragOffset(); - } - - public XAxisRenderer getRendererXAxis() { - return mXAxisRenderer; - } - - public YAxisRenderer getRendererLeftYAxis() { - return mAxisRendererLeft; - } - - public YAxisRenderer getRendererRightYAxis() { - return mAxisRendererRight; - } - - public float getYChartMax() { - return Math.max(mAxisLeft.mAxisMaximum, mAxisRight.mAxisMaximum); - } - - public float getYChartMin() { - return Math.min(mAxisLeft.mAxisMinimum, mAxisRight.mAxisMinimum); - } - - /** - * Returns true if either the left or the right or both axes are inverted. - * - * @return - */ - public boolean isAnyAxisInverted() { - if (mAxisLeft.isInverted()) - return true; - if (mAxisRight.isInverted()) - return true; - return false; - } - - @Override - public void setPaint(Paint p, int which) { - super.setPaint(p, which); - - switch (which) { - case PAINT_GRID_BACKGROUND: - mGridBackgroundPaint = p; - break; - } - } - - @Override - public Paint getPaint(int which) { - Paint p = super.getPaint(which); - if (p != null) - return p; - - switch (which) { - case PAINT_GRID_BACKGROUND: - return mGridBackgroundPaint; - } - - return null; - } - - /** - * Default formatter that calculates the position of the filled line. - * - * @author Philipp Jahoda - */ - protected class DefaultFillFormatter implements FillFormatter { - - @Override - public float getFillLinePosition(LineDataSet dataSet, LineData data, - float chartMaxY, float chartMinY) { - - float fillMin = 0f; - - if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) { - fillMin = 0f; - } else { - - if (!getAxis(dataSet.getAxisDependency()).isStartAtZeroEnabled()) { - - float max, min; - - if (data.getYMax() > 0) - max = 0f; - else - max = chartMaxY; - if (data.getYMin() < 0) - min = 0f; - else - min = chartMinY; - - fillMin = dataSet.getYMin() >= 0 ? min : max; - } else { - fillMin = 0f; - } - - } - - return fillMin; - } - } -} 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 791a2b82d8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ /dev/null @@ -1,265 +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.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.Entry; -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.Highlight; -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); - mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, - mLeftAxisTransformer); - mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, - mRightAxisTransformer); - mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, - mLeftAxisTransformer, this); - } - - @Override - protected 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 += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.LEFT_OF_CHART - || mLegend.getPosition() == LegendPosition.LEFT_OF_CHART_CENTER) { - - offsetLeft += mLegend.mTextWidthMax + mLegend.getXOffset() * 2f; - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - offsetBottom += mLegend.mTextHeightMax * 3f; - } - } - - // offsets for y-labels - if (mAxisLeft.needsOffset()) { - offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels()); - } - - if (mAxisRight.needsOffset()) { - offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight - .getPaintAxisLabels()); - } - - float xlabelwidth = mXAxis.mLabelWidth; - - 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; - } - } - - float min = Utils.convertDpToPixel(10f); - - mViewPortHandler.restrainViewPort(Math.max(min, offsetLeft), Math.max(min, offsetTop), - Math.max(min, offsetRight), Math.max(min, 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.mLabelHeight) - / (mViewPortHandler.contentHeight() * values[Matrix.MSCALE_Y])); - - if (mXAxis.mAxisLabelModulus < 1) - mXAxis.mAxisLabelModulus = 1; - } - - @Override - public RectF getBarBounds(BarEntry e) { - - BarDataSet 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 (mDataNotSet || mData == null) { - Log.e(LOG_TAG, "Can't select by touch. No data set."); - return null; - } - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - mLeftAxisTransformer.pixelsToValue(pts); - - if (pts[1] < mXChartMin || pts[1] > mXChartMax) - return null; - - return getHighlight(pts[1], pts[0]); - } - - /** - * 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/charts/LineChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java deleted file mode 100644 index ee6b58aeca..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/LineChart.java +++ /dev/null @@ -1,95 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.renderer.LineChartRenderer; -import com.github.mikephil.charting.utils.FillFormatter; - -/** - * Chart that draws lines, surfaces, circles, ... - * - * @author Philipp Jahoda - */ -public class LineChart extends BarLineChartBase implements LineDataProvider { - - /** the width of the highlighning line */ - protected float mHighlightWidth = 3f; - - private FillFormatter mFillFormatter; - - public LineChart(Context context) { - super(context); - } - - public LineChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LineChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); - - mFillFormatter = new DefaultFillFormatter(); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - // // if there is only one value in the chart - // if (mOriginalData.getYValCount() == 1 - // || mOriginalData.getYValCount() <= mOriginalData.getDataSetCount()) { - // mDeltaX = 1; - // } - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - } - - /** - * set the width of the highlightning lines, default 3f - * - * @param width - */ - public void setHighlightLineWidth(float width) { - mHighlightWidth = width; - } - - /** - * returns the width of the highlightning line, default 3f - * - * @return - */ - public float getHighlightLineWidth() { - return mHighlightWidth; - } - - @Override - public void setFillFormatter(FillFormatter formatter) { - - if (formatter == null) - formatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - - @Override - public FillFormatter getFillFormatter() { - return mFillFormatter; - } - - @Override - public LineData getLineData() { - return mData; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java deleted file mode 100644 index e464e0d47d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieChart.java +++ /dev/null @@ -1,522 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.DataSet; -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.renderer.PieChartRenderer; -import com.github.mikephil.charting.utils.Utils; - -import java.util.List; - -/** - * View that represents a pie chart. Draws cake like slices. - * - * @author Philipp Jahoda - */ -public class PieChart extends PieRadarChartBase { - - /** - * rect object that represents the bounds of the piechart, needed for - * drawing the circle - */ - private RectF mCircleBox = new RectF(); - - /** flag indicating if the x-labels should be drawn or not */ - private boolean mDrawXLabels = true; - - /** array that holds the width of each pie-slice in degrees */ - private float[] mDrawAngles; - - /** array that holds the absolute angle in degrees of each slice */ - private float[] mAbsoluteAngles; - - /** if true, the white hole inside the chart will be drawn */ - private boolean mDrawHole = true; - - /** if true, the values inside the piechart are drawn as percent values */ - private boolean mUsePercentValues = false; - - /** if true, the slices of the piechart are rounded */ - private boolean mDrawRoundedSlices = false; - - /** - * variable for the text that is drawn in the center of the pie-chart. If - * this value is null, the default is "Total Value\n + getYValueSum()" - */ - private String mCenterText = ""; - - /** - * indicates the size of the hole in the center of the piechart, default: - * radius / 2 - */ - private float mHoleRadiusPercent = 50f; - - /** - * the radius of the transparent circle next to the chart-hole in the center - */ - private float mTransparentCircleRadiusPercent = 55f; - - /** if enabled, centertext is drawn */ - private boolean mDrawCenterText = true; - - public PieChart(Context context) { - super(context); - } - - public PieChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PieChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mDataNotSet) - return; - - mRenderer.drawData(canvas); - - if (mHighlightEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(canvas, mIndicesToHightlight); - - mRenderer.drawExtras(canvas); - - mRenderer.drawValues(canvas); - - mLegendRenderer.renderLegend(canvas); - - drawDescription(canvas); - - // canvas.drawBitmap(mDrawBitmap, 0, 0, mDrawPaint); - } - - @Override - protected void calculateOffsets() { - super.calculateOffsets(); - - // prevent nullpointer when no data set - if (mDataNotSet) - return; - - float diameter = getDiameter(); - float boxSize = diameter / 2f; - - PointF c = getCenterOffsets(); - - // create the circle box that will contain the pie-chart (the bounds of - // the pie-chart) - mCircleBox.set(c.x - boxSize, c.y - boxSize, - c.x + boxSize, c.y + boxSize); - } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - calcAngles(); - } - - /** PieChart does not support MarkerView */ - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - return new float[0]; - } - - /** - * calculates the needed angles for the chart slices - */ - private void calcAngles() { - - mDrawAngles = new float[mData.getYValCount()]; - mAbsoluteAngles = new float[mData.getYValCount()]; - - List dataSets = mData.getDataSets(); - - int cnt = 0; - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - PieDataSet set = dataSets.get(i); - List entries = set.getYVals(); - - for (int j = 0; j < entries.size(); j++) { - - mDrawAngles[cnt] = calcAngle(Math.abs(entries.get(j).getVal())); - - if (cnt == 0) { - mAbsoluteAngles[cnt] = mDrawAngles[cnt]; - } else { - mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt]; - } - - cnt++; - } - } - - } - - /** - * checks if the given index in the given DataSet is set for highlighting or - * not - * - * @param xIndex - * @param dataSetIndex - * @return - */ - public boolean needsHighlight(int xIndex, int dataSetIndex) { - - // no highlight - if (!valuesToHighlight() || dataSetIndex < 0) - return false; - - for (int i = 0; i < mIndicesToHightlight.length; i++) - - // check if the xvalue for the given dataset needs highlight - if (mIndicesToHightlight[i].getXIndex() == xIndex - && mIndicesToHightlight[i].getDataSetIndex() == dataSetIndex) - return true; - - return false; - } - - /** - * calculates the needed angle for a given value - * - * @param value - * @return - */ - private float calcAngle(float value) { - return value / mData.getYValueSum() * 360f; - } - - @Override - public int getIndexForAngle(float angle) { - - // take the current angle of the chart into consideration - float a = Utils.getNormalizedAngle(angle - getRotationAngle()); - - for (int i = 0; i < mAbsoluteAngles.length; i++) { - if (mAbsoluteAngles[i] > a) - return i; - } - - return -1; // return -1 if no index found - } - - /** - * Returns the index of the DataSet this x-index belongs to. - * - * @param xIndex - * @return - */ - public int getDataSetIndexForIndex(int xIndex) { - - List> dataSets = mData.getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getEntryForXIndex(xIndex) != null) - return i; - } - - return -1; - } - - /** - * returns an integer array of all the different angles the chart slices - * have the angles in the returned array determine how much space (of 360°) - * each slice takes - * - * @return - */ - public float[] getDrawAngles() { - return mDrawAngles; - } - - /** - * returns the absolute angles of the different chart slices (where the - * slices end) - * - * @return - */ - public float[] getAbsoluteAngles() { - return mAbsoluteAngles; - } - - /** - * Sets the color for the hole that is drawn in the center of the PieChart - * (if enabled). NOTE: Use setHoleColorTransparent(boolean enabled) to make - * the hole transparent. - * - * @param color - */ - public void setHoleColor(int color) { - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode(null); - ((PieChartRenderer) mRenderer).getPaintHole().setColor(color); - } - - /** - * Set the hole in the center of the PieChart transparent. Thank you, code - * provided by: - * - * @link https://github.com/tbarthel-fr - * @param enable - */ - public void setHoleColorTransparent(boolean enable) { - if (enable) { - ((PieChartRenderer) mRenderer).getPaintHole().setColor(0xFFFFFFFF); - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode( - new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - } else { - ((PieChartRenderer) mRenderer).getPaintHole().setXfermode(null); - } - } - - /** - * Returns true if the hole in the center of the PieChart is transparent, - * false if not. - * - * @return true if hole is transparent. - */ - public boolean isHoleTransparent() { - return ((PieChartRenderer) mRenderer).getPaintHole().getXfermode() != null; - } - - /** - * set this to true to draw the pie center empty - * - * @param enabled - */ - public void setDrawHoleEnabled(boolean enabled) { - this.mDrawHole = enabled; - } - - /** - * returns true if the hole in the center of the pie-chart is set to be - * visible, false if not - * - * @return - */ - public boolean isDrawHoleEnabled() { - return mDrawHole; - } - - /** - * sets the text that is displayed in the center of the pie-chart. By - * default, the text is "Total Value + sumofallvalues" - * - * @param text - */ - public void setCenterText(String text) { - mCenterText = text; - } - - /** - * returns the text that is drawn in the center of the pie-chart - * - * @return - */ - public String getCenterText() { - return mCenterText; - } - - /** - * set this to true to draw the text that is displayed in the center of the - * pie chart - * - * @param enabled - */ - public void setDrawCenterText(boolean enabled) { - this.mDrawCenterText = enabled; - } - - /** - * returns true if drawing the center text is enabled - * - * @return - */ - public boolean isDrawCenterTextEnabled() { - return mDrawCenterText; - } - - @Override - protected float getRequiredBottomOffset() { - return mLegendRenderer.getLabelPaint().getTextSize() * 4f; - } - - @Override - protected float getRequiredBaseOffset() { - return 0; - } - - @Override - public float getRadius() { - if (mCircleBox == null) - return 0; - else - return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f); - } - - /** - * returns the circlebox, the boundingbox of the pie-chart slices - * - * @return - */ - public RectF getCircleBox() { - return mCircleBox; - } - - /** - * returns the center of the circlebox - * - * @return - */ - public PointF getCenterCircleBox() { - return new PointF(mCircleBox.centerX(), mCircleBox.centerY()); - } - - /** - * sets the typeface for the center-text paint - * - * @param t - */ - public void setCenterTextTypeface(Typeface t) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t); - } - - /** - * Sets the size of the center text of the PieChart in dp. - * - * @param size - */ - public void setCenterTextSize(float sizeDp) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize( - Utils.convertDpToPixel(sizeDp)); - } - - /** - * Sets the size of the center text of the PieChart in pixels. - * - * @param size - */ - public void setCenterTextSizePixels(float sizePixels) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); - } - - /** - * Sets the color of the center text of the PieChart. - * - * @param color - */ - public void setCenterTextColor(int color) { - ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color); - } - - /** - * sets the radius of the hole in the center of the piechart in percent of - * the maximum radius (max = the radius of the whole chart), default 50% - * - * @param size - */ - public void setHoleRadius(final float percent) { - mHoleRadiusPercent = percent; - } - - /** - * Returns the size of the hole radius in percent of the total radius. - * - * @return - */ - public float getHoleRadius() { - return mHoleRadiusPercent; - } - - public void setTransparentCircleColor(int color) { - ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setColor(color); - } - - /** - * sets the radius of the transparent circle that is drawn next to the hole - * in the piechart in percent of the maximum radius (max = the radius of the - * whole chart), default 55% -> means 5% larger than the center-hole by - * default - * - * @param percent - */ - public void setTransparentCircleRadius(final float percent) { - mTransparentCircleRadiusPercent = percent; - } - - public float getTransparentCircleRadius() { - return mTransparentCircleRadiusPercent; - } - - /** - * set this to true to draw the x-value text into the pie slices - * - * @param enabled - */ - public void setDrawSliceText(boolean enabled) { - mDrawXLabels = enabled; - } - - /** - * returns true if drawing x-values is enabled, false if not - * - * @return - */ - public boolean isDrawSliceTextEnabled() { - return mDrawXLabels; - } - - /** - * Returns true if the chart is set to draw each end of a pie-slice - * "rounded". - * - * @return - */ - public boolean isDrawRoundedSlicesEnabled() { - return mDrawRoundedSlices; - } - - /** - * If this is enabled, values inside the PieChart are drawn in percent and - * not with their original value. Values provided for the ValueFormatter to - * format are then provided in percent. - * - * @param enabled - */ - public void setUsePercentValues(boolean enabled) { - mUsePercentValues = enabled; - } - - /** - * Returns true if using percentage values is enabled for the chart. - * - * @return - */ - public boolean isUsePercentValuesEnabled() { - return mUsePercentValues; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java deleted file mode 100644 index 47434ebc95..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/PieRadarChartBase.java +++ /dev/null @@ -1,474 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.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.data.ChartData; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.listener.PieRadarChartTouchListener; -import com.github.mikephil.charting.utils.SelInfo; -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 */ - private float mRotationAngle = 270f; - - /** holds the raw version of the current rotation angle of the chart */ - private float mRawRotationAngle = 270f; - - /** flag that indicates if rotation is enabled or not */ - protected boolean mRotateEnabled = true; - - /** the pie- and radarchart touchlistener */ - protected OnTouchListener mListener; - - public PieRadarChartBase(Context context) { - super(context); - } - - public PieRadarChartBase(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mListener = new PieRadarChartTouchListener(this); - } - - @Override - protected void calcMinMax() { - mDeltaX = mData.getXVals().size() - 1; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - // use the pie- and radarchart listener own listener - if (mTouchEnabled && mListener != null) - return mListener.onTouch(this, event); - else - return super.onTouchEvent(event); - } - - @Override - public void computeScroll() { - - if (mListener instanceof PieRadarChartTouchListener) - ((PieRadarChartTouchListener) mListener).computeScroll(); - } - - @Override - public void notifyDataSetChanged() { - if (mDataNotSet) - return; - - calcMinMax(); - - mLegendRenderer.computeLegend(mData); - - calculateOffsets(); - } - - @Override - protected void calculateOffsets() { - - float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f; - - if (mLegend != null && mLegend.isEnabled()) { - - if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART_CENTER) { - - // this is the space between the legend and the chart - float spacing = Utils.convertDpToPixel(13f); - - legendRight = getFullLegendWidth() + 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 = getFullLegendWidth() + 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 = getFullLegendWidth() + 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 = getFullLegendWidth() + 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; - } - - } else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT - || mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) { - - legendBottom = getRequiredBottomOffset(); - } - - legendLeft += getRequiredBaseOffset(); - legendRight += getRequiredBaseOffset(); - legendTop += getRequiredBaseOffset(); - } - - float min = Utils.convertDpToPixel(10f); - - float offsetLeft = Math.max(min, legendLeft); - float offsetTop = Math.max(min, legendTop); - float offsetRight = Math.max(min, legendRight); - float offsetBottom = Math.max(min, Math.max(getRequiredBaseOffset(), legendBottom)); - - mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); - - if (mLogEnabled) - Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop - + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); - } - - /** - * returns the angle relative to the chart center for the given point on the - * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH, - * 90° is EAST, ... - * - * @param x - * @param y - * @return - */ - public float getAngleForPoint(float x, float y) { - - PointF c = getCenterOffsets(); - - double tx = x - c.x, ty = y - c.y; - double length = Math.sqrt(tx * tx + ty * ty); - double r = Math.acos(ty / length); - - float angle = (float) Math.toDegrees(r); - - if (x > c.x) - angle = 360f - angle; - - // add 90° because chart starts EAST - angle = angle + 90f; - - // neutralize overflow - if (angle > 360f) - angle = angle - 360f; - - return angle; - } - - /** - * Calculates the position around a center point, depending on the distance - * from the center, and the angle of the position around the center. - * - * @param center - * @param dist - * @param angle in degrees, converted to radians internally - * @return - */ - protected PointF getPosition(PointF 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)))); - return p; - } - - /** - * Returns the distance of a certain point on the chart to the center of the - * chart. - * - * @param c the center - * @param x - * @param y - * @return - */ - public float distanceToCenter(float x, float y) { - - PointF c = getCenterOffsets(); - - float dist = 0f; - - float xDist = 0f; - float yDist = 0f; - - if (x > c.x) { - xDist = x - c.x; - } else { - xDist = c.x - x; - } - - if (y > c.y) { - yDist = y - c.y; - } else { - yDist = c.y - y; - } - - // pythagoras - dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0)); - - return dist; - } - - /** - * Returns the xIndex for the given angle around the center of the chart. - * Returns -1 if not found / outofbounds. - * - * @param angle - * @return - */ - public abstract int getIndexForAngle(float angle); - - /** - * Set an offset for the rotation of the RadarChart in degrees. Default 270f - * --> top (NORTH) - * - * @param angle - */ - public void setRotationAngle(float angle) { - mRawRotationAngle = angle; - mRotationAngle = Utils.getNormalizedAngle(mRawRotationAngle); - } - - /** - * gets the raw version of the current rotation angle of the pie chart - * the returned value could be any value, negative or positive, outside of the 360 degrees. - * this is used when working with rotation direction, mainly by gestures and animations. - * - * @return - */ - public float getRawRotationAngle() { - return mRawRotationAngle; - } - - /** - * gets a normalized version of the current rotation angle of the pie chart, - * which will always be between 0.0 < 360.0 - * - * @return - */ - public float getRotationAngle() { - return mRotationAngle; - } - - /** - * Set this to true to enable the rotation / spinning of the chart by touch. - * Set it to false to disable it. Default: true - * - * @param enabled - */ - public void setRotationEnabled(boolean enabled) { - mRotateEnabled = enabled; - } - - /** - * Returns true if rotation of the chart by touch is enabled, false if not. - * - * @return - */ - public boolean isRotationEnabled() { - return mRotateEnabled; - } - - /** - * returns the diameter of the pie- or radar-chart - * - * @return - */ - public float getDiameter() { - RectF content = mViewPortHandler.getContentRect(); - return Math.min(content.width(), content.height()); - } - - /** - * Returns the radius of the chart in pixels. - * - * @return - */ - public abstract float getRadius(); - - /** - * Returns the required bottom offset for the chart. - * - * @return - */ - protected abstract float getRequiredBottomOffset(); - - /** - * Returns the base offset needed for the chart without calculating the - * legend size. - * - * @return - */ - protected abstract float getRequiredBaseOffset(); - - /** - * Returns the required right offset for the chart. - * - * @return - */ - private float getFullLegendWidth() { - return mLegend.mTextWidthMax + mLegend.getFormSize() + mLegend.getFormToTextSpace(); - } - - /** - * set a new (e.g. custom) charttouchlistener NOTE: make sure to - * setTouchEnabled(true); if you need touch gestures on the chart - * - * @param l - */ - public void setOnTouchListener(OnTouchListener l) { - this.mListener = l; - } - - @Override - public float getYChartMax() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public float getYChartMin() { - // TODO Auto-generated method stub - return 0; - } - - /** - * Returns an array of SelInfo objects for the given x-index. The SelInfo - * 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 getYValsAtIndex(int xIndex) { - - List vals = new ArrayList(); - - for (int i = 0; i < mData.getDataSetCount(); i++) { - - DataSet dataSet = mData.getDataSetByIndex(i); - - // extract all y-values from all DataSets at the given x-index - float yVal = dataSet.getYValForXIndex(xIndex); - - if (!Float.isNaN(yVal)) { - vals.add(new SelInfo(yVal, i, dataSet)); - } - } - - return vals; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO ANIMATION */ - - /** - * Applys a spin animation to the Chart. - * - * @param durationmillis - * @param fromangle - * @param toangle - */ - @SuppressLint("NewApi") - public void spin(int durationmillis, float fromangle, float toangle, Easing.EasingOption easing) { - - if (android.os.Build.VERSION.SDK_INT < 11) - return; - - setRotationAngle(fromangle); - - ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, - toangle); - spinAnimator.setDuration(durationmillis); - spinAnimator.setInterpolator(Easing.getEasingFunctionFromOption(easing)); - - spinAnimator.addUpdateListener(new AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - postInvalidate(); - } - }); - spinAnimator.start(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java deleted file mode 100644 index 4011fa0608..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/charts/ScatterChart.java +++ /dev/null @@ -1,86 +0,0 @@ - -package com.github.mikephil.charting.charts; - -import android.content.Context; -import android.util.AttributeSet; - -import com.github.mikephil.charting.data.ScatterData; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; -import com.github.mikephil.charting.renderer.ScatterChartRenderer; - -/** - * The ScatterChart. Draws dots, triangles, squares and custom shapes into the - * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the - * worst performance. - * - * @author Philipp Jahoda - */ -public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { - - /** - * enum that defines the shape that is drawn where the values are, CIRCLE - * and SCQUARE offer the best performance, TRIANGLE has the worst - * performance. - */ - public enum ScatterShape { - CROSS, TRIANGLE, CIRCLE, SQUARE - } - - public ScatterChart(Context context) { - super(context); - } - - public ScatterChart(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ScatterChart(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void init() { - super.init(); - - mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); - mXChartMin = -0.5f; - } - - // @Override - // protected void calculateOffsets() { - // super.calculateOffsets(); - // - // float offset = mData.getGreatestShapeSize() / 2f; - // mViewPortHandler.restrainViewPort(mViewPortHandler.offsetLeft() - offset, - // mViewPortHandler.offsetTop(), mViewPortHandler.offsetRight() - offset, - // mViewPortHandler.offsetBottom()); - // - // prepareOffsetMatrix(); - // } - - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (mDeltaX == 0 && mData.getYValCount() > 0) - mDeltaX = 1; - - mXChartMax += 0.5f; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - } - - /** - * Returns all possible predefined ScatterShapes. - * - * @return - */ - public static ScatterShape[] getAllPossibleShapes() { - return new ScatterShape[] { - ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS - }; - } - - public ScatterData getScatterData() { - return mData; - }; -} 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 f9eb05d2ec..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/AxisBase.java +++ /dev/null @@ -1,285 +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 b79d3ca3b2..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/Legend.java +++ /dev/null @@ -1,490 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Paint; - -import com.github.mikephil.charting.utils.Utils; - -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, - PIECHART_CENTER - } - - public enum LegendForm { - SQUARE, CIRCLE, LINE - } - - public enum LegendDirection { - LEFT_TO_RIGHT, RIGHT_TO_LEFT - } - - /** the legend colors */ - private int[] mColors; - - /** the legend labels */ - private String[] mLabels; - - /** 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 = 5f; - - /** - * 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; - - /** default constructor */ - public Legend() { - - mFormSize = Utils.convertDpToPixel(8f); - mXEntrySpace = Utils.convertDpToPixel(6f); - mYEntrySpace = Utils.convertDpToPixel(5f); - mFormToTextSpace = Utils.convertDpToPixel(5f); - mTextSize = Utils.convertDpToPixel(10f); - mStackSpace = Utils.convertDpToPixel(3f); - this.mXOffset = Utils.convertDpToPixel(5f); - this.mYOffset = Utils.convertDpToPixel(6f); - } - - /** - * 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); - } - - public void setColors(List colors) { - mColors = Utils.convertIntegers(colors);; - } - - public void setLabels(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[] getLegendLabels() { - return mLabels; - } - - /** - * Sets a custom array of labels for the legend. Make sure the labels array - * has the same length as the colors array. - * - * @param labels - */ - public void setLabels(String[] labels) { - this.mLabels = labels; - } - - /** - * Returns the legend-label at the given index. - * - * @param index - * @return - */ - public String getLabel(int index) { - return mLabels[index]; - } - - /** - * 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); - } - -// /** -// * applies the state from the legend in the parameter to this legend (except -// * colors, labels and offsets) -// * -// * @param l -// */ -// public void apply(Legend l) { -// -// mPosition = l.mPosition; -// mShape = l.mShape; -// mTypeface = l.mTypeface; -// mFormSize = l.mFormSize; -// mXEntrySpace = l.mXEntrySpace; -// mYEntrySpace = l.mYEntrySpace; -// mFormToTextSpace = l.mFormToTextSpace; -// mTextSize = l.mTextSize; -// mStackSpace = l.mStackSpace; -// mTextColor = l.mTextColor; -// mEnabled = l.mEnabled; -// mXOffset = l.mXOffset; -// mYOffset = l.mYOffset; -// } - - /** - * 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] != -2) - 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; - - /** - * 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) { - - 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 { - - 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 14a0aed6c4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/MarkerView.java +++ /dev/null @@ -1,97 +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; - -/** - * 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(); - posy += getYOffset(); - - // 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 dataSetIndex the index of the DataSet the selected value is in - */ - public abstract void refreshContent(Entry e, int dataSetIndex); - - /** - * 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. - * - * @return - */ - public abstract int getXOffset(); - - /** - * 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. - * - * @return - */ - public abstract int getYOffset(); -} 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 1e03100fbf..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/XAxis.java +++ /dev/null @@ -1,200 +0,0 @@ - -package com.github.mikephil.charting.components; - -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 calculated by the - * calcTextWidth() method of the utils - */ - public int mLabelWidth = 1; - - /** - * height of the x-axis labels in pixels - this is calculated by the - * calcTextHeight() method of the utils - */ - public int mLabelHeight = 1; - - /** - * 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; - - /** - * the modulus that indicates if a value at a specified index in an - * array(list) for the y-axis-labels is drawn or not. If index % modulus == - * 0 DRAW, else dont draw. THIS IS ONLY FOR HORIZONTAL BARCHART. - */ - public int mYAxisLabelModulus = 1; - - /** - * 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(); - } - - /** - * 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; - } - - /** - * 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 space - */ - 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 - * - * @param space - */ - 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; - } - - @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/components/YAxis.java b/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java deleted file mode 100644 index 85eb9327d1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/components/YAxis.java +++ /dev/null @@ -1,409 +0,0 @@ - -package com.github.mikephil.charting.components; - -import android.graphics.Paint; - -import com.github.mikephil.charting.utils.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -/** - * Class representing the y-axis labels settings and its entries. Only use the - * setter methods to modify it. Do not access public variables directly. Be - * aware that not all features the YLabels class provides are suitable for the - * RadarChart. Customizations that affect the value range of the axis need to be - * applied before setting data for the chart. - * - * @author Philipp Jahoda - */ -public class YAxis extends AxisBase { - - /** custom formatter that is used instead of the auto-formatter if set */ - protected ValueFormatter mValueFormatter; - - /** 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; - - /** indicates if the top y-label entry is drawn or not */ - private boolean mDrawTopYLabelEntry = true; - - /** if true, the y-labels show only the minimum and maximum value */ - protected boolean mShowOnlyMinMax = false; - - /** flag that indicates if the axis is inverted or not */ - protected boolean mInverted = false; - - /** if true, the y-label entries will always start at zero */ - protected boolean mStartAtZero = true; - - /** 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 - */ - protected float mSpacePercentTop = 10f; - - /** - * axis space from the smallest value to the bottom in percent of the total - * axis range - */ - protected float mSpacePercentBottom = 10f; - - public float mAxisMaximum = 0f; - public float mAxisMinimum = 0f; - - /** the total range of values this axis covers */ - public float mAxisRange = 0f; - - /** the position of the y-labels relative to the chart */ - private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; - - /** enum for the position of the y-labels relative to the chart */ - public enum YAxisLabelPosition { - OUTSIDE_CHART, INSIDE_CHART - } - - /** the side this axis object represents */ - private AxisDependency mAxisDependency; - - /** - * Enum that specifies the axis a DataSet should be plotted against, either - * LEFT or RIGHT. - * - * @author Philipp Jahoda - */ - public enum AxisDependency { - LEFT, RIGHT - } - - public YAxis() { - super(); - this.mAxisDependency = AxisDependency.LEFT; - } - - public YAxis(AxisDependency position) { - super(); - this.mAxisDependency = position; - } - - public AxisDependency getAxisDependency() { - return mAxisDependency; - } - - /** - * returns the position of the y-labels - */ - public YAxisLabelPosition getLabelPosition() { - return mPosition; - } - - /** - * sets the position of the y-labels - * - * @param pos - */ - public void setPosition(YAxisLabelPosition pos) { - mPosition = pos; - } - - /** - * returns true if drawing the top y-axis label entry is enabled - * - * @return - */ - public boolean isDrawTopYLabelEntryEnabled() { - return mDrawTopYLabelEntry; - } - - /** - * set this to true to enable drawing the top y-label entry. Disabling this - * can be helpful when the top y-label and left x-label interfere with each - * other. default: true - * - * @param enabled - */ - public void setDrawTopYLabelEntry(boolean enabled) { - mDrawTopYLabelEntry = enabled; - } - - /** - * sets the number of label entries for the y-axis max = 25, min = 2, - * default: 6, be aware that this number is not fixed and can only be - * approximated - * - * @param yCount - */ - public void setLabelCount(int yCount) { - - if (yCount > 25) - yCount = 25; - if (yCount < 2) - yCount = 2; - - mLabelCount = yCount; - } - - /** - * Returns the number of label entries the y-axis should have - * - * @return - */ - public int getLabelCount() { - return mLabelCount; - } - - /** - * If enabled, the YLabels will only show the minimum and maximum value of - * the chart. This will ignore/override the set label count. - * - * @param enabled - */ - public void setShowOnlyMinMax(boolean enabled) { - mShowOnlyMinMax = enabled; - } - - /** - * Returns true if showing only min and max value is enabled. - * - * @return - */ - public boolean isShowOnlyMinMaxEnabled() { - return mShowOnlyMinMax; - } - - /** - * If this is set to true, the y-axis is inverted which means that low - * values are on top of the chart, high values on bottom. - * - * @param enabled - */ - public void setInverted(boolean enabled) { - mInverted = enabled; - } - - /** - * If this returns true, the y-axis is inverted. - * - * @return - */ - public boolean isInverted() { - return mInverted; - } - - /** - * enable this to force the y-axis labels to always start at zero - * - * @param enabled - */ - public void setStartAtZero(boolean enabled) { - this.mStartAtZero = enabled; - } - - /** - * returns true if the chart is set to start at zero, false otherwise - * - * @return - */ - public boolean isStartAtZeroEnabled() { - return mStartAtZero; - } - - 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; - } - - /** - * Sets the top axis space in percent of the full range. Default 10f - * - * @param percent - */ - public void setSpaceTop(float percent) { - mSpacePercentTop = percent; - } - - /** - * Returns the top axis space in percent of the full range. Default 10f - * - * @return - */ - public float getSpaceTop() { - return mSpacePercentTop; - } - - /** - * Sets the bottom axis space in percent of the full range. Default 10f - * - * @param percent - */ - public void setSpaceBottom(float percent) { - mSpacePercentBottom = percent; - } - - /** - * Returns the bottom axis space in percent of the full range. Default 10f - * - * @return - */ - public float getSpaceBottom() { - return mSpacePercentBottom; - } - - public float getRequiredWidthSpace(Paint p) { - - p.setTextSize(mTextSize); - - String label = getLongestLabel(); - return (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; - } - - public float getRequiredHeightSpace(Paint p) { - - p.setTextSize(mTextSize); - - String label = getLongestLabel(); - 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). - * - * @param index - * @return - */ - public String getFormattedLabel(int index) { - - if (index < 0 || index >= mEntries.length) - return ""; - else - return getValueFormatter().getFormattedValue(mEntries[index]); - } - - /** - * Sets the formatter to be used for drawing the values inside the chart. If - * no formatter is set, the chart will automatically determine a reasonable - * formatting (concerning decimals) for all the values that are drawn inside - * the chart. Use chart.getDefaultValueFormatter() to use the formatter - * calculated by the chart. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - - if (f == null) - return; - else - mValueFormatter = f; - } - - /** - * Returns the formatter used for drawing the values inside the chart. - * - * @return - */ - public ValueFormatter getValueFormatter() { - return mValueFormatter; - } - - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public boolean needsDefaultFormatter() { - if (mValueFormatter == null) - return true; - if (mValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; - } - - /** - * Returns true if this axis needs horizontal offset, false if no offset is - * needed. - * - * @return - */ - public boolean needsOffset() { - if (isEnabled() && isDrawLabelsEnabled() - && getLabelPosition() == YAxisLabelPosition.OUTSIDE_CHART) - return true; - else - return false; - } -} 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 3cbd1b1e9f..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarData.java +++ /dev/null @@ -1,112 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -/** - * Data object that represents all data for the BarChart. - * - * @author Philipp Jahoda - */ -public class BarData extends BarLineScatterCandleData { - - /** 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, BarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BarData(String[] xVals, BarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(BarDataSet 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/BarDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java deleted file mode 100644 index e03557cef0..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarDataSet.java +++ /dev/null @@ -1,216 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Color; - -import java.util.ArrayList; -import java.util.List; - -public class BarDataSet extends BarLineScatterCandleDataSet { - - /** 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; - - /** - * the overall entry count, including counting each stack-value individually - */ - private int mEntryCountStacks = 0; - - /** - * array of labels used to describe the different values of the stacked bars - */ - private String[] mStackLabels = new String[] { - "Stack" - }; - - public BarDataSet(List yVals, String label) { - super(yVals, label); - - mHighLightColor = Color.rgb(0, 0, 0); - - calcStackSize(yVals); - calcEntryCountIncludingStacks(yVals); - } - - @Override - public DataSet copy() { - - List yVals = new ArrayList(); - - for (int i = 0; i < mYVals.size(); i++) { - yVals.add(((BarEntry) mYVals.get(i)).copy()); - } - - 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; - - return copied; - } - - /** - * Calculates the total number of entries this DataSet represents, including - * stacks. All values belonging to a stack are calculated separately. - */ - private void calcEntryCountIncludingStacks(List yVals) { - - mEntryCountStacks = 0; - - for (int i = 0; i < yVals.size(); i++) { - - float[] vals = yVals.get(i).getVals(); - - if (vals == null) - mEntryCountStacks++; - else - mEntryCountStacks += vals.length; - } - } - - /** - * calculates the maximum stacksize that occurs in the Entries array of this - * DataSet - */ - private void calcStackSize(List yVals) { - - for (int i = 0; i < yVals.size(); i++) { - - float[] vals = yVals.get(i).getVals(); - - if (vals != null && vals.length > mStackSize) - mStackSize = vals.length; - } - } - - /** - * Returns the maximum number of bars that can be stacked upon another in - * this DataSet. - * - * @return - */ - public int getStackSize() { - return mStackSize; - } - - /** - * Returns true if this DataSet is stacked (stacksize > 1) or not. - * - * @return - */ - public boolean isStacked() { - return mStackSize > 1 ? true : false; - } - - /** - * returns the overall entry count, including counting each stack-value - * individually - * - * @return - */ - public int getEntryCountStacks() { - return mEntryCountStacks; - } - - /** - * returns the space between bars in percent of the whole width of one value - * - * @return - */ - public float getBarSpacePercent() { - return mBarSpace * 100f; - } - - /** - * returns the space between bars as the actual value (0 - 1.0f) - * - * @return - */ - public float getBarSpace() { - return mBarSpace; - } - - /** - * sets the space between the bars in percent 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; - } - - /** - * Returns the color used for drawing the bar-shadows. The bar shadows is a - * surface behind the bar that indicates the maximum value. - * - * @return - */ - 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; - } - - /** - * Returns the alpha value (transparency) that is used for drawing the - * highlight indicator. - * - * @return - */ - 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; - } - - /** - * returns the labels used for the different value-stacks - * - * @return - */ - public String[] getStackLabels() { - return mStackLabels; - } -} 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 cd1a63c68d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarEntry.java +++ /dev/null @@ -1,144 +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; - - /** - * Constructor for stacked bar entries. - * - * @param vals - the stack values - * @param xIndex - */ - public BarEntry(float[] vals, int xIndex) { - super(calcSum(vals), xIndex); - - this.mVals = vals; - } - - /** - * 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 - * @param xIndex - * @param label Additional description label. - */ - public BarEntry(float[] vals, int xIndex, String label) { - super(calcSum(vals), xIndex, label); - - this.mVals = vals; - } - - /** - * 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.mVals = 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) { - mVals = vals; - } - - /** - * Returns the closest value inside the values array (for stacked barchart) - * to the value given as a parameter. The closest value must be higher - * (above) the provided value. - * - * @param val - * @return - */ - public int getClosestIndexAbove(float val) { - - if (mVals == null) - return 0; - - int index = mVals.length - 1; - float remainder = 0f; - - while(index > 0 && val > mVals[index] + remainder) { - remainder += mVals[index]; - index--; - } - - return index; - } - - 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; - } - - /** - * Calculates the sum across all values. - * - * @param vals - * @return - */ - private static float calcSum(float[] vals) { - - float sum = 0f; - - for (float f : vals) - sum += f; - - return sum; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java b/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java deleted file mode 100644 index b9dbda531d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleData.java +++ /dev/null @@ -1,33 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.List; - -/** - * Baseclass for all Line, Bar and ScatterData. - * - * @author Philipp Jahoda - */ -public abstract class BarLineScatterCandleData> - extends ChartData { - - public BarLineScatterCandleData() { - super(); - } - - public BarLineScatterCandleData(List xVals) { - super(xVals); - } - - public BarLineScatterCandleData(String[] xVals) { - super(xVals); - } - - public BarLineScatterCandleData(List xVals, List sets) { - super(xVals, sets); - } - - public BarLineScatterCandleData(String[] xVals, List sets) { - super(xVals, sets); - } -} 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 943cf902eb..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleData.java +++ /dev/null @@ -1,54 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -public class BubbleData extends BarLineScatterCandleData { - - 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, BubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public BubbleData(String[] xVals, BubbleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(BubbleDataSet 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 (BubbleDataSet 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 d82eb87d03..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/BubbleDataSet.java +++ /dev/null @@ -1,139 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Color; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -public class BubbleDataSet extends BarLineScatterCandleDataSet { - - // 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); - } - - /** - * Sets the width of the circle that surrounds the bubble when highlighted, - * in dp. - * - * @param width - */ - public void setHighlightCircleWidth(float width) { - mHighlightCircleWidth = Utils.convertDpToPixel(width); - } - - public float getHighlightCircleWidth() { - return mHighlightCircleWidth; - } - - /** - * Sets a color with a specific alpha value. - * @param color - * @param alpha from 0-255 - */ - public void setColor(int color, int alpha) { - super.setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))); - } - - @Override - protected void calcMinMax() { - - final List entries = getYVals(); - - // need chart width to guess this properly - - for (BubbleEntry entry : entries) { - - 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 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; - } - - public float getXMax() { - return mXMax; - } - - public float getXMin() { - return mXMin; - } - - 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 1b25d5075c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleData.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -public class CandleData extends BarLineScatterCandleData { - - 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, CandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public CandleData(String[] xVals, CandleDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(CandleDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java deleted file mode 100644 index afb4a6bf82..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CandleDataSet.java +++ /dev/null @@ -1,240 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Paint; - -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -/** - * DataSet for the CandleStickChart. - * - * @author Philipp Jahoda - */ -public class CandleDataSet extends BarLineScatterCandleDataSet { - - /** the width of the shadow of the candle */ - private float mShadowWidth = 3f; - - /** the space between the candle entries, default 0.1f (10%) */ - private float mBodySpace = 0.1f; - - /** paint style when open <= close */ - protected Paint.Style mIncreasingPaintStyle = Paint.Style.FILL; - - /** paint style when open > close */ - protected Paint.Style mDecreasingPaintStyle = Paint.Style.STROKE; - - /** 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; - - public CandleDataSet(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(((CandleEntry) mYVals.get(i)).copy()); - } - - CandleDataSet copied = new CandleDataSet(yVals, getLabel()); - copied.mColors = mColors; - copied.mShadowWidth = mShadowWidth; - copied.mBodySpace = mBodySpace; - copied.mHighLightColor = mHighLightColor; - copied.mIncreasingPaintStyle = mIncreasingPaintStyle; - copied.mDecreasingPaintStyle = mDecreasingPaintStyle; - copied.mShadowColor = mShadowColor; - - return copied; - } - - @Override - protected void calcMinMax() { - // super.calcMinMax(); - - if (mYVals.size() == 0) { - return; - } - - List entries = mYVals; - - mYMin = entries.get(0).getLow(); - mYMax = entries.get(0).getHigh(); - - for (int i = 0; i < entries.size(); i++) { - - CandleEntry e = entries.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 setBodySpace(float space) { - - if (space < 0f) - space = 0f; - if (space > 0.45f) - space = 0.45f; - - mBodySpace = space; - } - - /** - * Returns the space that is left out on the left and right side of each - * candle. - * - * @return - */ - public float getBodySpace() { - return mBodySpace; - } - - /** - * Sets the width of the candle-shadow-line in pixels. Default 3f. - * - * @param width - */ - public void setShadowWidth(float width) { - mShadowWidth = Utils.convertDpToPixel(width); - } - - /** - * Returns the width of the candle-shadow-line in pixels. - * - * @return - */ - public float getShadowWidth() { - return mShadowWidth; - } - - // TODO - /** - * It is necessary to implement ColorsList class that will encapsulate - * colors list functionality, because It's wrong to copy paste setColor, - * addColor, ... resetColors for each time when we want to add a coloring - * options for one of objects - * - * @author Mesrop - */ - - /** BELOW THIS COLOR HANDLING */ - - /** - * Sets the one and ONLY color that should be used for this DataSet when - * open > close. - * - * @param color - */ - public void setDecreasingColor(int color) { - mDecreasingColor = color; - } - - /** - * Returns the decreasing color. - * - * @return - */ - public int getDecreasingColor() { - return mDecreasingColor; - } - - /** - * 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; - } - - /** - * Returns the increasing color. - * - * @return - */ - public int getIncreasingColor() { - return mIncreasingColor; - } - - /** - * Returns paint style when open > close - * - * @return - */ - public Paint.Style getDecreasingPaintStyle() { - return mDecreasingPaintStyle; - } - - /** - * Sets paint style when open > close - * - * @param decreasingPaintStyle - */ - public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) { - this.mDecreasingPaintStyle = decreasingPaintStyle; - } - - /** - * Returns paint style when open <= close - * - * @return - */ - public Paint.Style getIncreasingPaintStyle() { - return mIncreasingPaintStyle; - } - - /** - * Sets paint style when open <= close - * - * @param paintStyle - */ - public void setIncreasingPaintStyle(Paint.Style paintStyle) { - this.mIncreasingPaintStyle = paintStyle; - } - - /** - * Returns shadow color for all entries - * - * @return - */ - public int getShadowColor() { - return mShadowColor; - } - - /** - * Sets shadow color for all entries - * - * @param shadowColor - */ - public void setShadowColor(int shadowColor) { - this.mShadowColor = shadowColor; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java deleted file mode 100644 index 2a04822117..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ChartData.java +++ /dev/null @@ -1,936 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Typeface; -import android.util.Log; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.ValueFormatter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Class that holds all relevant data that represents the chart. That involves - * at least one (or more) DataSets, and an array of x-values. - * - * @author Philipp Jahoda - */ -public abstract class ChartData> { - - /** maximum y-value in the y-value array across all axes */ - protected float mYMax = 0.0f; - - /** the minimum y-value in the y-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; - - /** the total sum of all y-values */ - private float mYValueSum = 0f; - - /** total number of y-values across all DataSet objects */ - private int mYValCount = 0; - - /** - * contains the average length (in characters) an entry in the x-vals array - * has - */ - private float mXValAverageLength = 0; - - /** holds all x-values the chart represents */ - protected List mXVals; - - /** array that holds all DataSets the ChartData object represents */ - protected List mDataSets; - - public ChartData() { - mXVals = new ArrayList(); - mDataSets = new ArrayList(); - } - - /** - * 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(mDataSets); - } - - /** - * Constructor for only x-values. This constructor can be used for setting - * up an empty chart without data. - * - * @param xVals - */ - public ChartData(String[] xVals) { - this.mXVals = arrayToList(xVals); - this.mDataSets = new ArrayList(); - init(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 - */ - public ChartData(List xVals, List sets) { - this.mXVals = xVals; - this.mDataSets = sets; - - init(mDataSets); - } - - /** - * constructor that takes string array instead of List string - * - * @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 - */ - public ChartData(String[] xVals, List sets) { - this.mXVals = arrayToList(xVals); - this.mDataSets = sets; - - init(mDataSets); - } - - /** - * Turns an array of strings into an List of strings. - * - * @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(List> dataSets) { - - isLegal(dataSets); - - calcMinMax(dataSets); - calcYValueSum(dataSets); - calcYValueCount(dataSets); - - calcXValAverageLength(); - } - - /** - * calculates the average length (in characters) across all x-value strings - */ - private void calcXValAverageLength() { - - if (mXVals.size() <= 0) { - mXValAverageLength = 1; - return; - } - - float sum = 1f; - - for (int i = 0; i < mXVals.size(); i++) { - sum += mXVals.get(i).length(); - } - - mXValAverageLength = sum / (float) mXVals.size(); - } - - /** - * Checks if the combination of x-values array and DataSet array is legal or - * not. - * - * @param dataSets - */ - private void isLegal(List> dataSets) { - - if (dataSets == null) - return; - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i) - .getYVals() - .size() > mXVals.size()) { - throw new IllegalArgumentException( - "One or more of the DataSet Entry arrays are longer than the x-values array of this ChartData object."); - } - } - } - - /** - * Call this method to let the CartData know that the underlying data has - * changed. - */ - public void notifyDataChanged() { - init(mDataSets); - } - - /** - * calc minimum and maximum y value over all datasets - */ - protected void calcMinMax(List> dataSets) { - - if (dataSets == null || dataSets.size() < 1) { - - mYMax = 0f; - mYMin = 0f; - } else { - - // calculate absolute min and max - mYMin = dataSets.get(0).getYMin(); - mYMax = dataSets.get(0).getYMax(); - - for (int i = 0; i < dataSets.size(); i++) { - if (dataSets.get(i).getYMin() < mYMin) - mYMin = dataSets.get(i).getYMin(); - - if (dataSets.get(i).getYMax() > mYMax) - mYMax = dataSets.get(i).getYMax(); - } - - // left axis - T firstLeft = getFirstLeft(); - - if (firstLeft != null) { - - mLeftAxisMax = firstLeft.getYMax(); - mLeftAxisMin = firstLeft.getYMin(); - - for (DataSet dataSet : dataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) { - if (dataSet.getYMin() < mLeftAxisMin) - mLeftAxisMin = dataSet.getYMin(); - - if (dataSet.getYMax() > mLeftAxisMax) - mLeftAxisMax = dataSet.getYMax(); - } - } - } - - // right axis - T firstRight = getFirstRight(); - - if (firstRight != null) { - - mRightAxisMax = firstRight.getYMax(); - mRightAxisMin = firstRight.getYMin(); - - for (DataSet dataSet : dataSets) { - if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { - if (dataSet.getYMin() < mRightAxisMin) - mRightAxisMin = dataSet.getYMin(); - - if (dataSet.getYMax() > mRightAxisMax) - mRightAxisMax = dataSet.getYMax(); - } - } - } - - // in case there is only one axis, adjust the second axis - handleEmptyAxis(firstLeft, firstRight); - } - } - - /** - * calculates the sum of all y-values in all datasets - */ - protected void calcYValueSum(List> dataSets) { - - mYValueSum = 0; - - if (dataSets == null) - return; - - for (int i = 0; i < dataSets.size(); i++) { - mYValueSum += Math.abs(dataSets.get(i).getYValueSum()); - } - } - - /** - * Calculates the total number of y-values across all DataSets the ChartData - * represents. - * - * @return - */ - protected void calcYValueCount(List> dataSets) { - - mYValCount = 0; - - if (dataSets == null) - return; - - int count = 0; - - for (int i = 0; i < dataSets.size(); i++) { - count += dataSets.get(i).getEntryCount(); - } - - mYValCount = count; - } - - /** ONLY GETTERS AND SETTERS BELOW THIS */ - - /** - * returns the number of LineDataSets this object contains - * - * @return - */ - public int getDataSetCount() { - if (mDataSets == null) - return 0; - return mDataSets.size(); - } - - /** - * Returns the smallest y-value the data object contains. - * - * @return - */ - public float getYMin() { - return mYMin; - } - - /** - * Returns the minimum y-value for the specified axis. - * - * @param axis - * @return - */ - public float getYMin(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMin; - else - return mRightAxisMin; - } - - /** - * Returns the greatest y-value the data object contains. - * - * @return - */ - public float getYMax() { - return mYMax; - } - - /** - * Returns the maximum y-value for the specified axis. - * - * @param axis - * @return - */ - public float getYMax(AxisDependency axis) { - if (axis == AxisDependency.LEFT) - return mLeftAxisMax; - else - return mRightAxisMax; - } - - /** - * returns the average length (in characters) across all values in the - * x-vals array - * - * @return - */ - public float getXValAverageLength() { - return mXValAverageLength; - } - - /** - * Returns the total y-value sum across all DataSet objects the this object - * represents. - * - * @return - */ - public float getYValueSum() { - return mYValueSum; - } - - /** - * Returns the total number of y-values across all DataSet objects the this - * object represents. - * - * @return - */ - public int getYValCount() { - return mYValCount; - } - - /** - * returns the x-values the chart represents - * - * @return - */ - public List getXVals() { - return mXVals; - } - - /** - * Adds a new x-value to the chart data. - * - * @param xVal - */ - public void addXValue(String xVal) { - - mXValAverageLength = (mXValAverageLength + xVal.length()) / 2f; - mXVals.add(xVal); - } - - /** - * Removes the x-value at the specified index. - * - * @param index - */ - public void removeXValue(int index) { - mXVals.remove(index); - } - - /** - * Returns an the array of DataSets this object holds. - * - * @return - */ - public List getDataSets() { - return mDataSets; - } - - /** - * Retrieve the index of a DataSet with a specific label from the ChartData. - * Search can be case sensitive or not. IMPORTANT: This method does - * calculations at runtime, do not over-use in performance critical - * situations. - * - * @param dataSets the DataSet array to search - * @param type - * @param ignorecase if true, the search is not case-sensitive - * @return - */ - protected int getDataSetIndexByLabel(List dataSets, String label, - boolean ignorecase) { - - if (ignorecase) { - for (int i = 0; i < dataSets.size(); i++) - if (label.equalsIgnoreCase(dataSets.get(i).getLabel())) - return i; - } else { - for (int i = 0; i < dataSets.size(); i++) - if (label.equals(dataSets.get(i).getLabel())) - return i; - } - - return -1; - } - - /** - * returns the 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() { - - String[] types = new String[mDataSets.size()]; - - for (int i = 0; i < mDataSets.size(); i++) { - types[i] = mDataSets.get(i).getLabel(); - } - - return types; - } - - /** - * Get the Entry for a corresponding highlight object - * - * @param highlight - * @return the entry that is highlighted - */ - public Entry getEntryForHighlight(Highlight highlight) { - return mDataSets.get(highlight.getDataSetIndex()).getEntryForXIndex( - highlight.getXIndex()); - } - - /** - * Returns the DataSet object with the given label. Search can be case - * sensitive or not. IMPORTANT: This method does calculations at runtime. - * Use with care in performance critical situations. - * - * @param label - * @param ignorecase - * @return - */ - public T getDataSetByLabel(String label, boolean ignorecase) { - - int index = getDataSetIndexByLabel(mDataSets, label, ignorecase); - - if (index < 0 || index >= mDataSets.size()) - return null; - else - return mDataSets.get(index); - } - - /** - * Returns the DataSet object at the given index. - * - * @param index - * @return - */ - public T getDataSetByIndex(int index) { - - if (mDataSets == null || index < 0 || index >= mDataSets.size()) - return null; - - return mDataSets.get(index); - } - - /** - * Adds a DataSet dynamically. - * - * @param d - */ - public void addDataSet(T d) { - - if (d == null) - return; - - mYValCount += d.getEntryCount(); - mYValueSum += d.getYValueSum(); - - 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(); - } - } - - 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; - } - } - - /** - * Removes the given DataSet from this data object. Also recalculates all - * minimum and maximum values. Returns true if a DataSet was removed, false - * if no DataSet could be removed. - * - * @param d - */ - public boolean removeDataSet(T d) { - - if (d == null) - return false; - - boolean removed = mDataSets.remove(d); - - // if a DataSet was removed - if (removed) { - - mYValCount -= d.getEntryCount(); - mYValueSum -= d.getYValueSum(); - - calcMinMax(mDataSets); - } - - return removed; - } - - /** - * Removes the DataSet at the given index in the DataSet array from the data - * object. Also recalculates all minimum and maximum values. Returns true if - * a DataSet was removed, false if no DataSet could be removed. - * - * @param index - */ - public boolean removeDataSet(int index) { - - if (index >= mDataSets.size() || index < 0) - return false; - - T set = mDataSets.get(index); - return removeDataSet(set); - } - - /** - * Adds an Entry to the DataSet at the specified index. Entries are added to - * the end of the list. - * - * @param entry - * @param dataSetIndex - */ - public void addEntry(Entry e, int dataSetIndex) { - - if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) { - - float val = e.getVal(); - - mYValCount += 1; - mYValueSum += val; - - if (mYMax < val) - mYMax = val; - if (mYMin > val) - mYMin = val; - - T set = mDataSets.get(dataSetIndex); - - if (set != null) { - - 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(); - } - - handleEmptyAxis(getFirstLeft(), getFirstRight()); - - // add the entry to the dataset - set.addEntry(e); - } - } else { - Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); - } - } - - /** - * Removes the given Entry object from the DataSet at the specified index. - * - * @param e - * @param dataSetIndex - */ - public boolean removeEntry(Entry e, int dataSetIndex) { - - // entry null, outofbounds - if (e == null || dataSetIndex >= mDataSets.size()) - return false; - - // remove the entry from the dataset - boolean removed = mDataSets.get(dataSetIndex).removeEntry(e.getXIndex()); - - if (removed) { - - float val = e.getVal(); - - mYValCount -= 1; - mYValueSum -= val; - - calcMinMax(mDataSets); - } - - return removed; - } - - /** - * Removes the Entry object at the given xIndex from the 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 dataSetIndex - * @return - */ - public boolean removeEntry(int xIndex, int dataSetIndex) { - - if (dataSetIndex >= mDataSets.size()) - return false; - - T dataSet = mDataSets.get(dataSetIndex); - Entry e = dataSet.getEntryForXIndex(xIndex); - - return removeEntry(e, dataSetIndex); - } - - /** - * Returns the DataSet that contains the provided Entry, or null, if no - * DataSet contains this Entry. - * - * @param e - * @return - */ - public T getDataSetForEntry(Entry e) { - - if (e == null) - return null; - - for (int i = 0; i < mDataSets.size(); i++) { - - T set = mDataSets.get(i); - - for (int j = 0; j < set.getEntryCount(); j++) { - if (e.equalTo(set.getEntryForXIndex(e.getXIndex()))) - return set; - } - } - - return null; - } - - /** - * Returns all colors used across all DataSet objects this object - * represents. - * - * @return - */ - public int[] getColors() { - - if (mDataSets == null) - return null; - - int clrcnt = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - clrcnt += mDataSets.get(i).getColors().size(); - } - - int[] colors = new int[clrcnt]; - int cnt = 0; - - for (int i = 0; i < mDataSets.size(); i++) { - - List clrs = mDataSets.get(i).getColors(); - - for (Integer clr : clrs) { - colors[cnt] = clr; - cnt++; - } - } - - return colors; - } - - /** - * Returns the index of the provided DataSet inside the DataSets array of - * this data object. Returns -1 if the DataSet was not found. - * - * @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; - } - - public T getFirstLeft() { - for (T dataSet : mDataSets) { - if (dataSet.getAxisDependency() == AxisDependency.LEFT) - return dataSet; - } - - return null; - } - - public T getFirstRight() { - for (T dataSet : mDataSets) { - 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. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - if (f == null) - return; - else { - for (DataSet set : mDataSets) { - set.setValueFormatter(f); - } - } - } - - /** - * Sets the color of the value-text (color in which the value-labels are - * drawn) for all DataSets this data object contains. - * - * @param color - */ - public void setValueTextColor(int color) { - for (DataSet set : mDataSets) { - set.setValueTextColor(color); - } - } - - /** - * Sets the Typeface for all value-labels for all DataSets this data object - * contains. - * - * @param color - */ - public void setValueTypeface(Typeface tf) { - for (DataSet set : mDataSets) { - set.setValueTypeface(tf); - } - } - - /** - * Sets the size (in dp) of the value-text for all DataSets this data object - * contains. - * - * @param color - */ - public void setValueTextSize(float size) { - for (DataSet set : mDataSets) { - set.setValueTextSize(size); - } - } - - /** - * Enables / disables drawing values (value-text) for all DataSets this data - * object contains. - * - * @param enabled - */ - public void setDrawValues(boolean enabled) { - for (DataSet set : mDataSets) { - set.setDrawValues(enabled); - } - } - - /** - * Clears this data object from all DataSets and removes all Entries. Don't - * forget to invalidate the chart after this. - */ - public void clearValues() { - 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. - * - * @param dataSet - * @return - */ - public boolean contains(T dataSet) { - - for (T set : mDataSets) { - if (set.equals(dataSet)) - return true; - } - - return false; - } -} 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 22c3e7a537..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/CombinedData.java +++ /dev/null @@ -1,95 +0,0 @@ - -package com.github.mikephil.charting.data; - -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 BarLineScatterCandleData> { - - 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(data.getDataSets()); - } - - public void setData(BarData data) { - mBarData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(ScatterData data) { - mScatterData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(CandleData data) { - mCandleData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - public void setData(BubbleData data) { - mBubbleData = data; - mDataSets.addAll(data.getDataSets()); - init(data.getDataSets()); - } - - 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; - } - - @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(); - } -} 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 ca170fa37d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/DataSet.java +++ /dev/null @@ -1,731 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.content.Context; -import android.graphics.Color; -import android.graphics.Typeface; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; - -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 { - - /** List representing all colors that are used for this DataSet */ - protected List mColors = null; - - /** 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; - - /** the total sum of all y-values */ - private float mYValueSum = 0f; - - /** label that describes the DataSet or the data the DataSet represents */ - private String mLabel = "DataSet"; - - /** flag that indicates if the DataSet is visible or not */ - private boolean mVisible = true; - - /** if true, y-values are drawn on the chart */ - protected boolean mDrawValues = true; - - /** the color used for the value-text */ - private int mValueColor = Color.BLACK; - - /** the size of the value-text labels */ - private float mValueTextSize = 17f; - - /** the typeface used for the value text */ - private Typeface mValueTypeface; - - /** custom formatter that is used instead of the auto-formatter if set */ - protected ValueFormatter mValueFormatter; - - /** this specifies which axis this DataSet should be plotted against */ - protected AxisDependency mAxisDependency = AxisDependency.LEFT; - - /** - * 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) { - - this.mLabel = label; - this.mYVals = yVals; - - if (mYVals == null) - mYVals = new ArrayList(); - - mColors = new ArrayList(); - - // default color - mColors.add(Color.rgb(140, 234, 255)); - - calcMinMax(); - calcYValueSum(); - } - - /** - * Use this method to tell the data set that the underlying data has changed - */ - public void notifyDataSetChanged() { - calcMinMax(); - calcYValueSum(); - } - - /** - * calc minimum and maximum y value - */ - protected void calcMinMax() { - if (mYVals.size() == 0) { - return; - } - - mYMin = mYVals.get(0).getVal(); - mYMax = mYVals.get(0).getVal(); - - for (int i = 0; i < mYVals.size(); i++) { - - Entry e = mYVals.get(i); - - if (e != null) { - - if (e.getVal() < mYMin) - mYMin = e.getVal(); - - if (e.getVal() > mYMax) - mYMax = e.getVal(); - } - } - } - - /** - * calculates the sum of all y-values - */ - private void calcYValueSum() { - - mYValueSum = 0; - - for (int i = 0; i < mYVals.size(); i++) { - Entry e = mYVals.get(i); - if (e != null) - mYValueSum += Math.abs(e.getVal()); - } - } - - /** - * returns the number of y-values this DataSet represents - * - * @return - */ - public int getEntryCount() { - return mYVals.size(); - } - - /** - * 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 - */ - public float getYValForXIndex(int xIndex) { - - Entry e = getEntryForXIndex(xIndex); - - if (e != null) - return e.getVal(); - else - return Float.NaN; - } - - /** - * Returns the first Entry object found at the given xIndex with binary - * search. If the no Entry at the specifed x-index is found, this method - * returns the Entry at the closest x-index. Returns null if no Entry object - * at that index. INFORMATION: This method does calculations at runtime. Do - * not over-use in performance critical situations. - * - * @param xIndex - * @return - */ - public T getEntryForXIndex(int x) { - - int low = 0; - int high = mYVals.size() - 1; - T closest = null; - - while (low <= high) { - int m = (high + low) / 2; - - if (x == mYVals.get(m).getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == x) - m--; - - return mYVals.get(m); - } - - if (x > mYVals.get(m).getXIndex()) - low = m + 1; - else - high = m - 1; - - closest = mYVals.get(m); - } - - return closest; - } - - /** - * 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 x) { - - 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 (x == entry.getXIndex()) { - while (m > 0 && mYVals.get(m - 1).getXIndex() == x) - m--; - - high = mYVals.size(); - for (; m < high; m++) - { - entry = mYVals.get(m); - if (entry.getXIndex() == x) - { - entries.add(entry); - } - else - { - break; - } - } - } - - if (x > entry.getXIndex()) - low = m + 1; - else - high = m - 1; - } - - return entries; - } - - /** - * returns the DataSets Entry array - * - * @return - */ - public List getYVals() { - return mYVals; - } - - /** - * gets the sum of all y-values - * - * @return - */ - public float getYValueSum() { - return mYValueSum; - } - - /** - * returns the minimum y-value this DataSet holds - * - * @return - */ - public float getYMin() { - return mYMin; - } - - /** - * returns the maximum y-value this DataSet holds - * - * @return - */ - public float getYMax() { - return mYMax; - } - - /** - * Returns the number of entries this DataSet holds. - * - * @return - */ - public int getValueCount() { - return mYVals.size(); - } - - /** - * The xIndex of an Entry object is provided. This method returns the actual - * index in the Entry array of the DataSet. IMPORTANT: This method does - * calculations at runtime, do not over-use in performance critical - * situations. - * - * @param xIndex - * @return - */ - public int getIndexInEntries(int xIndex) { - - for (int i = 0; i < mYVals.size(); i++) { - if (xIndex == mYVals.get(i).getXIndex()) - return i; - } - - return -1; - } - - /** - * 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: " + mLabel + ", entries: " + mYVals.size() + "\n"); - return buffer.toString(); - } - - /** - * Sets the label string that describes the DataSet. - * - * @return - */ - public void setLabel(String label) { - mLabel = label; - } - - /** - * Returns the label string that describes the DataSet. - * - * @return - */ - public String getLabel() { - return mLabel; - } - - /** - * Set the visibility of this DataSet. If not visible, the DataSet will not - * be drawn to the chart upon refreshing it. - * - * @param visible - */ - public void setVisible(boolean visible) { - mVisible = visible; - } - - /** - * Returns true if this DataSet is visible inside the chart, or false if it - * is currently hidden. - * - * @return - */ - public boolean isVisible() { - return mVisible; - } - - /** - * Returns the axis this DataSet should be plotted against. - * - * @return - */ - public AxisDependency getAxisDependency() { - return mAxisDependency; - } - - /** - * Set the y-axis this DataSet should be plotted against (either LEFT or - * RIGHT). Default: LEFT - * - * @param dependency - */ - public void setAxisDependency(AxisDependency dependency) { - mAxisDependency = dependency; - } - - /** - * 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 - * - * @param enabled - */ - public void setDrawValues(boolean enabled) { - this.mDrawValues = enabled; - } - - /** - * returns true if y-value drawing is enabled, false if not - * - * @return - */ - public boolean isDrawValuesEnabled() { - return mDrawValues; - } - - /** - * Adds an Entry to the DataSet dynamically. This will also recalculate the - * current minimum and maximum values of the DataSet and the value-sum. - * - * @param d - */ - @SuppressWarnings("unchecked") - public void addEntry(Entry 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; - } - - mYValueSum += val; - - // add the entry - mYVals.add((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 - */ - public boolean removeEntry(T e) { - - if (e == null) - return false; - - // remove the entry - boolean removed = mYVals.remove(e); - - if (removed) { - - float val = e.getVal(); - mYValueSum -= val; - - calcMinMax(); - } - - return removed; - } - - /** - * Removes the Entry object that has the given xIndex from the DataSet. - * Returns true if an Entry was removed, false if no Entry could be removed. - * - * @param xIndex - */ - public boolean removeEntry(int xIndex) { - - T e = getEntryForXIndex(xIndex); - return removeEntry(e); - } - - /** BELOW THIS COLOR HANDLING */ - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. If you are using colors from the resources, - * make sure that the colors are already prepared (by calling - * getResources().getColor(...)) before adding them to the DataSet. - * - * @param colors - */ - public void setColors(List colors) { - this.mColors = colors; - } - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. If you are using colors from the resources, - * make sure that the colors are already prepared (by calling - * getResources().getColor(...)) before adding them to the DataSet. - * - * @param colors - */ - public void setColors(int[] colors) { - this.mColors = ColorTemplate.createColors(colors); - } - - /** - * Sets the colors that should be used fore this DataSet. Colors are reused - * as soon as the number of Entries the DataSet represents is higher than - * the size of the colors array. You can use - * "new int[] { R.color.red, R.color.green, ... }" to provide colors for - * this method. Internally, the colors are resolved using - * getResources().getColor(...) - * - * @param colors - */ - public void setColors(int[] colors, Context c) { - - List clrs = new ArrayList(); - - for (int color : colors) { - clrs.add(c.getResources().getColor(color)); - } - - mColors = clrs; - } - - /** - * Adds a new color to the colors array of the DataSet. - * - * @param color - */ - public void addColor(int color) { - if (mColors == null) - mColors = new ArrayList(); - mColors.add(color); - } - - /** - * Sets the one and ONLY color that should be used for this DataSet. - * Internally, this recreates the colors array and adds the specified color. - * - * @param color - */ - public void setColor(int color) { - resetColors(); - mColors.add(color); - } - - /** - * returns all the colors that are set for this DataSet - * - * @return - */ - public List getColors() { - return mColors; - } - - /** - * Returns the color at the given index of the DataSet's color array. - * Performs a IndexOutOfBounds check by modulus. - * - * @param index - * @return - */ - public int getColor(int index) { - return mColors.get(index % mColors.size()); - } - - /** - * Returns the first color (index 0) of the colors-array this DataSet - * contains. - * - * @return - */ - public int getColor() { - return mColors.get(0); - } - - /** - * Resets all colors of this DataSet and recreates the colors array. - */ - public void resetColors() { - mColors = new ArrayList(); - } - - /** - * Returns the position of the provided entry in the DataSets Entry array. - * Returns -1 if doesn't exist. - * - * @param e - * @return - */ - public int getEntryPosition(Entry e) { - - for (int i = 0; i < mYVals.size(); i++) { - if (e.equalTo(mYVals.get(i))) - return i; - } - - return -1; - } - - /** - * Sets the formatter to be used for drawing the values inside the chart. If - * no formatter is set, the chart will automatically determine a reasonable - * formatting (concerning decimals) for all the values that are drawn inside - * the chart. Use chart.getDefaultValueFormatter() to use the formatter - * calculated by the chart. - * - * @param f - */ - public void setValueFormatter(ValueFormatter f) { - - if (f == null) - return; - else - mValueFormatter = f; - } - - /** - * Returns the formatter used for drawing the values inside the chart. - * - * @return - */ - public ValueFormatter getValueFormatter() { - if (mValueFormatter == null) - return new DefaultValueFormatter(1); - return mValueFormatter; - } - - /** - * If this component has no ValueFormatter or is only equipped with the - * default one (no custom set), return true. - * - * @return - */ - public boolean needsDefaultFormatter() { - if (mValueFormatter == null) - return true; - if (mValueFormatter instanceof DefaultValueFormatter) - return true; - - return false; - } - - /** - * Sets the color the value-labels of this DataSet should have. - * - * @param color - */ - public void setValueTextColor(int color) { - mValueColor = color; - } - - public int getValueTextColor() { - return mValueColor; - } - - /** - * Sets a Typeface for the value-labels of this DataSet. - * - * @param tf - */ - public void setValueTypeface(Typeface tf) { - mValueTypeface = tf; - } - - public Typeface getValueTypeface() { - return mValueTypeface; - } - - /** - * Sets the text-size of the value-labels of this DataSet in dp. - * - * @param size - */ - public void setValueTextSize(float size) { - mValueTextSize = Utils.convertDpToPixel(size); - } - - /** - * Returns the text-size of the labels that are displayed above the values. - * - * @return - */ - public float getValueTextSize() { - return mValueTextSize; - } - - /** - * Checks if this DataSet 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 (Entry entry : mYVals) { - if (entry.equals(e)) - return true; - } - - return false; - } - - /** - * Removes all values from this DataSet and recalculates min and max value. - */ - public void clear() { - mYVals.clear(); - notifyDataSetChanged(); - } -} 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 e57852ea33..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/Entry.java +++ /dev/null @@ -1,145 +0,0 @@ - -package com.github.mikephil.charting.data; - -/** - * 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 { - - /** 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(); - } -} 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 c653072475..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/LineData.java +++ /dev/null @@ -1,47 +0,0 @@ - -package com.github.mikephil.charting.data; - -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 BarLineScatterCandleData { - - 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, LineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public LineData(String[] xVals, LineDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(LineDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/com/github/mikephil/charting/data/PieData.java deleted file mode 100644 index cb4f886b2c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieData.java +++ /dev/null @@ -1,74 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -/** - * A PieData object can only represent one DataSet. Unlike all other charts, the - * legend labels of the PieChart are created from the x-values array, and not - * from the DataSet labels. Each PieData object can only represent one - * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). - * - * @author Philipp Jahoda - */ -public class PieData extends ChartData { - - public PieData() { - super(); - } - - public PieData(List xVals) { - super(xVals); - } - - public PieData(String[] xVals) { - super(xVals); - } - - public PieData(List xVals, PieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public PieData(String[] xVals, PieDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(PieDataSet dataSet) { - List sets = new ArrayList(); - sets.add(dataSet); - return sets; - } - - /** - * Sets the PieDataSet this data object should represent. - * - * @param dataSet - */ - public void setDataSet(PieDataSet dataSet) { - mDataSets.clear(); - mDataSets.add(dataSet); - init(mDataSets); - } - - /** - * Returns the DataSet this PieData object represents. A PieData object can - * only contain one DataSet. - * - * @return - */ - public PieDataSet getDataSet() { - return mDataSets.get(0); - } - - @Override - public PieDataSet getDataSetByIndex(int index) { - return index == 0 ? getDataSet() : null; - } - - @Override - public PieDataSet getDataSetByLabel(String label, boolean ignorecase) { - return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0) - : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null; - } -} 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 eb1d979307..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/PieDataSet.java +++ /dev/null @@ -1,83 +0,0 @@ - -package com.github.mikephil.charting.data; - -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -public class PieDataSet extends DataSet { - - /** the space in degrees 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, default: 0° - * --> no space, maximum 45, minimum 0 (no space) - * - * @param degrees - */ - public void setSliceSpace(float degrees) { - - if (degrees > 45) - degrees = 45f; - if (degrees < 0) - degrees = 0f; - - mSliceSpace = degrees; - } - - /** - * returns the space that is set to be between the piechart-slices of this - * DataSet, in degrees - * - * @return - */ - 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); - } - - /** - * returns the distance a highlighted piechart slice is "shifted" away from - * the chart-center - * - * @return - */ - 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 444c13fb05..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarData.java +++ /dev/null @@ -1,47 +0,0 @@ - -package com.github.mikephil.charting.data; - -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, RadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public RadarData(String[] xVals, RadarDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(RadarDataSet 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 83cf2907c9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/RadarDataSet.java +++ /dev/null @@ -1,28 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -public class RadarDataSet extends LineRadarDataSet { - - 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 22adb5e55d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterData.java +++ /dev/null @@ -1,61 +0,0 @@ - -package com.github.mikephil.charting.data; - -import java.util.ArrayList; -import java.util.List; - -public class ScatterData extends BarLineScatterCandleData { - - 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, ScatterDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - public ScatterData(String[] xVals, ScatterDataSet dataSet) { - super(xVals, toList(dataSet)); - } - - private static List toList(ScatterDataSet 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 (ScatterDataSet 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 9efca9b760..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/data/ScatterDataSet.java +++ /dev/null @@ -1,113 +0,0 @@ - -package com.github.mikephil.charting.data; - -import android.graphics.Path; - -import com.github.mikephil.charting.charts.ScatterChart.ScatterShape; -import com.github.mikephil.charting.utils.Utils; - -import java.util.ArrayList; -import java.util.List; - -public class ScatterDataSet extends BarLineScatterCandleDataSet { - - /** the size the scattershape will have, in screen 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; - - /** - * 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); - - // mShapeSize = Utils.convertDpToPixel(8f); - } - - @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.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 = Utils.convertDpToPixel(size); - } - - /** - * returns the currently set scatter shape size - * - * @return - */ - 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(ScatterShape shape) { - mScatterShape = shape; - } - - /** - * returns all the different scattershapes the chart uses - * - * @return - */ - public ScatterShape getScatterShape() { - return mScatterShape; - } - - /** - * Sets a path object as the shape to be drawn where the values are at. Do - * not forget to call setScatterShape(...) and set the shape to - * ScatterShape.CUSTOM. - * - * @param shape - */ - public void setCustomScatterShape(Path shape) { - mCustomScatterPath = shape; - } - - /** - * returns the custom path / shape that is specified to be drawn where the - * values are at - * - * @return - */ - public Path getCustomScatterShape() { - return mCustomScatterPath; - } -} 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/interfaces/BarDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/BarDataProvider.java deleted file mode 100644 index 6b9b19da8d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarDataProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.data.BarData; - -public interface BarDataProvider extends BarLineScatterCandleDataProvider { - - public BarData getBarData(); - public boolean isDrawBarShadowEnabled(); - public boolean isDrawValueAboveBarEnabled(); - public boolean isDrawHighlightArrowEnabled(); - public boolean isDrawValuesForWholeStackEnabled(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java deleted file mode 100644 index 368f87c1ca..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/BarLineScatterCandleDataProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.components.YAxis.AxisDependency; -import com.github.mikephil.charting.utils.Transformer; - -public interface BarLineScatterCandleDataProvider extends ChartInterface { - - public Transformer getTransformer(AxisDependency axis); - public int getMaxVisibleCount(); - public boolean isInverted(AxisDependency axis); - - public int getLowestVisibleXIndex(); - public int getHighestVisibleXIndex(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java deleted file mode 100644 index c24b50a36e..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/ChartInterface.java +++ /dev/null @@ -1,38 +0,0 @@ - -package com.github.mikephil.charting.interfaces; - -import android.graphics.PointF; -import android.graphics.RectF; - -import com.github.mikephil.charting.utils.ValueFormatter; - -/** - * Interface that provides everything there is to know about the dimensions, - * bounds, and range of the chart. - * - * @author Philipp Jahoda - */ -public interface ChartInterface { - - public float getXChartMin(); - - public float getXChartMax(); - - public float getYChartMin(); - - public float getYChartMax(); - - public int getXValCount(); - - public int getWidth(); - - public int getHeight(); - - public PointF getCenterOfView(); - - public PointF getCenterOffsets(); - - public RectF getContentRect(); - - public ValueFormatter getDefaultValueFormatter(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java b/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java deleted file mode 100644 index 479e0d86bc..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/LineDataProvider.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.github.mikephil.charting.interfaces; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.utils.FillFormatter; - -public interface LineDataProvider extends BarLineScatterCandleDataProvider { - - public LineData getLineData(); - - /** - * 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); - - /** - * Returns the FillFormatter that handles the position of the filled-line. - * - * @return - */ - public FillFormatter getFillFormatter(); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java deleted file mode 100644 index 6ebad1ecc5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartGestureListener.java +++ /dev/null @@ -1,62 +0,0 @@ - -package com.github.mikephil.charting.listener; - -import android.view.MotionEvent; - -/** - * Listener for callbacks when doing gestures on the chart. - * - * @author Philipp Jahoda - */ -public interface OnChartGestureListener { - - /** - * Callbacks when the chart is longpressed. - * - * @param me - */ - public void onChartLongPressed(MotionEvent me); - - /** - * Callbacks when the chart is double-tapped. - * - * @param me - */ - public void onChartDoubleTapped(MotionEvent me); - - /** - * Callbacks when the chart is single-tapped. - * - * @param me - */ - public void onChartSingleTapped(MotionEvent me); - - /** - * Callbacks then a fling gesture is made on the chart. - * - * @param me1 - * @param me2 - * @param velocityX - * @param velocityY - */ - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); - - /** - * Callbacks when the chart is scaled / zoomed via pinch zoom gesture. - * - * @param me - * @param scaleX scalefactor on the x-axis - * @param scaleY scalefactor on the y-axis - */ - public void onChartScale(MotionEvent me, float scaleX, float scaleY); - - /** - * Callbacks when the chart is moved / translated via drag gesture. - * - * @param me - * @param dX translation distance on the x-axis - * @param dY translation distance on the y-axis - */ - public void onChartTranslate(MotionEvent me, float dX, float dY); - -} 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 86d4186351..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/AxisRenderer.java +++ /dev/null @@ -1,121 +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 d06fb15223..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/BarChartRenderer.java +++ /dev/null @@ -1,393 +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.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.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; -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++) { - BarDataSet set = barData.getDataSetByIndex(i); - mBarBuffers[i] = new BarBuffer(set.getValueCount() * 4 * set.getStackSize(), - barData.getGroupSpace(), - barData.getDataSetCount(), set.isStacked()); - } - } - - @Override - public void drawData(Canvas c) { - - BarData barData = mChart.getBarData(); - - for (int i = 0; i < barData.getDataSetCount(); i++) { - - BarDataSet set = barData.getDataSetByIndex(i); - - if (set.isVisible()) { - drawDataSet(c, set, i); - } - } - } - - protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - mShadowPaint.setColor(dataSet.getBarShadowColor()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - List entries = dataSet.getYVals(); - - // 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(entries); - - 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 y the y-position - * @param barspace the space between bars - * @param from - * @param trans - */ - protected void prepareBarHighlight(float x, float y, float barspaceHalf, float from, - Transformer trans) { - - float barWidth = 0.5f; - - float left = x - barWidth + barspaceHalf; - float right = x + barWidth - barspaceHalf; - float top = y >= from ? y : from; - float bottom = y <= from ? y : from; - - 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(5f); - float posOffset = 0f; - float negOffset = 0f; - boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - BarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - 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; - } - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - ValueFormatter formatter = dataSet.getValueFormatter(); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - List entries = dataSet.getYVals(); - - float[] valuePoints = getTransformedValues(trans, entries, i); - - // if only single values are drawn (sum) - if (!mChart.isDrawValuesForWholeStackEnabled()) { - - 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; - - float val = entries.get(j / 2).getVal(); - - drawValue(c, formatter.getFormattedValue(val), valuePoints[j], - valuePoints[j + 1] + (val >= 0 ? posOffset : negOffset)); - } - - // 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 = entries.get(j / 2); - - float[] vals = e.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, formatter.getFormattedValue(e.getVal()), valuePoints[j], - valuePoints[j + 1] + (e.getVal() >= 0 ? posOffset : negOffset)); - - } else { - - float[] transformed = new float[vals.length * 2]; - int cnt = 0; - float add = e.getVal(); - - for (int k = 0; k < transformed.length; k += 2) { - - add -= vals[cnt]; - transformed[k + 1] = (vals[cnt] + add) * mAnimator.getPhaseY(); - cnt++; - } - - 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, formatter.getFormattedValue(vals[k / 2]), x, y); - } - } - } - } - } - } - } - - /** - * Draws a value at the specified x and y position. - * - * @param value - * @param xPos - * @param yPos - * @formatter - */ - protected void drawValue(Canvas c, String value, float xPos, float yPos) { - - c.drawText(value, xPos, yPos, - mValuePaint); - } - - @Override - public void drawExtras(Canvas c) { - } - - @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(); - BarDataSet set = mChart.getBarData().getDataSetByIndex(dataSetIndex); - - if (set == null) - continue; - - float barspaceHalf = set.getBarSpace() / 2f; - - Transformer trans = mChart.getTransformer(set.getAxisDependency()); - - mHighlightPaint.setColor(set.getHighLightColor()); - mHighlightPaint.setAlpha(set.getHighLightAlpha()); - - // check outofbounds - if (index < mChart.getBarData().getYValCount() && index >= 0 - && index < (mChart.getXChartMax() * mAnimator.getPhaseX()) / setCount) { - - BarEntry e = mChart.getBarData().getDataSetByIndex(dataSetIndex) - .getEntryForXIndex(index); - - if (e == null) - 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; - float y = isStack ? e.getVals()[h.getStackIndex()] - + e.getBelowSum(h.getStackIndex()) : e.getVal(); - - // this is where the bar starts - float from = isStack ? e.getBelowSum(h.getStackIndex()) : 0f; - - prepareBarHighlight(x, y, barspaceHalf, from, trans); - - c.drawRect(mBarRect, mHighlightPaint); - - if (mChart.isDrawHighlightArrowEnabled()) { - - mHighlightPaint.setAlpha(255); - - // distance between highlight arrow and bar - float offsetY = mAnimator.getPhaseY() * 0.07f; - - Path arrow = new Path(); - arrow.moveTo(x + 0.5f, y + offsetY * 0.3f); - arrow.lineTo(x + 0.2f, y + offsetY); - arrow.lineTo(x + 0.8f, y + offsetY); - - trans.pathValueToPixel(arrow); - c.drawPath(arrow, mHighlightPaint); - } - } - } - } - - public float[] getTransformedValues(Transformer trans, List entries, - int dataSetIndex) { - return trans.generateTransformedValuesBarChart(entries, dataSetIndex, - mChart.getBarData(), - mAnimator.getPhaseY()); - } - - protected boolean passesCheck() { - return mChart.getBarData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX(); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java deleted file mode 100644 index 598cbae367..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java +++ /dev/null @@ -1,304 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.buffer.CandleBodyBuffer; -import com.github.mikephil.charting.buffer.CandleShadowBuffer; -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.Entry; -import com.github.mikephil.charting.interfaces.CandleDataProvider; -import com.github.mikephil.charting.utils.ColorTemplate; -import com.github.mikephil.charting.utils.Highlight; -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 CandleStickChartRenderer extends DataRenderer { - - protected CandleDataProvider mChart; - - private CandleShadowBuffer[] mShadowBuffers; - private CandleBodyBuffer[] mBodyBuffers; - - public CandleStickChartRenderer(CandleDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - } - - @Override - public void initBuffers() { - CandleData candleData = mChart.getCandleData(); - mShadowBuffers = new CandleShadowBuffer[candleData.getDataSetCount()]; - mBodyBuffers = new CandleBodyBuffer[candleData.getDataSetCount()]; - - for (int i = 0; i < mShadowBuffers.length; i++) { - CandleDataSet set = candleData.getDataSetByIndex(i); - mShadowBuffers[i] = new CandleShadowBuffer(set.getValueCount() * 4); - mBodyBuffers[i] = new CandleBodyBuffer(set.getValueCount() * 4); - } - } - - @Override - public void drawData(Canvas c) { - - CandleData candleData = mChart.getCandleData(); - - for (CandleDataSet set : candleData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, CandleDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - int dataSetIndex = mChart.getCandleData().getIndexOfDataSet(dataSet); - - List entries = dataSet.getYVals(); - - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - int range = (maxx - minx) * 4; - int to = (int)Math.ceil((maxx - minx) * phaseX + minx); - - CandleShadowBuffer shadowBuffer = mShadowBuffers[dataSetIndex]; - shadowBuffer.setPhases(phaseX, phaseY); - shadowBuffer.limitFrom(minx); - shadowBuffer.limitTo(maxx); - shadowBuffer.feed(entries); - - trans.pointValuesToPixel(shadowBuffer.buffer); - - mRenderPaint.setStyle(Paint.Style.STROKE); - - // If not set, use default functionality for backward compatibility - if (dataSet.getShadowColor() == ColorTemplate.COLOR_NONE) { - mRenderPaint.setColor(dataSet.getColor()); - } else { - mRenderPaint.setColor(dataSet.getShadowColor()); - } - - mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); - - // draw the shadow - c.drawLines(shadowBuffer.buffer, 0, range, mRenderPaint); - - CandleBodyBuffer bodyBuffer = mBodyBuffers[dataSetIndex]; - bodyBuffer.setBodySpace(dataSet.getBodySpace()); - bodyBuffer.setPhases(phaseX, phaseY); - bodyBuffer.limitFrom(minx); - bodyBuffer.limitTo(maxx); - bodyBuffer.feed(entries); - - trans.pointValuesToPixel(bodyBuffer.buffer); - - // draw the body - for (int j = 0; j < range; j += 4) { - - // get the entry - CandleEntry e = entries.get(j / 4 + minx); - - if (!fitsBounds(e.getXIndex(), mMinX, to)) - continue; - - float leftBody = bodyBuffer.buffer[j]; - float open = bodyBuffer.buffer[j + 1]; - float rightBody = bodyBuffer.buffer[j + 2]; - float close = bodyBuffer.buffer[j + 3]; - - // draw body differently for increasing and decreasing entry - if (open > close) { // decreasing - - if (dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE) { - mRenderPaint.setColor(dataSet.getColor(j / 4 + minx)); - } else { - mRenderPaint.setColor(dataSet.getDecreasingColor()); - } - - mRenderPaint.setStyle(dataSet.getDecreasingPaintStyle()); - // draw the body - c.drawRect(leftBody, close, rightBody, open, mRenderPaint); - - } else if(open < close) { - - if (dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE) { - mRenderPaint.setColor(dataSet.getColor(j / 4 + minx)); - } else { - mRenderPaint.setColor(dataSet.getIncreasingColor()); - } - - mRenderPaint.setStyle(dataSet.getIncreasingPaintStyle()); - // draw the body - c.drawRect(leftBody, open, rightBody, close, mRenderPaint); - } else { // equal values - - mRenderPaint.setColor(Color.BLACK); - mRenderPaint.setStyle(Paint.Style.STROKE); - c.drawLine(leftBody, open, rightBody, close, mRenderPaint); - } - } - } - - // /** - // * Transforms the values of an entry in order to draw the candle-body. - // * - // * @param bodyPoints - // * @param e - // * @param bodySpace - // */ - // private void transformBody(float[] bodyPoints, CandleEntry e, float - // bodySpace, Transformer trans) { - // - // float phase = mAnimator.getPhaseY(); - // - // bodyPoints[0] = e.getXIndex() - 0.5f + bodySpace; - // bodyPoints[1] = e.getClose() * phase; - // bodyPoints[2] = e.getXIndex() + 0.5f - bodySpace; - // bodyPoints[3] = e.getOpen() * phase; - // - // trans.pointValuesToPixel(bodyPoints); - // } - // - // /** - // * Transforms the values of an entry in order to draw the candle-shadow. - // * - // * @param shadowPoints - // * @param e - // */ - // private void transformShadow(float[] shadowPoints, CandleEntry e, - // Transformer trans) { - // - // float phase = mAnimator.getPhaseY(); - // - // shadowPoints[0] = e.getXIndex(); - // shadowPoints[1] = e.getHigh() * phase; - // shadowPoints[2] = e.getXIndex(); - // shadowPoints[3] = e.getLow() * phase; - // - // trans.pointValuesToPixel(shadowPoints); - // } - - @Override - public void drawValues(Canvas c) { - - // if values are drawn - if (mChart.getCandleData().getYValCount() < mChart.getMaxVisibleCount() - * mViewPortHandler.getScaleX()) { - - List dataSets = mChart.getCandleData().getDataSets(); - - for (int i = 0; i < dataSets.size(); i++) { - - CandleDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - List entries = dataSet.getYVals(); - - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - float[] positions = trans.generateTransformedValuesCandle( - entries, mAnimator.getPhaseX(), mAnimator.getPhaseY(), minx, maxx); - - float yOffset = Utils.convertDpToPixel(5f); - - 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; - - float val = entries.get(j / 2 + minx).getHigh(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), x, y - yOffset, - mValuePaint); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - CandleDataSet set = mChart.getCandleData().getDataSetByIndex( - indices[i].getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - CandleEntry e = set.getEntryForXIndex(xIndex); - - if (e == null) - continue; - - float low = e.getLow() * mAnimator.getPhaseY(); - float high = e.getHigh() * mAnimator.getPhaseY(); - - float min = mChart.getYChartMin(); - float max = mChart.getYChartMax(); - - float[] vertPts = new float[] { - xIndex - 0.5f, max, xIndex - 0.5f, min, xIndex + 0.5f, max, xIndex + 0.5f, - min - }; - - float[] horPts = new float[] { - mChart.getXChartMin(), low, mChart.getXChartMax(), low, mChart.getXChartMin(), high, mChart.getXChartMax(), high - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(vertPts); - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(horPts); - - // draw the vertical highlight lines - c.drawLines(vertPts, mHighlightPaint); - - // draw the horizontal highlight lines - c.drawLines(horPts, mHighlightPaint); - } - } - -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java deleted file mode 100644 index 32f92968bf..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/CombinedChartRenderer.java +++ /dev/null @@ -1,127 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; - -import com.github.mikephil.charting.animation.ChartAnimator; -import com.github.mikephil.charting.charts.CombinedChart; -import com.github.mikephil.charting.charts.CombinedChart.DrawOrder; -import com.github.mikephil.charting.interfaces.BarLineScatterCandleDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.ArrayList; -import java.util.List; - -public class CombinedChartRenderer extends DataRenderer { - - /** - * all rederers for the different kinds of data this combined-renderer can - * draw - */ - protected List mRenderers; - - public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - - createRenderers(chart, animator, viewPortHandler); - } - - /** - * 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) { - - mRenderers = new ArrayList(); - - DrawOrder[] orders = chart.getDrawOrder(); - - for (DrawOrder order : orders) { - - switch (order) { - case BAR: - if (chart.getBarData() != null) - mRenderers.add(new BarChartRenderer(chart, animator, viewPortHandler)); - break; - - case BUBBLE: - if (chart.getBubbleData() != null) - mRenderers.add(new BubbleChartRenderer(chart, animator, viewPortHandler)); - break; - case LINE: - if (chart.getLineData() != null) - mRenderers.add(new LineChartRenderer(chart, animator, viewPortHandler)); - break; - case CANDLE: - if (chart.getCandleData() != null) - mRenderers.add(new CandleStickChartRenderer(chart, animator, - viewPortHandler)); - break; - case SCATTER: - if (chart.getScatterData() != null) - mRenderers.add(new ScatterChartRenderer(chart, animator, viewPortHandler)); - break; - } - } - } - - @Override - public void initBuffers() { - - for (DataRenderer renderer : mRenderers) - renderer.initBuffers(); - } - - @Override - public void drawData(Canvas c) { - - for (DataRenderer renderer : mRenderers) - renderer.drawData(c); - } - - @Override - public void drawValues(Canvas c) { - - for (DataRenderer renderer : mRenderers) - renderer.drawValues(c); - } - - @Override - public void drawExtras(Canvas c) { - - for (DataRenderer renderer : mRenderers) - renderer.drawExtras(c); - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - for (DataRenderer renderer : mRenderers) - renderer.drawHighlighted(c, indices); - } - - @Override - public void calcXBounds(BarLineScatterCandleDataProvider chart, int xAxisModulus) { - for (DataRenderer renderer : mRenderers) - renderer.calcXBounds(chart, xAxisModulus); - } - - /** - * Returns the sub-renderer object at the specified index. - * - * @param index - * @return - */ - public DataRenderer getSubRenderer(int index) { - if (index >= mRenderers.size() || index < 0) - return null; - else - return mRenderers.get(index); - } -} 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 81242a6438..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/HorizontalBarChartRenderer.java +++ /dev/null @@ -1,292 +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.BarDataSet; -import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.interfaces.BarDataProvider; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -/** - * Renderer for the HorizontalBarChart. - * - * @author Philipp Jahoda - */ -public class HorizontalBarChartRenderer extends BarChartRenderer { - - private float mYOffset = 0f; - - 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++) { - BarDataSet set = barData.getDataSetByIndex(i); - mBarBuffers[i] = new HorizontalBarBuffer(set.getValueCount() * 4 * set.getStackSize(), - barData.getGroupSpace(), - barData.getDataSetCount(), set.isStacked()); - } - } - - protected void drawDataSet(Canvas c, BarDataSet dataSet, int index) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - mShadowPaint.setColor(dataSet.getBarShadowColor()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - List entries = dataSet.getYVals(); - - // 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(entries); - - 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; - boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled(); - - if (drawValueAboveBar) - mValuePaint.setTextAlign(Align.LEFT); - else - mValuePaint.setTextAlign(Align.RIGHT); - - for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) { - - BarDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - boolean isInverted = mChart.isInverted(dataSet.getAxisDependency()); - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - mYOffset = Utils.calcTextHeight(mValuePaint, "10") / 2f; - - ValueFormatter formatter = dataSet.getValueFormatter(); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - List entries = dataSet.getYVals(); - - float[] valuePoints = getTransformedValues(trans, entries, i); - - // if only single values are drawn (sum) - if (!mChart.isDrawValuesForWholeStackEnabled()) { - - for (int j = 0; j < valuePoints.length * mAnimator.getPhaseX(); j += 2) { - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - float val = entries.get(j / 2).getVal(); - String valueText = formatter.getFormattedValue(val); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); - posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); - - if (isInverted) - { - posOffset = -posOffset - valueTextWidth; - negOffset = -negOffset - valueTextWidth; - } - - drawValue(c, valueText, valuePoints[j] + (val >= 0 ? posOffset : negOffset), - valuePoints[j + 1]); - } - - // 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 = entries.get(j / 2); - - float[] vals = e.getVals(); - - // we still draw stacked bars, but there is one - // non-stacked - // in between - if (vals == null) { - - if (!mViewPortHandler.isInBoundsX(valuePoints[j])) - continue; - - if (!mViewPortHandler.isInBoundsTop(valuePoints[j + 1])) - break; - - if (!mViewPortHandler.isInBoundsBottom(valuePoints[j + 1])) - continue; - - float val = e.getVal(); - String valueText = formatter.getFormattedValue(val); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); - posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)); - negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus); - - if (isInverted) - { - posOffset = -posOffset - valueTextWidth; - negOffset = -negOffset - valueTextWidth; - } - - drawValue(c, valueText, valuePoints[j] - + (e.getVal() >= 0 ? posOffset : negOffset), - valuePoints[j + 1]); - - } else { - - float[] transformed = new float[vals.length * 2]; - int cnt = 0; - float add = e.getVal(); - - for (int k = 0; k < transformed.length; k += 2) { - - add -= vals[cnt]; - transformed[k] = (vals[cnt] + add) * mAnimator.getPhaseY(); - cnt++; - } - - trans.pointValuesToPixel(transformed); - - for (int k = 0; k < transformed.length; k += 2) { - - float val = vals[k / 2]; - String valueText = formatter.getFormattedValue(val); - - // calculate the correct offset depending on the draw position of the value - float valueTextWidth = Utils.calcTextWidth(mValuePaint, valueText); - 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.isInBoundsX(x)) - continue; - - if (!mViewPortHandler.isInBoundsTop(y)) - break; - - if (!mViewPortHandler.isInBoundsBottom(y)) - continue; - - drawValue(c, valueText, x, y); - } - } - } - } - } - } - } - - @Override - protected void prepareBarHighlight(float x, float y, float barspaceHalf, float from, - Transformer trans) { - - float top = x - 0.5f + barspaceHalf; - float bottom = x + 0.5f - barspaceHalf; - float left = y >= from ? y : from; - float right = y <= from ? y : from; - - mBarRect.set(left, top, right, bottom); - - trans.rectValueToPixelHorizontal(mBarRect, mAnimator.getPhaseY()); - } - - @Override - public float[] getTransformedValues(Transformer trans, List entries, - int dataSetIndex) { - return trans.generateTransformedValuesHorizontalBarChart(entries, dataSetIndex, - mChart.getBarData(), mAnimator.getPhaseY()); - } - - @Override - protected void drawValue(Canvas c, String value, float xPos, float yPos) { - super.drawValue(c, value, xPos, yPos + mYOffset); - } - - @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 61263bf3be..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LegendRenderer.java +++ /dev/null @@ -1,481 +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.BarDataSet; -import com.github.mikephil.charting.data.ChartData; -import com.github.mikephil.charting.data.DataSet; -import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.data.PieDataSet; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.ArrayList; -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) { - - 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++) { - - DataSet dataSet = data.getDataSetByIndex(i); - - List clrs = dataSet.getColors(); - int entryCount = dataSet.getEntryCount(); - - // if we have a barchart with stacked bars - if (dataSet instanceof BarDataSet && ((BarDataSet) dataSet).isStacked()) { - - BarDataSet bds = (BarDataSet) 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)); - } - - // add the legend description label - colors.add(-2); - labels.add(bds.getLabel()); - - } else if (dataSet instanceof PieDataSet) { - - List xVals = data.getXVals(); - PieDataSet pds = (PieDataSet) dataSet; - - for (int j = 0; j < clrs.size() && j < entryCount && j < xVals.size(); j++) { - - labels.add(xVals.get(j)); - colors.add(clrs.get(j)); - } - - // add the legend description label - colors.add(-2); - labels.add(pds.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)); - } - } - } - - mLegend.setColors(colors); - mLegend.setLabels(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); - } - - 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()); - - String[] labels = mLegend.getLegendLabels(); - 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(); - - // the amount of pixels the text needs to be set down to be on the same - // height as the form - float textDrop = (Utils.calcTextHeight(mLegendLabelPaint, "AQJ") + formSize) / 2f; - - float posX, posY; - - // contains the stacked legend size in pixels - float stack = 0f; - - boolean wasStacked = false; - - float yoffset = mLegend.getYOffset(); - float xoffset = mLegend.getXOffset(); - - switch (mLegend.getPosition()) { - case BELOW_CHART_LEFT: - - posX = mViewPortHandler.contentLeft() + xoffset; - posY = mViewPortHandler.getChartHeight() - yoffset; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) { - posX += mLegend.mNeededWidth; - } - - for (int i = 0, count = labels.length; i < count; i++) { - - boolean drawingForm = colors[i] != -2; - - if (drawingForm) { - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= formSize; - - drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += formSize; - } - - // grouped forms have null labels - if (labels[i] != null) { - - // spacing between form and label - if (drawingForm) - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? - -formToTextSpace : - formToTextSpace; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - - drawLabel(c, posX, posY, labels[i]); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? - -xEntrySpace : - xEntrySpace; - } else { - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; - } - } - break; - case BELOW_CHART_RIGHT: - - posX = mViewPortHandler.contentRight() - xoffset; - posY = mViewPortHandler.getChartHeight() - yoffset; - - for (int i = 0, count = labels.length; i < count; i++) { - - boolean drawingForm = colors[i] != -2; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT && drawingForm) { - posX -= formSize; - drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend); - posX -= formToTextSpace; - } - - if (labels[i] != null) { - posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - drawLabel(c, posX, posY, labels[i]); - } - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT && drawingForm) { - posX -= formToTextSpace + formSize; - drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend); - } - - posX -= labels[i] != null ? xEntrySpace : stackSpace; - } - break; - case BELOW_CHART_CENTER: - - posX = mViewPortHandler.getChartWidth() / 2f + (direction == Legend.LegendDirection.LEFT_TO_RIGHT ? -mLegend.mNeededWidth / 2f : mLegend.mNeededWidth / 2f); - posY = mViewPortHandler.getChartHeight() - yoffset; - - for (int i = 0; i < labels.length; i++) { - - boolean drawingForm = colors[i] != -2; - - if (drawingForm) { - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= formSize; - - drawForm(c, posX, posY - mLegend.mTextHeightMax / 2f, i, mLegend); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += formSize; - } - - // grouped forms have null labels - if (labels[i] != null) { - - // spacing between form and label - if (drawingForm) - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? - -formToTextSpace : - formToTextSpace; - - if (direction == Legend.LegendDirection.RIGHT_TO_LEFT) - posX -= Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - - drawLabel(c, posX, posY, labels[i]); - - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - posX += Utils.calcTextWidth(mLegendLabelPaint, labels[i]); - - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? - -xEntrySpace : - xEntrySpace; - } else { - posX += direction == Legend.LegendDirection.RIGHT_TO_LEFT ? -stackSpace : stackSpace; - } - } - - break; - case 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(); - - for (int i = 0; i < labels.length; i++) { - - boolean drawingForm = colors[i] != -2; - float x = posX; - - if (drawingForm) { - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += stack; - else - x -= formSize - stack; - - drawForm(c, x, posY, 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 + mLegend.mTextHeightMax / 2f, mLegend.getLabel(i)); - - posY += textDrop; - } else { - posY += mLegend.mTextHeightMax * 3f; - drawLabel(c, x, posY - mLegend.mTextHeightMax, mLegend.getLabel(i)); - } - - // make a step down - posY += mLegend.getYEntrySpace(); - stack = 0f; - } else { - stack += formSize + stackSpace; - wasStacked = true; - } - } - - break; - 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: - - boolean isRightAligned = mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART || - mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - mLegend.getPosition() == 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 (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART || - mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART) { - posY = mViewPortHandler.contentTop() + yoffset; - } else if (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_CENTER || - mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART_CENTER) { - posY = mViewPortHandler.getChartHeight() / 2f - mLegend.mNeededHeight / 2f; - } else /*if (mLegend.getPosition() == Legend.LegendPosition.RIGHT_OF_CHART_INSIDE || - mLegend.getPosition() == Legend.LegendPosition.LEFT_OF_CHART_INSIDE)*/ { - posY = mViewPortHandler.contentTop() + yoffset; - } - - for (int i = 0; i < labels.length; i++) { - - Boolean drawingForm = colors[i] != -2; - float x = posX; - - if (drawingForm) { - if (direction == Legend.LegendDirection.LEFT_TO_RIGHT) - x += stack; - else - x -= formSize - stack; - - drawForm(c, x, posY, 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 + mLegend.mTextHeightMax / 2f, mLegend.getLabel(i)); - - posY += textDrop; - } else { - posY += mLegend.mTextHeightMax * 3f; - drawLabel(c, x, posY - mLegend.mTextHeightMax, mLegend.getLabel(i)); - } - - // make a step down - posY += mLegend.getYEntrySpace(); - 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 - * @param y - * @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] == -2) - 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 604681459c..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/LineChartRenderer.java +++ /dev/null @@ -1,563 +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 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.data.Entry; -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class LineChartRenderer extends DataRenderer { - - 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 Bitmap mDrawBitmap; - - /** - * on this canvas, the paths are rendered, it is initialized with the - * pathBitmap - */ - protected Canvas mBitmapCanvas; - - 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++) { - LineDataSet 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.getWidth() != width) - || (mDrawBitmap.getHeight() != height)) { - - if (width > 0 && height > 0) { - - mDrawBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); - mBitmapCanvas = new Canvas(mDrawBitmap); - } else - return; - } - - mDrawBitmap.eraseColor(Color.TRANSPARENT); - - LineData lineData = mChart.getLineData(); - - for (LineDataSet set : lineData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - - c.drawBitmap(mDrawBitmap, 0, 0, mRenderPaint); - } - - protected void drawDataSet(Canvas c, LineDataSet dataSet) { - - List entries = dataSet.getYVals(); - - if (entries.size() < 1) - return; - - mRenderPaint.setStrokeWidth(dataSet.getLineWidth()); - mRenderPaint.setPathEffect(dataSet.getDashPathEffect()); - - // if drawing cubic lines is enabled - if (dataSet.isDrawCubicEnabled()) { - - drawCubic(c, dataSet, entries); - - // draw normal (straight) lines - } else { - drawLinear(c, dataSet, entries); - } - - mRenderPaint.setPathEffect(null); - } - - /** - * Draws a cubic line. - * - * @param c - * @param dataSet - * @param entries - */ - protected void drawCubic(Canvas c, LineDataSet dataSet, List entries) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - 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 = entries.get(minx); - Entry prev = entries.get(minx); - Entry cur = entries.get(minx); - Entry next = entries.get(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, entries.size() - 1); j < count; j++) { - - prevPrev = entries.get(j == 1 ? 0 : j - 2); - prev = entries.get(j - 1); - cur = entries.get(j); - next = entries.get(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 > entries.size() - 1) { - - prevPrev = entries.get((entries.size() >= 3) ? entries.size() - 3 - : entries.size() - 2); - prev = entries.get(entries.size() - 2); - cur = entries.get(entries.size() - 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, LineDataSet dataSet, Path spline, Transformer trans, - int from, int to) { - - float fillMin = mChart.getFillFormatter() - .getFillLinePosition(dataSet, mChart.getLineData(), mChart.getYChartMax(), - mChart.getYChartMin()); - - spline.lineTo(to - 1, fillMin); - spline.lineTo(from, fillMin); - spline.close(); - - mRenderPaint.setStyle(Paint.Style.FILL); - - mRenderPaint.setColor(dataSet.getFillColor()); - // filled is drawn with less alpha - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - - trans.pathValueToPixel(spline); - mBitmapCanvas.drawPath(spline, mRenderPaint); - - mRenderPaint.setAlpha(255); - } - - /** - * Draws a normal line. - * - * @param c - * @param dataSet - * @param entries - */ - protected void drawLinear(Canvas c, LineDataSet dataSet, List entries) { - - 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); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - int range = (maxx - minx) * 4 - 4; - - LineBuffer buffer = mLineBuffers[dataSetIndex]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(entries); - - 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() && entries.size() > 0) { - drawLinearFill(c, dataSet, entries, minx, maxx, trans); - } - } - - protected void drawLinearFill(Canvas c, LineDataSet dataSet, List entries, int minx, - int maxx, - Transformer trans) { - - mRenderPaint.setStyle(Paint.Style.FILL); - - mRenderPaint.setColor(dataSet.getFillColor()); - // filled is drawn with less alpha - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - - Path filled = generateFilledPath( - entries, - mChart.getFillFormatter().getFillLinePosition(dataSet, mChart.getLineData(), - mChart.getYChartMax(), mChart.getYChartMin()), minx, maxx); - - trans.pathValueToPixel(filled); - - c.drawPath(filled, mRenderPaint); - - // restore alpha - mRenderPaint.setAlpha(255); - } - - /** - * Generates the path that is used for filled drawing. - * - * @param entries - * @return - */ - private Path generateFilledPath(List entries, float fillMin, int from, int to) { - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - Path filled = new Path(); - filled.moveTo(entries.get(from).getXIndex(), fillMin); - filled.lineTo(entries.get(from).getXIndex(), entries.get(from).getVal() * phaseY); - - // create a new path - for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) { - - Entry e = entries.get(x); - filled.lineTo(e.getXIndex(), e.getVal() * phaseY); - } - - // close up - filled.lineTo( - entries.get( - Math.max( - Math.min((int) Math.ceil((to - from) * phaseX + from) - 1, - entries.size() - 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++) { - - LineDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - 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.getCircleSize() * 1.75f); - - if (!dataSet.isDrawCirclesEnabled()) - valOffset = valOffset / 2; - - List entries = dataSet.getYVals(); - - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - float[] positions = trans.generateTransformedValuesLine( - entries, 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; - - float val = entries.get(j / 2 + minx).getVal(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), x, - y - valOffset, - mValuePaint); - } - } - } - } - - @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++) { - - LineDataSet dataSet = dataSets.get(i); - - if (!dataSet.isVisible() || !dataSet.isDrawCirclesEnabled()) - continue; - - mCirclePaintInner.setColor(dataSet.getCircleHoleColor()); - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - List entries = dataSet.getYVals(); - - Entry entryFrom = dataSet.getEntryForXIndex((mMinX < 0) ? 0 : mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); - - CircleBuffer buffer = mCircleBuffers[i]; - buffer.setPhases(phaseX, phaseY); - buffer.limitFrom(minx); - buffer.limitTo(maxx); - buffer.feed(entries); - - trans.pointValuesToPixel(buffer.buffer); - - float halfsize = dataSet.getCircleSize() / 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.getCircleSize(), - 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++) { - - LineDataSet set = mChart.getLineData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - float y = set.getYValForXIndex(xIndex) * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[] { - xIndex, mChart.getYChartMax(), xIndex, mChart.getYChartMin(), mChart.getXChartMin(), y, - mChart.getXChartMax(), y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the highlight lines - c.drawLines(pts, mHighlightPaint); - } - } -} 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 7af1d060a7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/PieChartRenderer.java +++ /dev/null @@ -1,422 +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.PointF; -import android.graphics.RectF; - -import com.github.mikephil.charting.animation.ChartAnimator; -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.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -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 Paint mCenterTextPaint; - - /** Bitmap for drawing the center hole */ - protected Bitmap 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); - - mCenterTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mCenterTextPaint.setColor(Color.BLACK); - mCenterTextPaint.setTextSize(Utils.convertDpToPixel(12f)); - mCenterTextPaint.setTextAlign(Align.CENTER); - - mValuePaint.setTextSize(Utils.convertDpToPixel(13f)); - mValuePaint.setColor(Color.WHITE); - mValuePaint.setTextAlign(Align.CENTER); - } - - public Paint getPaintHole() { - return mHolePaint; - } - - public Paint getPaintTransparentCircle() { - return mTransparentCirclePaint; - } - - public Paint 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.getWidth() != width) - || (mDrawBitmap.getHeight() != height)) { - - if (width > 0 && height > 0) { - - mDrawBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444); - mBitmapCanvas = new Canvas(mDrawBitmap); - } else - return; - } - - mDrawBitmap.eraseColor(Color.TRANSPARENT); - - PieData pieData = mChart.getData(); - - for (PieDataSet set : pieData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, PieDataSet dataSet) { - - float angle = mChart.getRotationAngle(); - - List entries = dataSet.getYVals(); - float[] drawAngles = mChart.getDrawAngles(); - - for (int j = 0; j < entries.size(); j++) { - - float newangle = drawAngles[j]; - float sliceSpace = dataSet.getSliceSpace(); - - Entry e = entries.get(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)); - mBitmapCanvas.drawArc(mChart.getCircleBox(), - (angle + sliceSpace / 2f) * mAnimator.getPhaseY(), - (newangle - sliceSpace / 2f) * mAnimator.getPhaseY(), - true, mRenderPaint); - } - } - - angle += newangle * mAnimator.getPhaseX(); - } - } - - @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 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(); - boolean drawXVals = mChart.isDrawSliceTextEnabled(); - - int cnt = 0; - - for (int i = 0; i < dataSets.size(); i++) { - - PieDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled() && !drawXVals) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - List entries = dataSet.getYVals(); - - for (int j = 0, maxEntry = Math.min( - (int) Math.ceil(entries.size() * mAnimator.getPhaseX()), entries.size()); j < maxEntry; j++) { - - // offset needed to center the drawn text in the slice - float offset = drawAngles[cnt] / 2; - - // calculate the text position - float x = (float) (r - * Math.cos(Math.toRadians((rotationAngle + absoluteAngles[cnt] - offset) - * mAnimator.getPhaseY())) + center.x); - float y = (float) (r - * Math.sin(Math.toRadians((rotationAngle + absoluteAngles[cnt] - offset) - * mAnimator.getPhaseY())) + center.y); - - float value = mChart.isUsePercentValuesEnabled() ? entries.get(j).getVal() - / mChart.getYValueSum() * 100f : entries.get(j).getVal(); - - String val = dataSet.getValueFormatter().getFormattedValue(value); - - float lineHeight = Utils.calcTextHeight(mValuePaint, val) - + Utils.convertDpToPixel(4f); - - boolean drawYVals = dataSet.isDrawValuesEnabled(); - - // draw everything, depending on settings - if (drawXVals && drawYVals) { - - c.drawText(val, x, y, mValuePaint); - if (j < data.getXValCount()) - c.drawText(data.getXVals().get(j), x, y + lineHeight, - mValuePaint); - - } else if (drawXVals && !drawYVals) { - if (j < data.getXValCount()) - c.drawText(data.getXVals().get(j), x, y + lineHeight / 2f, mValuePaint); - } else if (!drawXVals && drawYVals) { - - c.drawText(val, x, y + lineHeight / 2f, mValuePaint); - } - - cnt++; - } - } - } - - @Override - public void drawExtras(Canvas c) { - // drawCircles(c); - drawHole(c); - c.drawBitmap(mDrawBitmap, 0, 0, mRenderPaint); - drawCenterText(c); - } - - /** - * draws the hole in the center of the chart and the transparent circle / - * hole - */ - protected void drawHole(Canvas c) { - - if (mChart.isDrawHoleEnabled()) { - - float transparentCircleRadius = mChart.getTransparentCircleRadius(); - float holeRadius = mChart.getHoleRadius(); - float radius = mChart.getRadius(); - - PointF center = mChart.getCenterCircleBox(); - - if (transparentCircleRadius > holeRadius && mAnimator.getPhaseX() >= 1f - && mAnimator.getPhaseY() >= 1f) { - - int color = mTransparentCirclePaint.getColor(); - - // make transparent - mTransparentCirclePaint.setColor(color & 0x60FFFFFF); - - // draw the transparent-circle - mBitmapCanvas.drawCircle(center.x, center.y, - radius / 100 * transparentCircleRadius, mTransparentCirclePaint); - - mTransparentCirclePaint.setColor(color); - } - - // draw the hole-circle - mBitmapCanvas.drawCircle(center.x, center.y, - radius / 100 * holeRadius, mHolePaint); - } - } - - /** - * draws the description text in the center of the pie chart makes most - * sense when center-hole is enabled - */ - protected void drawCenterText(Canvas c) { - - String centerText = mChart.getCenterText(); - - if (mChart.isDrawCenterTextEnabled() && centerText != null) { - - PointF center = mChart.getCenterCircleBox(); - - // get all lines from the text - String[] lines = centerText.split("\n"); - - float maxlineheight = 0f; - - // calc the maximum line height - for (String line : lines) { - float curHeight = Utils.calcTextHeight(mCenterTextPaint, line); - if (curHeight > maxlineheight) - maxlineheight = curHeight; - } - - float linespacing = maxlineheight * 0.25f; - - float totalheight = maxlineheight * lines.length - linespacing * (lines.length - 1); - - int cnt = lines.length; - - float y = center.y; - - for (int i = 0; i < lines.length; i++) { - - String line = lines[lines.length - i - 1]; - - c.drawText(line, center.x, y - + maxlineheight * cnt - totalheight / 2f, - mCenterTextPaint); - cnt--; - y -= linespacing; - } - } - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - float rotationAngle = mChart.getRotationAngle(); - float angle = 0f; - - float[] drawAngles = mChart.getDrawAngles(); - float[] absoluteAngles = mChart.getAbsoluteAngles(); - - for (int i = 0; i < indices.length; i++) { - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - if (xIndex >= drawAngles.length) - continue; - - if (xIndex == 0) - angle = rotationAngle; - else - angle = rotationAngle + absoluteAngles[xIndex - 1]; - - angle *= mAnimator.getPhaseY(); - - float sliceDegrees = drawAngles[xIndex]; - - PieDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - float shift = set.getSelectionShift(); - RectF circleBox = mChart.getCircleBox(); - - /** - * Make the box containing current arc larger equally in every - * dimension, to preserve shape of arc. Code provided by: - * - * @link https://github.com/wogg - */ - RectF highlighted = new RectF(circleBox.left - shift, - circleBox.top - shift, - circleBox.right + shift, - circleBox.bottom + shift); - - mRenderPaint.setColor(set.getColor(xIndex)); - - // redefine the rect that contains the arc so that the - // highlighted pie is not cut off - mBitmapCanvas.drawArc(highlighted, angle + set.getSliceSpace() / 2f, sliceDegrees - * mAnimator.getPhaseY() - - set.getSliceSpace() / 2f, true, mRenderPaint); - } - } - - /** - * This gives all pie-slices a rounded edge. - * - * @param c - */ - protected void drawRoundedSlices(Canvas c) { - - if (!mChart.isDrawRoundedSlicesEnabled()) - return; - - PieDataSet dataSet = mChart.getData().getDataSet(); - - if (!dataSet.isVisible()) - return; - - PointF center = mChart.getCenterCircleBox(); - float r = mChart.getRadius(); - - // calculate the radius of the "slice-circle" - float circleRadius = (r - (r * mChart.getHoleRadius() / 100f)) / 2f; - - List entries = dataSet.getYVals(); - float[] drawAngles = mChart.getDrawAngles(); - float angle = mChart.getRotationAngle(); - - for (int j = 0; j < entries.size(); j++) { - - float newangle = drawAngles[j]; - - Entry e = entries.get(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 + newangle) - * mAnimator.getPhaseY())) + center.x); - float y = (float) ((r - circleRadius) - * Math.sin(Math.toRadians((angle + newangle) - * mAnimator.getPhaseY())) + center.y); - - mRenderPaint.setColor(dataSet.getColor(j)); - mBitmapCanvas.drawCircle(x, y, circleRadius, mRenderPaint); - } - - angle += newangle * mAnimator.getPhaseX(); - } - } -} 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 7496c6a451..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/RadarChartRenderer.java +++ /dev/null @@ -1,237 +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 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.data.RadarDataSet; -import com.github.mikephil.charting.utils.Highlight; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class RadarChartRenderer extends DataRenderer { - - 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(); - - for (RadarDataSet set : radarData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, RadarDataSet dataSet) { - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - List entries = dataSet.getYVals(); - - Path surface = new Path(); - - for (int j = 0; j < entries.size(); j++) { - - mRenderPaint.setColor(dataSet.getColor(j)); - - Entry e = entries.get(j); - - PointF p = Utils.getPosition(center, (e.getVal() - mChart.getYChartMin()) * factor, - sliceangle * j + mChart.getRotationAngle()); - - if (j == 0) - surface.moveTo(p.x, p.y); - else - surface.lineTo(p.x, p.y); - } - - surface.close(); - - // draw filled - if (dataSet.isDrawFilledEnabled()) { - mRenderPaint.setStyle(Paint.Style.FILL); - mRenderPaint.setAlpha(dataSet.getFillAlpha()); - c.drawPath(surface, mRenderPaint); - mRenderPaint.setAlpha(255); - } - - 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); - } - - @Override - public void drawValues(Canvas c) { - - 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++) { - - RadarDataSet dataSet = mChart.getData().getDataSetByIndex(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - List entries = dataSet.getYVals(); - - for (int j = 0; j < entries.size(); j++) { - - Entry e = entries.get(j); - - PointF p = Utils.getPosition(center, (e.getVal() - mChart.getYChartMin()) * factor, - sliceangle * j + mChart.getRotationAngle()); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(e.getVal()), - p.x, p.y - yoffset, mValuePaint); - } - } - } - - @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()); - - for (int i = 0; i < mChart.getData().getXValCount(); i++) { - - 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 sliceangle = mChart.getSliceAngle(); - float factor = mChart.getFactor(); - - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < indices.length; i++) { - - RadarDataSet set = mChart.getData() - .getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - // get the index to highlight - int xIndex = indices[i].getXIndex(); - - Entry e = set.getEntryForXIndex(xIndex); - int j = set.getEntryPosition(e); - float y = (e.getVal() - mChart.getYChartMin()); - - PointF p = Utils.getPosition(center, y * factor, - sliceangle * j + mChart.getRotationAngle()); - - float[] pts = new float[] { - p.x, 0, p.x, mViewPortHandler.getChartHeight(), 0, p.y, - mViewPortHandler.getChartWidth(), p.y - }; - - c.drawLines(pts, mHighlightPaint); - } - } - -} 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 6ea83adc52..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/Renderer.java +++ /dev/null @@ -1,71 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import com.github.mikephil.charting.interfaces.BarLineScatterCandleDataProvider; -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). - * - * @param chart - * @param modulus - */ - public void calcXBounds(BarLineScatterCandleDataProvider chart, int xAxisModulus) { - - int low = chart.getLowestVisibleXIndex(); - int high = chart.getHighestVisibleXIndex(); - - int subLow = (low % xAxisModulus == 0) ? xAxisModulus : 0; - - mMinX = Math.max((low / xAxisModulus) * (xAxisModulus) - subLow, 0); - mMaxX = Math.min((high / xAxisModulus) * (xAxisModulus) + xAxisModulus, (int) chart.getXChartMax()); - -// double minx = trans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), 0).x; -// double maxx = trans.getValuesByTouchPoint(mViewPortHandler.contentRight(), 0).x; -// -// if (!Double.isInfinite(minx)) -// mMinX = (int) minx; -// if (!Double.isInfinite(maxx)) -// mMaxX = (int) Math.ceil(maxx); - } -} 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 3d279091f5..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/ScatterChartRenderer.java +++ /dev/null @@ -1,283 +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.data.ScatterDataSet; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; -import com.github.mikephil.charting.utils.Highlight; -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 DataRenderer { - - protected ScatterDataProvider mChart; - - protected ScatterBuffer[] mScatterBuffers; - - public ScatterChartRenderer(ScatterDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { - super(animator, viewPortHandler); - mChart = chart; - - mRenderPaint.setStrokeWidth(Utils.convertDpToPixel(1f)); - } - - @Override - public void initBuffers() { - - ScatterData scatterData = mChart.getScatterData(); - - mScatterBuffers = new ScatterBuffer[scatterData.getDataSetCount()]; - - for (int i = 0; i < mScatterBuffers.length; i++) { - ScatterDataSet set = scatterData.getDataSetByIndex(i); - mScatterBuffers[i] = new ScatterBuffer(set.getEntryCount() * 2); - } - } - - @Override - public void drawData(Canvas c) { - - ScatterData scatterData = mChart.getScatterData(); - - for (ScatterDataSet set : scatterData.getDataSets()) { - - if (set.isVisible()) - drawDataSet(c, set); - } - } - - protected void drawDataSet(Canvas c, ScatterDataSet dataSet) { - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseX = mAnimator.getPhaseX(); - float phaseY = mAnimator.getPhaseY(); - - List entries = dataSet.getYVals(); - - float shapeHalf = dataSet.getScatterShapeSize() / 2f; - - ScatterShape shape = dataSet.getScatterShape(); - - ScatterBuffer buffer = mScatterBuffers[mChart.getScatterData().getIndexOfDataSet( - dataSet)]; - buffer.setPhases(phaseX, phaseY); - buffer.feed(entries); - - trans.pointValuesToPixel(buffer.buffer); - - switch (shape) { - case SQUARE: - - mRenderPaint.setStyle(Style.FILL); - - 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.drawRect(buffer.buffer[i] - shapeHalf, - buffer.buffer[i + 1] - shapeHalf, buffer.buffer[i] - + shapeHalf, buffer.buffer[i + 1] - + shapeHalf, mRenderPaint); - } - break; - case CIRCLE: - - mRenderPaint.setStyle(Style.FILL); - - 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.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); - tri.close(); - - c.drawPath(tri, mRenderPaint); - tri.reset(); - } - break; - case CROSS: - - mRenderPaint.setStyle(Style.STROKE); - - 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; - 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++) { - - ScatterDataSet dataSet = dataSets.get(i); - - if (!dataSet.isDrawValuesEnabled()) - continue; - - // apply the text-styling defined by the DataSet - applyValueTextStyle(dataSet); - - List entries = dataSet.getYVals(); - - float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesScatter(entries, - mAnimator.getPhaseY()); - - float shapeSize = 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; - - float val = entries.get(j / 2).getVal(); - - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), positions[j], - positions[j + 1] - shapeSize, - mValuePaint); - } - } - } - } - - @Override - public void drawExtras(Canvas c) { - } - - @Override - public void drawHighlighted(Canvas c, Highlight[] indices) { - - for (int i = 0; i < indices.length; i++) { - - ScatterDataSet set = mChart.getScatterData().getDataSetByIndex(indices[i] - .getDataSetIndex()); - - if (set == null) - continue; - - mHighlightPaint.setColor(set.getHighLightColor()); - - int xIndex = indices[i].getXIndex(); // get the - // x-position - - if (xIndex > mChart.getXChartMax() * mAnimator.getPhaseX()) - continue; - - float y = set.getYValForXIndex(xIndex) * mAnimator.getPhaseY(); // get - // the - // y-position - - float[] pts = new float[] { - xIndex, mChart.getYChartMax(), xIndex, mChart.getYChartMin(), 0, y, - mChart.getXChartMax(), y - }; - - mChart.getTransformer(set.getAxisDependency()).pointValuesToPixel(pts); - // draw the highlight lines - c.drawLines(pts, mHighlightPaint); - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java deleted file mode 100644 index 426abd7c78..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ /dev/null @@ -1,252 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Path; - -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.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class XAxisRenderer extends AxisRenderer { - - protected XAxis mXAxis; - - public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { - super(viewPortHandler, trans); - - this.mXAxis = xAxis; - - mAxisLabelPaint.setColor(Color.BLACK); - mAxisLabelPaint.setTextAlign(Align.CENTER); - mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); - } - - public void computeAxis(float xValAverageLength, List xValues) { - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - - StringBuffer a = new StringBuffer(); - - int max = (int) Math.round(xValAverageLength - + mXAxis.getSpaceBetweenLabels()); - - for (int i = 0; i < max; i++) { - a.append("h"); - } - - mXAxis.mLabelWidth = Utils.calcTextWidth(mAxisLabelPaint, a.toString()); - mXAxis.mLabelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); - mXAxis.setValues(xValues); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float yoffset = Utils.convertDpToPixel(4f); - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setColor(mXAxis.getTextColor()); - - if (mXAxis.getPosition() == XAxisPosition.TOP) { - - drawLabels(c, mViewPortHandler.offsetTop() - yoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.5f); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - drawLabels(c, mViewPortHandler.contentBottom() - yoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - drawLabels(c, mViewPortHandler.offsetTop() + yoffset + mXAxis.mLabelHeight); - - } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.offsetTop() - yoffset); - drawLabels(c, mViewPortHandler.contentBottom() + mXAxis.mLabelHeight + yoffset * 1.6f); - } - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - protected void drawLabels(Canvas c, float pos) { - - // pre allocate to save performance (dont allocate in loop) - float[] position = new float[] { - 0f, 0f - }; - - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsX(position[0])) { - - String label = mXAxis.getValues().get(i); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i == mXAxis.getValues().size() - 1 && mXAxis.getValues().size() > 1) { - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - position[0] += width / 2; - } - } - - c.drawText(label, position[0], - pos, - mAxisLabelPaint); - } - } - } - - @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()); - mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); - - for (int i = mMinX; i <= mMaxX; i += mXAxis.mAxisLabelModulus) { - - position[0] = i; - - mTrans.pointValuesToPixel(position); - - if (position[0] >= mViewPortHandler.offsetLeft() - && position[0] <= mViewPortHandler.getChartWidth()) { - - c.drawLine(position[0], mViewPortHandler.offsetTop(), position[0], - mViewPortHandler.contentBottom(), mGridPaint); - } - } - } - - /** - * 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[] pts = new float[4]; - Path limitLinePath = new Path(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - pts[0] = l.getLimit(); - pts[2] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - pts[1] = mViewPortHandler.contentTop(); - pts[3] = mViewPortHandler.contentBottom(); - - limitLinePath.moveTo(pts[0], pts[1]); - limitLinePath.lineTo(pts[2], pts[3]); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - float xOffset = l.getLineWidth(); - float add = Utils.convertDpToPixel(4f); - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - float yOffset = Utils.calcTextHeight(mLimitLinePaint, label) + add / 2f; - - if (l.getLabelPosition() == LimitLine.LimitLabelPosition.POS_RIGHT) { - c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentBottom() - add, mLimitLinePaint); - } else { - c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentTop() + yOffset, mLimitLinePaint); - } - } - } - } -} 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 fb792687d4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererBarChart.java +++ /dev/null @@ -1,111 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; - -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) { - - // 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 (width > mViewPortHandler.offsetRight() * 2 - && position[0] + width > mViewPortHandler.getChartWidth()) - position[0] -= width / 2; - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - position[0] += width / 2; - } - } - - c.drawText(label, position[0], - pos, - mAxisLabelPaint); - } - } - } - - @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/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java deleted file mode 100644 index dc6a52ec6d..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ /dev/null @@ -1,240 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Path; - -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.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 XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, - Transformer trans, BarChart chart) { - super(viewPortHandler, xAxis, trans, chart); - } - - @Override - public void computeAxis(float xValAverageLength, List xValues) { - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mXAxis.setValues(xValues); - - String longest = mXAxis.getLongestLabel(); - mXAxis.mLabelWidth = (int) (Utils.calcTextWidth(mAxisLabelPaint, longest) + mXAxis.getXOffset() * 3.5f); - mXAxis.mLabelHeight = Utils.calcTextHeight(mAxisLabelPaint, longest); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float xoffset = mXAxis.getXOffset(); - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setColor(mXAxis.getTextColor()); - - if (mXAxis.getPosition() == XAxisPosition.TOP) { - - mAxisLabelPaint.setTextAlign(Align.LEFT); - drawLabels(c, mViewPortHandler.contentRight() + xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - - mAxisLabelPaint.setTextAlign(Align.RIGHT); - drawLabels(c, mViewPortHandler.contentLeft() - xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - - mAxisLabelPaint.setTextAlign(Align.LEFT); - drawLabels(c, mViewPortHandler.contentLeft() + xoffset); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - - mAxisLabelPaint.setTextAlign(Align.RIGHT); - drawLabels(c, mViewPortHandler.contentRight() - xoffset); - - } else { // BOTH SIDED - - drawLabels(c, mViewPortHandler.contentLeft()); - drawLabels(c, mViewPortHandler.contentRight()); - } - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - @Override - protected void drawLabels(Canvas c, float pos) { - - // 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[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; - } - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - String label = mXAxis.getValues().get(i); - c.drawText(label, pos, position[1] + mXAxis.mLabelHeight / 2f, - mAxisLabelPaint); - } - } - } - - @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) { - - position[1] = i * step + i * bd.getGroupSpace() - 0.5f; - - mTrans.pointValuesToPixel(position); - - if (mViewPortHandler.isInBoundsY(position[1])) { - - c.drawLine(mViewPortHandler.contentLeft(), position[1], - mViewPortHandler.contentRight(), position[1], mGridPaint); - } - } - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * Draws the LimitLines associated with this axis to the screen. - * This is the standard YAxis renderer using the XAxis limit lines. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mXAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = new float[2]; - Path limitLinePath = new Path(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - - pts[1] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); - limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - // c.drawLines(pts, mLimitLinePaint); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - float xOffset = Utils.convertDpToPixel(4f); - float yOffset = l.getLineWidth() + Utils.calcTextHeight(mLimitLinePaint, label) - / 2f; - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - if (l.getLabelPosition() == LimitLine.LimitLabelPosition.POS_RIGHT) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, mViewPortHandler.contentRight() - - xOffset, - pts[1] - yOffset, mLimitLinePaint); - - } else { - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, mViewPortHandler.offsetLeft() - + xOffset, - pts[1] - yOffset, mLimitLinePaint); - } - } - } - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java deleted file mode 100644 index 7016e1f091..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ /dev/null @@ -1,329 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Path; - -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.Transformer; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -import java.util.List; - -public class YAxisRenderer extends AxisRenderer { - - protected YAxis mYAxis; - - public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { - super(viewPortHandler, trans); - - this.mYAxis = yAxis; - - mAxisLabelPaint.setColor(Color.BLACK); - mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); - } - - /** - * 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); - } - - // 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) { - mYAxis.mEntries[i] = (float) f; - } - } - - if (interval < 1) { - mYAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mYAxis.mDecimals = 0; - } - } - - /** - * draws the y-axis labels to the screen - */ - @Override - 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); - - mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setColor(mYAxis.getTextColor()); - - float xoffset = mYAxis.getXOffset(); - float yoffset = Utils.calcTextHeight(mAxisLabelPaint, "A") / 2.5f; - - AxisDependency dependency = mYAxis.getAxisDependency(); - YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); - - float xPos = 0f; - - if (dependency == AxisDependency.LEFT) { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - mAxisLabelPaint.setTextAlign(Align.RIGHT); - xPos = mViewPortHandler.offsetLeft() - xoffset; - } else { - mAxisLabelPaint.setTextAlign(Align.LEFT); - xPos = mViewPortHandler.offsetLeft() + xoffset; - } - - } else { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - mAxisLabelPaint.setTextAlign(Align.LEFT); - xPos = mViewPortHandler.contentRight() + xoffset; - } else { - mAxisLabelPaint.setTextAlign(Align.RIGHT); - xPos = mViewPortHandler.contentRight() - xoffset; - } - } - - drawYLabels(c, xPos, positions, yoffset); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) - return; - - mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); - - if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { - c.drawLine(mViewPortHandler.contentLeft(), - mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } else { - c.drawLine(mViewPortHandler.contentRight(), - mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), - mViewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the y-labels on the specified x-position - * - * @param fixedPosition - * @param positions - */ - protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { - - // draw - for (int i = 0; i < mYAxis.mEntryCount; i++) { - - String text = mYAxis.getFormattedLabel(i); - - if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1) - return; - - c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint); - } - } - - @Override - public void renderGridLines(Canvas c) { - - if (!mYAxis.isDrawGridLinesEnabled() || !mYAxis.isEnabled()) - return; - - // pre alloc - float[] position = new float[2]; - - 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); - - gridLinePath.moveTo(mViewPortHandler.offsetLeft(), position[1]); - gridLinePath.lineTo(mViewPortHandler.contentRight(), - position[1]); - - c.drawPath(gridLinePath, mGridPaint); - - gridLinePath.reset(); - } - } - - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mYAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = new float[2]; - Path limitLinePath = new Path(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - - pts[1] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); - limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - // c.drawLines(pts, mLimitLinePaint); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - float xOffset = Utils.convertDpToPixel(4f); - float yOffset = l.getLineWidth() + Utils.calcTextHeight(mLimitLinePaint, label) - / 2f; - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - if (l.getLabelPosition() == LimitLabelPosition.POS_RIGHT) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, mViewPortHandler.contentRight() - - xOffset, - pts[1] - yOffset, mLimitLinePaint); - - } else { - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, mViewPortHandler.offsetLeft() - + xOffset, - pts[1] - yOffset, mLimitLinePaint); - } - } - } - } -} 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 0c7a46c307..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ /dev/null @@ -1,173 +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); - } - - // 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; - } - - 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; - } - - 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); - - 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/ColorFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/ColorFormatter.java deleted file mode 100644 index f646d0e4a9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorFormatter.java +++ /dev/null @@ -1,15 +0,0 @@ - -package com.github.mikephil.charting.utils; - -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 { - - public int getColor(Entry e, int index); -} \ No newline at end of file diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java deleted file mode 100644 index f63c77edc7..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/DefaultValueFormatter.java +++ /dev/null @@ -1,40 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * Default formatter used for formatting values. Uses a DecimalFormat with - * pre-calculated number of digits (depending on max and min value). - * - * @author Philipp Jahoda - */ -public class DefaultValueFormatter implements ValueFormatter { - - /** decimalformat for formatting */ - private DecimalFormat mFormat; - - /** - * Constructor that specifies to how many digits the value should be - * formatted. - * - * @param digits - */ - public DefaultValueFormatter(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) { - // avoid memory allocations here (for performance) - return mFormat.format(value); - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java deleted file mode 100644 index 0fe37914b3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/FillFormatter.java +++ /dev/null @@ -1,27 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.LineData; -import com.github.mikephil.charting.data.LineDataSet; - -/** - * Interface for providing a custom logic to where the filling line of a DataSet - * should end. If setFillEnabled(...) is set to true. - * - * @author Philipp Jahoda - */ -public interface FillFormatter { - - /** - * Returns the vertical (y-axis) position where the filled-line of the - * DataSet should end. - * - * @param dataSet - * @param data - * @param chartMaxY - * @param chartMinY - * @return - */ - public float getFillLinePosition(LineDataSet dataSet, LineData data, float chartMaxY, - float chartMinY); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java b/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java deleted file mode 100644 index 63c4457d34..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Highlight.java +++ /dev/null @@ -1,99 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * 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; - - /** - * constructor - * - * @param x the index of the highlighted value on the x-axis - * @param val the value at the position the user touched - * @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 val the value at the position the user touched - * @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; - } - - /** - * 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 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/utils/LargeValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/LargeValueFormatter.java deleted file mode 100644 index 119c671588..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/LargeValueFormatter.java +++ /dev/null @@ -1,60 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * Predefined value-formatter that formats large numbers in a pretty way. - * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; - * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = - * 10m; 1000000000 = 1b; Special thanks to Roman Gromov - * (https://github.com/romangromov) for this piece of code. - * - * @author Philipp Jahoda - * @author Oleksandr Tyshkovets - */ -public class LargeValueFormatter implements ValueFormatter { - - private static final String[] SUFFIX = new String[] { - "", "k", "m", "b", "t" - }; - private static final int MAX_LENGTH = 4; - - private DecimalFormat mFormat; - private String mText = ""; - - public LargeValueFormatter() { - mFormat = new DecimalFormat("###E0"); - } - - /** - * Creates a formatter that appends a specified text to the result string - * @param text a text that will be appended - */ - public LargeValueFormatter(String appendix) { - this(); - mText = appendix; - } - - @Override - public String getFormattedValue(float value) { - return makePretty(value) + mText; - } - - /** - * Formats each number properly. Special thanks to Roman Gromov - * (https://github.com/romangromov) for this piece of code. - */ - private String makePretty(double number) { - - String r = mFormat.format(number); - - r = r.replaceAll("E[0-9]", SUFFIX[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]); - - while (r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")) { - r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); - } - - return r; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java deleted file mode 100644 index b69b3e9ba9..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/PercentFormatter.java +++ /dev/null @@ -1,24 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import java.text.DecimalFormat; - -/** - * This ValueFormatter is just for convenience and simply puts a "%" sign after - * each value. (Recommeded for PieChart) - * - * @author Philipp Jahoda - */ -public class PercentFormatter implements ValueFormatter { - - protected DecimalFormat mFormat; - - public PercentFormatter() { - mFormat = new DecimalFormat("###,###,##0.0"); - } - - @Override - public String getFormattedValue(float value) { - return mFormat.format(value) + " %"; - } -} 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 97c4aeead8..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/PointD.java +++ /dev/null @@ -1,23 +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/SelInfo.java b/MPChartLib/src/com/github/mikephil/charting/utils/SelInfo.java deleted file mode 100644 index 4c3749a9a1..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/SelInfo.java +++ /dev/null @@ -1,25 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import com.github.mikephil.charting.data.DataSet; - -/** - * Class that encapsulates information of a value that has been - * selected/highlighted and its DataSet index. The SelInfo 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 SelInfo { - - public float val; - public int dataSetIndex; - public DataSet dataSet; - - public SelInfo(float val, int dataSetIndex, DataSet set) { - this.val = val; - this.dataSetIndex = dataSetIndex; - this.dataSet = set; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java deleted file mode 100644 index d87a5f5a8a..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Transformer.java +++ /dev/null @@ -1,450 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.graphics.Matrix; -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 java.util.List; - -/** - * Transformer class that contains all matrices and is responsible for - * transforming values into pixels on the screen and backwards. - * - * @author Philipp Jahoda - */ -public class Transformer { - - /** matrix to map the values to the screen pixels */ - protected Matrix mMatrixValueToPx = new Matrix(); - - /** matrix for handling the different offsets of the chart */ - protected Matrix mMatrixOffset = new Matrix(); - - protected ViewPortHandler mViewPortHandler; - - public Transformer(ViewPortHandler viewPortHandler) { - this.mViewPortHandler = viewPortHandler; - } - - /** - * Prepares the matrix that transforms values to pixels. Calculates the - * scale factors from the charts size and offsets. - * - * @param chart - */ - public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) { - - float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); - float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); - - // setup all matrices - mMatrixValueToPx.reset(); - mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); - mMatrixValueToPx.postScale(scaleX, -scaleY); - } - - // /** - // * Prepares the transformation matrix with the specified scales. - // * - // * @param chart - // * @param scaleX - // * @param scaleY - // */ - // public void prepareMatrixValuePx(ChartInterface chart, float scaleX, - // float scaleY) { - // - // mMatrixValueToPx.reset(); - // mMatrixValueToPx.postTranslate(0, -chart.getYChartMin()); - // mMatrixValueToPx.postScale(scaleX, -scaleY); - // } - - /** - * Prepares the matrix that contains all offsets. - * - * @param chart - */ - public void prepareMatrixOffset(boolean inverted) { - - mMatrixOffset.reset(); - - // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); - - if (!inverted) - mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), - mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); - else { - mMatrixOffset - .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); - mMatrixOffset.postScale(1.0f, -1.0f); - } - - // mMatrixOffset.set(offset); - - // mMatrixOffset.reset(); - // - // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - - // mOffsetBottom); - } - - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the SCATTERCHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesScatter(List entries, - float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the BUBBLECHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesBubble(List entries, - float phaseX, float phaseY, int from, int to) { - - final int count = (int) Math.ceil(to - from) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; - - float[] valuePoints = new float[count]; - - for (int j = 0; j < count; j += 2) { - - Entry e = entries.get(j / 2 + from); - - if (e != null) { - valuePoints[j] = (float) (e.getXIndex() - from) * phaseX + from; - valuePoints[j + 1] = e.getVal() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the LINECHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesLine(List entries, - float phaseX, float phaseY, int from, int to) { - - final int count = (int)Math.ceil((to - from) * phaseX) * 2; - - float[] valuePoints = new float[count]; - - for (int j = 0; j < count; j += 2) { - - Entry e = entries.get(j / 2 + from); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getVal() * phaseY; - } - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * Transforms an List of Entry into a float array containing the x and - * y values transformed with all matrices for the CANDLESTICKCHART. - * - * @param entries - * @return - */ - public float[] generateTransformedValuesCandle(List entries, - float phaseX, float phaseY, int from, int to) { - - final int count = (int)Math.ceil((to - from) * phaseX) * 2; - - float[] valuePoints = new float[count]; - - for (int j = 0; j < count; j += 2) { - - CandleEntry e = entries.get(j / 2 + from); - - if (e != null) { - valuePoints[j] = e.getXIndex(); - valuePoints[j + 1] = e.getHigh() * phaseY; - } - } - - pointValuesToPixel(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 entries - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesBarChart(List entries, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + (j / 2 * (setCount - 1)) + dataSet + space * (j / 2) - + space / 2f; - float y = e.getVal(); - - valuePoints[j] = x; - valuePoints[j + 1] = y * phaseY; - } - - pointValuesToPixel(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 entries - * @param dataSet the dataset index - * @return - */ - public float[] generateTransformedValuesHorizontalBarChart(List entries, - int dataSet, BarData bd, float phaseY) { - - float[] valuePoints = new float[entries.size() * 2]; - - int setCount = bd.getDataSetCount(); - float space = bd.getGroupSpace(); - - for (int j = 0; j < valuePoints.length; j += 2) { - - Entry e = entries.get(j / 2); - - // calculate the x-position, depending on datasetcount - float x = e.getXIndex() + (j / 2 * (setCount - 1)) + dataSet + space * (j / 2) - + space / 2f ; - float y = e.getVal(); - - valuePoints[j] = y * phaseY; - valuePoints[j + 1] = x; - } - - pointValuesToPixel(valuePoints); - - return valuePoints; - } - - /** - * transform a path with all the given matrices VERY IMPORTANT: keep order - * to value-touch-offset - * - * @param path - */ - public void pathValueToPixel(Path path) { - - path.transform(mMatrixValueToPx); - path.transform(mViewPortHandler.getMatrixTouch()); - path.transform(mMatrixOffset); - } - - /** - * Transforms multiple paths will all matrices. - * - * @param paths - */ - public void pathValuesToPixel(List paths) { - - for (int i = 0; i < paths.size(); i++) { - pathValueToPixel(paths.get(i)); - } - } - - /** - * Transform an array of points with all matrices. VERY IMPORTANT: Keep - * matrix order "value-touch-offset" when transforming. - * - * @param pts - */ - public void pointValuesToPixel(float[] pts) { - - mMatrixValueToPx.mapPoints(pts); - mViewPortHandler.getMatrixTouch().mapPoints(pts); - mMatrixOffset.mapPoints(pts); - } - - /** - * Transform a rectangle with all matrices. - * - * @param r - */ - public void rectValueToPixel(RectF r) { - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * Transform a rectangle with all matrices with potential animation phases. - * - * @param r - * @param phaseY - */ - public void rectValueToPixel(RectF r, float phaseY) { - - // multiply the height of the rect with the phase - if (r.top > 0) - r.top *= phaseY; - else - r.bottom *= phaseY; - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * Transform a rectangle with all matrices with potential animation phases. - * - * @param r - * @param phaseY - */ - public void rectValueToPixelHorizontal(RectF r, float phaseY) { - - // multiply the height of the rect with the phase - if (r.left > 0) - r.left *= phaseY; - else - r.right *= phaseY; - - mMatrixValueToPx.mapRect(r); - mViewPortHandler.getMatrixTouch().mapRect(r); - mMatrixOffset.mapRect(r); - } - - /** - * transforms multiple rects with all matrices - * - * @param rects - */ - public void rectValuesToPixel(List rects) { - - for (int i = 0; i < rects.size(); i++) - rectValueToPixel(rects.get(i)); - } - - /** - * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) - * into values on the chart. - * - * @param pixels - */ - public void pixelsToValue(float[] pixels) { - - Matrix tmp = new Matrix(); - - // invert all matrixes to convert back to the original value - mMatrixOffset.invert(tmp); - tmp.mapPoints(pixels); - - mViewPortHandler.getMatrixTouch().invert(tmp); - tmp.mapPoints(pixels); - - mMatrixValueToPx.invert(tmp); - tmp.mapPoints(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 - * coordinates / values in the chart. This is the opposite method to - * getPixelsForValues(...). - * - * @param x - * @param y - * @return - */ - public PointD getValuesByTouchPoint(float x, float y) { - - // create an array of the touch-point - float[] pts = new float[2]; - pts[0] = x; - pts[1] = y; - - pixelsToValue(pts); - - double xTouchVal = pts[0]; - double yTouchVal = pts[1]; - - return new PointD(xTouchVal, yTouchVal); - } - - // /** - // * transforms the given rect objects with the touch matrix only - // * - // * @param paths - // */ - // public void transformRectsTouch(List rects) { - // for (int i = 0; i < rects.size(); i++) { - // mMatrixTouch.mapRect(rects.get(i)); - // } - // } - // - // /** - // * transforms the given path objects with the touch matrix only - // * - // * @param paths - // */ - // public void transformPathsTouch(List paths) { - // for (int i = 0; i < paths.size(); i++) { - // paths.get(i).transform(mMatrixTouch); - // } - // } - - public Matrix getValueMatrix() { - return mMatrixValueToPx; - } - - public Matrix getOffsetMatrix() { - return mMatrixOffset; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java deleted file mode 100644 index 54c311c5c3..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/Utils.java +++ /dev/null @@ -1,533 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Paint; -import android.graphics.PointF; -import android.graphics.Rect; -import android.os.Build; -import android.util.DisplayMetrics; -import android.util.Log; -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 java.text.DecimalFormat; -import java.util.List; - -/** - * Utilities class that has some helper methods. Needs to be initialized by - * calling Utils.init(...) before usage. Inside the Chart.init() method, this is - * done, if the Utils are used before that, Utils.init(...) needs to be called - * manually. - * - * @author Philipp Jahoda - */ -public abstract class Utils { - - private static DisplayMetrics mMetrics; - private static int mMinimumFlingVelocity = 50; - private static int mMaximumFlingVelocity = 8000; - - /** - * initialize method, called inside the Chart.init() method. - * - * @param res - */ - @SuppressWarnings("deprecation") - public static void init(Context context) { - - if (context == null) { - // noinspection deprecation - mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); - // noinspection deprecation - mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); - - Log.e("MPAndroidChart, Utils.init(...)", "PROVIDED CONTEXT OBJECT IS NULL"); - - } else { - ViewConfiguration viewConfiguration = ViewConfiguration.get(context); - mMinimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); - mMaximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); - - Resources res = context.getResources(); - mMetrics = res.getDisplayMetrics(); - } - } - - /** - * initialize method, called inside the Chart.init() method. backwards - * compatibility - to not break existing code - * - * @param res - */ - @Deprecated - public static void init(Resources res) { - - mMetrics = res.getDisplayMetrics(); - - // noinspection deprecation - mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); - // noinspection deprecation - mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); - } - - /** - * format a number properly with the given number of digits - * - * @param number the number to format - * @param digits the number of digits - * @return - */ - public static String formatDecimal(double number, int digits) { - - StringBuffer a = new StringBuffer(); - for (int i = 0; i < digits; i++) { - if (i == 0) - a.append("."); - a.append("0"); - } - - DecimalFormat nf = new DecimalFormat("###,###,###,##0" + a.toString()); - String formatted = nf.format(number); - - return formatted; - } - - /** - * This method converts dp unit to equivalent pixels, depending on device - * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. - * - * @param dp A value in dp (density independent pixels) unit. Which we need - * to convert into pixels - * @return A float value to represent px equivalent to dp depending on - * device density - */ - 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."); - 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; - } - - /** - * This method converts device specific pixels to density independent - * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. - * - * @param px A value in px (pixels) unit. Which we need to convert into db - * @return A float value to represent dp equivalent to px value - */ - 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."); - 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; - } - - /** - * calculates the approximate width of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @return - */ - public static int calcTextWidth(Paint paint, String demoText) { - return (int) paint.measureText(demoText); - } - - /** - * calculates the approximate height of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @return - */ - public static int calcTextHeight(Paint paint, String demoText) { - - Rect r = new Rect(); - paint.getTextBounds(demoText, 0, demoText.length(), r); - return r.height(); - } - - // /** - // * returns the appropriate number of format digits for a delta value - // * - // * @param delta - // * @return - // */ - // public static int getFormatDigits(float delta) { - // - // if (delta < 0.1) { - // return 6; - // } else if (delta <= 1) { - // return 4; - // } else if (delta < 4) { - // return 3; - // } else if (delta < 20) { - // return 2; - // } else if (delta < 60) { - // return 1; - // } else { - // return 0; - // } - // } - - /** - * returns the appropriate number of format digits for a legend value - * - * @param delta - * @param bonus - additional digits - * @return - */ - public static int getLegendFormatDigits(float step, int bonus) { - - if (step < 0.0000099) { - return 6 + bonus; - } else if (step < 0.000099) { - return 5 + bonus; - } else if (step < 0.00099) { - return 4 + bonus; - } else if (step < 0.0099) { - return 3 + bonus; - } else if (step < 0.099) { - return 2 + bonus; - } else if (step < 0.99) { - return 1 + bonus; - } else { - return 0 + bonus; - } - } - - /** - * Math.pow(...) is very expensive, so avoid calling it and create it - * yourself. - */ - private static final int POW_10[] = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 - }; - - /** - * Formats the given number to the given number of decimals, and returns the - * number as a string, maximum 35 characters. - * - * @param number - * @param digitCount - * @param separateTousands set this to true to separate thousands values - * @return - */ - public static String formatNumber(float number, int digitCount, boolean separateThousands) { - - char[] out = new char[35]; - - boolean neg = false; - if (number == 0) { - return "0"; - } - - boolean zero = false; - if (number < 1 && number > -1) { - zero = true; - } - - if (number < 0) { - neg = true; - number = -number; - } - - if (digitCount > POW_10.length) { - digitCount = POW_10.length - 1; - } - - number *= POW_10[digitCount]; - long lval = Math.round(number); - int ind = out.length - 1; - int charCount = 0; - boolean decimalPointAdded = false; - - while (lval != 0 || charCount < (digitCount + 1)) { - int digit = (int) (lval % 10); - lval = lval / 10; - out[ind--] = (char) (digit + '0'); - charCount++; - - // add decimal point - if (charCount == digitCount) { - out[ind--] = ','; - charCount++; - decimalPointAdded = true; - - // add thousand separators - } else if (separateThousands && lval != 0 && charCount > digitCount) { - - if (decimalPointAdded) { - - if ((charCount - digitCount) % 4 == 0) { - out[ind--] = '.'; - charCount++; - } - - } else { - - if ((charCount - digitCount) % 4 == 3) { - out[ind--] = '.'; - charCount++; - } - } - } - } - - // if number around zero (between 1 and -1) - if (zero) { - out[ind--] = '0'; - charCount += 1; - } - - // if the number is negative - if (neg) { - out[ind--] = '-'; - charCount += 1; - } - - int start = out.length - charCount; - - // use this instead of "new String(...)" because of issue < Android 4.0 - return String.valueOf(out, start, out.length - start); - } - - /** - * rounds the given number to the next significant number - * - * @param number - * @return - */ - public static float roundToNextSignificant(double number) { - 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); - final long shifted = Math.round(number * magnitude); - return shifted / magnitude; - } - - /** - * Returns the appropriate number of decimals to be used for the provided - * number. - * - * @param number - * @return - */ - public static int getDecimals(float number) { - - float i = roundToNextSignificant(number); - return (int) Math.ceil(-Math.log10(i)) + 2; - } - - /** - * Converts the provided Integer List to an int array. - * - * @param integers - * @return - */ - 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(); - } - - return ret; - } - - /** - * Converts the provided String List to a String array. - * - * @param labels - * @return - */ - public static String[] convertStrings(List strings) { - - String[] ret = new String[strings.size()]; - - for (int i = 0; i < ret.length; i++) { - ret[i] = strings.get(i); - } - - return ret; - } - - /** - * Replacement for the Math.nextUp(...) method that is only available in - * HONEYCOMB and higher. Dat's some seeeeek sheeet. - * - * @param d - * @return - */ - public static double nextUp(double d) { - if (d == Double.POSITIVE_INFINITY) - return d; - else { - d += 0.0d; - return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + - ((d >= 0.0d) ? +1L : -1L)); - } - } - - /** - * Returns the index of the DataSet that contains the closest value on the - * y-axis. This is needed for highlighting. - * - * @param valsAtIndex all the values at a specific index - * @return - */ - public static int getClosestDataSetIndex(List valsAtIndex, float val, - AxisDependency axis) { - - int index = -1; - float distance = Float.MAX_VALUE; - - for (int i = 0; i < valsAtIndex.size(); i++) { - - SelInfo 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; - } - } - } - - // Log.i(LOG_TAG, "Closest DataSet index: " + index); - - 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++) { - - SelInfo sel = valsAtIndex.get(i); - - if (sel.dataSet.getAxisDependency() == axis) { - - float cdistance = Math.abs((float) sel.val - val); - if (cdistance < distance) { - distance = cdistance; - } - } - } - - return distance; - } - - /** - * Calculates the position around a center point, depending on the distance - * from the center, and the angle of the position around the center. - * - * @param center - * @param dist - * @param angle in degrees, converted to radians internally - * @return - */ - public static PointF getPosition(PointF 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)))); - return p; - } - - public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, - VelocityTracker tracker) { - - // Check the dot product of current velocities. - // If the pointer that left was opposing another velocity vector, clear. - tracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); - final int upIndex = ev.getActionIndex(); - final int id1 = ev.getPointerId(upIndex); - final float x1 = tracker.getXVelocity(id1); - final float y1 = tracker.getYVelocity(id1); - for (int i = 0, count = ev.getPointerCount(); i < count; i++) { - if (i == upIndex) - continue; - - final int id2 = ev.getPointerId(i); - final float x = x1 * tracker.getXVelocity(id2); - final float y = y1 * tracker.getYVelocity(id2); - - final float dot = x + y; - if (dot < 0) { - tracker.clear(); - break; - } - } - } - - /** - * Original method view.postInvalidateOnAnimation() only supportd in API >= - * 16, This is a replica of the code from ViewCompat. - * - * @param view - */ - @SuppressLint("NewApi") - public static void postInvalidateOnAnimation(View view) { - if (Build.VERSION.SDK_INT >= 16) - view.postInvalidateOnAnimation(); - else - view.postInvalidateDelayed(10); - } - - public static int getMinimumFlingVelocity() { - return mMinimumFlingVelocity; - } - - public static int getMaximumFlingVelocity() { - return mMaximumFlingVelocity; - } - - /** returns an angle between 0.f < 360.f (not less than zero, less than 360) */ - public static float getNormalizedAngle(float angle) { - while (angle < 0.f) - angle += 360.f; - - return angle % 360.f; - } -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java b/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java deleted file mode 100644 index 843fa9c7d6..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ValueFormatter.java +++ /dev/null @@ -1,23 +0,0 @@ - -package com.github.mikephil.charting.utils; - -/** - * Interface that allows custom formatting of all values and value-labels - * displayed inside the chart. Simply create your own formatting class and let - * it implement ValueFormatter. Then override the getFormattedLabel(...) method - * and return whatever you want. - * - * @author Philipp Jahoda - */ -public interface ValueFormatter { - - /** - * Called when a value (from labels, or inside the chart) is formatted - * before being drawn. For performance reasons, avoid excessive calculations - * and memory allocations inside this method. - * - * @param value the value to be formatted - * @return the formatted label ready for being drawn - */ - public String getFormattedValue(float value); -} diff --git a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java deleted file mode 100644 index 5aae8036b4..0000000000 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ViewPortHandler.java +++ /dev/null @@ -1,482 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.RectF; -import android.view.View; - -public class ViewPortHandler { - - /** matrix used for touch events */ - protected final Matrix mMatrixTouch = new Matrix(); - - /** this rectangle defines the area in which graph values can be drawn */ - protected RectF mContentRect = new RectF(); - - protected float mChartWidth = 0f; - protected float mChartHeight = 0f; - - /** minimum scale value on the y-axis */ - private float mMinScaleY = 1f; - - /** minimum scale value on the x-axis */ - private float mMinScaleX = 1f; - - /** contains the current scale factor of the x-axis */ - private float mScaleX = 1f; - - /** contains the current scale factor of the y-axis */ - private float mScaleY = 1f; - - /** offset that allows the chart to be dragged over its bounds on the x-axis */ - private float mTransOffsetX = 0f; - - /** offset that allows the chart to be dragged over its bounds on the x-axis */ - private float mTransOffsetY = 0f; - - public ViewPortHandler() { - - } - - /** - * Sets the width and height of the chart. - * - * @param width - * @param height - */ - - public void setChartDimens(float width, float height) { - - float offsetLeft = this.offsetLeft(); - float offsetTop = this.offsetTop(); - float offsetRight = this.offsetRight(); - float offsetBottom = this.offsetBottom(); - - mChartHeight = height; - mChartWidth = width; - - restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); - - } - - public boolean hasChartDimens() { - if (mChartHeight > 0 && mChartWidth > 0) - return true; - else - return false; - } - - public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, - float offsetBottom) { - mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight - - offsetBottom); - } - - public float offsetLeft() { - return mContentRect.left; - } - - public float offsetRight() { - return mChartWidth - mContentRect.right; - } - - public float offsetTop() { - return mContentRect.top; - } - - public float offsetBottom() { - return mChartHeight - mContentRect.bottom; - } - - public float contentTop() { - return mContentRect.top; - } - - public float contentLeft() { - return mContentRect.left; - } - - public float contentRight() { - return mContentRect.right; - } - - public float contentBottom() { - return mContentRect.bottom; - } - - public float contentWidth() { - return mContentRect.width(); - } - - public float contentHeight() { - return mContentRect.height(); - } - - public RectF getContentRect() { - return mContentRect; - } - - public PointF getContentCenter() { - return new PointF(mContentRect.centerX(), mContentRect.centerY()); - } - - public float getChartHeight() { - return mChartHeight; - } - - public float getChartWidth() { - return mChartWidth; - } - - /** - * ################ ################ ################ ################ - */ - /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ - - /** - * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom - * center. - * - * @param x - * @param y - */ - public Matrix zoomIn(float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(1.4f, 1.4f, x, y); - - return save; - } - - /** - * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom - * center. - */ - public Matrix zoomOut(float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - save.postScale(0.7f, 0.7f, x, y); - - return save; - } - - /** - * Zooms in or out by the given scale factor. x and y are the coordinates - * (in pixels) of the zoom center. - * - * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in - * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in - * @param x - * @param y - */ - public Matrix zoom(float scaleX, float scaleY, float x, float y) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - // Log.i(LOG_TAG, "Zooming, x: " + x + ", y: " + y); - - save.postScale(scaleX, scaleY, x, y); - - return save; - } - - /** - * Resets all zooming and dragging and makes the chart fit exactly it's - * bounds. - */ - public Matrix fitScreen() { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - float[] vals = new float[9]; - - save.getValues(vals); - - // reset all translations and scaling - vals[Matrix.MTRANS_X] = 0f; - vals[Matrix.MTRANS_Y] = 0f; - vals[Matrix.MSCALE_X] = 1f; - vals[Matrix.MSCALE_Y] = 1f; - - save.setValues(vals); - - return save; - } - - /** - * 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 - * not possible. Makes most sense in combination with the - * setScaleMinima(...) method. - * - * @param pts the position to center view viewport to - * @param chart - * @return save - */ - public synchronized void centerViewPort(final float[] transformedPts, final View view) { - - Matrix save = new Matrix(); - save.set(mMatrixTouch); - - final float x = transformedPts[0] - offsetLeft(); - final float y = transformedPts[1] - offsetTop(); - - //Log.i("", "Moving view to x: " + x + ", y: " + y); - - save.postTranslate(-x, -y); - - refresh(save, view, false); - - // final View v = chart.getChartView(); - // - // v.post(new Runnable() { - // - // @Override - // public void run() { - // Matrix save = new Matrix(); - // save.set(mMatrixTouch); - // - // final float x = transformedPts[0] - offsetLeft(); - // final float y = transformedPts[1] - offsetTop(); - // - // save.postTranslate(-x, -y); - // - // refresh(save, chart, false); - // } - // }); - } - - /** - * call this method to refresh the graph with a given matrix - * - * @param newMatrix - * @return - */ - public Matrix refresh(Matrix newMatrix, View chart, boolean invalidate) { - - mMatrixTouch.set(newMatrix); - - // make sure scale and translation are within their bounds - limitTransAndScale(mMatrixTouch, mContentRect); - - if (invalidate) - chart.invalidate(); - - newMatrix.set(mMatrixTouch); - return newMatrix; - } - - /** - * limits the maximum scale and X translation of the given matrix - * - * @param matrix - */ - public void limitTransAndScale(Matrix matrix, RectF content) { - - float[] vals = new float[9]; - matrix.getValues(vals); - - float curTransX = vals[Matrix.MTRANS_X]; - float curScaleX = vals[Matrix.MSCALE_X]; - - float curTransY = vals[Matrix.MTRANS_Y]; - float curScaleY = vals[Matrix.MSCALE_Y]; - - // min scale-x is 1f - mScaleX = Math.max(mMinScaleX, curScaleX); - - // min scale-y is 1f - mScaleY = Math.max(mMinScaleY, curScaleY); - - float width = 0f; - float height = 0f; - - if (content != null) { - width = content.width(); - height = content.height(); - } - - float maxTransX = -width * (mScaleX - 1f); - float newTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); - - // if(curScaleX < mMinScaleX) { - // newTransX = (-width * (mScaleX - 1f)) / 2f; - // } - - float maxTransY = height * (mScaleY - 1f); - float newTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); - - // if(curScaleY < mMinScaleY) { - // newTransY = (height * (mScaleY - 1f)) / 2f; - // } - - vals[Matrix.MTRANS_X] = newTransX; - vals[Matrix.MSCALE_X] = mScaleX; - - vals[Matrix.MTRANS_Y] = newTransY; - vals[Matrix.MSCALE_Y] = mScaleY; - - matrix.setValues(vals); - } - - public void setMinimumScaleX(float xScale) { - - if (xScale < 1f) - xScale = 1f; - - mMinScaleX = xScale; - - limitTransAndScale(mMatrixTouch, mContentRect); - } - - public void setMinimumScaleY(float yScale) { - - if (yScale < 1f) - yScale = 1f; - - mMinScaleY = yScale; - - limitTransAndScale(mMatrixTouch, mContentRect); - } - - /** - * Returns the charts-touch matrix used for translation and scale on touch. - * - * @return - */ - public Matrix getMatrixTouch() { - return mMatrixTouch; - } - - /** - * ################ ################ ################ ################ - */ - /** BELOW METHODS FOR BOUNDS CHECK */ - - public boolean isInBoundsX(float x) { - if (isInBoundsLeft(x) && isInBoundsRight(x)) - return true; - else - return false; - } - - public boolean isInBoundsY(float y) { - if (isInBoundsTop(y) && isInBoundsBottom(y)) - return true; - else - return false; - } - - public boolean isInBounds(float x, float y) { - if (isInBoundsX(x) && isInBoundsY(y)) - return true; - else - return false; - } - - public boolean isInBoundsLeft(float x) { - return mContentRect.left <= x ? true : false; - } - - public boolean isInBoundsRight(float x) { - x = (float)((int)(x * 100.f)) / 100.f; - return mContentRect.right >= x ? true : false; - } - - public boolean isInBoundsTop(float y) { - return mContentRect.top <= y ? true : false; - } - - public boolean isInBoundsBottom(float y) { - y = (float)((int)(y * 100.f)) / 100.f; - return mContentRect.bottom >= y ? true : false; - } - - /** - * returns the current x-scale factor - */ - public float getScaleX() { - return mScaleX; - } - - /** - * returns the current y-scale factor - */ - public float getScaleY() { - return mScaleY; - } - - /** - * if the chart is fully zoomed out, return true - * - * @return - */ - public boolean isFullyZoomedOut() { - - if (isFullyZoomedOutX() && isFullyZoomedOutY()) - return true; - else - return false; - } - - /** - * Returns true if the chart is fully zoomed out on it's y-axis (vertical). - * - * @return - */ - public boolean isFullyZoomedOutY() { - if (mScaleY > mMinScaleY || mMinScaleY > 1f) - return false; - else - return true; - } - - /** - * Returns true if the chart is fully zoomed out on it's x-axis - * (horizontal). - * - * @return - */ - public boolean isFullyZoomedOutX() { - if (mScaleX > mMinScaleX || mMinScaleX > 1f) - return false; - else - return true; - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the x-axis. - * - * @param offset - */ - public void setDragOffsetX(float offset) { - mTransOffsetX = Utils.convertDpToPixel(offset); - } - - /** - * Set an offset in dp that allows the user to drag the chart over it's - * bounds on the y-axis. - * - * @param offset - */ - public void setDragOffsetY(float offset) { - mTransOffsetY = Utils.convertDpToPixel(offset); - } - - /** - * Returns true if both drag offsets (x and y) are zero or smaller. - * - * @return - */ - public boolean hasNoDragOffset() { - return mTransOffsetX <= 0 && mTransOffsetY <= 0 ? true : false; - } -} diff --git a/MPChartLib/AndroidManifest.xml b/MPChartLib/src/main/AndroidManifest.xml similarity index 56% rename from MPChartLib/AndroidManifest.xml rename to MPChartLib/src/main/AndroidManifest.xml index bda2ca4cb0..d75f87c7e2 100644 --- a/MPChartLib/AndroidManifest.xml +++ b/MPChartLib/src/main/AndroidManifest.xml @@ -1,12 +1,5 @@ - - - + zoom out, if > 1f --> zoom in + * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in + * @param x + * @param y + */ + public void zoom(float scaleX, float scaleY, float x, float y) { + + mViewPortHandler.zoom(scaleX, scaleY, x, -y, mZoomMatrixBuffer); + mViewPortHandler.refresh(mZoomMatrixBuffer, this, false); + + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + calculateOffsets(); + postInvalidate(); + } + + /** + * Zooms in or out by the given scale factor. + * x and y are the values (NOT PIXELS) of the zoom center.. + * + * @param scaleX + * @param scaleY + * @param xValue + * @param yValue + * @param axis the axis relative to which the zoom should take place + */ + public void zoom(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis) { + + Runnable job = ZoomJob.getInstance(mViewPortHandler, scaleX, scaleY, xValue, yValue, getTransformer(axis), axis, this); + addViewportJob(job); + } + + /** + * Zooms to the center of the chart with the given scale factor. + * + * @param scaleX + * @param scaleY + */ + public void zoomToCenter(float scaleX, float scaleY) { + + MPPointF center = getCenterOffsets(); + + Matrix save = mZoomMatrixBuffer; + mViewPortHandler.zoom(scaleX, scaleY, center.x, -center.y, save); + mViewPortHandler.refresh(save, this, false); + } + + /** + * Zooms by the specified scale factor to the specified values on the specified axis. + * + * @param scaleX + * @param scaleY + * @param xValue + * @param yValue + * @param axis + * @param duration + */ + @TargetApi(11) + public void zoomAndCenterAnimated(float scaleX, float scaleY, float xValue, float yValue, AxisDependency axis, + long duration) { + + MPPointD origin = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + 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); + + 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 = mFitScreenMatrixBuffer; + mViewPortHandler.fitScreen(save); + mViewPortHandler.refresh(save, this, false); + + calculateOffsets(); + postInvalidate(); + } + + /** + * Sets the minimum scale factor value to which can be zoomed out. 1f = + * fitScreen + * + * @param scaleX + * @param scaleY + */ + public void setScaleMinima(float scaleX, float scaleY) { + mViewPortHandler.setMinimumScaleX(scaleX); + mViewPortHandler.setMinimumScaleY(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 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 = 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 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 = mXAxis.mAxisRange / (minXRange); + mViewPortHandler.setMaximumScaleX(xScale); + } + + /** + * 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 minScale = mXAxis.mAxisRange / minXRange; + float maxScale = mXAxis.mAxisRange / maxXRange; + mViewPortHandler.setMinMaxScaleX(minScale, maxScale); + } + + /** + * Sets the size of the area (range on the y-axis) that should be maximum + * visible at once. + * + * @param maxYRange the maximum visible range on the y-axis + * @param axis the axis for which this limit should apply + */ + public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / maxYRange; + mViewPortHandler.setMinimumScaleY(yScale); + } + + /** + * Sets the size of the area (range on the y-axis) that should be minimum visible at once, no further zooming in possible. + * + * @param minYRange + * @param axis the axis for which this limit should apply + */ + public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) { + float yScale = getAxisRange(axis) / minYRange; + mViewPortHandler.setMaximumScaleY(yScale); + } + + /** + * 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); + } + + + /** + * Moves the left side of the current viewport to the specified x-position. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + */ + public void moveViewToX(float xValue) { + + 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. + * 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 moveViewTo(float xValue, float yValue, AxisDependency axis) { + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + 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-value + * and center the viewport to the y value animated. + * This also refreshes the chart by calling invalidate(). + * + * @param xValue + * @param yValue + * @param axis + * @param duration the duration of the animation in milliseconds + */ + @TargetApi(11) + public void moveViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { + + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, xValue, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + + addViewportJob(job); + + MPPointD.recycleInstance(bounds); + } + + /** + * Centers the viewport to the specified y value on the y-axis. + * This also refreshes the chart by calling invalidate(). + * + * @param yValue + * @param axis - which axis should be used as a reference for the y-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 yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + Runnable job = MoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this); + + addViewportJob(job); + } + + /** + * This will move the center of the current viewport to the specified + * x and y value animated. + * + * @param xValue + * @param yValue + * @param axis + * @param duration the duration of the animation in milliseconds + */ + @TargetApi(11) + public void centerViewToAnimated(float xValue, float yValue, AxisDependency axis, long duration) { + + MPPointD bounds = getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), axis); + + float yInView = getAxisRange(axis) / mViewPortHandler.getScaleY(); + float xInView = getXAxis().mAxisRange / mViewPortHandler.getScaleX(); + + Runnable job = AnimatedMoveViewJob.getInstance(mViewPortHandler, + xValue - xInView / 2f, yValue + yInView / 2f, + getTransformer(axis), this, (float) bounds.x, (float) bounds.y, duration); + + addViewportJob(job); + + MPPointD.recycleInstance(bounds); + } + + /** + * flag that indicates if a custom viewport offset has been set + */ + private boolean mCustomViewPortEnabled = false; + + /** + * Sets custom offsets for the current ViewPort (the offsets on the sides of + * the actual chart window). Setting this will prevent the chart from + * automatically calculating it's offsets. Use resetViewPortOffsets() to + * undo this. ONLY USE THIS WHEN YOU KNOW WHAT YOU ARE DOING, else use + * setExtraOffsets(...). + * + * @param left + * @param top + * @param right + * @param bottom + */ + public void setViewPortOffsets(final float left, final float top, + final float right, final float bottom) { + + mCustomViewPortEnabled = true; + post(new Runnable() { + + @Override + public void run() { + + mViewPortHandler.restrainViewPort(left, top, right, bottom); + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } + }); + } + + /** + * Resets all custom offsets set via setViewPortOffsets(...) method. Allows + * the chart to again calculate all offsets automatically. + */ + public void resetViewPortOffsets() { + mCustomViewPortEnabled = false; + calculateOffsets(); + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW IS GETTERS AND SETTERS */ + + /** + * Returns the range of the specified axis. + * + * @param axis + * @return + */ + protected float getAxisRange(AxisDependency axis) { + if (axis == AxisDependency.LEFT) + return mAxisLeft.mAxisRange; + else + return mAxisRight.mAxisRange; + } + + /** + * Sets the OnDrawListener + * + * @param drawListener + */ + public void setOnDrawListener(OnDrawListener drawListener) { + this.mDrawListener = drawListener; + } + + /** + * Gets the OnDrawListener. May be null. + * + * @return + */ + 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 MPPointF getPosition(Entry e, AxisDependency axis) { + + if (e == null) + return null; + + mGetPositionBuffer[0] = e.getX(); + mGetPositionBuffer[1] = e.getY(); + + getTransformer(axis).pointValuesToPixel(mGetPositionBuffer); + + return MPPointF.getInstance(mGetPositionBuffer[0], mGetPositionBuffer[1]); + } + + /** + * sets the number of maximum visible drawn values on the chart only active + * when setDrawValues() is enabled + * + * @param count + */ + public void setMaxVisibleValueCount(int count) { + this.mMaxVisibleCount = count; + } + + public int getMaxVisibleCount() { + return mMaxVisibleCount; + } + + /** + * Set this to true to allow highlighting per dragging over the chart + * surface when it is fully zoomed out. Default: true + * + * @param enabled + */ + public void setHighlightPerDragEnabled(boolean enabled) { + mHighlightPerDragEnabled = enabled; + } + + public boolean isHighlightPerDragEnabled() { + return mHighlightPerDragEnabled; + } + + /** + * Sets the color for the background of the chart-drawing area (everything + * behind the grid lines). + * + * @param color + */ + public void setGridBackgroundColor(int color) { + mGridBackgroundPaint.setColor(color); + } + + /** + * Set this to true to enable dragging (moving the chart with the finger) + * for the chart (this does not effect scaling). + * + * @param enabled + */ + public void setDragEnabled(boolean enabled) { + this.mDragXEnabled = enabled; + this.mDragYEnabled = enabled; + } + + /** + * Returns true if dragging is enabled for the chart, false if not. + * + * @return + */ + public boolean isDragEnabled() { + 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; + } + + /** + * Set this to true to enable scaling (zooming in and out by gesture) for + * the chart (this does not effect dragging) on both X- and Y-Axis. + * + * @param enabled + */ + public void setScaleEnabled(boolean enabled) { + this.mScaleXEnabled = enabled; + this.mScaleYEnabled = enabled; + } + + public void setScaleXEnabled(boolean enabled) { + mScaleXEnabled = enabled; + } + + public void setScaleYEnabled(boolean enabled) { + mScaleYEnabled = enabled; + } + + public boolean isScaleXEnabled() { + return mScaleXEnabled; + } + + public boolean isScaleYEnabled() { + return mScaleYEnabled; + } + + /** + * Set this to true to enable zooming in by double-tap on the chart. + * Default: enabled + * + * @param enabled + */ + public void setDoubleTapToZoomEnabled(boolean enabled) { + mDoubleTapToZoomEnabled = enabled; + } + + /** + * Returns true if zooming via double-tap is enabled false if not. + * + * @return + */ + public boolean isDoubleTapToZoomEnabled() { + return mDoubleTapToZoomEnabled; + } + + /** + * set this to true to draw the grid background, false if not + * + * @param enabled + */ + public void setDrawGridBackground(boolean enabled) { + mDrawGridBackground = 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. + * + * @param enabled + */ + 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. + * + * @param width + */ + public void setBorderWidth(float width) { + mBorderPaint.setStrokeWidth(Utils.convertDpToPixel(width)); + } + + /** + * Sets the color of the chart border lines. + * + * @param color + */ + public void setBorderColor(int color) { + mBorderPaint.setColor(color); + } + + /** + * Gets the minimum offset (padding) around the chart, defaults to 15.f + */ + public float getMinOffset() { + return mMinOffset; + } + + /** + * Sets the minimum offset (padding) around the chart, defaults to 15.f + */ + public void setMinOffset(float minOffset) { + mMinOffset = minOffset; + } + + /** + * Returns true if keeping the position on rotation is enabled and false if not. + */ + public boolean isKeepPositionOnRotation() { + return mKeepPositionOnRotation; + } + + /** + * 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 MPPointD). This method transforms pixel coordinates to + * coordinates / values in the chart. This is the opposite method to + * getPixelForValues(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getValuesByTouchPoint(float x, float y, AxisDependency axis) { + MPPointD result = MPPointD.getInstance(0, 0); + getValuesByTouchPoint(x, y, axis, result); + return result; + } + + 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(...). + * + * @param x + * @param y + * @return + */ + public MPPointD getPixelForValues(float x, float y, AxisDependency axis) { + return getTransformer(axis).getPixelForValues(x, y); + } + + /** + * returns the Entry object displayed at the touched position of the chart + * + * @param x + * @param y + * @return + */ + public Entry getEntryByTouchPoint(float x, float y) { + Highlight h = getHighlightByTouchPoint(x, y); + if (h != null) { + return mData.getEntryForHighlight(h); + } + return null; + } + + /** + * returns the DataSet object displayed at the touched position of the chart + * + * @param x + * @param y + * @return + */ + public IBarLineScatterCandleBubbleDataSet getDataSetByTouchPoint(float x, float y) { + Highlight h = getHighlightByTouchPoint(x, y); + if (h != null) { + return mData.getDataSetByIndex(h.getDataSetIndex()); + } + 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. + * + * @return + */ + @Override + 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. + * + * @return + */ + @Override + 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()); + } + + /** + * returns the current x-scale factor + */ + public float getScaleX() { + if (mViewPortHandler == null) + return 1f; + else + return mViewPortHandler.getScaleX(); + } + + /** + * returns the current y-scale factor + */ + public float getScaleY() { + if (mViewPortHandler == null) + return 1f; + else + return mViewPortHandler.getScaleY(); + } + + /** + * if the chart is fully zoomed out, return true + * + * @return + */ + public boolean isFullyZoomedOut() { + return mViewPortHandler.isFullyZoomedOut(); + } + + /** + * Returns the left y-axis object. In the horizontal bar-chart, this is the + * top axis. + * + * @return + */ + public YAxis getAxisLeft() { + return mAxisLeft; + } + + /** + * Returns the right y-axis object. In the horizontal bar-chart, this is the + * bottom axis. + * + * @return + */ + public YAxis getAxisRight() { + return mAxisRight; + } + + /** + * Returns the y-axis object to the corresponding AxisDependency. In the + * horizontal bar-chart, LEFT == top, RIGHT == BOTTOM + * + * @param axis + * @return + */ + public YAxis getAxis(AxisDependency axis) { + if (axis == AxisDependency.LEFT) + return mAxisLeft; + else + return mAxisRight; + } + + @Override + public boolean isInverted(AxisDependency axis) { + return getAxis(axis).isInverted(); + } + + /** + * 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 + * + * @param enabled + */ + public void setPinchZoom(boolean enabled) { + mPinchZoomEnabled = enabled; + } + + /** + * returns true if pinch-zoom is enabled, false if not + * + * @return + */ + public boolean isPinchZoomEnabled() { + return mPinchZoomEnabled; + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the x-axis. + * + * @param offset + */ + public void setDragOffsetX(float offset) { + mViewPortHandler.setDragOffsetX(offset); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the y-axis. + * + * @param offset + */ + public void setDragOffsetY(float offset) { + mViewPortHandler.setDragOffsetY(offset); + } + + /** + * Returns true if both drag offsets (x and y) are zero or smaller. + * + * @return + */ + public boolean hasNoDragOffset() { + return mViewPortHandler.hasNoDragOffset(); + } + + public XAxisRenderer getRendererXAxis() { + return mXAxisRenderer; + } + + /** + * Sets a custom XAxisRenderer and overrides the existing (default) one. + * + * @param xAxisRenderer + */ + public void setXAxisRenderer(XAxisRenderer xAxisRenderer) { + mXAxisRenderer = xAxisRenderer; + } + + public YAxisRenderer getRendererLeftYAxis() { + return mAxisRendererLeft; + } + + /** + * Sets a custom axis renderer for the left axis and overwrites the existing one. + * + * @param rendererLeftYAxis + */ + public void setRendererLeftYAxis(YAxisRenderer rendererLeftYAxis) { + mAxisRendererLeft = rendererLeftYAxis; + } + + public YAxisRenderer getRendererRightYAxis() { + return mAxisRendererRight; + } + + /** + * Sets a custom axis renderer for the right acis and overwrites the existing one. + * + * @param rendererRightYAxis + */ + public void setRendererRightYAxis(YAxisRenderer rendererRightYAxis) { + mAxisRendererRight = rendererRightYAxis; + } + + @Override + public float getYChartMax() { + return Math.max(mAxisLeft.mAxisMaximum, mAxisRight.mAxisMaximum); + } + + @Override + public float getYChartMin() { + return Math.min(mAxisLeft.mAxisMinimum, mAxisRight.mAxisMinimum); + } + + /** + * Returns true if either the left or the right or both axes are inverted. + * + * @return + */ + public boolean isAnyAxisInverted() { + if (mAxisLeft.isInverted()) + return true; + if (mAxisRight.isInverted()) + return true; + return false; + } + + /** + * Flag that indicates if auto scaling on the y axis is enabled. This is + * especially interesting for charts displaying financial data. + * + * @param enabled the y axis automatically adjusts to the min and max y + * values of the current x axis range whenever the viewport + * changes + */ + public void setAutoScaleMinMaxEnabled(boolean enabled) { + mAutoScaleMinMaxEnabled = enabled; + } + + /** + * @return true if auto scaling on the y axis is enabled. + * @default false + */ + public boolean isAutoScaleMinMaxEnabled() { + return mAutoScaleMinMaxEnabled; + } + + @Override + public void setPaint(Paint p, int which) { + super.setPaint(p, which); + + switch (which) { + case PAINT_GRID_BACKGROUND: + mGridBackgroundPaint = p; + break; + } + } + + @Override + public Paint getPaint(int which) { + Paint p = super.getPaint(which); + if (p != null) + return p; + + switch (which) { + case PAINT_GRID_BACKGROUND: + return mGridBackgroundPaint; + } + + 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 58% 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 6e61689740..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,8 +5,7 @@ import android.util.AttributeSet; import com.github.mikephil.charting.data.BubbleData; -import com.github.mikephil.charting.data.BubbleDataSet; -import com.github.mikephil.charting.interfaces.BubbleDataProvider; +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 (BubbleDataSet 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 79% 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 f8d1deb139..fa36e3522f 100644 --- a/MPChartLib/src/com/github/mikephil/charting/charts/CandleStickChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/CandleStickChart.java @@ -5,12 +5,12 @@ import android.util.AttributeSet; import com.github.mikephil.charting.data.CandleData; -import com.github.mikephil.charting.interfaces.CandleDataProvider; +import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider; import com.github.mikephil.charting.renderer.CandleStickChartRenderer; /** * Financial chart type that draws candle-sticks (OHCL chart). - * + * * @author Philipp Jahoda */ public class CandleStickChart extends BarLineChartBase 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 50% 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 6568995f6d..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,21 +27,27 @@ 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.DataSet; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.interfaces.ChartInterface; +import com.github.mikephil.charting.formatter.DefaultValueFormatter; +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; import com.github.mikephil.charting.listener.OnChartGestureListener; 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.DefaultValueFormatter; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.utils.MPPointF; import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ValueFormatter; import com.github.mikephil.charting.utils.ViewPortHandler; import java.io.File; @@ -49,21 +55,21 @@ 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 +public abstract class Chart>> extends ViewGroup implements ChartInterface { public static final String LOG_TAG = "MPAndroidChart"; - /** flag that indicates if logging is enabled or not */ + /** + * flag that indicates if logging is enabled or not + */ protected boolean mLogEnabled = false; /** @@ -72,19 +78,28 @@ public abstract class Chart set : mData.getDataSets()) { - if (set.needsDefaultFormatter()) - set.setValueFormatter(mDefaultFormatter); + for (IDataSet set : mData.getDataSets()) { + if (set.needsFormatter() || set.getValueFormatter() == mDefaultValueFormatter) + set.setValueFormatter(mDefaultValueFormatter); } // let the chart know there is new data @@ -289,13 +310,15 @@ public void setData(T data) { */ public void clear() { mData = null; - mDataNotSet = true; + mOffsetsCalculated = false; + mIndicesToHighlight = null; + mChartTouchListener.setLastHighlighted(null); invalidate(); } /** - * Removes all DataSets (and thereby Entries) from the chart. Does not - * remove the x-values. Also refreshes the chart by calling invalidate(). + * Removes all DataSets (and thereby Entries) from the chart. Does not set the data object to null. Also refreshes the + * chart by calling invalidate(). */ public void clearValues() { mData.clearValues(); @@ -314,7 +337,7 @@ public boolean isEmpty() { return true; else { - if (mData.getYValCount() <= 0) + if (mData.getEntryCount() <= 0) return true; else return false; @@ -323,31 +346,33 @@ public boolean isEmpty() { /** * Lets the chart know its underlying data has changed and performs all - * necessary recalculations. + * necessary recalculations. It is crucial that this method is called + * everytime data is changed dynamically. Not calling this method can lead + * to crashes or unexpected behaviour. */ public abstract void notifyDataSetChanged(); /** - * calculates the offsets of the chart to the border depending on the + * Calculates the offsets of the chart to the border depending on the * position of an eventual legend or depending on the length of the y-axis * and x-axis labels and their position */ protected abstract void calculateOffsets(); /** - * calcualtes the y-min and y-max value and the y-delta and x-delta value + * Calculates the y-min and y-max value and the y-delta and x-delta value */ protected abstract void calcMinMax(); /** - * calculates the required number of digits for the values that might be + * Calculates the required number of digits for the values that might be * drawn in the chart (if enabled), and creates the default-value-formatter */ - protected void calculateFormatter(float min, float max) { + protected void setupDefaultFormatter(float min, float max) { float reference = 0f; - if (mData == null || mData.getXValCount() < 2) { + if (mData == null || mData.getEntryCount() < 2) { reference = Math.max(Math.abs(min), Math.abs(max)); } else { @@ -355,40 +380,44 @@ protected void calculateFormatter(float min, float max) { } int digits = Utils.getDecimals(reference); - mDefaultFormatter = new DefaultValueFormatter(digits); - } - /** flag that indicates if offsets calculation has already been done or not */ - private boolean mOffsetsCalculated = false; + // setup the formatter with a new number of digits + mDefaultValueFormatter.setup(digits); + } /** - * Bitmap object used for drawing. This is necessary because hardware - * acceleration uses OpenGL which only allows a specific texture size to be - * drawn on the canvas directly. - **/ - protected Bitmap mDrawBitmap; - - /** paint object used for drawing the bitmap */ - protected Paint mDrawPaint; + * flag that indicates if offsets calculation has already been done or not + */ + private boolean mOffsetsCalculated = false; @Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); - if (mDataNotSet || mData == null || mData.getYValCount() <= 0) { // check - // if - // there - // is - // data + if (mData == null) { + + boolean hasText = !TextUtils.isEmpty(mNoDataText); + + if (hasText) { + MPPointF pt = getCenter(); - // if no data, inform the user - canvas.drawText(mNoDataText, getWidth() / 2, getHeight() / 2, mInfoPaint); + switch (mInfoPaint.getTextAlign()) { + case LEFT: + pt.x = 0; + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; - if (!TextUtils.isEmpty(mNoDataTextDescription)) { - float textOffset = -mInfoPaint.ascent() + mInfoPaint.descent(); - canvas.drawText(mNoDataTextDescription, getWidth() / 2, (getHeight() / 2) - + textOffset, mInfoPaint); + case RIGHT: + pt.x *= 2.0; + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; + + default: + canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint); + break; + } } + return; } @@ -397,33 +426,35 @@ protected void onDraw(Canvas canvas) { calculateOffsets(); mOffsetsCalculated = true; } - - // if (mDrawCanvas == null) { - // mDrawCanvas = new Canvas(mDrawBitmap); - // } - - // clear everything - // mDrawBitmap.eraseColor(Color.TRANSPARENT); } - /** the custom position of the description text */ - private PointF mDescriptionPosition; - /** - * draws the description text in the bottom right corner of the chart + * Draws the description text in the bottom right corner of the chart (per default) */ protected void drawDescription(Canvas c) { - if (!mDescription.equals("")) { + // check if description should be drawn + if (mDescription != null && mDescription.isEnabled()) { + + MPPointF position = mDescription.getPosition(); - if (mDescriptionPosition == null) { + mDescPaint.setTypeface(mDescription.getTypeface()); + mDescPaint.setTextSize(mDescription.getTextSize()); + mDescPaint.setColor(mDescription.getTextColor()); + mDescPaint.setTextAlign(mDescription.getTextAlign()); - c.drawText(mDescription, getWidth() - mViewPortHandler.offsetRight() - 10, - getHeight() - mViewPortHandler.offsetBottom() - - 10, mDescPaint); + float x, y; + + // if no position specified, draw on default position + if (position == null) { + x = getWidth() - mViewPortHandler.offsetRight() - mDescription.getXOffset(); + y = getHeight() - mViewPortHandler.offsetBottom() - mDescription.getYOffset(); } else { - c.drawText(mDescription, mDescriptionPosition.x, mDescriptionPosition.y, mDescPaint); + x = position.x; + y = position.y; } + + c.drawText(mDescription.getText(), x, y, mDescPaint); } } @@ -436,16 +467,55 @@ protected void drawDescription(Canvas c) { * array of Highlight objects that reference the highlighted slices in the * chart */ - protected Highlight[] mIndicesToHightlight = new Highlight[0]; + protected Highlight[] mIndicesToHighlight; + + /** + * The maximum distance in dp away from an entry causing it to highlight. + */ + protected float mMaxHighlightDistance = 0f; + + @Override + public float getMaxHighlightDistance() { + return mMaxHighlightDistance; + } + + /** + * Sets the maximum distance in screen dp a touch can be away from an entry to cause it to get highlighted. + * Default: 500dp + * + * @param distDp + */ + public void setMaxHighlightDistance(float distDp) { + mMaxHighlightDistance = Utils.convertDpToPixel(distDp); + } /** - * Returns the array of currently highlighted values. This might be null or - * empty if nothing is highlighted. - * + * Returns the array of currently highlighted values. This might a null or + * empty array if nothing is highlighted. + * * @return */ public Highlight[] getHighlighted() { - return mIndicesToHightlight; + return mIndicesToHighlight; + } + + /** + * Returns true if values can be highlighted via tap gesture, false if not. + * + * @return + */ + public boolean isHighlightPerTapEnabled() { + return mHighLightPerTapEnabled; + } + + /** + * Set this to false to prevent values from being highlighted by tap gesture. + * Values can still be highlighted via drag or programmatically. Default: true + * + * @param enabled + */ + public void setHighlightPerTapEnabled(boolean enabled) { + mHighLightPerTapEnabled = enabled; } /** @@ -456,85 +526,238 @@ public Highlight[] getHighlighted() { * @return */ public boolean valuesToHighlight() { - return mIndicesToHightlight == null || mIndicesToHightlight.length <= 0 - || mIndicesToHightlight[0] == null ? false + return mIndicesToHighlight == null || mIndicesToHighlight.length <= 0 + || mIndicesToHighlight[0] == null ? false : true; } + /** + * Sets the last highlighted value for the touchlistener. + * + * @param highs + */ + protected void setLastHighlighted(Highlight[] highs) { + + if (highs == null || highs.length <= 0 || highs[0] == null) { + mChartTouchListener.setLastHighlighted(null); + } else { + mChartTouchListener.setLastHighlighted(highs[0]); + } + } + /** * Highlights the values at the given indices in the given DataSets. Provide * null or an empty array to undo all highlighting. This should be used to - * programmatically highlight values. This DOES NOT generate a callback to - * the OnChartValueSelectedListener. + * programmatically highlight values. + * This method *will not* call the listener. * * @param highs */ public void highlightValues(Highlight[] highs) { // set the indices to highlight - mIndicesToHightlight = highs; + mIndicesToHighlight = highs; + + setLastHighlighted(highs); // redraw the chart invalidate(); } /** - * Highlights the value at the given x-index in the given DataSet. Provide - * -1 as the x-index or dataSetIndex to undo all highlighting. - * - * @param xIndex - * @param dataSetIndex + * 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 + * @param dataIndex The data index to search in (only used in CombinedChartView currently) */ - public void highlightValue(int xIndex, int dataSetIndex) { + public void highlightValue(float x, int dataSetIndex, int dataIndex) { + highlightValue(x, dataSetIndex, dataIndex, true); + } - if (xIndex < 0 || dataSetIndex < 0 || xIndex >= 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 method *will not* call the listener. + * + * @param highlight contains information about which entry should be highlighted + */ + public void highlightValue(Highlight highlight) { + highlightValue(highlight, false); + } + /** * Highlights the value selected by touch gesture. Unlike * highlightValues(...), this generates a callback to the * OnChartValueSelectedListener. * - * @param highs + * @param high - the highlight object + * @param callListener - call the listener */ - public void highlightTouch(Highlight high) { + public void highlightValue(Highlight high, boolean callListener) { + + Entry e = null; if (high == null) - mIndicesToHightlight = null; + mIndicesToHighlight = null; else { if (mLogEnabled) Log.i(LOG_TAG, "Highlighted: " + high.toString()); - // set the indices to highlight - mIndicesToHightlight = new Highlight[] { - high - }; + e = mData.getEntryForHighlight(high); + if (e == null) { + mIndicesToHighlight = null; + high = null; + } else { + + // set the indices to highlight + mIndicesToHighlight = new Highlight[]{ + high + }; + } } - // redraw the chart - invalidate(); + setLastHighlighted(mIndicesToHighlight); - if (mSelectionListener != null) { + if (callListener && mSelectionListener != null) { if (!valuesToHighlight()) mSelectionListener.onNothingSelected(); else { - - Entry e = mData.getEntryForHighlight(high); - // notify the listener - mSelectionListener.onValueSelected(e, high.getDataSetIndex(), high); + mSelectionListener.onValueSelected(e, high); } } + + // redraw the chart + invalidate(); + } + + /** + * 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 + */ + 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); + } + + /** + * Set a new (e.g. custom) ChartTouchListener NOTE: make sure to + * setTouchEnabled(true); if you need touch gestures on the chart + * + * @param l + */ + public void setOnTouchListener(ChartTouchListener l) { + this.mChartTouchListener = l; + } + + /** + * Returns an instance of the currently active touch listener. + * + * @return + */ + public ChartTouchListener getOnTouchListener() { + return mChartTouchListener; } /** @@ -542,11 +765,15 @@ public void highlightTouch(Highlight high) { */ /** BELOW CODE IS FOR THE MARKER VIEW */ - /** if set to true, the marker view is drawn when a value is clicked */ - protected boolean mDrawMarkerViews = true; + /** + * if set to true, the marker view is drawn when a value is clicked + */ + protected boolean mDrawMarkers = true; - /** the view that represents the marker */ - protected MarkerView mMarkerView; + /** + * the view that represents the marker + */ + protected IMarker mMarker; /** * draws all MarkerViews on the highlighted positions @@ -554,62 +781,46 @@ public void highlightTouch(Highlight high) { 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 < mIndicesToHightlight.length; i++) { + for (int i = 0; i < mIndicesToHighlight.length; i++) { - int xIndex = mIndicesToHightlight[i].getXIndex(); - int dataSetIndex = mIndicesToHightlight[i].getDataSetIndex(); + Highlight highlight = mIndicesToHighlight[i]; - if (xIndex <= mDeltaX && xIndex <= mDeltaX * mAnimator.getPhaseX()) { + IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex()); - Entry e = mData.getEntryForHighlight(mIndicesToHightlight[i]); + Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]); + int entryIndex = set.getEntryIndex(e); - // make sure entry not null - if (e == null) - continue; + // make sure entry not null + if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX()) + continue; - float[] pos = getMarkerPosition(e, dataSetIndex); + float[] pos = getMarkerPosition(highlight); - // check bounds - if (!mViewPortHandler.isInBounds(pos[0], pos[1])) - continue; + // check bounds + if (!mViewPortHandler.isInBounds(pos[0], pos[1])) + continue; - // callbacks to update the content - mMarkerView.refreshContent(e, dataSetIndex); + // callbacks to update the content + mMarker.refreshContent(e, 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]); - - mMarkerView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mMarkerView.layout(0, 0, mMarkerView.getMeasuredWidth(), - mMarkerView.getMeasuredHeight()); - - 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 xIndex - * @param dataSetIndex + * @param high * @return */ - protected abstract float[] getMarkerPosition(Entry e, int dataSetIndex); + protected float[] getMarkerPosition(Highlight high) { + return new float[]{high.getDrawX(), high.getDrawY()}; + } /** * ################ ################ ################ ################ @@ -619,7 +830,7 @@ protected void drawMarkers(Canvas canvas) { /** * Returns the animator responsible for animating chart values. - * + * * @return */ public ChartAnimator getAnimator() { @@ -644,7 +855,7 @@ public void setDragDecelerationEnabled(boolean enabled) { /** * Returns drag deceleration friction coefficient - * + * * @return */ public float getDragDecelerationFrictionCoef() { @@ -654,17 +865,17 @@ public float getDragDecelerationFrictionCoef() { /** * Deceleration friction coefficient in [0 ; 1] interval, higher values * indicate that speed will decrease slowly, for example if it set to 0, it - * will stop immediately. 1 is an invalid value, and will be converted to 0.999f - * automatically. + * will stop immediately. 1 is an invalid value, and will be converted to + * 0.999f automatically. * * @param newValue */ public void setDragDecelerationFrictionCoef(float newValue) { - + if (newValue < 0.f) newValue = 0.f; - - if(newValue >= 1f) + + if (newValue >= 1f) newValue = 0.999f; mDragDecelerationFrictionCoef = newValue; @@ -684,46 +895,15 @@ public void setDragDecelerationFrictionCoef(float newValue) { * * @param durationMillisX * @param durationMillisY - * @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 + * @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) { + 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 @@ -732,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); } /** @@ -747,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); } @@ -760,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. @@ -780,6 +967,7 @@ public void animateY(int durationMillis, Easing.EasingOption easing) { * * @param durationMillis */ + @RequiresApi(11) public void animateX(int durationMillis) { mAnimator.animateX(durationMillis); } @@ -792,6 +980,7 @@ public void animateX(int durationMillis) { * * @param durationMillis */ + @RequiresApi(11) public void animateY(int durationMillis) { mAnimator.animateY(durationMillis); } @@ -805,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); } @@ -814,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; } /** @@ -852,34 +1054,6 @@ public OnChartGestureListener getOnChartGestureListener() { return mGestureListener; } - /** - * If set to true, value highlighting is enabled which means that values can - * be highlighted programmatically or by touch gesture. - * - * @param enabled - */ - public void setHighlightEnabled(boolean enabled) { - mHighlightEnabled = enabled; - } - - /** - * returns true if highlighting of values is enabled, false if not - * - * @return - */ - public boolean isHighlightEnabled() { - return mHighlightEnabled; - } - - /** - * returns the total value (sum) of all y-values across all DataSets - * - * @return - */ - public float getYValueSum() { - return mData.getYValueSum(); - } - /** * returns the current y-max value across all DataSets * @@ -900,130 +1074,116 @@ 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(); + public float getXRange() { + return mXAxis.mAxisRange; } - + /** - * returns the average value of all values the chart holds + * Returns a recyclable MPPointF instance. + * Returns the center point of the chart (the whole View) in pixels. * * @return */ - public float getAverage() { - return getYValueSum() / mData.getYValCount(); + public MPPointF getCenter() { + return MPPointF.getInstance(getWidth() / 2f, getHeight() / 2f); } /** - * returns the average value for a specific DataSet (with a specific label) - * in the chart + * Returns a recyclable MPPointF instance. + * Returns the center of the chart taking offsets under consideration. + * (returns the center of the content rectangle) * - * @param dataSetLabel * @return */ - public float getAverage(String dataSetLabel) { - - DataSet ds = mData.getDataSetByLabel(dataSetLabel, true); - - return ds.getYValueSum() - / ds.getEntryCount(); + @Override + public MPPointF getCenterOffsets() { + return mViewPortHandler.getContentCenter(); } /** - * returns the total number of values the chart holds (across all DataSets) + * Sets extra offsets (around the chart view) to be appended to the + * auto-calculated offsets. * - * @return + * @param left + * @param top + * @param right + * @param bottom */ - public int getValueCount() { - return mData.getYValCount(); + public void setExtraOffsets(float left, float top, float right, float bottom) { + setExtraLeftOffset(left); + setExtraTopOffset(top); + setExtraRightOffset(right); + setExtraBottomOffset(bottom); } /** - * Returns the center point of the chart (the whole View) in pixels. - * - * @return + * Set an extra offset to be appended to the viewport's top */ - public PointF getCenter() { - return new PointF(getWidth() / 2f, getHeight() / 2f); + public void setExtraTopOffset(float offset) { + mExtraTopOffset = Utils.convertDpToPixel(offset); } /** - * Returns the center of the chart taking offsets under consideration. - * (returns the center of the content rectangle) - * - * @return + * @return the extra offset to be appended to the viewport's top */ - @Override - public PointF getCenterOffsets() { - return mViewPortHandler.getContentCenter(); + public float getExtraTopOffset() { + return mExtraTopOffset; } /** - * set a description text that appears in the bottom right corner of the - * chart, size = Y-legend text size - * - * @param desc + * Set an extra offset to be appended to the viewport's right */ - public void setDescription(String desc) { - if (desc == null) - desc = ""; - this.mDescription = desc; + public void setExtraRightOffset(float offset) { + mExtraRightOffset = Utils.convertDpToPixel(offset); } /** - * Sets a custom position for the description text in pixels on the screen. - * - * @param x - xcoordinate - * @param y - ycoordinate + * @return the extra offset to be appended to the viewport's right */ - public void setDescriptionPosition(float x, float y) { - mDescriptionPosition = new PointF(x, y); + public float getExtraRightOffset() { + return mExtraRightOffset; } /** - * sets the typeface for the description paint - * - * @param t + * Set an extra offset to be appended to the viewport's bottom */ - public void setDescriptionTypeface(Typeface t) { - mDescPaint.setTypeface(t); + public void setExtraBottomOffset(float offset) { + mExtraBottomOffset = Utils.convertDpToPixel(offset); } /** - * sets the size of the description text in pixels, min 6f, max 16f - * - * @param size + * @return the extra offset to be appended to the viewport's bottom */ - public void setDescriptionTextSize(float size) { - - if (size > 16f) - size = 16f; - if (size < 6f) - size = 6f; + public float getExtraBottomOffset() { + return mExtraBottomOffset; + } - mDescPaint.setTextSize(Utils.convertDpToPixel(size)); + /** + * Set an extra offset to be appended to the viewport's left + */ + public void setExtraLeftOffset(float offset) { + mExtraLeftOffset = Utils.convertDpToPixel(offset); } /** - * Sets the color of the description text. - * - * @param color + * @return the extra offset to be appended to the viewport's left */ - public void setDescriptionColor(int color) { - mDescPaint.setColor(color); + public float getExtraLeftOffset() { + return mExtraLeftOffset; } /** - * Set this to true to enable logcat outputs for the chart. Default: - * disabled + * Set this to true to enable logcat outputs for the chart. Beware that + * logcat output decreases rendering performance. Default: disabled. * * @param enabled */ @@ -1033,7 +1193,7 @@ public void setLogEnabled(boolean enabled) { /** * Returns true if log-output is enabled for the chart, fals if not. - * + * * @return */ public boolean isLogEnabled() { @@ -1051,40 +1211,31 @@ 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; - } - - // /** - // * Sets the offsets from the border of the view to the actual chart in - // every - // * direction manually. This method needs to be recalled everytime a new - // data - // * object is set for the chart. Provide density pixels -> they are then - // * rendered to pixels inside the chart. - // * - // * @param left - // * @param right - // * @param top - // * @param bottom - // */ - // public void setOffsets(float left, float top, float right, float bottom) - // { - // - // mOffsetBottom = Utils.convertDpToPixel(bottom); - // mOffsetLeft = Utils.convertDpToPixel(left); - // mOffsetRight = Utils.convertDpToPixel(right); - // mOffsetTop = Utils.convertDpToPixel(top); - // - // mTrans.prepareMatrixValuePx(this); - // mTrans.prepareMatrixOffset(this); - // prepareContentRect(); - // } + 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); + } /** * Set this to false to disable all gestures and touches on the chart, @@ -1097,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; } /** @@ -1128,7 +1308,7 @@ public Legend getLegend() { /** * Returns the renderer object responsible for rendering / drawing the * Legend. - * + * * @return */ public LegendRenderer getLegendRenderer() { @@ -1164,7 +1344,9 @@ public void enableScroll() { parent.requestDisallowInterceptTouchEvent(false); } - /** paint for the grid background (only line and barchart) */ + /** + * paint for the grid background (only line and barchart) + */ public static final int PAINT_GRID_BACKGROUND = 4; /** @@ -1173,25 +1355,33 @@ public void enableScroll() { */ public static final int PAINT_INFO = 7; - /** paint for the description text in the bottom right corner */ + /** + * paint for the description text in the bottom right corner + */ public static final int PAINT_DESCRIPTION = 11; - /** paint for the hole in the middle of the pie chart */ + /** + * paint for the hole in the middle of the pie chart + */ public static final int PAINT_HOLE = 13; - /** paint for the text in the middle of the pie chart */ + /** + * paint for the text in the middle of the pie chart + */ public static final int PAINT_CENTER_TEXT = 14; - /** paint used for the legend */ + /** + * paint used for the legend + */ public static final int PAINT_LEGEND_LABEL = 18; /** * set a new paint object for the specified parameter in the chart e.g. * Chart.PAINT_VALUES * - * @param p the new paint object + * @param p the new paint object * @param which Chart.PAINT_VALUES, Chart.PAINT_GRID, Chart.PAINT_VALUES, - * ... + * ... */ public void setPaint(Paint p, int which) { @@ -1222,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++) { - - DataSet set = mData.getDataSetByIndex(i); - - Entry e = set.getEntryForXIndex(xIndex); - - if (e != null) { - vals.add(e); - } - } - - return vals; + public void setDrawMarkers(boolean enabled) { + mDrawMarkers = enabled; } /** @@ -1291,20 +1452,10 @@ public T getData() { return mData; } - /** - * returns the percentage the given value has of the total y-value sum - * - * @param val - * @return - */ - public float getPercentOfTotal(float val) { - return val / mData.getYValueSum() * 100f; - } - /** * Returns the ViewPortHandler of the chart that is responsible for the * content area of the chart and its offsets and dimensions. - * + * * @return */ public ViewPortHandler getViewPortHandler() { @@ -1313,7 +1464,7 @@ public ViewPortHandler getViewPortHandler() { /** * Returns the Renderer object the chart uses for drawing data. - * + * * @return */ public DataRenderer getRenderer() { @@ -1322,7 +1473,7 @@ public DataRenderer getRenderer() { /** * Sets a new DataRenderer object for the chart. - * + * * @param renderer */ public void setRenderer(DataRenderer renderer) { @@ -1331,8 +1482,27 @@ public void setRenderer(DataRenderer renderer) { mRenderer = renderer; } + public IHighlighter getHighlighter() { + return mHighlighter; + } + + /** + * Sets a custom highligher object for the chart that handles / processes + * all highlight touch events performed on the chart-view. + * + * @param highlighter + */ + public void setHighlighter(ChartHighlighter highlighter) { + mHighlighter = highlighter; + } + + /** + * Returns a recyclable MPPointF instance. + * + * @return + */ @Override - public PointF getCenterOfView() { + public MPPointF getCenterOfView() { return getCenter(); } @@ -1373,6 +1543,8 @@ public Bitmap getChartBitmap() { */ public boolean saveToPath(String title, String pathOnSD) { + + Bitmap b = getChartBitmap(); OutputStream stream = null; @@ -1397,16 +1569,19 @@ public boolean saveToPath(String title, String pathOnSD) { } /** - * Saves the current state of the chart to the gallery as a JPEG image. The - * filename and compression can be set. 0 == maximum compression, 100 = low + * Saves the current state of the chart to the gallery as an image type. The + * compression must be set for JPEG only. 0 == maximum compression, 100 = low * compression (high quality). NOTE: Needs permission WRITE_EXTERNAL_STORAGE * - * @param fileName e.g. "my_image" - * @param quality e.g. 50, min = 0, max = 100 - * @return returns true if saving was successfull, false if not - */ - public boolean saveToGallery(String fileName, int quality) { - + * @param fileName e.g. "my_image" + * @param subFolderPath e.g. "ChartPics" + * @param fileDescription e.g. "Chart details" + * @param format e.g. Bitmap.CompressFormat.PNG + * @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) { // restrain quality if (quality < 0 || quality > 100) quality = 50; @@ -1414,23 +1589,40 @@ public boolean saveToGallery(String fileName, int quality) { long currentTime = System.currentTimeMillis(); File extBaseDir = Environment.getExternalStorageDirectory(); - File file = new File(extBaseDir.getAbsolutePath() + "/DCIM"); + File file = new File(extBaseDir.getAbsolutePath() + "/DCIM/" + subFolderPath); if (!file.exists()) { if (!file.mkdirs()) { return false; } } + String mimeType = ""; + switch (format) { + case PNG: + mimeType = "image/png"; + if (!fileName.endsWith(".png")) + fileName += ".png"; + break; + case WEBP: + mimeType = "image/webp"; + if (!fileName.endsWith(".webp")) + fileName += ".webp"; + break; + case JPEG: + default: + mimeType = "image/jpeg"; + if (!(fileName.endsWith(".jpg") || fileName.endsWith(".jpeg"))) + fileName += ".jpg"; + break; + } + String filePath = file.getAbsolutePath() + "/" + fileName; FileOutputStream out = null; try { out = new FileOutputStream(filePath); Bitmap b = getChartBitmap(); - - b.compress(Bitmap.CompressFormat.JPEG, quality, out); // control - // the jpeg - // quality + b.compress(format, quality, out); out.flush(); out.close(); @@ -1449,86 +1641,121 @@ public boolean saveToGallery(String fileName, int quality) { values.put(Images.Media.TITLE, fileName); values.put(Images.Media.DISPLAY_NAME, fileName); values.put(Images.Media.DATE_ADDED, currentTime); - values.put(Images.Media.MIME_TYPE, "image/jpeg"); - values.put(Images.Media.DESCRIPTION, "MPAndroidChart-Library Save"); + values.put(Images.Media.MIME_TYPE, mimeType); + values.put(Images.Media.DESCRIPTION, fileDescription); values.put(Images.Media.ORIENTATION, 0); values.put(Images.Media.DATA, filePath); values.put(Images.Media.SIZE, size); - return getContext().getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values) == null - ? false : true; + return getContext().getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values) != null; } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); + /** + * Saves the current state of the chart to the gallery as a JPEG image. The + * filename and compression can be set. 0 == maximum compression, 100 = low + * compression (high quality). NOTE: Needs permission WRITE_EXTERNAL_STORAGE + * + * @param fileName e.g. "my_image" + * @param quality e.g. 50, min = 0, max = 100 + * @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.PNG, quality); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - - // prepareContentRect(); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).layout(left, top, right, bottom); - } + /** + * 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); } - /** tasks to be done after the view is setup */ - protected ArrayList mJobs = new ArrayList(); - /** - * Adds a job to be executed after the chart-view is setup (after - * onSizeChanged(...) is called). - * - * @param job + * tasks to be done after the view is setup */ - public void addJob(Runnable job) { - mJobs.add(job); - } + protected ArrayList mJobs = new ArrayList(); - public void removeJob(Runnable job) { + public void removeViewportJob(Runnable job) { mJobs.remove(job); } - public void clearAllJobs() { + public void clearAllViewportJobs() { mJobs.clear(); } + /** + * Either posts a job immediately if the chart has already setup it's + * dimensions or adds the job to the execution queue. + * + * @param job + */ + public void addViewportJob(Runnable job) { + + if (mViewPortHandler.hasChartDimens()) { + post(job); + } else { + mJobs.add(job); + } + } + /** * Returns all jobs that are scheduled to be executed after * onSizeChanged(...). - * + * * @return */ public ArrayList getJobs() { return mJobs; } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).layout(left, top, right, bottom); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int size = (int) Utils.convertDpToPixel(50f); + setMeasuredDimension( + Math.max(getSuggestedMinimumWidth(), + resolveSize(size, + widthMeasureSpec)), + Math.max(getSuggestedMinimumHeight(), + resolveSize(size, + heightMeasureSpec))); + } + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (mLogEnabled) Log.i(LOG_TAG, "OnSizeChanged()"); if (w > 0 && h > 0 && w < 10000 && h < 10000) { - // create a new bitmap with the new dimensions - - if (mDrawBitmap != null) - mDrawBitmap.recycle(); - - mDrawBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444); - 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); } @@ -1536,20 +1763,60 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { /** * Setting this to true will set the layer-type HARDWARE for the view, false * will set layer-type SOFTWARE. - * + * * @param enabled */ 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); + } - 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."); + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + //Log.i(LOG_TAG, "Detaching..."); + + if (mUnbind) + unbindDrawables(this); + } + + /** + * unbind flag + */ + private boolean mUnbind = false; + + /** + * Unbind all drawables to avoid memory leaks. + * Link: http://stackoverflow.com/a/6779164/1590502 + * + * @param view + */ + private void unbindDrawables(View view) { + + if (view.getBackground() != null) { + view.getBackground().setCallback(null); + } + if (view instanceof ViewGroup) { + for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { + unbindDrawables(((ViewGroup) view).getChildAt(i)); + } + ((ViewGroup) view).removeAllViews(); } } + + /** + * Set this to true to enable "unbinding" of drawables. When a View is detached + * from a window. This helps avoid memory leaks. + * Default: false + * Link: http://stackoverflow.com/a/6779164/1590502 + * + * @param enabled + */ + public void setUnbindEnabled(boolean enabled) { + this.mUnbind = enabled; + } } 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 53% 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 f9b0504205..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,37 +2,30 @@ 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.BubbleDataSet; 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.interfaces.BarDataProvider; -import com.github.mikephil.charting.interfaces.BubbleDataProvider; -import com.github.mikephil.charting.interfaces.CandleDataProvider; -import com.github.mikephil.charting.interfaces.LineDataProvider; -import com.github.mikephil.charting.interfaces.ScatterDataProvider; +import com.github.mikephil.charting.highlight.CombinedHighlighter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; import com.github.mikephil.charting.renderer.CombinedChartRenderer; -import com.github.mikephil.charting.utils.FillFormatter; /** * This chart class allows the combination of lines, bars, scatter and candle * data all displayed in one chart area. - * + * * @author Philipp Jahoda */ -public class CombinedChart extends BarLineChartBase implements LineDataProvider, - BarDataProvider, ScatterDataProvider, CandleDataProvider, BubbleDataProvider { - - /** the fill-formatter used for determining the position of the fill-line */ - protected FillFormatter mFillFormatter; - - /** 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 @@ -40,11 +33,11 @@ public class CombinedChart extends BarLineChartBase implements Lin */ private boolean mDrawValueAboveBar = true; + /** - * if set to true, all values of a stack are drawn individually, and not - * just their sum + * flag that indicates whether the highlight should be full-bar oriented, or single-value? */ - private boolean mDrawValuesForWholeStack = true; + protected boolean mHighlightFullBarEnabled = false; /** * if set to true, a grey area is drawn behind each bar that indicates the @@ -52,9 +45,7 @@ public class CombinedChart extends BarLineChartBase implements Lin */ 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 @@ -80,56 +71,56 @@ public CombinedChart(Context context, AttributeSet attrs, int defStyle) { protected void init() { super.init(); - mFillFormatter = new DefaultFillFormatter(); - // mRenderer = new CombinedChartRenderer(this, mAnimator, - // mViewPortHandler); - } + // Default values are not ready here yet + mDrawOrder = new DrawOrder[]{ + DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER + }; - @Override - protected void calcMinMax() { - super.calcMinMax(); - - if (getBarData() != null || getCandleData() != null || getBubbleData() != null) { - mXChartMin = -0.5f; - mXChartMax = mData.getXVals().size() - 0.5f; + setHighlighter(new CombinedHighlighter(this, this)); - if (getBubbleData() != null) { + // Old default behaviour + setHighlightFullBarEnabled(true); - for (BubbleDataSet set : getBubbleData().getDataSets()) { - - final float xmin = set.getXMin(); - final float xmax = set.getXMax(); - - if (xmin < mXChartMin) - mXChartMin = xmin; - - if (xmax > mXChartMax) - mXChartMax = xmax; - } - } + mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + } - mDeltaX = Math.abs(mXChartMax - mXChartMin); - } + @Override + public CombinedData getCombinedData() { + return mData; } @Override public void setData(CombinedData data) { super.setData(data); - mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler); + setHighlighter(new CombinedHighlighter(this, this)); + ((CombinedChartRenderer)mRenderer).createRenderers(); mRenderer.initBuffers(); } - public void setFillFormatter(FillFormatter formatter) { - - if (formatter == null) - formatter = new DefaultFillFormatter(); - else - mFillFormatter = formatter; - } - + /** + * 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 FillFormatter getFillFormatter() { - return mFillFormatter; + 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 @@ -177,58 +168,48 @@ public boolean isDrawValueAboveBarEnabled() { return mDrawValueAboveBar; } - @Override - public boolean isDrawHighlightArrowEnabled() { - return mDrawHighlightArrow; - } - - @Override - public boolean isDrawValuesForWholeStackEnabled() { - return mDrawValuesForWholeStack; - } - - /** - * 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. - * + * * @param enabled */ public void setDrawValueAboveBar(boolean enabled) { mDrawValueAboveBar = enabled; } - /** - * if set to true, all values of a stack are drawn individually, and not - * just their sum - * - * @param enabled - */ - public void setDrawValuesForWholeStack(boolean enabled) { - mDrawValuesForWholeStack = enabled; - } /** * If set to true, a grey area is drawn behind each bar that indicates the * maximum value. Enabling his will reduce performance by about 50%. - * + * * @param enabled */ public void setDrawBarShadow(boolean enabled) { mDrawBarShadow = enabled; } + /** + * Set this to true to make the highlight operation full-bar oriented, + * false to make it highlight single values (relevant only for stacked). + * + * @param enabled + */ + public void setHighlightFullBarEnabled(boolean enabled) { + mHighlightFullBarEnabled = enabled; + } + + /** + * @return true the highlight operation is be full-bar oriented, false if single-value + */ + @Override + public boolean isHighlightFullBarEnabled() { + return mHighlightFullBarEnabled; + } + /** * Returns the currently set draw order. - * + * * @return */ public DrawOrder[] getDrawOrder() { @@ -240,7 +221,7 @@ public DrawOrder[] getDrawOrder() { * earlier you place them in the provided array, the further they will be in * the background. e.g. if you provide new DrawOrer[] { DrawOrder.BAR, * DrawOrder.LINE }, the bars will be drawn behind the lines. - * + * * @param order */ public void setDrawOrder(DrawOrder[] order) { @@ -248,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/main/java/com/github/mikephil/charting/charts/LineChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java new file mode 100644 index 0000000000..aa7afc4c85 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/LineChart.java @@ -0,0 +1,50 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; + +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.renderer.LineChartRenderer; + +/** + * Chart that draws lines, surfaces, circles, ... + * + * @author Philipp Jahoda + */ +public class LineChart extends BarLineChartBase implements LineDataProvider { + + public LineChart(Context context) { + super(context); + } + + public LineChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LineChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler); + } + + @Override + public LineData getLineData() { + return mData; + } + + @Override + protected void onDetachedFromWindow() { + // releases the bitmap in the renderer to avoid oom error + if (mRenderer != null && mRenderer instanceof LineChartRenderer) { + ((LineChartRenderer) mRenderer).releaseBitmap(); + } + super.onDetachedFromWindow(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java new file mode 100644 index 0000000000..de11b3a844 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieChart.java @@ -0,0 +1,804 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.util.AttributeSet; + +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.PieData; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.highlight.PieHighlighter; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; +import com.github.mikephil.charting.renderer.PieChartRenderer; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; + +/** + * View that represents a pie chart. Draws cake like slices. + * + * @author Philipp Jahoda + */ +public class PieChart extends PieRadarChartBase { + + /** + * rect object that represents the bounds of the piechart, needed for + * drawing the circle + */ + private RectF mCircleBox = new RectF(); + + /** + * flag indicating if entry labels should be drawn or not + */ + private boolean mDrawEntryLabels = true; + + /** + * array that holds the width of each pie-slice in degrees + */ + private float[] mDrawAngles = new float[1]; + + /** + * array that holds the absolute angle in degrees of each slice + */ + private float[] mAbsoluteAngles = new float[1]; + + /** + * if true, the white hole inside the chart will be drawn + */ + private boolean mDrawHole = true; + + /** + * if true, the hole will see-through to the inner tips of the slices + */ + private boolean mDrawSlicesUnderHole = false; + + /** + * if true, the values inside the piechart are drawn as percent values + */ + private boolean mUsePercentValues = false; + + /** + * if true, the slices of the piechart are rounded + */ + private boolean mDrawRoundedSlices = false; + + /** + * variable for the text that is drawn in the center of the pie-chart + */ + private CharSequence mCenterText = ""; + + private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0); + + /** + * indicates the size of the hole in the center of the piechart, default: + * radius / 2 + */ + private float mHoleRadiusPercent = 50f; + + /** + * the radius of the transparent circle next to the chart-hole in the center + */ + protected float mTransparentCircleRadiusPercent = 55f; + + /** + * if enabled, centertext is drawn + */ + private boolean mDrawCenterText = true; + + private float mCenterTextRadiusPercent = 100.f; + + protected float mMaxAngle = 360f; + + /** + * Minimum angle to draw slices, this only works if there is enough room for all slices to have + * the minimum angle, default 0f. + */ + private float mMinAngleForSlices = 0f; + + public PieChart(Context context) { + super(context); + } + + public PieChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PieChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler); + mXAxis = null; + + mHighlighter = new PieHighlighter(this); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mData == null) + return; + + mRenderer.drawData(canvas); + + if (valuesToHighlight()) + mRenderer.drawHighlighted(canvas, mIndicesToHighlight); + + mRenderer.drawExtras(canvas); + + mRenderer.drawValues(canvas); + + mLegendRenderer.renderLegend(canvas); + + drawDescription(canvas); + + drawMarkers(canvas); + } + + @Override + public void calculateOffsets() { + super.calculateOffsets(); + + // prevent nullpointer when no data set + if (mData == null) + return; + + float diameter = getDiameter(); + float radius = diameter / 2f; + + MPPointF c = getCenterOffsets(); + + float shift = mData.getDataSet().getSelectionShift(); + + // create the circle box that will contain the pie-chart (the bounds of + // the pie-chart) + mCircleBox.set(c.x - radius + shift, + c.y - radius + shift, + c.x + radius - shift, + c.y + radius - shift); + + MPPointF.recycleInstance(c); + } + + @Override + protected void calcMinMax() { + calcAngles(); + } + + @Override + protected float[] getMarkerPosition(Highlight highlight) { + + MPPointF center = getCenterCircleBox(); + float r = getRadius(); + + float off = r / 10f * 3.6f; + + if (isDrawHoleEnabled()) { + off = (r - (r / 100f * getHoleRadius())) / 2f; + } + + r -= off; // offset to keep things inside the chart + + float rotationAngle = getRotationAngle(); + + int entryIndex = (int) highlight.getX(); + + // offset needed to center the drawn text in the slice + float offset = mDrawAngles[entryIndex] / 2; + + // calculate the text position + float x = (float) (r + * Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) + * mAnimator.getPhaseY())) + center.x); + float y = (float) (r + * Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset) + * mAnimator.getPhaseY())) + center.y); + + MPPointF.recycleInstance(center); + return new float[]{x, y}; + } + + /** + * calculates the needed angles for the chart slices + */ + private void calcAngles() { + + int entryCount = mData.getEntryCount(); + + if (mDrawAngles.length != entryCount) { + mDrawAngles = new float[entryCount]; + } else { + for (int i = 0; i < entryCount; i++) { + mDrawAngles[i] = 0; + } + } + if (mAbsoluteAngles.length != entryCount) { + mAbsoluteAngles = new float[entryCount]; + } else { + for (int i = 0; i < entryCount; i++) { + mAbsoluteAngles[i] = 0; + } + } + + float yValueSum = mData.getYValueSum(); + + List dataSets = mData.getDataSets(); + + boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle; + float[] minAngles = new float[entryCount]; + + int cnt = 0; + float offset = 0f; + float diff = 0f; + + for (int i = 0; i < mData.getDataSetCount(); i++) { + + IPieDataSet set = dataSets.get(i); + + for (int j = 0; j < set.getEntryCount(); j++) { + + float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum); + + if (hasMinAngle) { + float temp = drawAngle - mMinAngleForSlices; + if (temp <= 0) { + minAngles[cnt] = mMinAngleForSlices; + offset += -temp; + } else { + minAngles[cnt] = drawAngle; + diff += temp; + } + } + + mDrawAngles[cnt] = drawAngle; + + if (cnt == 0) { + mAbsoluteAngles[cnt] = mDrawAngles[cnt]; + } else { + mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt]; + } + + cnt++; + } + } + + if (hasMinAngle) { + // Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract + // This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work! + for (int i = 0; i < entryCount; i++) { + minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset; + if (i == 0) { + mAbsoluteAngles[0] = minAngles[0]; + } else { + mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i]; + } + } + + mDrawAngles = minAngles; + } + } + + /** + * Checks if the given index is set to be highlighted. + * + * @param index + * @return + */ + public boolean needsHighlight(int index) { + + // no highlight + if (!valuesToHighlight()) + return false; + + for (int i = 0; i < mIndicesToHighlight.length; i++) + + // check if the xvalue for the given dataset needs highlight + if ((int) mIndicesToHighlight[i].getX() == index) + return true; + + return false; + } + + /** + * calculates the needed angle for a given value + * + * @param value + * @return + */ + private float calcAngle(float value) { + return calcAngle(value, mData.getYValueSum()); + } + + /** + * calculates the needed angle for a given value + * + * @param value + * @param yValueSum + * @return + */ + private float calcAngle(float value, float yValueSum) { + return value / yValueSum * mMaxAngle; + } + + /** + * This will throw an exception, PieChart has no XAxis object. + * + * @return + */ + @Deprecated + @Override + public XAxis getXAxis() { + throw new RuntimeException("PieChart has no XAxis"); + } + + @Override + public int getIndexForAngle(float angle) { + + // take the current angle of the chart into consideration + float a = Utils.getNormalizedAngle(angle - getRotationAngle()); + + for (int i = 0; i < mAbsoluteAngles.length; i++) { + if (mAbsoluteAngles[i] > a) + return i; + } + + return -1; // return -1 if no index found + } + + /** + * Returns the index of the DataSet this x-index belongs to. + * + * @param xIndex + * @return + */ + public int getDataSetIndexForIndex(int xIndex) { + + List dataSets = mData.getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null) + return i; + } + + return -1; + } + + /** + * returns an integer array of all the different angles the chart slices + * have the angles in the returned array determine how much space (of 360°) + * each slice takes + * + * @return + */ + public float[] getDrawAngles() { + return mDrawAngles; + } + + /** + * returns the absolute angles of the different chart slices (where the + * slices end) + * + * @return + */ + public float[] getAbsoluteAngles() { + return mAbsoluteAngles; + } + + /** + * Sets the color for the hole that is drawn in the center of the PieChart + * (if enabled). + * + * @param color + */ + public void setHoleColor(int color) { + ((PieChartRenderer) mRenderer).getPaintHole().setColor(color); + } + + /** + * Enable or disable the visibility of the inner tips of the slices behind the hole + */ + public void setDrawSlicesUnderHole(boolean enable) { + mDrawSlicesUnderHole = enable; + } + + /** + * Returns true if the inner tips of the slices are visible behind the hole, + * false if not. + * + * @return true if slices are visible behind the hole. + */ + public boolean isDrawSlicesUnderHoleEnabled() { + return mDrawSlicesUnderHole; + } + + /** + * set this to true to draw the pie center empty + * + * @param enabled + */ + public void setDrawHoleEnabled(boolean enabled) { + this.mDrawHole = enabled; + } + + /** + * returns true if the hole in the center of the pie-chart is set to be + * visible, false if not + * + * @return + */ + public boolean isDrawHoleEnabled() { + return mDrawHole; + } + + /** + * Sets the text String that is displayed in the center of the PieChart. + * + * @param text + */ + public void setCenterText(CharSequence text) { + if (text == null) + mCenterText = ""; + else + mCenterText = text; + } + + /** + * returns the text that is drawn in the center of the pie-chart + * + * @return + */ + public CharSequence getCenterText() { + return mCenterText; + } + + /** + * set this to true to draw the text that is displayed in the center of the + * pie chart + * + * @param enabled + */ + public void setDrawCenterText(boolean enabled) { + this.mDrawCenterText = enabled; + } + + /** + * returns true if drawing the center text is enabled + * + * @return + */ + public boolean isDrawCenterTextEnabled() { + return mDrawCenterText; + } + + @Override + protected float getRequiredLegendOffset() { + return mLegendRenderer.getLabelPaint().getTextSize() * 2.f; + } + + @Override + protected float getRequiredBaseOffset() { + return 0; + } + + @Override + public float getRadius() { + if (mCircleBox == null) + return 0; + else + return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f); + } + + /** + * returns the circlebox, the boundingbox of the pie-chart slices + * + * @return + */ + public RectF getCircleBox() { + return mCircleBox; + } + + /** + * returns the center of the circlebox + * + * @return + */ + public MPPointF getCenterCircleBox() { + return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY()); + } + + /** + * sets the typeface for the center-text paint + * + * @param t + */ + public void setCenterTextTypeface(Typeface t) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t); + } + + /** + * Sets the size of the center text of the PieChart in dp. + * + * @param sizeDp + */ + public void setCenterTextSize(float sizeDp) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize( + Utils.convertDpToPixel(sizeDp)); + } + + /** + * Sets the size of the center text of the PieChart in pixels. + * + * @param sizePixels + */ + public void setCenterTextSizePixels(float sizePixels) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels); + } + + /** + * Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0 + * + * @param x + * @param y + */ + public void setCenterTextOffset(float x, float y) { + mCenterTextOffset.x = Utils.convertDpToPixel(x); + mCenterTextOffset.y = Utils.convertDpToPixel(y); + } + + /** + * Returns the offset on the x- and y-axis the center text has in dp. + * + * @return + */ + public MPPointF getCenterTextOffset() { + return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y); + } + + /** + * Sets the color of the center text of the PieChart. + * + * @param color + */ + public void setCenterTextColor(int color) { + ((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color); + } + + /** + * sets the radius of the hole in the center of the piechart in percent of + * the maximum radius (max = the radius of the whole chart), default 50% + * + * @param percent + */ + public void setHoleRadius(final float percent) { + mHoleRadiusPercent = percent; + } + + /** + * Returns the size of the hole radius in percent of the total radius. + * + * @return + */ + public float getHoleRadius() { + return mHoleRadiusPercent; + } + + /** + * Sets the color the transparent-circle should have. + * + * @param color + */ + public void setTransparentCircleColor(int color) { + + Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle(); + int alpha = p.getAlpha(); + p.setColor(color); + p.setAlpha(alpha); + } + + /** + * sets the radius of the transparent circle that is drawn next to the hole + * in the piechart in percent of the maximum radius (max = the radius of the + * whole chart), default 55% -> means 5% larger than the center-hole by + * default + * + * @param percent + */ + public void setTransparentCircleRadius(final float percent) { + mTransparentCircleRadiusPercent = percent; + } + + public float getTransparentCircleRadius() { + return mTransparentCircleRadiusPercent; + } + + /** + * Sets the amount of transparency the transparent circle should have 0 = fully transparent, + * 255 = fully opaque. + * Default value is 100. + * + * @param alpha 0-255 + */ + public void setTransparentCircleAlpha(int alpha) { + ((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha); + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * Deprecated -> use setDrawEntryLabels(...) instead. + * + * @param enabled + */ + @Deprecated + public void setDrawSliceText(boolean enabled) { + mDrawEntryLabels = enabled; + } + + /** + * Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class). + * + * @param enabled + */ + public void setDrawEntryLabels(boolean enabled) { + mDrawEntryLabels = enabled; + } + + /** + * Returns true if drawing the entry labels is enabled, false if not. + * + * @return + */ + public boolean isDrawEntryLabelsEnabled() { + return mDrawEntryLabels; + } + + /** + * Sets the color the entry labels are drawn with. + * + * @param color + */ + public void setEntryLabelColor(int color) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color); + } + + /** + * Sets a custom Typeface for the drawing of the entry labels. + * + * @param tf + */ + public void setEntryLabelTypeface(Typeface tf) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf); + } + + /** + * Sets the size of the entry labels in dp. Default: 13dp + * + * @param size + */ + public void setEntryLabelTextSize(float size) { + ((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size)); + } + + /** + * Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled + * and if the slices are not drawn under the hole. + * + * @param enabled draw curved ends of slices + */ + public void setDrawRoundedSlices(boolean enabled) { + mDrawRoundedSlices = enabled; + } + + /** + * Returns true if the chart is set to draw each end of a pie-slice + * "rounded". + * + * @return + */ + public boolean isDrawRoundedSlicesEnabled() { + return mDrawRoundedSlices; + } + + /** + * If this is enabled, values inside the PieChart are drawn in percent and + * not with their original value. Values provided for the IValueFormatter to + * format are then provided in percent. + * + * @param enabled + */ + public void setUsePercentValues(boolean enabled) { + mUsePercentValues = enabled; + } + + /** + * Returns true if using percentage values is enabled for the chart. + * + * @return + */ + public boolean isUsePercentValuesEnabled() { + return mUsePercentValues; + } + + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + public void setCenterTextRadiusPercent(float percent) { + mCenterTextRadiusPercent = percent; + } + + /** + * the rectangular radius of the bounding box for the center text, as a percentage of the pie + * hole + * default 1.f (100%) + */ + public float getCenterTextRadiusPercent() { + return mCenterTextRadiusPercent; + } + + public float getMaxAngle() { + return mMaxAngle; + } + + /** + * Sets the max angle that is used for calculating the pie-circle. 360f means + * it's a full PieChart, 180f results in a half-pie-chart. Default: 360f + * + * @param maxangle min 90, max 360 + */ + public void setMaxAngle(float maxangle) { + + if (maxangle > 360) + maxangle = 360f; + + if (maxangle < 90) + maxangle = 90f; + + this.mMaxAngle = maxangle; + } + + /** + * The minimum angle slices on the chart are rendered with, default is 0f. + * + * @return minimum angle for slices + */ + public float getMinAngleForSlices() { + return mMinAngleForSlices; + } + + /** + * Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()} + * and {@link #invalidate()} when changing this, only works if there is enough room for all + * slices to have the minimum angle. + * + * @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)} + */ + public void setMinAngleForSlices(float minAngle) { + + if (minAngle > (mMaxAngle / 2f)) + minAngle = mMaxAngle / 2f; + else if (minAngle < 0) + minAngle = 0f; + + this.mMinAngleForSlices = minAngle; + } + + @Override + protected void onDetachedFromWindow() { + // releases the bitmap in the renderer to avoid oom error + if (mRenderer != null && mRenderer instanceof PieChartRenderer) { + ((PieChartRenderer) mRenderer).releaseBitmap(); + } + super.onDetachedFromWindow(); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java new file mode 100644 index 0000000000..e6d38d3a01 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/PieRadarChartBase.java @@ -0,0 +1,499 @@ + +package com.github.mikephil.charting.charts; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import com.github.mikephil.charting.animation.Easing; +import com.github.mikephil.charting.animation.Easing.EasingFunction; +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; +import com.github.mikephil.charting.listener.PieRadarChartTouchListener; +import com.github.mikephil.charting.utils.MPPointF; +import com.github.mikephil.charting.utils.Utils; + +/** + * Baseclass of PieChart and RadarChart. + * + * @author Philipp Jahoda + */ +public abstract class PieRadarChartBase>> + extends 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 + */ + private float mRawRotationAngle = 270f; + + /** + * flag that indicates if rotation is enabled or not + */ + protected boolean mRotateEnabled = true; + + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ + protected float mMinOffset = 0.f; + + public PieRadarChartBase(Context context) { + super(context); + } + + public PieRadarChartBase(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void init() { + super.init(); + + mChartTouchListener = new PieRadarChartTouchListener(this); + } + + @Override + protected void calcMinMax() { + //mXAxis.mAxisRange = mData.getXVals().size() - 1; + } + + @Override + public int getMaxVisibleCount() { + return mData.getEntryCount(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // use the pie- and radarchart listener own listener + if (mTouchEnabled && mChartTouchListener != null) + return mChartTouchListener.onTouch(this, event); + else + return super.onTouchEvent(event); + } + + @Override + public void computeScroll() { + + if (mChartTouchListener instanceof PieRadarChartTouchListener) + ((PieRadarChartTouchListener) mChartTouchListener).computeScroll(); + } + + @Override + public void notifyDataSetChanged() { + if (mData == null) + return; + + calcMinMax(); + + if (mLegend != null) + mLegendRenderer.computeLegend(mData); + + calculateOffsets(); + } + + @Override + public void calculateOffsets() { + + float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f; + + if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) { + + float fullLegendWidth = Math.min(mLegend.mNeededWidth, + mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent()); + + switch (mLegend.getOrientation()) { + case VERTICAL: { + float xLegendOffset = 0.f; + + if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT + || mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT) { + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.CENTER) { + // this is the space between the legend and the chart + final float spacing = Utils.convertDpToPixel(13f); + + xLegendOffset = fullLegendWidth + spacing; + + } else { + // this is the space between the legend and the chart + float spacing = Utils.convertDpToPixel(8f); + + float legendWidth = fullLegendWidth + spacing; + float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax; + + MPPointF center = getCenter(); + + float bottomX = mLegend.getHorizontalAlignment() == + Legend.LegendHorizontalAlignment.RIGHT + ? getWidth() - legendWidth + 15.f + : legendWidth - 15.f; + float bottomY = legendHeight + 15.f; + float distLegend = distanceToCenter(bottomX, bottomY); + + MPPointF reference = getPosition(center, getRadius(), + getAngleForPoint(bottomX, bottomY)); + + float distReference = distanceToCenter(reference.x, reference.y); + float minOffset = Utils.convertDpToPixel(5f); + + if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) { + xLegendOffset = legendWidth; + } else if (distLegend < distReference) { + + float diff = distReference - distLegend; + xLegendOffset = minOffset + diff; + } + + MPPointF.recycleInstance(center); + MPPointF.recycleInstance(reference); + } + } + + switch (mLegend.getHorizontalAlignment()) { + case LEFT: + legendLeft = xLegendOffset; + break; + + case RIGHT: + legendRight = xLegendOffset; + break; + + case CENTER: + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + case BOTTOM: + legendBottom = Math.min(mLegend.mNeededHeight, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + break; + } + break; + } + } + break; + + case HORIZONTAL: + float yLegendOffset = 0.f; + + if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.TOP || + mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.BOTTOM) { + + // It's possible that we do not need this offset anymore as it + // is available through the extraOffsets, but changing it can mean + // changing default visibility for existing apps. + float yOffset = getRequiredLegendOffset(); + + yLegendOffset = Math.min(mLegend.mNeededHeight + yOffset, + mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent()); + + switch (mLegend.getVerticalAlignment()) { + case TOP: + legendTop = yLegendOffset; + break; + case BOTTOM: + legendBottom = yLegendOffset; + break; + } + } + break; + } + + legendLeft += getRequiredBaseOffset(); + legendRight += getRequiredBaseOffset(); + legendTop += getRequiredBaseOffset(); + legendBottom += getRequiredBaseOffset(); + } + + float minOffset = Utils.convertDpToPixel(mMinOffset); + + if (this instanceof RadarChart) { + XAxis x = this.getXAxis(); + + if (x.isEnabled() && x.isDrawLabelsEnabled()) { + minOffset = Math.max(minOffset, x.mLabelRotatedWidth); + } + } + + legendTop += getExtraTopOffset(); + legendRight += getExtraRightOffset(); + legendBottom += getExtraBottomOffset(); + legendLeft += getExtraLeftOffset(); + + float offsetLeft = Math.max(minOffset, legendLeft); + float offsetTop = Math.max(minOffset, legendTop); + float offsetRight = Math.max(minOffset, legendRight); + float offsetBottom = Math.max(minOffset, Math.max(getRequiredBaseOffset(), legendBottom)); + + mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); + + if (mLogEnabled) + Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + + ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom); + } + + /** + * returns the angle relative to the chart center for the given point on the + * chart in degrees. The angle is always between 0 and 360°, 0° is NORTH, + * 90° is EAST, ... + * + * @param x + * @param y + * @return + */ + public float getAngleForPoint(float x, float y) { + + MPPointF c = getCenterOffsets(); + + double tx = x - c.x, ty = y - c.y; + double length = Math.sqrt(tx * tx + ty * ty); + double r = Math.acos(ty / length); + + float angle = (float) Math.toDegrees(r); + + if (x > c.x) + angle = 360f - angle; + + // add 90° because chart starts EAST + angle = angle + 90f; + + // neutralize overflow + if (angle > 360f) + angle = angle - 360f; + + MPPointF.recycleInstance(c); + + return angle; + } + + /** + * Returns a recyclable MPPointF instance. + * Calculates the position around a center point, depending on the distance + * from the center, and the angle of the position around the center. + * + * @param center + * @param dist + * @param angle in degrees, converted to radians internally + * @return + */ + public MPPointF getPosition(MPPointF center, float dist, float angle) { + + MPPointF p = MPPointF.getInstance(0, 0); + getPosition(center, dist, angle, p); + return p; + } + + public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) { + outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle))); + outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle))); + } + + /** + * Returns the distance of a certain point on the chart to the center of the + * chart. + * + * @param x + * @param y + * @return + */ + public float distanceToCenter(float x, float y) { + + MPPointF c = getCenterOffsets(); + + float dist = 0f; + + float xDist = 0f; + float yDist = 0f; + + if (x > c.x) { + xDist = x - c.x; + } else { + xDist = c.x - x; + } + + if (y > c.y) { + yDist = y - c.y; + } else { + yDist = c.y - y; + } + + // pythagoras + dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0)); + + MPPointF.recycleInstance(c); + + return dist; + } + + /** + * Returns the xIndex for the given angle around the center of the chart. + * Returns -1 if not found / outofbounds. + * + * @param angle + * @return + */ + public abstract int getIndexForAngle(float angle); + + /** + * Set an offset for the rotation of the RadarChart in degrees. Default 270f + * --> top (NORTH) + * + * @param angle + */ + public void setRotationAngle(float angle) { + mRawRotationAngle = angle; + mRotationAngle = Utils.getNormalizedAngle(mRawRotationAngle); + } + + /** + * gets the raw version of the current rotation angle of the pie chart the + * returned value could be any value, negative or positive, outside of the + * 360 degrees. this is used when working with rotation direction, mainly by + * gestures and animations. + * + * @return + */ + public float getRawRotationAngle() { + return mRawRotationAngle; + } + + /** + * gets a normalized version of the current rotation angle of the pie chart, + * which will always be between 0.0 < 360.0 + * + * @return + */ + public float getRotationAngle() { + return mRotationAngle; + } + + /** + * Set this to true to enable the rotation / spinning of the chart by touch. + * Set it to false to disable it. Default: true + * + * @param enabled + */ + public void setRotationEnabled(boolean enabled) { + mRotateEnabled = enabled; + } + + /** + * Returns true if rotation of the chart by touch is enabled, false if not. + * + * @return + */ + public boolean isRotationEnabled() { + return mRotateEnabled; + } + + /** + * Gets the minimum offset (padding) around the chart, defaults to 0.f + */ + public float getMinOffset() { + return mMinOffset; + } + + /** + * Sets the minimum offset (padding) around the chart, defaults to 0.f + */ + public void setMinOffset(float minOffset) { + mMinOffset = minOffset; + } + + /** + * returns the diameter of the pie- or radar-chart + * + * @return + */ + public float getDiameter() { + RectF content = mViewPortHandler.getContentRect(); + content.left += getExtraLeftOffset(); + content.top += getExtraTopOffset(); + content.right -= getExtraRightOffset(); + content.bottom -= getExtraBottomOffset(); + return Math.min(content.width(), content.height()); + } + + /** + * Returns the radius of the chart in pixels. + * + * @return + */ + public abstract float getRadius(); + + /** + * Returns the required offset for the chart legend. + * + * @return + */ + protected abstract float getRequiredLegendOffset(); + + /** + * Returns the base offset needed for the chart without calculating the + * legend size. + * + * @return + */ + protected abstract float getRequiredBaseOffset(); + + @Override + public float getYChartMax() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public float getYChartMin() { + // TODO Auto-generated method stub + return 0; + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW THIS RELATED TO ANIMATION */ + + /** + * Applys a spin animation to the Chart. + * + * @param durationmillis + * @param fromangle + * @param toangle + */ + @SuppressLint("NewApi") + public void spin(int durationmillis, float fromangle, float toangle, EasingFunction easing) { + + setRotationAngle(fromangle); + + ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, + toangle); + spinAnimator.setDuration(durationmillis); + spinAnimator.setInterpolator(easing); + + spinAnimator.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + postInvalidate(); + } + }); + spinAnimator.start(); + } +} 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 65% 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 6c03096d46..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,15 +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.RadarHighlighter; import com.github.mikephil.charting.renderer.RadarChartRenderer; import com.github.mikephil.charting.renderer.XAxisRendererRadarChart; import com.github.mikephil.charting.renderer.YAxisRendererRadarChart; @@ -21,34 +19,50 @@ /** * Implementation of the RadarChart, a "spidernet"-like chart. It works best * when displaying 5-10 entries per DataSet. - * + * * @author Philipp Jahoda */ public class RadarChart extends PieRadarChartBase { - /** width of the main web lines */ + /** + * width of the main web lines + */ private float mWebLineWidth = 2.5f; - /** width of the inner web lines */ + /** + * width of the inner web lines + */ private float mInnerWebLineWidth = 1.5f; - /** color for the main web lines */ + /** + * color for the main web lines + */ private int mWebColor = Color.rgb(122, 122, 122); - /** color for the inner web */ + /** + * color for the inner web + */ private int mWebColorInner = Color.rgb(122, 122, 122); - /** transparency the grid is drawn with (0-255) */ + /** + * transparency the grid is drawn with (0-255) + */ private int mWebAlpha = 150; - /** flag indicating if the web lines should be drawn or not */ + /** + * flag indicating if the web lines should be drawn or not + */ private boolean mDrawWeb = true; - /** the object reprsenting the y-axis labels */ - private YAxis mYAxis; + /** + * modulus that determines how many labels and web-lines are skipped before the next is drawn + */ + private int mSkipWebLineCount = 0; - /** the object representing the x-axis labels */ - private XAxis mXAxis; + /** + * the object reprsenting the y-axis labels + */ + private YAxis mYAxis; protected YAxisRendererRadarChart mYAxisRenderer; protected XAxisRendererRadarChart mXAxisRenderer; @@ -70,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); @@ -79,68 +92,30 @@ 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 = mData.getYMin(AxisDependency.LEFT); - float maxLeft = mData.getYMax(AxisDependency.LEFT); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - float leftRange = Math.abs(maxLeft - (mYAxis.isStartAtZeroEnabled() ? 0 : minLeft)); - - float topSpaceLeft = leftRange / 100f * mYAxis.getSpaceTop(); - float bottomSpaceLeft = leftRange / 100f * mYAxis.getSpaceBottom(); - - mXChartMax = mData.getXVals().size() - 1; - mDeltaX = Math.abs(mXChartMax - mXChartMin); - - mYAxis.mAxisMaximum = !Float.isNaN(mYAxis.getAxisMaxValue()) ? mYAxis - .getAxisMaxValue() : maxLeft + topSpaceLeft; - mYAxis.mAxisMinimum = !Float.isNaN(mYAxis.getAxisMinValue()) ? mYAxis - .getAxisMinValue() : minLeft - bottomSpaceLeft; - - // consider starting at zero (0) - if (mYAxis.isStartAtZeroEnabled()) - mYAxis.mAxisMinimum = 0f; - - mYAxis.mAxisRange = Math.abs(mYAxis.mAxisMaximum - mYAxis.mAxisMinimum); - } - - @Override - protected float[] getMarkerPosition(Entry e, int dataSetIndex) { - - 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 public void notifyDataSetChanged() { - if (mDataNotSet) + if (mData == null) return; calcMinMax(); - if (mYAxis.needsDefaultFormatter()) { - mYAxis.setValueFormatter(mDefaultFormatter); - } + mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted()); + mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false); - mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum); - mXAxisRenderer.computeAxis(mData.getXValAverageLength(), mData.getXVals()); - - mLegendRenderer.computeLegend(mData); + if (mLegend != null && !mLegend.isLegendCustom()) + mLegendRenderer.computeLegend(mData); calculateOffsets(); } @@ -149,20 +124,30 @@ public void notifyDataSetChanged() { protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mDataNotSet) + 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 (mHighlightEnabled && valuesToHighlight()) - mRenderer.drawHighlighted(canvas, mIndicesToHightlight); + if (valuesToHighlight()) + mRenderer.drawHighlighted(canvas, mIndicesToHighlight); + + if (mYAxis.isEnabled() && !mYAxis.isDrawLimitLinesBehindDataEnabled()) + mYAxisRenderer.renderLimitLines(canvas); mYAxisRenderer.renderAxisLabels(canvas); @@ -173,28 +158,25 @@ protected void onDraw(Canvas canvas) { drawDescription(canvas); drawMarkers(canvas); - -// canvas.drawBitmap(mDrawBitmap, 0, 0, mDrawPaint); } /** * Returns the factor that is needed to transform values into pixels. - * + * * @return */ 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; } /** * Returns the angle that each slice in the radar chart occupies. - * + * * @return */ public float getSliceAngle() { - return 360f / (float) mData.getXValCount(); + return 360f / (float) mData.getMaxEntryCountSet().getEntryCount(); } @Override @@ -205,36 +187,35 @@ 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; } /** * Returns the object that represents all y-labels of the RadarChart. - * + * * @return */ 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. - * + * * @param width */ public void setWebLineWidth(float width) { @@ -248,7 +229,7 @@ public float getWebLineWidth() { /** * Sets the width of the web lines that are in between the lines coming from * the center. - * + * * @param width */ public void setWebLineWidthInner(float width) { @@ -262,7 +243,7 @@ public float getWebLineWidthInner() { /** * Sets the transparency (alpha) value for all web lines, default: 150, 255 * = 100% opaque, 0 = 100% transparent - * + * * @param alpha */ public void setWebAlpha(int alpha) { @@ -271,7 +252,7 @@ public void setWebAlpha(int alpha) { /** * Returns the alpha value for all web lines. - * + * * @return */ public int getWebAlpha() { @@ -282,7 +263,7 @@ public int getWebAlpha() { * Sets the color for the web lines that come from the center. Don't forget * to use getResources().getColor(...) when loading a color from the * resources. Default: Color.rgb(122, 122, 122) - * + * * @param color */ public void setWebColor(int color) { @@ -297,7 +278,7 @@ public int getWebColor() { * Sets the color for the web lines in between the lines that come from the * center. Don't forget to use getResources().getColor(...) when loading a * color from the resources. Default: Color.rgb(122, 122, 122) - * + * * @param color */ public void setWebColorInner(int color) { @@ -311,21 +292,43 @@ public int getWebColorInner() { /** * If set to true, drawing the web is enabled, if set to false, drawing the * whole web is disabled. Default: true - * + * * @param enabled */ public void setDrawWeb(boolean enabled) { mDrawWeb = enabled; } + /** + * Sets the number of web-lines that should be skipped on chart web before the + * next one is drawn. This targets the lines that come from the center of the RadarChart. + * + * @param count if count = 1 -> 1 line is skipped in between + */ + public void setSkipWebLineCount(int count) { + + mSkipWebLineCount = Math.max(0, count); + } + + /** + * Returns the modulus that is used for skipping web-lines. + * + * @return + */ + public int getSkipWebLineCount() { + return mSkipWebLineCount; + } + @Override - protected float getRequiredBottomOffset() { - return mLegendRenderer.getLabelPaint().getTextSize() * 6.5f; + protected float getRequiredLegendOffset() { + return mLegendRenderer.getLabelPaint().getTextSize() * 4.f; } @Override protected float getRequiredBaseOffset() { - return mXAxis.mLabelWidth; + return mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled() ? + mXAxis.mLabelRotatedWidth : + Utils.convertDpToPixel(10f); } @Override @@ -350,7 +353,7 @@ public float getYChartMin() { /** * Returns the range of y-values this chart can display. - * + * * @return */ public float getYRange() { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java new file mode 100644 index 0000000000..37e8395b5e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/ScatterChart.java @@ -0,0 +1,77 @@ + +package com.github.mikephil.charting.charts; + +import android.content.Context; +import android.util.AttributeSet; + +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider; +import com.github.mikephil.charting.renderer.ScatterChartRenderer; + +/** + * The ScatterChart. Draws dots, triangles, squares and custom shapes into the + * Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the + * worst performance. + * + * @author Philipp Jahoda + */ +public class ScatterChart extends BarLineChartBase implements ScatterDataProvider { + + public ScatterChart(Context context) { + super(context); + } + + public ScatterChart(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ScatterChart(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + + @Override + protected void init() { + super.init(); + + mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler); + + getXAxis().setSpaceMin(0.5f); + getXAxis().setSpaceMax(0.5f); + } + + @Override + public ScatterData getScatterData() { + return mData; + } + + /** + * Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with. + * If a ScatterShape is specified for a ScatterDataSet, the required renderer is set. + */ + public enum ScatterShape { + + SQUARE("SQUARE"), + CIRCLE("CIRCLE"), + TRIANGLE("TRIANGLE"), + CROSS("CROSS"), + X("X"), + CHEVRON_UP("CHEVRON_UP"), + CHEVRON_DOWN("CHEVRON_DOWN"); + + private final String shapeIdentifier; + + ScatterShape(final String shapeIdentifier) { + this.shapeIdentifier = shapeIdentifier; + } + + @Override + public String toString() { + return shapeIdentifier; + } + + public static ScatterShape[] getAllDefaultShapes() { + return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN}; + } + } +} 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 74% 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 bdcaa4a56e..d3a1d4d82a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/ComponentBase.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/ComponentBase.java @@ -7,30 +7,43 @@ import com.github.mikephil.charting.utils.Utils; /** - * This class encapsulates everything both Axis and Legend have in common. - * + * This class encapsulates everything both Axis, Legend and LimitLines have in common. + * * @author Philipp Jahoda */ public abstract class ComponentBase { - /** flag that indicates if this axis / legend is enabled or not */ + /** + * flag that indicates if this axis / legend is enabled or not + */ 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; - /** the typeface used for the labels */ + /** + * the typeface used for the labels + */ protected Typeface mTypeface = null; - /** the text size of the labels */ - protected float mTextSize = 10f; + /** + * the text size of the labels + */ + protected float mTextSize = Utils.convertDpToPixel(10f); - /** the text color to use for the labels */ + /** + * the text color to use for the labels + */ protected int mTextColor = Color.BLACK; + public ComponentBase() { } @@ -38,7 +51,7 @@ public ComponentBase() { /** * Returns the used offset on the x-axis for drawing the axis or legend * labels. This offset is applied before and after the label. - * + * * @return */ public float getXOffset() { @@ -47,7 +60,7 @@ public float getXOffset() { /** * Sets the used x-axis offset for the labels on this axis. - * + * * @param xOffset */ public void setXOffset(float xOffset) { @@ -57,7 +70,7 @@ public void setXOffset(float xOffset) { /** * Returns the used offset on the x-axis for drawing the axis labels. This * offset is applied before and after the label. - * + * * @return */ public float getYOffset() { @@ -65,9 +78,11 @@ public float getYOffset() { } /** - * Sets the used x-axis offset for the labels on this axis. - * - * @param xOffset + * Sets the used y-axis offset for the labels on this axis. For the legend, + * higher offset means the legend as a whole will be placed further away + * from the top. + * + * @param yOffset */ public void setYOffset(float yOffset) { mYOffset = Utils.convertDpToPixel(yOffset); @@ -75,7 +90,7 @@ public void setYOffset(float yOffset) { /** * returns the Typeface used for the labels, returns null if none is set - * + * * @return */ public Typeface getTypeface() { @@ -84,7 +99,7 @@ public Typeface getTypeface() { /** * sets a specific Typeface for the labels - * + * * @param tf */ public void setTypeface(Typeface tf) { @@ -92,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) { @@ -108,18 +123,19 @@ 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 */ public float getTextSize() { return mTextSize; } + /** * Sets the text color to use for the labels. Make sure to use * getResources().getColor(...) when using a color from the resources. - * + * * @param color */ public void setTextColor(int color) { @@ -128,7 +144,7 @@ public void setTextColor(int color) { /** * Returns the text color that is set for the labels. - * + * * @return */ public int getTextColor() { @@ -139,7 +155,7 @@ public int getTextColor() { * Set this to true if this component should be enabled (should be drawn), * false if not. If disabled, nothing of this component will be drawn. * Default: true - * + * * @param enabled */ public void setEnabled(boolean enabled) { @@ -148,7 +164,7 @@ public void setEnabled(boolean enabled) { /** * Returns true if this comonent is enabled (should be drawn), false if not. - * + * * @return */ public boolean isEnabled() { 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 84% rename from MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java index b332761c22..8fcdee8fc1 100644 --- a/MPChartLib/src/com/github/mikephil/charting/components/LimitLine.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/LimitLine.java @@ -4,6 +4,7 @@ import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; +import android.graphics.Typeface; import com.github.mikephil.charting.utils.Utils; @@ -14,7 +15,7 @@ * * @author Philipp Jahoda */ -public class LimitLine { +public class LimitLine extends ComponentBase { /** limit / maximum (the y-value or xIndex) */ private float mLimit = 0f; @@ -25,12 +26,6 @@ public class LimitLine { /** the color of the limit line */ private int mLineColor = Color.rgb(237, 91, 91); - /** the color of the value-text */ - private int mValueTextColor = Color.BLACK; - - /** the size of the label text */ - private float mTextSize = 13f; - /** the style of the label text */ private Paint.Style mTextStyle = Paint.Style.FILL_AND_STROKE; @@ -41,11 +36,11 @@ public class LimitLine { private DashPathEffect mDashPathEffect = null; /** indicates the position of the LimitLine label */ - private LimitLabelPosition mLabelPosition = LimitLabelPosition.POS_RIGHT; + private LimitLabelPosition mLabelPosition = LimitLabelPosition.RIGHT_TOP; /** enum that indicates the position of the LimitLine label */ public enum LimitLabelPosition { - POS_LEFT, POS_RIGHT + LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM } /** @@ -161,24 +156,6 @@ public DashPathEffect getDashPathEffect() { return mDashPathEffect; } - /** - * Sets the color of the value-text that is drawn next to the LimitLine. - * - * @param color - */ - public void setTextColor(int color) { - mValueTextColor = color; - } - - /** - * Returns the color of the value-text that is drawn next to the LimitLine. - * - * @return - */ - public int getTextColor() { - return mValueTextColor; - } - /** * Sets the color of the value-text that is drawn next to the LimitLine. * Default: Paint.Style.FILL_AND_STROKE @@ -235,22 +212,4 @@ public void setLabel(String label) { public String getLabel() { return mLabel; } - - /** - * Sets the size of the label-text. - * - * @param size - */ - public void setTextSize(float size) { - mTextSize = Utils.convertDpToPixel(size); - } - - /** - * Returns the size of the label text. - * - * @return - */ - public float getTextSize() { - return mTextSize; - } } 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/main/java/com/github/mikephil/charting/components/YAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java new file mode 100644 index 0000000000..d2071ec5a8 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/YAxis.java @@ -0,0 +1,467 @@ +package com.github.mikephil.charting.components; + +import android.graphics.Color; +import android.graphics.Paint; + +import com.github.mikephil.charting.utils.Utils; + +/** + * Class representing the y-axis labels settings and its entries. Only use the setter methods to + * modify it. Do not + * access public variables directly. Be aware that not all features the YLabels class provides + * are suitable for the + * RadarChart. Customizations that affect the value range of the axis need to be applied before + * setting data for the + * chart. + * + * @author Philipp Jahoda + */ +public class YAxis extends AxisBase { + + /** + * indicates if the bottom y-label entry is drawn or not + */ + private boolean mDrawBottomYLabelEntry = true; + + /** + * indicates if the top y-label entry is drawn or not + */ + private boolean mDrawTopYLabelEntry = true; + + /** + * flag that indicates if the axis is inverted or not + */ + protected boolean mInverted = false; + + /** + * flag that indicates if the zero-line should be drawn regardless of other grid lines + */ + protected boolean mDrawZeroLine = false; + + /** + * flag indicating that auto scale min restriction should be used + */ + private boolean mUseAutoScaleRestrictionMin = false; + + /** + * flag indicating that auto scale max restriction should be used + */ + private boolean mUseAutoScaleRestrictionMax = false; + + /** + * Color of the zero line + */ + protected int mZeroLineColor = Color.GRAY; + + /** + * Width of the zero line in pixels + */ + protected float mZeroLineWidth = 1f; + + /** + * axis space from the largest value to the top in percent of the total axis range + */ + protected float mSpacePercentTop = 10f; + + /** + * axis space from the smallest value to the bottom in percent of the total axis range + */ + protected float mSpacePercentBottom = 10f; + + /** + * the position of the y-labels relative to the chart + */ + private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART; + + /** + * the horizontal offset of the y-label + */ + private float mXLabelOffset = 0.0f; + + /** + * enum for the position of the y-labels relative to the chart + */ + public enum YAxisLabelPosition { + OUTSIDE_CHART, INSIDE_CHART + } + + /** + * the side this axis object represents + */ + private AxisDependency mAxisDependency; + + /** + * the minimum width that the axis should take (in dp). + *

+ * default: 0.0 + */ + protected float mMinWidth = 0.f; + + /** + * the maximum width that the axis can take (in dp). + * use Inifinity for disabling the maximum + * default: Float.POSITIVE_INFINITY (no maximum specified) + */ + protected float mMaxWidth = Float.POSITIVE_INFINITY; + + /** + * Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT. + * + * @author Philipp Jahoda + */ + public enum AxisDependency { + LEFT, RIGHT + } + + public YAxis() { + super(); + + // default left + this.mAxisDependency = AxisDependency.LEFT; + this.mYOffset = 0f; + } + + public YAxis(AxisDependency position) { + super(); + this.mAxisDependency = position; + this.mYOffset = 0f; + } + + public AxisDependency getAxisDependency() { + return mAxisDependency; + } + + /** + * @return the minimum width that the axis should take (in dp). + */ + public float getMinWidth() { + return mMinWidth; + } + + /** + * Sets the minimum width that the axis should take (in dp). + * + * @param minWidth + */ + public void setMinWidth(float minWidth) { + mMinWidth = minWidth; + } + + /** + * @return the maximum width that the axis can take (in dp). + */ + public float getMaxWidth() { + return mMaxWidth; + } + + /** + * Sets the maximum width that the axis can take (in dp). + * + * @param maxWidth + */ + public void setMaxWidth(float maxWidth) { + mMaxWidth = maxWidth; + } + + /** + * returns the position of the y-labels + */ + public YAxisLabelPosition getLabelPosition() { + return mPosition; + } + + /** + * sets the position of the y-labels + * + * @param pos + */ + public void setPosition(YAxisLabelPosition pos) { + mPosition = pos; + } + + /** + * returns the horizontal offset of the y-label + */ + public float getLabelXOffset() { + return mXLabelOffset; + } + + /** + * sets the horizontal offset of the y-label + * + * @param xOffset + */ + public void setLabelXOffset(float xOffset) { + mXLabelOffset = xOffset; + } + + /** + * returns true if drawing the top y-axis label entry is enabled + * + * @return + */ + public boolean isDrawTopYLabelEntryEnabled() { + return mDrawTopYLabelEntry; + } + + /** + * returns true if drawing the bottom y-axis label entry is enabled + * + * @return + */ + public boolean isDrawBottomYLabelEntryEnabled() { + return mDrawBottomYLabelEntry; + } + + /** + * set this to true to enable drawing the top y-label entry. Disabling this can be helpful + * when the top y-label and + * left x-label interfere with each other. default: true + * + * @param enabled + */ + public void setDrawTopYLabelEntry(boolean enabled) { + mDrawTopYLabelEntry = enabled; + } + + /** + * If this is set to true, the y-axis is inverted which means that low values are on top of + * the chart, high values + * on bottom. + * + * @param enabled + */ + public void setInverted(boolean enabled) { + mInverted = enabled; + } + + /** + * If this returns true, the y-axis is inverted. + * + * @return + */ + public boolean isInverted() { + return mInverted; + } + + /** + * This method is deprecated. + * Use setAxisMinimum(...) / setAxisMaximum(...) instead. + * + * @param startAtZero + */ + @Deprecated + public void setStartAtZero(boolean startAtZero) { + if (startAtZero) + setAxisMinimum(0f); + else + resetAxisMinimum(); + } + + /** + * Sets the top axis space in percent of the full range. Default 10f + * + * @param percent + */ + public void setSpaceTop(float percent) { + mSpacePercentTop = percent; + } + + /** + * Returns the top axis space in percent of the full range. Default 10f + * + * @return + */ + public float getSpaceTop() { + return mSpacePercentTop; + } + + /** + * Sets the bottom axis space in percent of the full range. Default 10f + * + * @param percent + */ + public void setSpaceBottom(float percent) { + mSpacePercentBottom = percent; + } + + /** + * Returns the bottom axis space in percent of the full range. Default 10f + * + * @return + */ + public float getSpaceBottom() { + return mSpacePercentBottom; + } + + public boolean isDrawZeroLineEnabled() { + return mDrawZeroLine; + } + + /** + * Set this to true to draw the zero-line regardless of weather other + * grid-lines are enabled or not. Default: false + * + * @param mDrawZeroLine + */ + public void setDrawZeroLine(boolean mDrawZeroLine) { + this.mDrawZeroLine = mDrawZeroLine; + } + + public int getZeroLineColor() { + return mZeroLineColor; + } + + /** + * Sets the color of the zero line + * + * @param color + */ + public void setZeroLineColor(int color) { + mZeroLineColor = color; + } + + public float getZeroLineWidth() { + return mZeroLineWidth; + } + + /** + * Sets the width of the zero line in dp + * + * @param width + */ + public void setZeroLineWidth(float width) { + this.mZeroLineWidth = Utils.convertDpToPixel(width); + } + + /** + * This is for normal (not horizontal) charts horizontal spacing. + * + * @param p + * @return + */ + public float getRequiredWidthSpace(Paint p) { + + p.setTextSize(mTextSize); + + String label = getLongestLabel(); + float width = (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f; + + float minWidth = getMinWidth(); + float maxWidth = getMaxWidth(); + + if (minWidth > 0.f) + minWidth = Utils.convertDpToPixel(minWidth); + + if (maxWidth > 0.f && maxWidth != Float.POSITIVE_INFINITY) + maxWidth = Utils.convertDpToPixel(maxWidth); + + width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width)); + + return width; + } + + /** + * This is for HorizontalBarChart vertical spacing. + * + * @param p + * @return + */ + public float getRequiredHeightSpace(Paint p) { + + p.setTextSize(mTextSize); + + String label = getLongestLabel(); + return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f; + } + + /** + * Returns true if this axis needs horizontal offset, false if no offset is needed. + * + * @return + */ + public boolean needsOffset() { + if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition + .OUTSIDE_CHART) + return true; + else + return false; + } + + /** + * Returns true if autoscale restriction for axis min value is enabled + */ + @Deprecated + public boolean isUseAutoScaleMinRestriction( ) { + return mUseAutoScaleRestrictionMin; + } + + /** + * Sets autoscale restriction for axis min value as enabled/disabled + */ + @Deprecated + public void setUseAutoScaleMinRestriction( boolean isEnabled ) { + mUseAutoScaleRestrictionMin = isEnabled; + } + + /** + * Returns true if autoscale restriction for axis max value is enabled + */ + @Deprecated + public boolean isUseAutoScaleMaxRestriction() { + return mUseAutoScaleRestrictionMax; + } + + /** + * Sets autoscale restriction for axis max value as enabled/disabled + */ + @Deprecated + public void setUseAutoScaleMaxRestriction( boolean isEnabled ) { + mUseAutoScaleRestrictionMax = isEnabled; + } + + + @Override + public void calculate(float dataMin, float dataMax) { + + float min = dataMin; + float max = dataMax; + + // Make sure max is greater than min + // Discussion: https://github.com/danielgindi/Charts/pull/3650#discussion_r221409991 + if (min > max) + { + if (mCustomAxisMax && mCustomAxisMin) + { + float t = min; + min = max; + max = t; + } + else if (mCustomAxisMax) + { + min = max < 0f ? max * 1.5f : max * 0.5f; + } + else if (mCustomAxisMin) + { + max = min < 0f ? min * 0.5f : min * 1.5f; + } + } + + float range = Math.abs(max - min); + + // in case all values are equal + if (range == 0f) { + max = max + 1f; + min = min - 1f; + } + + // recalculate + range = Math.abs(max - min); + + // calc extra spacing + this.mAxisMinimum = mCustomAxisMin ? this.mAxisMinimum : min - (range / 100f) * getSpaceBottom(); + this.mAxisMaximum = mCustomAxisMax ? this.mAxisMaximum : max + (range / 100f) * getSpaceTop(); + + this.mAxisRange = Math.abs(this.mAxisMinimum - this.mAxisMaximum); + } +} 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/main/java/com/github/mikephil/charting/data/BarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java new file mode 100644 index 0000000000..e65638805b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarDataSet.java @@ -0,0 +1,299 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Color; + +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; +import com.github.mikephil.charting.utils.Fill; + +import java.util.ArrayList; +import java.util.List; + +public class BarDataSet extends BarLineScatterCandleBubbleDataSet implements IBarDataSet { + + /** + * the maximum number of bars that are stacked upon each other, this value + * is calculated from the Entries that are added to the DataSet + */ + private int mStackSize = 1; + + /** + * the color used for drawing the bar shadows + */ + private int mBarShadowColor = Color.rgb(215, 215, 215); + + private float mBarBorderWidth = 0.0f; + + private int mBarBorderColor = Color.BLACK; + + /** + * the alpha value used to draw the highlight indicator bar + */ + private int mHighLightAlpha = 120; + + /** + * the overall entry count, including counting each stack-value individually + */ + private int mEntryCountStacks = 0; + + /** + * array of labels used to describe the different values of the stacked bars + */ + private String[] mStackLabels = new String[]{}; + + protected List mFills = null; + + public BarDataSet(List yVals, String label) { + super(yVals, label); + + mHighLightColor = Color.rgb(0, 0, 0); + + calcStackSize(yVals); + calcEntryCountIncludingStacks(yVals); + } + + @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; + } + + protected void copy(BarDataSet barDataSet) { + super.copy(barDataSet); + barDataSet.mStackSize = mStackSize; + barDataSet.mBarShadowColor = mBarShadowColor; + barDataSet.mBarBorderWidth = mBarBorderWidth; + barDataSet.mStackLabels = mStackLabels; + barDataSet.mHighLightAlpha = mHighLightAlpha; + } + + @Override + public List getFills() { + return mFills; + } + + @Override + public Fill getFill(int index) { + return mFills.get(index % mFills.size()); + } + + /** + * This method is deprecated. + * Use getFills() instead. + */ + @Deprecated + public List 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; + } + + /** + * Calculates the total number of entries this DataSet represents, including + * stacks. All values belonging to a stack are calculated separately. + */ + private void calcEntryCountIncludingStacks(List yVals) { + + mEntryCountStacks = 0; + + for (int i = 0; i < yVals.size(); i++) { + + float[] vals = yVals.get(i).getYVals(); + + if (vals == null) + mEntryCountStacks++; + else + mEntryCountStacks += vals.length; + } + } + + /** + * calculates the maximum stacksize that occurs in the Entries array of this + * DataSet + */ + private void calcStackSize(List yVals) { + + for (int i = 0; i < yVals.size(); i++) { + + float[] vals = yVals.get(i).getYVals(); + + if (vals != null && vals.length > mStackSize) + mStackSize = vals.length; + } + } + + @Override + protected void calcMinMax(BarEntry e) { + + if (e != null && !Float.isNaN(e.getY())) { + + if (e.getYVals() == null) { + + if (e.getY() < mYMin) + mYMin = e.getY(); + + if (e.getY() > mYMax) + mYMax = e.getY(); + } else { + + if (-e.getNegativeSum() < mYMin) + mYMin = -e.getNegativeSum(); + + if (e.getPositiveSum() > mYMax) + mYMax = e.getPositiveSum(); + } + + calcMinMaxX(e); + } + } + + @Override + public int getStackSize() { + return mStackSize; + } + + @Override + public boolean isStacked() { + return mStackSize > 1 ? true : false; + } + + /** + * returns the overall entry count, including counting each stack-value + * individually + * + * @return + */ + public int getEntryCountStacks() { + return mEntryCountStacks; + } + + /** + * Sets the color used for drawing the bar-shadows. The bar shadows is a + * surface behind the bar that indicates the maximum value. Don't for get to + * use getResources().getColor(...) to set this. Or Color.rgb(...). + * + * @param color + */ + public void setBarShadowColor(int color) { + mBarShadowColor = color; + } + + @Override + public int getBarShadowColor() { + return mBarShadowColor; + } + + /** + * Sets the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + public void setBarBorderWidth(float width) { + mBarBorderWidth = width; + } + + /** + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + @Override + public float getBarBorderWidth() { + return mBarBorderWidth; + } + + /** + * Sets the color drawing borders around the bars. + * + * @return + */ + public void setBarBorderColor(int color) { + mBarBorderColor = color; + } + + /** + * Returns the color drawing borders around the bars. + * + * @return + */ + @Override + public int getBarBorderColor() { + return mBarBorderColor; + } + + /** + * Set the alpha value (transparency) that is used for drawing the highlight + * indicator bar. min = 0 (fully transparent), max = 255 (fully opaque) + * + * @param alpha + */ + public void setHighLightAlpha(int alpha) { + mHighLightAlpha = alpha; + } + + @Override + public int getHighLightAlpha() { + return mHighLightAlpha; + } + + /** + * Sets labels for different values of bar-stacks, in case there are one. + * + * @param labels + */ + public void setStackLabels(String[] labels) { + mStackLabels = labels; + } + + @Override + public String[] getStackLabels() { + return mStackLabels; + } +} 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/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java new file mode 100644 index 0000000000..c09eadec9b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleData.java @@ -0,0 +1,27 @@ + +package com.github.mikephil.charting.data; + +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; + +import java.util.List; + +/** + * Baseclass for all Line, Bar, Scatter, Candle and Bubble data. + * + * @author Philipp Jahoda + */ +public abstract class BarLineScatterCandleBubbleData> + extends ChartData { + + public BarLineScatterCandleBubbleData() { + super(); + } + + public BarLineScatterCandleBubbleData(T... sets) { + super(sets); + } + + public BarLineScatterCandleBubbleData(List sets) { + super(sets); + } +} diff --git a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java similarity index 53% rename from MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java index b0578704a8..eab6dccc55 100644 --- a/MPChartLib/src/com/github/mikephil/charting/data/BarLineScatterCandleDataSet.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BarLineScatterCandleBubbleDataSet.java @@ -3,19 +3,25 @@ import android.graphics.Color; +import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet; + import java.util.List; /** * Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart. - * + * * @author Philipp Jahoda */ -public abstract class BarLineScatterCandleDataSet extends DataSet { +public abstract class BarLineScatterCandleBubbleDataSet + extends DataSet + implements IBarLineScatterCandleBubbleDataSet { - /** default highlight color */ + /** + * default highlight color + */ protected int mHighLightColor = Color.rgb(255, 187, 115); - public BarLineScatterCandleDataSet(List yVals, String label) { + public BarLineScatterCandleBubbleDataSet(List yVals, String label) { super(yVals, label); } @@ -23,19 +29,20 @@ public BarLineScatterCandleDataSet(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) { mHighLightColor = color; } - /** - * Returns the color that is used for drawing the highlight indicators. - * - * @return - */ + @Override public int getHighLightColor() { return mHighLightColor; } + + protected void copy(BarLineScatterCandleBubbleDataSet barLineScatterCandleBubbleDataSet) { + super.copy(barLineScatterCandleBubbleDataSet); + barLineScatterCandleBubbleDataSet.mHighLightColor = mHighLightColor; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java new file mode 100644 index 0000000000..7e7445cac7 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.java @@ -0,0 +1,506 @@ +package com.github.mikephil.charting.data; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.formatter.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; +import java.util.List; + +/** + * Created by Philipp Jahoda on 21/10/15. + * This is the base dataset of all DataSets. It's purpose is to implement critical methods + * provided by the IDataSet interface. + */ +public abstract class BaseDataSet implements IDataSet { + + /** + * List representing all colors that are used for this DataSet + */ + protected List mColors = null; + + /** + * List representing all colors that are used for drawing the actual values for this DataSet + */ + protected List mValueColors = null; + + /** + * label that describes the DataSet or the data the DataSet represents + */ + private String mLabel = "DataSet"; + + /** + * this specifies which axis this DataSet should be plotted against + */ + protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT; + + /** + * if true, value highlightning is enabled + */ + protected boolean mHighlightEnabled = true; + + /** + * custom formatter that is used instead of the auto-formatter if set + */ + protected transient 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 + */ + protected float mValueTextSize = 17f; + + /** + * flag that indicates if the DataSet is visible or not + */ + protected boolean mVisible = true; + + /** + * Default constructor. + */ + public BaseDataSet() { + mColors = new ArrayList(); + mValueColors = new ArrayList(); + + // default color + mColors.add(Color.rgb(140, 234, 255)); + mValueColors.add(Color.BLACK); + } + + /** + * Constructor with label. + * + * @param label + */ + public BaseDataSet(String label) { + this(); + this.mLabel = label; + } + + /** + * Use this method to tell the data set that the underlying data has changed. + */ + public void notifyDataSetChanged() { + calcMinMax(); + } + + + /** + * ###### ###### COLOR GETTING RELATED METHODS ##### ###### + */ + + @Override + public List getColors() { + return mColors; + } + + public List getValueColors() { + return mValueColors; + } + + @Override + public int getColor() { + return mColors.get(0); + } + + @Override + public int getColor(int index) { + return mColors.get(index % mColors.size()); + } + + /** + * ###### ###### COLOR SETTING RELATED METHODS ##### ###### + */ + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. If you are using colors from the resources, + * make sure that the colors are already prepared (by calling + * getResources().getColor(...)) before adding them to the DataSet. + * + * @param colors + */ + public void setColors(List colors) { + this.mColors = colors; + } + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. If you are using colors from the resources, + * make sure that the colors are already prepared (by calling + * getResources().getColor(...)) before adding them to the DataSet. + * + * @param colors + */ + public void setColors(int... colors) { + this.mColors = ColorTemplate.createColors(colors); + } + + /** + * Sets the colors that should be used fore this DataSet. Colors are reused + * as soon as the number of Entries the DataSet represents is higher than + * the size of the colors array. You can use + * "new int[] { R.color.red, R.color.green, ... }" to provide colors for + * this method. Internally, the colors are resolved using + * getResources().getColor(...) + * + * @param colors + */ + public void setColors(int[] colors, Context c) { + + if (mColors == null) { + mColors = new ArrayList<>(); + } + + mColors.clear(); + + for (int color : colors) { + mColors.add(c.getResources().getColor(color)); + } + } + + /** + * Adds a new color to the colors array of the DataSet. + * + * @param color + */ + public void addColor(int color) { + if (mColors == null) + mColors = new ArrayList(); + mColors.add(color); + } + + /** + * Sets the one and ONLY color that should be used for this DataSet. + * Internally, this recreates the colors array and adds the specified color. + * + * @param color + */ + public void setColor(int color) { + resetColors(); + mColors.add(color); + } + + /** + * Sets a color with a specific alpha value. + * + * @param color + * @param alpha from 0-255 + */ + public void setColor(int color, int alpha) { + setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))); + } + + /** + * Sets colors with a specific alpha value. + * + * @param colors + * @param alpha + */ + public void setColors(int[] colors, int alpha) { + resetColors(); + for (int color : colors) { + addColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color))); + } + } + + /** + * Resets all colors of this DataSet and recreates the colors array. + */ + public void resetColors() { + if (mColors == null) { + mColors = new ArrayList(); + } + mColors.clear(); + } + + /** + * ###### ###### OTHER STYLING RELATED METHODS ##### ###### + */ + + @Override + public void setLabel(String label) { + mLabel = label; + } + + @Override + public String getLabel() { + return mLabel; + } + + @Override + public void setHighlightEnabled(boolean enabled) { + mHighlightEnabled = enabled; + } + + @Override + public boolean isHighlightEnabled() { + return mHighlightEnabled; + } + + @Override + public void setValueFormatter(IValueFormatter f) { + + if (f == null) + return; + else + mValueFormatter = f; + } + + @Override + 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(); + mValueColors.add(color); + } + + @Override + public void setValueTextColors(List colors) { + mValueColors = colors; + } + + @Override + public void setValueTypeface(Typeface tf) { + mValueTypeface = tf; + } + + @Override + public void setValueTextSize(float size) { + mValueTextSize = Utils.convertDpToPixel(size); + } + + @Override + public int getValueTextColor() { + return mValueColors.get(0); + } + + @Override + public int getValueTextColor(int index) { + return mValueColors.get(index % mValueColors.size()); + } + + @Override + public Typeface getValueTypeface() { + return mValueTypeface; + } + + @Override + public float getValueTextSize() { + return mValueTextSize; + } + + public void setForm(Legend.LegendForm form) { + mForm = form; + } + + @Override + public Legend.LegendForm getForm() { + return mForm; + } + + public void setFormSize(float formSize) { + mFormSize = formSize; + } + + @Override + public float getFormSize() { + return mFormSize; + } + + public void setFormLineWidth(float formLineWidth) { + mFormLineWidth = formLineWidth; + } + + @Override + public float getFormLineWidth() { + return mFormLineWidth; + } + + public void setFormLineDashEffect(DashPathEffect dashPathEffect) { + mFormLineDashEffect = dashPathEffect; + } + + @Override + public DashPathEffect getFormLineDashEffect() { + return mFormLineDashEffect; + } + + @Override + public void setDrawValues(boolean enabled) { + this.mDrawValues = enabled; + } + + @Override + public boolean isDrawValuesEnabled() { + return mDrawValues; + } + + @Override + public void setDrawIcons(boolean enabled) { + mDrawIcons = enabled; + } + + @Override + public boolean isDrawIconsEnabled() { + return mDrawIcons; + } + + @Override + public void setIconsOffset(MPPointF offsetDp) { + + mIconsOffset.x = offsetDp.x; + mIconsOffset.y = offsetDp.y; + } + + @Override + public MPPointF getIconsOffset() { + return mIconsOffset; + } + + @Override + public void setVisible(boolean visible) { + mVisible = visible; + } + + @Override + public boolean isVisible() { + return mVisible; + } + + @Override + public YAxis.AxisDependency getAxisDependency() { + return mAxisDependency; + } + + @Override + public void setAxisDependency(YAxis.AxisDependency dependency) { + mAxisDependency = dependency; + } + + + /** + * ###### ###### DATA RELATED METHODS ###### ###### + */ + + @Override + public int getIndexInEntries(int xIndex) { + + for (int i = 0; i < getEntryCount(); i++) { + if (xIndex == getEntryForIndex(i).getX()) + return i; + } + + return -1; + } + + @Override + public boolean removeFirst() { + + if (getEntryCount() > 0) { + + T entry = getEntryForIndex(0); + return removeEntry(entry); + } else + return false; + } + + @Override + public boolean removeLast() { + + if (getEntryCount() > 0) { + + T e = getEntryForIndex(getEntryCount() - 1); + return removeEntry(e); + } else + return false; + } + + @Override + public boolean removeEntryByXValue(float xValue) { + + T e = getEntryForXValue(xValue, Float.NaN); + return removeEntry(e); + } + + @Override + public boolean removeEntry(int index) { + + T e = getEntryForIndex(index); + return removeEntry(e); + } + + @Override + public boolean contains(T e) { + + for (int i = 0; i < getEntryCount(); i++) { + if (getEntryForIndex(i).equals(e)) + return true; + } + + return false; + } + + protected void copy(BaseDataSet baseDataSet) { + baseDataSet.mAxisDependency = mAxisDependency; + baseDataSet.mColors = mColors; + baseDataSet.mDrawIcons = mDrawIcons; + baseDataSet.mDrawValues = mDrawValues; + baseDataSet.mForm = mForm; + baseDataSet.mFormLineDashEffect = mFormLineDashEffect; + baseDataSet.mFormLineWidth = mFormLineWidth; + baseDataSet.mFormSize = mFormSize; + baseDataSet.mHighlightEnabled = mHighlightEnabled; + baseDataSet.mIconsOffset = mIconsOffset; + baseDataSet.mValueColors = mValueColors; + baseDataSet.mValueFormatter = mValueFormatter; + baseDataSet.mValueColors = mValueColors; + baseDataSet.mValueTextSize = mValueTextSize; + baseDataSet.mVisible = mVisible; + } +} 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/main/java/com/github/mikephil/charting/data/CandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java new file mode 100644 index 0000000000..dcd5b76cea --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/CandleDataSet.java @@ -0,0 +1,295 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Paint; + +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.utils.ColorTemplate; +import com.github.mikephil.charting.utils.Utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * DataSet for the CandleStickChart. + * + * @author Philipp Jahoda + */ +public class CandleDataSet extends LineScatterCandleRadarDataSet implements ICandleDataSet { + + /** + * the width of the shadow of the candle + */ + private float mShadowWidth = 3f; + + /** + * should the candle bars show? + * when false, only "ticks" will show + *

+ * - default: true + */ + private boolean mShowCandleBar = true; + + /** + * the space between the candle entries, default 0.1f (10%) + */ + private float mBarSpace = 0.1f; + + /** + * use candle color for the shadow + */ + private boolean mShadowColorSameAsCandle = false; + + /** + * paint style when open < close + * increasing candlesticks are traditionally hollow + */ + protected Paint.Style mIncreasingPaintStyle = Paint.Style.STROKE; + + /** + * paint style when open > close + * descreasing candlesticks are traditionally filled + */ + protected Paint.Style mDecreasingPaintStyle = Paint.Style.FILL; + + /** + * color for open == close + */ + protected int mNeutralColor = ColorTemplate.COLOR_SKIP; + + /** + * color for open < close + */ + protected int mIncreasingColor = ColorTemplate.COLOR_SKIP; + + /** + * color for open > close + */ + protected int mDecreasingColor = ColorTemplate.COLOR_SKIP; + + /** + * shadow line color, set -1 for backward compatibility and uses default + * color + */ + protected int mShadowColor = ColorTemplate.COLOR_SKIP; + + public CandleDataSet(List 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()); + } + CandleDataSet copied = new CandleDataSet(entries, getLabel()); + copy(copied); + return copied; + } + + protected void copy(CandleDataSet candleDataSet) { + super.copy(candleDataSet); + candleDataSet.mShadowWidth = mShadowWidth; + candleDataSet.mShowCandleBar = mShowCandleBar; + candleDataSet.mBarSpace = mBarSpace; + candleDataSet.mShadowColorSameAsCandle = mShadowColorSameAsCandle; + candleDataSet.mHighLightColor = mHighLightColor; + candleDataSet.mIncreasingPaintStyle = mIncreasingPaintStyle; + candleDataSet.mDecreasingPaintStyle = mDecreasingPaintStyle; + candleDataSet.mNeutralColor = mNeutralColor; + candleDataSet.mIncreasingColor = mIncreasingColor; + candleDataSet.mDecreasingColor = mDecreasingColor; + candleDataSet.mShadowColor = mShadowColor; + } + + @Override + protected void calcMinMax(CandleEntry e) { + + if (e.getLow() < mYMin) + mYMin = e.getLow(); + + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); + + calcMinMaxX(e); + } + + @Override + protected void calcMinMaxY(CandleEntry e) { + + if (e.getHigh() < mYMin) + mYMin = e.getHigh(); + + if (e.getHigh() > mYMax) + mYMax = e.getHigh(); + + if (e.getLow() < mYMin) + mYMin = e.getLow(); + + if (e.getLow() > mYMax) + mYMax = e.getLow(); + } + + /** + * Sets the space that is left out on the left and right side of each + * candle, default 0.1f (10%), max 0.45f, min 0f + * + * @param space + */ + public void setBarSpace(float space) { + + if (space < 0f) + space = 0f; + if (space > 0.45f) + space = 0.45f; + + mBarSpace = space; + } + + @Override + public float getBarSpace() { + return mBarSpace; + } + + /** + * Sets the width of the candle-shadow-line in pixels. Default 3f. + * + * @param width + */ + public void setShadowWidth(float width) { + mShadowWidth = Utils.convertDpToPixel(width); + } + + @Override + public float getShadowWidth() { + return mShadowWidth; + } + + /** + * Sets whether the candle bars should show? + * + * @param showCandleBar + */ + public void setShowCandleBar(boolean showCandleBar) { + mShowCandleBar = showCandleBar; + } + + @Override + public boolean getShowCandleBar() { + return mShowCandleBar; + } + + // TODO + /** + * It is necessary to implement ColorsList class that will encapsulate + * colors list functionality, because It's wrong to copy paste setColor, + * addColor, ... resetColors for each time when we want to add a coloring + * options for one of objects + * + * @author Mesrop + */ + + /** BELOW THIS COLOR HANDLING */ + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open == close. + * + * @param color + */ + public void setNeutralColor(int color) { + mNeutralColor = color; + } + + @Override + public int getNeutralColor() { + return mNeutralColor; + } + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open <= close. + * + * @param color + */ + public void setIncreasingColor(int color) { + mIncreasingColor = color; + } + + @Override + public int getIncreasingColor() { + return mIncreasingColor; + } + + /** + * Sets the one and ONLY color that should be used for this DataSet when + * open > close. + * + * @param color + */ + public void setDecreasingColor(int color) { + mDecreasingColor = color; + } + + @Override + public int getDecreasingColor() { + return mDecreasingColor; + } + + @Override + public Paint.Style getIncreasingPaintStyle() { + return mIncreasingPaintStyle; + } + + /** + * Sets paint style when open < close + * + * @param paintStyle + */ + public void setIncreasingPaintStyle(Paint.Style paintStyle) { + this.mIncreasingPaintStyle = paintStyle; + } + + @Override + public Paint.Style getDecreasingPaintStyle() { + return mDecreasingPaintStyle; + } + + /** + * Sets paint style when open > close + * + * @param decreasingPaintStyle + */ + public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) { + this.mDecreasingPaintStyle = decreasingPaintStyle; + } + + @Override + public int getShadowColor() { + return mShadowColor; + } + + /** + * Sets shadow color for all entries + * + * @param shadowColor + */ + public void setShadowColor(int shadowColor) { + this.mShadowColor = shadowColor; + } + + @Override + public boolean getShadowColorSameAsCandle() { + return mShadowColorSameAsCandle; + } + + /** + * Sets shadow color to be the same color as the candle color + * + * @param shadowColorSameAsCandle + */ + public void setShadowColorSameAsCandle(boolean shadowColorSameAsCandle) { + this.mShadowColorSameAsCandle = shadowColorSameAsCandle; + } +} 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/main/java/com/github/mikephil/charting/data/ChartData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java new file mode 100644 index 0000000000..bfc5ed7016 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/ChartData.java @@ -0,0 +1,821 @@ + +package com.github.mikephil.charting.data; + +import android.graphics.Typeface; +import android.util.Log; + +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class that holds all relevant data that represents the chart. That involves + * at least one (or more) DataSets, and an array of x-values. + * + * @author Philipp Jahoda + */ +public abstract class ChartData> { + + /** + * maximum y-value in the value array across all axes + */ + protected float mYMax = -Float.MAX_VALUE; + + /** + * the minimum y-value in the value array across all axes + */ + protected float mYMin = Float.MAX_VALUE; + + /** + * maximum x-value in the value array + */ + protected float mXMax = -Float.MAX_VALUE; + + /** + * minimum x-value in the value array + */ + protected float mXMin = Float.MAX_VALUE; + + + protected float mLeftAxisMax = -Float.MAX_VALUE; + + protected float mLeftAxisMin = Float.MAX_VALUE; + + protected float mRightAxisMax = -Float.MAX_VALUE; + + protected float mRightAxisMin = Float.MAX_VALUE; + + /** + * array that holds all DataSets the ChartData object represents + */ + protected List mDataSets; + + /** + * Default constructor. + */ + public ChartData() { + mDataSets = new ArrayList(); + } + + /** + * Constructor taking single or multiple DataSet objects. + * + * @param dataSets + */ + public ChartData(T... dataSets) { + mDataSets = arrayToList(dataSets); + notifyDataChanged(); + } + + /** + * Created because Arrays.asList(...) does not support modification. + * + * @param array + * @return + */ + private List arrayToList(T[] array) { + + List list = new ArrayList<>(); + + for (T set : array) { + list.add(set); + } + + return list; + } + + /** + * constructor for chart data + * + * @param sets the dataset array + */ + public ChartData(List sets) { + this.mDataSets = sets; + notifyDataChanged(); + } + + /** + * Call this method to let the ChartData know that the underlying data has + * changed. Calling this performs all necessary recalculations needed when + * the contained data has changed. + */ + public void notifyDataChanged() { + calcMinMax(); + } + + /** + * Calc minimum and maximum y-values over all DataSets. + * Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax. + * + * @param fromX the x-value to start the calculation from + * @param toX the x-value to which the calculation should be performed + */ + public void calcMinMaxY(float fromX, float toX) { + + for (T set : mDataSets) { + set.calcMinMaxY(fromX, toX); + } + + // apply the new data + calcMinMax(); + } + + /** + * Calc minimum and maximum values (both x and y) over all DataSets. + */ + protected void calcMinMax() { + + if (mDataSets == null) + return; + + mYMax = -Float.MAX_VALUE; + mYMin = Float.MAX_VALUE; + mXMax = -Float.MAX_VALUE; + mXMin = Float.MAX_VALUE; + + for (T set : mDataSets) { + calcMinMax(set); + } + + mLeftAxisMax = -Float.MAX_VALUE; + mLeftAxisMin = Float.MAX_VALUE; + mRightAxisMax = -Float.MAX_VALUE; + mRightAxisMin = Float.MAX_VALUE; + + // left axis + T firstLeft = getFirstLeft(mDataSets); + + if (firstLeft != null) { + + mLeftAxisMax = firstLeft.getYMax(); + mLeftAxisMin = firstLeft.getYMin(); + + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) { + if (dataSet.getYMin() < mLeftAxisMin) + mLeftAxisMin = dataSet.getYMin(); + + if (dataSet.getYMax() > mLeftAxisMax) + mLeftAxisMax = dataSet.getYMax(); + } + } + } + + // right axis + T firstRight = getFirstRight(mDataSets); + + if (firstRight != null) { + + mRightAxisMax = firstRight.getYMax(); + mRightAxisMin = firstRight.getYMin(); + + for (T dataSet : mDataSets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) { + if (dataSet.getYMin() < mRightAxisMin) + mRightAxisMin = dataSet.getYMin(); + + if (dataSet.getYMax() > mRightAxisMax) + mRightAxisMax = dataSet.getYMax(); + } + } + } + } + + /** ONLY GETTERS AND SETTERS BELOW THIS */ + + /** + * returns the number of LineDataSets this object contains + * + * @return + */ + public int getDataSetCount() { + if (mDataSets == null) + return 0; + return mDataSets.size(); + } + + /** + * Returns the smallest y-value the data object contains. + * + * @return + */ + public float getYMin() { + return mYMin; + } + + /** + * Returns the minimum y-value for the specified axis. + * + * @param axis + * @return + */ + public float getYMin(AxisDependency axis) { + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMin == Float.MAX_VALUE) { + return mRightAxisMin; + } else + return mLeftAxisMin; + } else { + if (mRightAxisMin == Float.MAX_VALUE) { + return mLeftAxisMin; + } else + return mRightAxisMin; + } + } + + /** + * Returns the greatest y-value the data object contains. + * + * @return + */ + public float getYMax() { + return mYMax; + } + + /** + * Returns the maximum y-value for the specified axis. + * + * @param axis + * @return + */ + public float getYMax(AxisDependency axis) { + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMax == -Float.MAX_VALUE) { + return mRightAxisMax; + } else + return mLeftAxisMax; + } else { + if (mRightAxisMax == -Float.MAX_VALUE) { + return mLeftAxisMax; + } else + return mRightAxisMax; + } + } + + /** + * Returns the minimum x-value this data object contains. + * + * @return + */ + public float getXMin() { + return mXMin; + } + + /** + * Returns the maximum x-value this data object contains. + * + * @return + */ + public float getXMax() { + return mXMax; + } + + /** + * Returns all DataSet objects this ChartData object holds. + * + * @return + */ + public List getDataSets() { + return mDataSets; + } + + /** + * Retrieve the index of a DataSet with a specific label from the ChartData. + * Search can be case sensitive or not. IMPORTANT: This method does + * calculations at runtime, do not over-use in performance critical + * situations. + * + * @param dataSets the DataSet array to search + * @param label + * @param ignorecase if true, the search is not case-sensitive + * @return + */ + protected int getDataSetIndexByLabel(List dataSets, String label, + boolean ignorecase) { + + if (ignorecase) { + for (int i = 0; i < dataSets.size(); i++) + if (label.equalsIgnoreCase(dataSets.get(i).getLabel())) + return i; + } else { + for (int i = 0; i < dataSets.size(); i++) + if (label.equals(dataSets.get(i).getLabel())) + return i; + } + + return -1; + } + + /** + * Returns the labels of all DataSets as a string array. + * + * @return + */ + public String[] getDataSetLabels() { + + String[] types = new String[mDataSets.size()]; + + for (int i = 0; i < mDataSets.size(); i++) { + types[i] = mDataSets.get(i).getLabel(); + } + + return types; + } + + /** + * Get the Entry for a corresponding highlight object + * + * @param highlight + * @return the entry that is highlighted + */ + public Entry getEntryForHighlight(Highlight highlight) { + if (highlight.getDataSetIndex() >= mDataSets.size()) + return null; + else { + return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY()); + } + } + + /** + * Returns the DataSet object with the given label. Search can be case + * sensitive or not. IMPORTANT: This method does calculations at runtime. + * Use with care in performance critical situations. + * + * @param label + * @param ignorecase + * @return + */ + public T getDataSetByLabel(String label, boolean ignorecase) { + + int index = getDataSetIndexByLabel(mDataSets, label, ignorecase); + + if (index < 0 || index >= mDataSets.size()) + return null; + else + return mDataSets.get(index); + } + + public T getDataSetByIndex(int index) { + + if (mDataSets == null || index < 0 || index >= mDataSets.size()) + return null; + + return mDataSets.get(index); + } + + /** + * Adds a DataSet dynamically. + * + * @param d + */ + public void addDataSet(T d) { + + if (d == null) + return; + + calcMinMax(d); + + mDataSets.add(d); + } + + /** + * Removes the given DataSet from this data object. Also recalculates all + * minimum and maximum values. Returns true if a DataSet was removed, false + * if no DataSet could be removed. + * + * @param d + */ + public boolean removeDataSet(T d) { + + if (d == null) + return false; + + boolean removed = mDataSets.remove(d); + + // if a DataSet was removed + if (removed) { + notifyDataChanged(); + } + + return removed; + } + + /** + * Removes the DataSet at the given index in the DataSet array from the data + * object. Also recalculates all minimum and maximum values. Returns true if + * a DataSet was removed, false if no DataSet could be removed. + * + * @param index + */ + public boolean removeDataSet(int index) { + + if (index >= mDataSets.size() || index < 0) + return false; + + T set = mDataSets.get(index); + return removeDataSet(set); + } + + /** + * Adds an Entry to the DataSet at the specified index. + * Entries are added to the end of the list. + * + * @param e + * @param dataSetIndex + */ + public void addEntry(Entry e, int dataSetIndex) { + + if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) { + + IDataSet set = mDataSets.get(dataSetIndex); + // add the entry to the dataset + if (!set.addEntry(e)) + return; + + calcMinMax(e, set.getAxisDependency()); + + } else { + Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low."); + } + } + + /** + * Adjusts the current minimum and maximum values based on the provided Entry object. + * + * @param e + * @param axis + */ + protected void calcMinMax(Entry e, AxisDependency axis) { + + if (mYMax < e.getY()) + mYMax = e.getY(); + if (mYMin > e.getY()) + mYMin = e.getY(); + + if (mXMax < e.getX()) + mXMax = e.getX(); + if (mXMin > e.getX()) + mXMin = e.getX(); + + if (axis == AxisDependency.LEFT) { + + if (mLeftAxisMax < e.getY()) + mLeftAxisMax = e.getY(); + if (mLeftAxisMin > e.getY()) + mLeftAxisMin = e.getY(); + } else { + if (mRightAxisMax < e.getY()) + mRightAxisMax = e.getY(); + if (mRightAxisMin > e.getY()) + mRightAxisMin = e.getY(); + } + } + + /** + * Adjusts the minimum and maximum values based on the given DataSet. + * + * @param d + */ + protected void calcMinMax(T d) { + + if (mYMax < d.getYMax()) + mYMax = d.getYMax(); + if (mYMin > d.getYMin()) + mYMin = d.getYMin(); + + if (mXMax < d.getXMax()) + mXMax = d.getXMax(); + if (mXMin > d.getXMin()) + mXMin = d.getXMin(); + + if (d.getAxisDependency() == AxisDependency.LEFT) { + + if (mLeftAxisMax < d.getYMax()) + mLeftAxisMax = d.getYMax(); + if (mLeftAxisMin > d.getYMin()) + mLeftAxisMin = d.getYMin(); + } else { + if (mRightAxisMax < d.getYMax()) + mRightAxisMax = d.getYMax(); + if (mRightAxisMin > d.getYMin()) + mRightAxisMin = d.getYMin(); + } + } + + /** + * Removes the given Entry object from the DataSet at the specified index. + * + * @param e + * @param dataSetIndex + */ + public boolean removeEntry(Entry e, int dataSetIndex) { + + // entry null, outofbounds + if (e == null || dataSetIndex >= mDataSets.size()) + return false; + + IDataSet set = mDataSets.get(dataSetIndex); + + if (set != null) { + // remove the entry from the dataset + boolean removed = set.removeEntry(e); + + if (removed) { + notifyDataChanged(); + } + + return removed; + } else + return false; + } + + /** + * Removes the Entry object closest to the given DataSet at the + * specified index. Returns true if an Entry was removed, false if no Entry + * was found that meets the specified requirements. + * + * @param xValue + * @param dataSetIndex + * @return + */ + public boolean removeEntry(float xValue, int dataSetIndex) { + + if (dataSetIndex >= mDataSets.size()) + return false; + + IDataSet dataSet = mDataSets.get(dataSetIndex); + Entry e = dataSet.getEntryForXValue(xValue, Float.NaN); + + if (e == null) + return false; + + return removeEntry(e, dataSetIndex); + } + + /** + * Returns the DataSet that contains the provided Entry, or null, if no + * DataSet contains this Entry. + * + * @param e + * @return + */ + public T getDataSetForEntry(Entry e) { + + if (e == null) + return null; + + for (int i = 0; i < mDataSets.size(); i++) { + + T set = mDataSets.get(i); + + for (int j = 0; j < set.getEntryCount(); j++) { + if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY()))) + return set; + } + } + + return null; + } + + /** + * Returns all colors used across all DataSet objects this object + * represents. + * + * @return + */ + public int[] getColors() { + + if (mDataSets == null) + return null; + + int clrcnt = 0; + + for (int i = 0; i < mDataSets.size(); i++) { + clrcnt += mDataSets.get(i).getColors().size(); + } + + int[] colors = new int[clrcnt]; + int cnt = 0; + + for (int i = 0; i < mDataSets.size(); i++) { + + List clrs = mDataSets.get(i).getColors(); + + for (Integer clr : clrs) { + colors[cnt] = clr; + cnt++; + } + } + + return colors; + } + + /** + * Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist. + * + * @param dataSet + * @return + */ + public int getIndexOfDataSet(T dataSet) { + return mDataSets.indexOf(dataSet); + } + + /** + * Returns the first DataSet from the datasets-array that has it's dependency on the left axis. + * Returns null if no DataSet with left dependency could be found. + * + * @return + */ + protected T getFirstLeft(List sets) { + for (T dataSet : sets) { + if (dataSet.getAxisDependency() == AxisDependency.LEFT) + return dataSet; + } + return null; + } + + /** + * Returns the first DataSet from the datasets-array that has it's dependency on the right axis. + * Returns null if no DataSet with right dependency could be found. + * + * @return + */ + public T getFirstRight(List sets) { + for (T dataSet : sets) { + if (dataSet.getAxisDependency() == AxisDependency.RIGHT) + return dataSet; + } + return null; + } + + /** + * Sets a custom IValueFormatter for all DataSets this data object contains. + * + * @param f + */ + public void setValueFormatter(IValueFormatter f) { + if (f == null) + return; + else { + for (IDataSet set : mDataSets) { + set.setValueFormatter(f); + } + } + } + + /** + * Sets the color of the value-text (color in which the value-labels are + * drawn) for all DataSets this data object contains. + * + * @param color + */ + public void setValueTextColor(int color) { + for (IDataSet set : mDataSets) { + set.setValueTextColor(color); + } + } + + /** + * Sets the same list of value-colors for all DataSets this + * data object contains. + * + * @param colors + */ + public void setValueTextColors(List colors) { + for (IDataSet set : mDataSets) { + set.setValueTextColors(colors); + } + } + + /** + * Sets the Typeface for all value-labels for all DataSets this data object + * contains. + * + * @param tf + */ + public void setValueTypeface(Typeface tf) { + for (IDataSet set : mDataSets) { + set.setValueTypeface(tf); + } + } + + /** + * Sets the size (in dp) of the value-text for all DataSets this data object + * contains. + * + * @param size + */ + public void setValueTextSize(float size) { + for (IDataSet set : mDataSets) { + set.setValueTextSize(size); + } + } + + /** + * Enables / disables drawing values (value-text) for all DataSets this data + * object contains. + * + * @param enabled + */ + public void setDrawValues(boolean enabled) { + for (IDataSet set : mDataSets) { + set.setDrawValues(enabled); + } + } + + /** + * Enables / disables highlighting values for all DataSets this data object + * contains. If set to true, this means that values can + * be highlighted programmatically or by touch gesture. + */ + public void setHighlightEnabled(boolean enabled) { + for (IDataSet set : mDataSets) { + set.setHighlightEnabled(enabled); + } + } + + /** + * Returns true if highlighting of all underlying values is enabled, false + * if not. + * + * @return + */ + public boolean isHighlightEnabled() { + for (IDataSet set : mDataSets) { + if (!set.isHighlightEnabled()) + return false; + } + return true; + } + + /** + * Clears this data object from all DataSets and removes all Entries. Don't + * forget to invalidate the chart after this. + */ + public void clearValues() { + if (mDataSets != null) { + mDataSets.clear(); + } + notifyDataChanged(); + } + + /** + * Checks if this data object contains the specified DataSet. Returns true + * if so, false if not. + * + * @param dataSet + * @return + */ + public boolean contains(T dataSet) { + + for (T set : mDataSets) { + if (set.equals(dataSet)) + return true; + } + + return false; + } + + /** + * Returns the total entry count across all DataSet objects this data object contains. + * + * @return + */ + public int getEntryCount() { + + int count = 0; + + for (T set : mDataSets) { + count += set.getEntryCount(); + } + + return count; + } + + /** + * Returns the DataSet object with the maximum number of entries or null if there are no DataSets. + * + * @return + */ + public T getMaxEntryCountSet() { + + if (mDataSets == null || mDataSets.isEmpty()) + return null; + + T max = mDataSets.get(0); + + for (T set : mDataSets) { + + if (set.getEntryCount() > max.getEntryCount()) + max = set; + } + + return max; + } +} 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 51% 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 bd3abf544d..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,45 +4,77 @@ import android.content.Context; import android.graphics.Color; import android.graphics.DashPathEffect; +import android.util.Log; +import com.github.mikephil.charting.formatter.DefaultFillFormatter; +import com.github.mikephil.charting.formatter.IFillFormatter; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.github.mikephil.charting.utils.Utils; import java.util.ArrayList; import java.util.List; -public class LineDataSet extends LineRadarDataSet { +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 + */ + private float mCircleRadius = 8f; - /** the radius of the circle-shaped value indicators */ - private float mCircleSize = 8f; + /** + * the hole radius of the circle-shaped value indicators + */ + private float mCircleHoleRadius = 4f; - /** sets the intensity of the cubic lines */ + /** + * 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; - /** if true, drawing circles is enabled */ - private boolean mDrawCircles = true; + /** + * formatter for customizing the position of the fill-line + */ + private IFillFormatter mFillFormatter = new DefaultFillFormatter(); - /** if true, cubic lines are drawn instead of linear */ - private boolean mDrawCubic = false; + /** + * if true, drawing circles is enabled + */ + private boolean mDrawCircles = true; private boolean mDrawCircleHole = true; + public LineDataSet(List yVals, String label) { super(yVals, label); - // mCircleSize = Utils.convertDpToPixel(4f); + // 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)); @@ -52,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.mCircleSize = mCircleSize; - 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) { @@ -87,43 +142,84 @@ public void setCubicIntensity(float intensity) { mCubicIntensity = intensity; } - /** - * Returns the intensity of the cubic lines (the effect intensity). - * - * @return - */ + @Override public float getCubicIntensity() { return mCubicIntensity; } + /** - * sets the size (radius) of the circle shpaed value indicators, default - * size = 4f - * + * Sets the radius of the drawn circles. + * Default radius = 4f, Min = 1f + * + * @param radius + */ + public void setCircleRadius(float radius) { + + if (radius >= 1f) { + mCircleRadius = Utils.convertDpToPixel(radius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 1"); + } + } + + @Override + public float getCircleRadius() { + return mCircleRadius; + } + + /** + * Sets the hole radius of the drawn circles. + * Default radius = 2f, Min = 0.5f + * + * @param holeRadius + */ + public void setCircleHoleRadius(float holeRadius) { + + if (holeRadius >= 0.5f) { + mCircleHoleRadius = Utils.convertDpToPixel(holeRadius); + } else { + Log.e("LineDataSet", "Circle radius cannot be < 0.5"); + } + } + + @Override + public float getCircleHoleRadius() { + return mCircleHoleRadius; + } + + /** + * sets the size (radius) of the circle shpaed value indicators, + * default size = 4f + *

+ * This method is deprecated because of unclarity. Use setCircleRadius instead. + * * @param size */ + @Deprecated public void setCircleSize(float size) { - mCircleSize = Utils.convertDpToPixel(size); + setCircleRadius(size); } /** - * returns the circlesize + * This function is deprecated because of unclarity. Use getCircleRadius instead. */ + @Deprecated public float getCircleSize() { - return mCircleSize; + return getCircleRadius(); } /** * Enables the line to be drawn in dashed mode, e.g. like this * "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF. * Keep in mind that hardware acceleration boosts performance. - * - * @param lineLength the length of the line pieces + * + * @param 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); } @@ -135,20 +231,12 @@ public void disableDashedLine() { mDashPathEffect = null; } - /** - * Returns true if the dashed-line effect is enabled, false if not. - * - * @return - */ + @Override public boolean isDashedLineEnabled() { return mDashPathEffect == null ? false : true; } - /** - * returns the DashPathEffect that is set for this DataSet - * - * @return - */ + @Override public DashPathEffect getDashPathEffect() { return mDashPathEffect; } @@ -156,61 +244,49 @@ 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) { this.mDrawCircles = enabled; } - /** - * returns true if drawing circles for this DataSet is enabled, false if not - * - * @return - */ + @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; + @Deprecated + @Override + public boolean isDrawCubicEnabled() { + return mMode == Mode.CUBIC_BEZIER; } - /** - * returns true if drawing cubic lines is enabled, false if not. - * - * @return - */ - public boolean isDrawCubicEnabled() { - return mDrawCubic; + @Deprecated + @Override + public boolean isDrawSteppedEnabled() { + return mMode == Mode.STEPPED; } /** ALL CODE BELOW RELATED TO CIRCLE-COLORS */ /** * returns all colors specified for the circles - * + * * @return */ public List getCircleColors() { return mCircleColors; } - /** - * Returns the color at the given index of the DataSet's circle-color array. - * Performs a IndexOutOfBounds check by modulus. - * - * @param index - * @return - */ + @Override public int getCircleColor(int index) { - return mCircleColors.get(index % mCircleColors.size()); + return mCircleColors.get(index); + } + + @Override + public int getCircleColorCount() { + return mCircleColors.size(); } /** @@ -219,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) { @@ -232,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); } @@ -246,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)); @@ -263,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) { @@ -275,37 +355,63 @@ 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; } - /** - * Returns the color of the inner circle. - * - * @return - */ + @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) { mDrawCircleHole = enabled; } + @Override public boolean isDrawCircleHoleEnabled() { return mDrawCircleHole; } + + /** + * Sets a custom IFillFormatter to the chart that handles the position of the + * filled-line for each DataSet. Set this to null to use the default logic. + * + * @param formatter + */ + public void setFillFormatter(IFillFormatter formatter) { + + if (formatter == null) + mFillFormatter = new DefaultFillFormatter(); + else + mFillFormatter = formatter; + } + + @Override + public IFillFormatter getFillFormatter() { + return mFillFormatter; + } + + public enum Mode { + LINEAR, + STEPPED, + CUBIC_BEZIER, + HORIZONTAL_BEZIER + } } 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 51% 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 64e0762d30..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,61 +1,85 @@ package com.github.mikephil.charting.data; +import android.annotation.TargetApi; import android.graphics.Color; +import android.graphics.drawable.Drawable; +import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet; import com.github.mikephil.charting.utils.Utils; import java.util.List; /** * Base dataset for line and radar DataSets. - * + * * @author Philipp Jahoda */ -public abstract class LineRadarDataSet extends BarLineScatterCandleDataSet { +public abstract class LineRadarDataSet extends LineScatterCandleRadarDataSet implements ILineRadarDataSet { - /** the color that is used for filling the line surface */ + // TODO: Move to using `Fill` class + /** + * the color that is used for filling the line surface + */ private int mFillColor = Color.rgb(140, 234, 255); - /** transparency used for filling line surface */ + /** + * 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 */ + /** + * the width of the drawn data lines + */ private float mLineWidth = 2.5f; - /** if true, the data will also be drawn filled */ + /** + * if true, the data will also be drawn filled + */ private boolean mDrawFilled = false; - -// private Shader mShader; - + + public LineRadarDataSet(List yVals, String label) { super(yVals, label); } - /** - * returns the color that is used for filling the line surface - * - * @return - */ + @Override public int getFillColor() { return mFillColor; } /** - * sets the color that is used for filling the line surface - * + * Sets the color that is used for filling the area below the line. + * Resets an eventually set "fillDrawable". + * * @param color */ public void setFillColor(int color) { mFillColor = color; + mFillDrawable = null; + } + + @Override + public Drawable getFillDrawable() { + return mFillDrawable; } /** - * returns the alpha value that is used for filling the line surface, - * default: 85 - * - * @return + * Sets the drawable to be used to fill the area below the line. + * + * @param drawable */ + @TargetApi(18) + public void setFillDrawable(Drawable drawable) { + this.mFillDrawable = drawable; + } + + @Override public int getFillAlpha() { return mFillAlpha; } @@ -63,8 +87,8 @@ public int getFillAlpha() { /** * sets the alpha value (transparency) that is used for filling the line * surface (0-255), default: 85 - * - * @param color + * + * @param alpha */ public void setFillAlpha(int alpha) { mFillAlpha = alpha; @@ -73,44 +97,39 @@ public void setFillAlpha(int 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.5f; + if (width < 0.0f) + width = 0.0f; if (width > 10.0f) width = 10.0f; mLineWidth = Utils.convertDpToPixel(width); } - /** - * returns the width of the drawn chart line - * - * @return - */ + @Override public float getLineWidth() { return mLineWidth; } - /** - * 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 - * - * @param filled - */ + @Override public void setDrawFilled(boolean filled) { mDrawFilled = filled; } - /** - * returns true if filled drawing is enabled, false if not - * - * @return - */ + @Override public boolean isDrawFilledEnabled() { return mDrawFilled; } + + protected void copy(LineRadarDataSet lineRadarDataSet) { + super.copy(lineRadarDataSet); + lineRadarDataSet.mDrawFilled = mDrawFilled; + lineRadarDataSet.mFillAlpha = mFillAlpha; + lineRadarDataSet.mFillColor = mFillColor; + lineRadarDataSet.mFillDrawable = mFillDrawable; + lineRadarDataSet.mLineWidth = mLineWidth; + } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java new file mode 100644 index 0000000000..d4618d809e --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/LineScatterCandleRadarDataSet.java @@ -0,0 +1,120 @@ +package com.github.mikephil.charting.data; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; +import com.github.mikephil.charting.utils.Utils; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 11/07/15. + */ +public abstract class LineScatterCandleRadarDataSet extends BarLineScatterCandleBubbleDataSet 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 LineScatterCandleRadarDataSet(List yVals, String label) { + super(yVals, label); + mHighlightLineWidth = Utils.convertDpToPixel(0.5f); + } + + /** + * Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn. + * @param enabled + */ + public void setDrawHorizontalHighlightIndicator(boolean enabled) { + this.mDrawHorizontalHighlightIndicator = enabled; + } + + /** + * Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn. + * @param enabled + */ + public void setDrawVerticalHighlightIndicator(boolean enabled) { + this.mDrawVerticalHighlightIndicator = enabled; + } + + /** + * Enables / disables both vertical and horizontal highlight-indicators. + * @param enabled + */ + public void setDrawHighlightIndicators(boolean enabled) { + setDrawVerticalHighlightIndicator(enabled); + setDrawHorizontalHighlightIndicator(enabled); + } + + @Override + public boolean isVerticalHighlightIndicatorEnabled() { + return mDrawVerticalHighlightIndicator; + } + + @Override + public boolean isHorizontalHighlightIndicatorEnabled() { + return mDrawHorizontalHighlightIndicator; + } + + /** + * Sets the width of the highlight line in dp. + * @param width + */ + public void setHighlightLineWidth(float width) { + mHighlightLineWidth = Utils.convertDpToPixel(width); + } + + @Override + public float getHighlightLineWidth() { + return mHighlightLineWidth; + } + + /** + * Enables the highlight-line to be drawn in dashed mode, e.g. like this "- - - - - -" + * + * @param lineLength the length of the line pieces + * @param spaceLength the length of space inbetween the line-pieces + * @param phase offset, in degrees (normally, use 0) + */ + public void enableDashedHighlightLine(float lineLength, float spaceLength, float phase) { + mHighlightDashPathEffect = new DashPathEffect(new float[] { + lineLength, spaceLength + }, phase); + } + + /** + * Disables the highlight-line to be drawn in dashed mode. + */ + public void disableDashedHighlightLine() { + mHighlightDashPathEffect = null; + } + + /** + * Returns true if the dashed-line effect is enabled for highlight lines, false if not. + * Default: disabled + * + * @return + */ + public boolean isDashedHighlightLineEnabled() { + return mHighlightDashPathEffect == null ? false : true; + } + + @Override + public DashPathEffect getDashPathEffectHighlight() { + return mHighlightDashPathEffect; + } + + protected void copy(LineScatterCandleRadarDataSet lineScatterCandleRadarDataSet) { + super.copy(lineScatterCandleRadarDataSet); + lineScatterCandleRadarDataSet.mDrawHorizontalHighlightIndicator = mDrawHorizontalHighlightIndicator; + lineScatterCandleRadarDataSet.mDrawVerticalHighlightIndicator = mDrawVerticalHighlightIndicator; + lineScatterCandleRadarDataSet.mHighlightLineWidth = mHighlightLineWidth; + lineScatterCandleRadarDataSet.mHighlightDashPathEffect = mHighlightDashPathEffect; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java new file mode 100644 index 0000000000..423ce19b16 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/PieData.java @@ -0,0 +1,100 @@ + +package com.github.mikephil.charting.data; + +import android.util.Log; + +import com.github.mikephil.charting.highlight.Highlight; +import com.github.mikephil.charting.interfaces.datasets.IPieDataSet; + +import java.util.ArrayList; +import java.util.List; + +/** + * A PieData object can only represent one DataSet. Unlike all other charts, the + * legend labels of the PieChart are created from the x-values array, and not + * from the DataSet labels. Each PieData object can only represent one + * PieDataSet (multiple PieDataSets inside a single PieChart are not possible). + * + * @author Philipp Jahoda + */ +public class PieData extends ChartData { + + public PieData() { + super(); + } + + public PieData(IPieDataSet dataSet) { + super(dataSet); + } + + /** + * Sets the PieDataSet this data object should represent. + * + * @param dataSet + */ + public void setDataSet(IPieDataSet dataSet) { + mDataSets.clear(); + mDataSets.add(dataSet); + notifyDataChanged(); + } + + /** + * Returns the DataSet this PieData object represents. A PieData object can + * only contain one DataSet. + * + * @return + */ + public IPieDataSet getDataSet() { + return mDataSets.get(0); + } + + @Override + public List 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. + * + * @param index + * @return + */ + @Override + public IPieDataSet getDataSetByIndex(int index) { + return index == 0 ? getDataSet() : null; + } + + @Override + public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) { + return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0) + : null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null; + } + + @Override + public Entry getEntryForHighlight(Highlight highlight) { + return getDataSet().getEntryForIndex((int) highlight.getX()); + } + + /** + * Returns the sum of all values in this PieData object. + * + * @return + */ + public float getYValueSum() { + + float sum = 0; + + for (int i = 0; i < getDataSet().getEntryCount(); i++) + sum += getDataSet().getEntryForIndex(i).getY(); + + + return sum; + } +} 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/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java new file mode 100644 index 0000000000..851faeb333 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultFillFormatter.java @@ -0,0 +1,45 @@ +package com.github.mikephil.charting.formatter; + + +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +/** + * Default formatter that calculates the position of the filled line. + * + * @author Philipp Jahoda + */ +public class DefaultFillFormatter implements IFillFormatter +{ + + @Override + public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) { + + float fillMin = 0f; + float chartMaxY = dataProvider.getYChartMax(); + float chartMinY = dataProvider.getYChartMin(); + + LineData data = dataProvider.getLineData(); + + if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) { + fillMin = 0f; + } else { + + float max, min; + + if (data.getYMax() > 0) + max = 0f; + else + max = chartMaxY; + if (data.getYMin() < 0) + min = 0f; + else + min = chartMinY; + + fillMin = dataSet.getYMin() >= 0 ? min : max; + } + + return fillMin; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java new file mode 100644 index 0000000000..e2fea4b079 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/DefaultValueFormatter.java @@ -0,0 +1,71 @@ + +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Default formatter used for formatting values inside the chart. Uses a DecimalFormat with + * pre-calculated number of digits (depending on max and min value). + * + * @author Philipp Jahoda + */ +public class DefaultValueFormatter implements IValueFormatter +{ + + /** + * DecimalFormat for formatting + */ + protected DecimalFormat mFormat; + + protected int mDecimalDigits; + + /** + * Constructor that specifies to how many digits the value should be + * formatted. + * + * @param digits + */ + public DefaultValueFormatter(int digits) { + setup(digits); + } + + /** + * Sets up the formatter with a given number of decimal digits. + * + * @param digits + */ + public void setup(int digits) { + + this.mDecimalDigits = digits; + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < digits; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + + // put more logic here ... + // avoid memory allocations here (for performance reasons) + + return mFormat.format(value); + } + + /** + * Returns the number of decimal digits this formatter uses. + * + * @return + */ + public int getDecimalDigits() { + return mDecimalDigits; + } +} 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/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java new file mode 100644 index 0000000000..e096cc6528 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IFillFormatter.java @@ -0,0 +1,24 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider; + +/** + * Interface for providing a custom logic to where the filling line of a LineDataSet + * should end. This of course only works if setFillEnabled(...) is set to true. + * + * @author Philipp Jahoda + */ +public interface IFillFormatter +{ + + /** + * Returns the vertical (y-axis) position where the filled-line of the + * LineDataSet should end. + * + * @param dataSet the ILineDataSet that is currently drawn + * @param dataProvider + * @return + */ + float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java new file mode 100644 index 0000000000..75d2363f26 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/IValueFormatter.java @@ -0,0 +1,29 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * 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 IValueFormatter. Then override the getFormattedValue(...) method + * and return whatever you want. + * + * @author Philipp Jahoda + */ +public interface IValueFormatter +{ + + /** + * Called when a value (from labels inside the chart) is formatted + * before being drawn. For performance reasons, avoid excessive calculations + * and memory allocations inside this method. + * + * @param value the value to be formatted + * @param entry the entry the value belongs to - in e.g. BarChart, this is of class BarEntry + * @param dataSetIndex the index of the DataSet the entry in focus belongs to + * @param viewPortHandler provides information about the current chart state (scale, translation, ...) + * @return the formatted label ready for being drawn + */ + String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler); +} 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/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java new file mode 100644 index 0000000000..211401ad8a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/LargeValueFormatter.java @@ -0,0 +1,103 @@ + +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; + +/** + * Predefined value-formatter that formats large numbers in a pretty way. + * Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k; + * 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 = + * 10m; 1000000000 = 1b; Special thanks to Roman Gromov + * (https://github.com/romangromov) for this piece of code. + * + * @author Philipp Jahoda + * @author Oleksandr Tyshkovets + */ +public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter +{ + + private String[] mSuffix = new String[]{ + "", "k", "m", "b", "t" + }; + private int mMaxLength = 5; + private DecimalFormat mFormat; + private String mText = ""; + + public LargeValueFormatter() { + mFormat = new DecimalFormat("###E00"); + } + + /** + * Creates a formatter that appends a specified text to the result string + * + * @param appendix a text that will be appended + */ + public LargeValueFormatter(String appendix) { + this(); + mText = appendix; + } + + // IValueFormatter + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return makePretty(value) + mText; + } + + // IAxisValueFormatter + @Override + public String getFormattedValue(float value, AxisBase axis) { + return makePretty(value) + mText; + } + + /** + * Set an appendix text to be added at the end of the formatted value. + * + * @param appendix + */ + public void setAppendix(String appendix) { + this.mText = appendix; + } + + /** + * Set custom suffix to be appended after the values. + * Default suffix: ["", "k", "m", "b", "t"] + * + * @param suffix new suffix + */ + public void setSuffix(String[] suffix) { + this.mSuffix = suffix; + } + + public void setMaxLength(int maxLength) { + this.mMaxLength = maxLength; + } + + /** + * Formats each number properly. Special thanks to Roman Gromov + * (https://github.com/romangromov) for this piece of code. + */ + private String makePretty(double number) { + + String r = mFormat.format(number); + + int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1)); + int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2)); + int combined = Integer.valueOf(numericValue2 + "" + numericValue1); + + r = r.replaceAll("E[0-9][0-9]", mSuffix[combined / 3]); + + while (r.length() > mMaxLength || r.matches("[0-9]+\\.[a-z]")) { + r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1); + } + + return r; + } + + public int getDecimalDigits() { + return 0; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java new file mode 100644 index 0000000000..de8a10255a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/PercentFormatter.java @@ -0,0 +1,49 @@ + +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; + +/** + * This IValueFormatter is just for convenience and simply puts a "%" sign after + * each value. (Recommeded for PieChart) + * + * @author Philipp Jahoda + */ +public class PercentFormatter implements IValueFormatter, IAxisValueFormatter +{ + + protected DecimalFormat mFormat; + + public PercentFormatter() { + mFormat = new DecimalFormat("###,###,##0.0"); + } + + /** + * Allow a custom decimalformat + * + * @param format + */ + public PercentFormatter(DecimalFormat format) { + this.mFormat = format; + } + + // IValueFormatter + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + return mFormat.format(value) + " %"; + } + + // IAxisValueFormatter + @Override + public String getFormattedValue(float value, AxisBase axis) { + return mFormat.format(value) + " %"; + } + + public int getDecimalDigits() { + return 1; + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java new file mode 100644 index 0000000000..0e8351634f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/formatter/StackedValueFormatter.java @@ -0,0 +1,75 @@ +package com.github.mikephil.charting.formatter; + +import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.utils.ViewPortHandler; + +import java.text.DecimalFormat; + +/** + * Created by Philipp Jahoda on 28/01/16. + *

+ * 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 IValueFormatter +{ + + /** + * if true, all stack values of the stacked bar entry are drawn, else only top + */ + private boolean mDrawWholeStack; + + /** + * a string that should be appended behind the value + */ + private String mAppendix; + + private DecimalFormat mFormat; + + /** + * Constructor. + * + * @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top + * @param appendix a string that should be appended behind the value + * @param decimals the number of decimal digits to use + */ + public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) { + this.mDrawWholeStack = drawWholeStack; + this.mAppendix = appendix; + + StringBuffer b = new StringBuffer(); + for (int i = 0; i < decimals; i++) { + if (i == 0) + b.append("."); + b.append("0"); + } + + this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString()); + } + + @Override + public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { + + if (!mDrawWholeStack && entry instanceof BarEntry) { + + BarEntry barEntry = (BarEntry) entry; + float[] vals = barEntry.getYVals(); + + if (vals != null) { + + // find out if we are on top of the stack + if (vals[vals.length - 1] == value) { + + // return the "sum" across all stack values + return mFormat.format(barEntry.getY()) + mAppendix; + } else { + return ""; // return empty + } + } + } + + // return the "proposed" value + return mFormat.format(value) + mAppendix; + } +} 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/main/java/com/github/mikephil/charting/highlight/Range.java b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java new file mode 100644 index 0000000000..96dd592b8f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/highlight/Range.java @@ -0,0 +1,38 @@ +package com.github.mikephil.charting.highlight; + +/** + * Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g. + * stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25). + */ +public final class Range { + + public float from; + public float to; + + public Range(float from, float to) { + this.from = from; + this.to = to; + } + + /** + * Returns true if this range contains (if the value is in between) the given value, false if not. + * + * @param value + * @return + */ + public boolean contains(float value) { + + if (value > from && value <= to) + return true; + else + return false; + } + + public boolean isLarger(float value) { + return value > to; + } + + public boolean isSmaller(float value) { + return value < from; + } +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java new file mode 100644 index 0000000000..9dfee07f9c --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.data.BarData; + +public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider { + + BarData getBarData(); + boolean isDrawBarShadowEnabled(); + boolean isDrawValueAboveBarEnabled(); + boolean isHighlightFullBarEnabled(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java new file mode 100644 index 0000000000..68beeb8d62 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BarLineScatterCandleBubbleDataProvider.java @@ -0,0 +1,16 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.components.YAxis.AxisDependency; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; +import com.github.mikephil.charting.utils.Transformer; + +public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface { + + Transformer getTransformer(AxisDependency axis); + boolean isInverted(AxisDependency axis); + + float getLowestVisibleX(); + float getHighestVisibleX(); + + BarLineScatterCandleBubbleData getData(); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/BubbleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/BubbleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java index 2161303e01..82ee30ad7c 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/BubbleDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/BubbleDataProvider.java @@ -1,9 +1,8 @@ -package com.github.mikephil.charting.interfaces; +package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.BubbleData; -public interface BubbleDataProvider extends BarLineScatterCandleDataProvider { +public interface BubbleDataProvider extends BarLineScatterCandleBubbleDataProvider { - public BubbleData getBubbleData(); - + BubbleData getBubbleData(); } diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java index 7e58121f8f..357403f98a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/CandleDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/CandleDataProvider.java @@ -1,8 +1,8 @@ -package com.github.mikephil.charting.interfaces; +package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.CandleData; -public interface CandleDataProvider extends BarLineScatterCandleDataProvider { +public interface CandleDataProvider extends BarLineScatterCandleBubbleDataProvider { - public CandleData getCandleData(); + CandleData getCandleData(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java new file mode 100644 index 0000000000..219b46bd82 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ChartInterface.java @@ -0,0 +1,69 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import android.graphics.RectF; + +import com.github.mikephil.charting.data.ChartData; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; + +/** + * Interface that provides everything there is to know about the dimensions, + * bounds, and range of the chart. + * + * @author Philipp Jahoda + */ +public interface ChartInterface { + + /** + * Returns the minimum x value of the chart, regardless of zoom or translation. + * + * @return + */ + float getXChartMin(); + + /** + * Returns the maximum x value of the chart, regardless of zoom or translation. + * + * @return + */ + float getXChartMax(); + + float getXRange(); + + /** + * Returns the minimum y value of the chart, regardless of zoom or translation. + * + * @return + */ + float getYChartMin(); + + /** + * Returns the maximum y value of the chart, regardless of zoom or translation. + * + * @return + */ + float getYChartMax(); + + /** + * Returns the maximum distance in scren dp a touch can be away from an entry to cause it to get highlighted. + * + * @return + */ + float getMaxHighlightDistance(); + + int getWidth(); + + int getHeight(); + + MPPointF getCenterOfView(); + + MPPointF getCenterOffsets(); + + RectF getContentRect(); + + 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/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java new file mode 100644 index 0000000000..5c23ac27b4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/LineDataProvider.java @@ -0,0 +1,11 @@ +package com.github.mikephil.charting.interfaces.dataprovider; + +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.LineData; + +public interface LineDataProvider extends BarLineScatterCandleBubbleDataProvider { + + LineData getLineData(); + + YAxis getAxis(YAxis.AxisDependency dependency); +} diff --git a/MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java similarity index 51% rename from MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java index a5570f1483..b58d5af95d 100644 --- a/MPChartLib/src/com/github/mikephil/charting/interfaces/ScatterDataProvider.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/dataprovider/ScatterDataProvider.java @@ -1,9 +1,8 @@ -package com.github.mikephil.charting.interfaces; +package com.github.mikephil.charting.interfaces.dataprovider; import com.github.mikephil.charting.data.ScatterData; -public interface ScatterDataProvider extends BarLineScatterCandleDataProvider { +public interface ScatterDataProvider extends BarLineScatterCandleBubbleDataProvider { - public ScatterData getScatterData(); - + ScatterData getScatterData(); } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java new file mode 100644 index 0000000000..5e82a48420 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarDataSet.java @@ -0,0 +1,71 @@ +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 { + + List getFills(); + + Fill getFill(int index); + + /** + * Returns true if this DataSet is stacked (stacksize > 1) or not. + * + * @return + */ + boolean isStacked(); + + /** + * Returns the maximum number of bars that can be stacked upon another in + * this DataSet. This should return 1 for non stacked bars, and > 1 for stacked bars. + * + * @return + */ + int getStackSize(); + + /** + * Returns the color used for drawing the bar-shadows. The bar shadows is a + * surface behind the bar that indicates the maximum value. + * + * @return + */ + int getBarShadowColor(); + + /** + * Returns the width used for drawing borders around the bars. + * If borderWidth == 0, no border will be drawn. + * + * @return + */ + float getBarBorderWidth(); + + /** + * Returns the color drawing borders around the bars. + * + * @return + */ + int getBarBorderColor(); + + /** + * Returns the alpha value (transparency) that is used for drawing the + * highlight indicator. + * + * @return + */ + int getHighLightAlpha(); + + + /** + * Returns the labels used for the different value-stacks in the legend. + * This is only relevant for stacked bar entries. + * + * @return + */ + String[] getStackLabels(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java new file mode 100644 index 0000000000..0f9454b2cd --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBarLineScatterCandleBubbleDataSet.java @@ -0,0 +1,16 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by philipp on 21/10/15. + */ +public interface IBarLineScatterCandleBubbleDataSet extends IDataSet { + + /** + * Returns the color that is used for drawing the highlight indicators. + * + * @return + */ + int getHighLightColor(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java new file mode 100644 index 0000000000..e284aac209 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IBubbleDataSet.java @@ -0,0 +1,27 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.BubbleEntry; + +/** + * Created by philipp on 21/10/15. + */ +public interface IBubbleDataSet extends IBarLineScatterCandleBubbleDataSet { + + /** + * Sets the width of the circle that surrounds the bubble when highlighted, + * in dp. + * + * @param width + */ + void setHighlightCircleWidth(float width); + + float getMaxSize(); + + boolean isNormalizeSizeEnabled(); + + /** + * Returns the width of the highlight-circle that surrounds the bubble + * @return + */ + float getHighlightCircleWidth(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java new file mode 100644 index 0000000000..1d004ed959 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ICandleDataSet.java @@ -0,0 +1,85 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.Paint; + +import com.github.mikephil.charting.data.CandleEntry; + +/** + * Created by philipp on 21/10/15. + */ +public interface ICandleDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the space that is left out on the left and right side of each + * candle. + * + * @return + */ + float getBarSpace(); + + /** + * Returns whether the candle bars should show? + * When false, only "ticks" will show + * + * - default: true + * + * @return + */ + boolean getShowCandleBar(); + + /** + * Returns the width of the candle-shadow-line in pixels. + * + * @return + */ + float getShadowWidth(); + + /** + * Returns shadow color for all entries + * + * @return + */ + int getShadowColor(); + + /** + * Returns the neutral color (for open == close) + * + * @return + */ + int getNeutralColor(); + + /** + * Returns the increasing color (for open < close). + * + * @return + */ + int getIncreasingColor(); + + /** + * Returns the decreasing color (for open > close). + * + * @return + */ + int getDecreasingColor(); + + /** + * Returns paint style when open < close + * + * @return + */ + Paint.Style getIncreasingPaintStyle(); + + /** + * Returns paint style when open > close + * + * @return + */ + Paint.Style getDecreasingPaintStyle(); + + /** + * Is the shadow color same as the candle color? + * + * @return + */ + boolean getShadowColorSameAsCandle(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java new file mode 100644 index 0000000000..fd8af7064b --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IDataSet.java @@ -0,0 +1,486 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; +import android.graphics.PointF; +import android.graphics.Typeface; + +import com.github.mikephil.charting.components.Legend; +import com.github.mikephil.charting.components.YAxis; +import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.formatter.IValueFormatter; +import com.github.mikephil.charting.utils.MPPointF; + +import java.util.List; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface IDataSet { + + /** ###### ###### DATA RELATED METHODS ###### ###### */ + + /** + * returns the minimum y-value this DataSet holds + * + * @return + */ + float getYMin(); + + /** + * returns the maximum y-value this DataSet holds + * + * @return + */ + 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() + * + * @return + */ + int getEntryCount(); + + /** + * 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 fromX + * @param toX + */ + void calcMinMaxY(float fromX, float toX); + + /** + * 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 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 getEntryForXValue(float xValue, float closestToY, DataSet.Rounding rounding); + + /** + * 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 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 + */ + List getEntriesForXValue(float xValue); + + /** + * Returns the Entry object found at the given index (NOT xIndex) in the values array. + * + * @param index + * @return + */ + T getEntryForIndex(int index); + + /** + * 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 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(float xValue, float closestToY, DataSet.Rounding rounding); + + /** + * Returns the position of the provided entry in the DataSets Entry array. + * Returns -1 if doesn't exist. + * + * @param e + * @return + */ + int getEntryIndex(T e); + + + /** + * This method returns the actual + * index in the Entry array of the DataSet for a given xIndex. IMPORTANT: This method does + * calculations at runtime, do not over-use in performance critical + * situations. + * + * @param xIndex + * @return + */ + int getIndexInEntries(int xIndex); + + /** + * Adds an Entry to the DataSet dynamically. + * Entries are added to the end of the list. + * This will also recalculate the current minimum and maximum + * values of the DataSet and the value-sum. + * + * @param e + */ + boolean addEntry(T e); + + + /** + * Adds an Entry to the DataSet dynamically. + * 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. + * + * @param e + */ + void addEntryOrdered(T e); + + /** + * Removes the first Entry (at index 0) of this DataSet from the entries array. + * Returns true if successful, false if not. + * + * @return + */ + boolean removeFirst(); + + /** + * Removes the last Entry (at index size-1) of this DataSet from the entries array. + * Returns true if successful, false if not. + * + * @return + */ + boolean removeLast(); + + /** + * 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 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 index); + + /** + * Checks if this DataSet 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 entry + * @return + */ + boolean contains(T entry); + + /** + * Removes all values from this DataSet and does all necessary recalculations. + */ + void clear(); + + + /** ###### ###### STYLING RELATED (& OTHER) METHODS ###### ###### */ + + /** + * Returns the label string that describes the DataSet. + * + * @return + */ + String getLabel(); + + /** + * Sets the label string that describes the DataSet. + * + * @param label + */ + void setLabel(String label); + + /** + * Returns the axis this DataSet should be plotted against. + * + * @return + */ + YAxis.AxisDependency getAxisDependency(); + + /** + * Set the y-axis this DataSet should be plotted against (either LEFT or + * RIGHT). Default: LEFT + * + * @param dependency + */ + void setAxisDependency(YAxis.AxisDependency dependency); + + /** + * returns all the colors that are set for this DataSet + * + * @return + */ + List getColors(); + + /** + * Returns the first color (index 0) of the colors-array this DataSet + * contains. This is only used for performance reasons when only one color is in the colors array (size == 1) + * + * @return + */ + int getColor(); + + /** + * Returns the color at the given index of the DataSet's color array. + * Performs a IndexOutOfBounds check by modulus. + * + * @param index + * @return + */ + int getColor(int index); + + /** + * returns true if highlighting of values is enabled, false if not + * + * @return + */ + boolean isHighlightEnabled(); + + /** + * If set to true, value highlighting is enabled which means that values can + * be highlighted programmatically or by touch gesture. + * + * @param enabled + */ + void setHighlightEnabled(boolean enabled); + + /** + * Sets the formatter to be used for drawing the values inside the chart. 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 + */ + void setValueFormatter(IValueFormatter f); + + /** + * Returns the formatter used for drawing the values inside the chart. + * + * @return + */ + 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. + * + * @param color + */ + void setValueTextColor(int color); + + /** + * Sets a list of colors to be used as the colors for the drawn values. + * + * @param colors + */ + void setValueTextColors(List colors); + + /** + * Sets a Typeface for the value-labels of this DataSet. + * + * @param tf + */ + void setValueTypeface(Typeface tf); + + /** + * Sets the text-size of the value-labels of this DataSet in dp. + * + * @param size + */ + void setValueTextSize(float size); + + /** + * Returns only the first color of all colors that are set to be used for the values. + * + * @return + */ + int getValueTextColor(); + + /** + * Returns the color at the specified index that is used for drawing the values inside the chart. + * Uses modulus internally. + * + * @param index + * @return + */ + int getValueTextColor(int index); + + /** + * Returns the typeface that is used for drawing the values inside the chart + * + * @return + */ + Typeface getValueTypeface(); + + /** + * Returns the text size that is used for drawing the values inside the chart + * + * @return + */ + float getValueTextSize(); + + /** + * 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); + + /** + * Returns true if y-value drawing is enabled, false if not + * + * @return + */ + 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. + * + * @param visible + */ + void setVisible(boolean visible); + + /** + * Returns true if this DataSet is visible inside the chart, or false if it + * is currently hidden. + * + * @return + */ + boolean isVisible(); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java new file mode 100644 index 0000000000..3f534fe848 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineDataSet.java @@ -0,0 +1,103 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.formatter.IFillFormatter; + +/** + * Created by Philpp Jahoda on 21/10/15. + */ +public interface ILineDataSet extends ILineRadarDataSet { + + /** + * Returns the drawing mode for this line dataset + * + * @return + */ + LineDataSet.Mode getMode(); + + /** + * 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. + * + * @param index + * @return + */ + 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 + * + * @return + */ + boolean isDrawCirclesEnabled(); + + /** + * Returns the color of the inner circle (the circle-hole). + * + * @return + */ + int getCircleHoleColor(); + + /** + * Returns true if drawing the circle-holes is enabled, false if not. + * + * @return + */ + boolean isDrawCircleHoleEnabled(); + + /** + * Returns the DashPathEffect that is used for drawing the lines. + * + * @return + */ + DashPathEffect getDashPathEffect(); + + /** + * Returns true if the dashed-line effect is enabled, false if not. + * If the DashPathEffect object is null, also return false here. + * + * @return + */ + boolean isDashedLineEnabled(); + + /** + * Returns the IFillFormatter that is set for this DataSet. + * + * @return + */ + IFillFormatter getFillFormatter(); +} \ No newline at end of file diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java new file mode 100644 index 0000000000..ce89822716 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineRadarDataSet.java @@ -0,0 +1,58 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.drawable.Drawable; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface ILineRadarDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the color that is used for filling the line surface area. + * + * @return + */ + int getFillColor(); + + /** + * Returns the drawable used for filling the area below the line. + * + * @return + */ + Drawable getFillDrawable(); + + /** + * Returns the alpha value that is used for filling the line surface, + * default: 85 + * + * @return + */ + int getFillAlpha(); + + /** + * Returns the stroke-width of the drawn line + * + * @return + */ + float getLineWidth(); + + /** + * Returns true if filled drawing is enabled, false if not + * + * @return + */ + boolean isDrawFilledEnabled(); + + /** + * Set to true if the DataSet should be drawn filled (surface), and not just + * 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 + */ + void setDrawFilled(boolean enabled); +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java new file mode 100644 index 0000000000..9ab6802556 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/ILineScatterCandleRadarDataSet.java @@ -0,0 +1,35 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import android.graphics.DashPathEffect; + +import com.github.mikephil.charting.data.Entry; + +/** + * Created by Philipp Jahoda on 21/10/15. + */ +public interface ILineScatterCandleRadarDataSet extends IBarLineScatterCandleBubbleDataSet { + + /** + * Returns true if vertical highlight indicator lines are enabled (drawn) + * @return + */ + boolean isVerticalHighlightIndicatorEnabled(); + + /** + * Returns true if vertical highlight indicator lines are enabled (drawn) + * @return + */ + boolean isHorizontalHighlightIndicatorEnabled(); + + /** + * Returns the line-width in which highlight lines are to be drawn. + * @return + */ + float getHighlightLineWidth(); + + /** + * Returns the DashPathEffect that is used for highlighting. + * @return + */ + DashPathEffect getDashPathEffectHighlight(); +} 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/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java new file mode 100644 index 0000000000..ac6122742a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/interfaces/datasets/IScatterDataSet.java @@ -0,0 +1,38 @@ +package com.github.mikephil.charting.interfaces.datasets; + +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; + +/** + * Created by philipp on 21/10/15. + */ +public interface IScatterDataSet extends ILineScatterCandleRadarDataSet { + + /** + * Returns the currently set scatter shape size + * + * @return + */ + float getScatterShapeSize(); + + /** + * Returns radius of the hole in the shape + * + * @return + */ + float getScatterShapeHoleRadius(); + + /** + * Returns the color for the hole in the shape + * + * @return + */ + int getScatterShapeHoleColor(); + + /** + * Returns the IShapeRenderer responsible for rendering this DataSet. + * + * @return + */ + 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/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java new file mode 100644 index 0000000000..f8b520a419 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedViewPortJob.java @@ -0,0 +1,99 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.Animator; +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. + */ +@SuppressLint("NewApi") +public abstract class AnimatedViewPortJob extends ViewPortJob implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener { + + protected ObjectAnimator animator; + + protected float phase; + + protected float xOrigin; + protected float yOrigin; + + public AnimatedViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, Transformer trans, View v, float xOrigin, float yOrigin, long duration) { + super(viewPortHandler, xValue, yValue, trans, v); + this.xOrigin = xOrigin; + this.yOrigin = yOrigin; + animator = ObjectAnimator.ofFloat(this, "phase", 0f, 1f); + animator.setDuration(duration); + animator.addUpdateListener(this); + animator.addListener(this); + } + + @SuppressLint("NewApi") + @Override + public void run() { + animator.start(); + } + + public float getPhase() { + return phase; + } + + public void setPhase(float phase) { + this.phase = phase; + } + + public float getXOrigin() { + return xOrigin; + } + + 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/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java new file mode 100644 index 0000000000..e5e4c417d3 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/AnimatedZoomJob.java @@ -0,0 +1,119 @@ +package com.github.mikephil.charting.jobs; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +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. + */ +@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; + + protected float zoomCenterX; + protected float zoomCenterY; + + protected YAxis yAxis; + + protected float xAxisRange; + + @SuppressLint("NewApi") + 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; + this.zoomCenterY = zoomCenterY; + this.zoomOriginX = zoomOriginX; + this.zoomOriginY = zoomOriginY; + this.animator.addListener(this); + this.yAxis = axis; + 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 = mOnAnimationUpdateMatrixBuffer; + mViewPortHandler.setZoom(scaleX, scaleY, save); + mViewPortHandler.refresh(save, view, false); + + float valsInView = yAxis.mAxisRange / mViewPortHandler.getScaleY(); + float xsInView = xAxisRange / mViewPortHandler.getScaleX(); + + pts[0] = zoomOriginX + ((zoomCenterX - xsInView / 2f) - zoomOriginX) * phase; + pts[1] = zoomOriginY + ((zoomCenterY + valsInView / 2f) - zoomOriginY) * phase; + + mTrans.pointValuesToPixel(pts); + + mViewPortHandler.translate(pts, save); + mViewPortHandler.refresh(save, view, true); + } + + @Override + public void onAnimationEnd(Animator animation) { + ((BarLineChartBase) view).calculateOffsets(); + view.postInvalidate(); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + 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/MoveViewJob.java b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java similarity index 53% rename from MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java index 8fddd48858..c424e4b87a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/jobs/MoveViewJob.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/jobs/ViewPortJob.java @@ -3,42 +3,45 @@ 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; /** * Runnable that is used for viewport modifications since they cannot be * executed at any time. This can be used to delay the execution of viewport - * modifications until the onSizeChanged(...) method of the chartview is called. + * modifications until the onSizeChanged(...) method of the chart-view is called. + * This is especially important if viewport modifying methods are called on the chart + * directly after initialization. * * @author Philipp Jahoda */ -public class MoveViewJob implements Runnable { +public abstract class ViewPortJob extends ObjectPool.Poolable implements Runnable { + + protected float[] pts = new float[2]; protected ViewPortHandler mViewPortHandler; - protected float xIndex = 0f; + protected float xValue = 0f; protected float yValue = 0f; protected Transformer mTrans; protected View view; - public MoveViewJob(ViewPortHandler viewPortHandler, float xIndex, float yValue, - Transformer trans, View v) { + public ViewPortJob(ViewPortHandler viewPortHandler, float xValue, float yValue, + Transformer trans, View v) { this.mViewPortHandler = viewPortHandler; - this.xIndex = xIndex; + this.xValue = xValue; this.yValue = yValue; this.mTrans = trans; this.view = v; - } - @Override - public void run() { + } - float[] pts = new float[] { - xIndex, yValue - }; + public float getXValue() { + return xValue; + } - mTrans.pointValuesToPixel(pts); - mViewPortHandler.centerViewPort(pts, view); + public float getYValue() { + return yValue; } } 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 54% 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 229ae57815..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,90 +1,95 @@ - package com.github.mikephil.charting.listener; import android.annotation.SuppressLint; import android.graphics.Matrix; import android.graphics.PointF; import android.util.Log; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.View.OnTouchListener; import android.view.animation.AnimationUtils; import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.charts.HorizontalBarChart; -import com.github.mikephil.charting.data.BarLineScatterCandleData; -import com.github.mikephil.charting.data.BarLineScatterCandleDataSet; -import com.github.mikephil.charting.data.DataSet; +import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.Highlight; +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 SimpleOnGestureListener implements OnTouchListener { - - // private static final long REFRESH_MILLIS = 20; +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(); - - /** center between two pointers (fingers on the display) */ - private PointF mTouchPointCenter = new PointF(); - - // states - private static final int NONE = 0; - private static final int DRAG = 1; - - private static final int X_ZOOM = 2; - private static final int Y_ZOOM = 3; - private static final int PINCH_ZOOM = 4; - private static final int POST_ZOOM = 5; + /** + * point where the touch action started + */ + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); - /** integer field that holds the current touch-state */ - private int mTouchMode = NONE; + /** + * center between two pointers (fingers on the display) + */ + private MPPointF mTouchPointCenter = MPPointF.getInstance(0,0); private float mSavedXDist = 1f; private float mSavedYDist = 1f; private float mSavedDist = 1f; - /** the last highlighted object */ - private Highlight mLastHighlighted; - - private DataSet mClosestDataSetToTouch; - - /** the chart the listener represents */ - private T mChart; + private IDataSet mClosestDataSetToTouch; - /** the gesturedetector used for detecting taps and longpresses, ... */ - private GestureDetector mGestureDetector; - - /** used for tracking velocity of dragging */ + /** + * used for tracking velocity of dragging + */ private VelocityTracker mVelocityTracker; private long mDecelerationLastTime = 0; - private PointF mDecelerationCurrentPoint = new PointF(); - private PointF mDecelerationVelocity = new PointF(); + private MPPointF mDecelerationCurrentPoint = MPPointF.getInstance(0,0); + private MPPointF mDecelerationVelocity = MPPointF.getInstance(0,0); + + /** + * the distance of movement that will be counted as a drag + */ + private float mDragTriggerDist; + + /** + * the minimum distance between the pointers that will trigger a zoom gesture + */ + private float mMinScalePointerDistance; - public BarLineChartTouchListener(T chart, Matrix touchMatrix) { - this.mChart = chart; + /** + * 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; - mGestureDetector = new GestureDetector(chart.getContext(), this); + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); + + this.mMinScalePointerDistance = Utils.convertDpToPixel(3.5f); } @SuppressLint("ClickableViewAccessibility") @@ -115,11 +120,14 @@ public boolean onTouch(View v, MotionEvent event) { case MotionEvent.ACTION_DOWN: + startAction(event); + stopDeceleration(); saveTouchStart(event); break; + case MotionEvent.ACTION_POINTER_DOWN: if (event.getPointerCount() >= 2) { @@ -142,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; + } } } @@ -153,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) { @@ -169,20 +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) { + + float distanceX = Math.abs(event.getX() - mTouchStartPoint.x); + float distanceY = Math.abs(event.getY() - mTouchStartPoint.y); - if (mChart.hasNoDragOffset()) { + // Disable dragging in a direction that's disallowed + if ((mChart.isDragXEnabled() || distanceY >= distanceX) && + (mChart.isDragYEnabled() || distanceY <= distanceX)) { - if (!mChart.isFullyZoomedOut() && mChart.isDragEnabled()) - mTouchMode = DRAG; - else { - if (mChart.isHighlightPerDragEnabled()) - performHighlightDrag(event); + mLastGesture = ChartGesture.DRAG; + mTouchMode = DRAG; + } + + } else { + + if (mChart.isHighlightPerDragEnabled()) { + mLastGesture = ChartGesture.DRAG; + + if (mChart.isHighlightPerDragEnabled()) + performHighlightDrag(event); + } } - } else if (mChart.isDragEnabled()) { - mTouchMode = DRAG; } + } break; @@ -202,13 +234,30 @@ 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 } } + if (mTouchMode == X_ZOOM || + mTouchMode == Y_ZOOM || + mTouchMode == PINCH_ZOOM || + mTouchMode == POST_ZOOM) { + + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + mChart.calculateOffsets(); + mChart.postInvalidate(); + } + mTouchMode = NONE; mChart.enableScroll(); @@ -217,6 +266,8 @@ public boolean onTouch(View v, MotionEvent event) { mVelocityTracker = null; } + endAction(event); + break; case MotionEvent.ACTION_POINTER_UP: Utils.velocityTrackerPointerUpCleanUpIfNecessary(event, mVelocityTracker); @@ -227,12 +278,11 @@ public boolean onTouch(View v, MotionEvent event) { case MotionEvent.ACTION_CANCEL: mTouchMode = NONE; - + endAction(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 @@ -245,141 +295,148 @@ 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; mMatrix.set(mSavedMatrix); 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) { - float scale = totalDist / mSavedDist; // total - // scale + mLastGesture = ChartGesture.PINCH_ZOOM; + + float scale = totalDist / mSavedDist; // total scale + + boolean isZoomingOut = (scale < 1); + + boolean canZoomMoreX = isZoomingOut ? + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + boolean canZoomMoreY = isZoomingOut ? + h.canZoomOutMoreY() : + h.canZoomInMoreY(); float scaleX = (mChart.isScaleXEnabled()) ? scale : 1f; float scaleY = (mChart.isScaleYEnabled()) ? scale : 1f; - mMatrix.set(mSavedMatrix); - mMatrix.postScale(scaleX, scaleY, t.x, t.y); + if (canZoomMoreY || canZoomMoreX) { + + mMatrix.set(mSavedMatrix); + mMatrix.postScale(scaleX, scaleY, t.x, t.y); - if (l != null) - l.onChartScale(event, scaleX, scaleY); + if (l != null) + l.onChartScale(event, scaleX, scaleY); + } } else if (mTouchMode == X_ZOOM && mChart.isScaleXEnabled()) { + mLastGesture = ChartGesture.X_ZOOM; + float xDist = getXDist(event); - float scaleX = xDist / mSavedXDist; // x-axis - // scale + float scaleX = xDist / mSavedXDist; // x-axis scale + + boolean isZoomingOut = (scaleX < 1); + boolean canZoomMoreX = isZoomingOut ? + h.canZoomOutMoreX() : + h.canZoomInMoreX(); + + if (canZoomMoreX) { - mMatrix.set(mSavedMatrix); - mMatrix.postScale(scaleX, 1f, t.x, t.y); + mMatrix.set(mSavedMatrix); + mMatrix.postScale(scaleX, 1f, t.x, t.y); - if (l != null) - l.onChartScale(event, scaleX, 1f); + if (l != null) + l.onChartScale(event, scaleX, 1f); + } } else if (mTouchMode == Y_ZOOM && mChart.isScaleYEnabled()) { + mLastGesture = ChartGesture.Y_ZOOM; + float yDist = getYDist(event); - float scaleY = yDist / mSavedYDist; // y-axis - // scale + 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); - /** - * Perform a highlight operation. - * - * @param e - */ - private void performHighlight(MotionEvent e) { - - Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + if (l != null) + l.onChartScale(event, 1f, scaleY); + } + } - if (h == null || h.equalTo(mLastHighlighted)) { - mChart.highlightTouch(null); - mLastHighlighted = null; - } else { - mLastHighlighted = h; - mChart.highlightTouch(h); + MPPointF.recycleInstance(t); + } } } /** * Highlights upon dragging, generates callbacks for the selection-listener. - * + * * @param e */ private void performHighlightDrag(MotionEvent e) { @@ -388,7 +445,7 @@ private void performHighlightDrag(MotionEvent e) { if (h != null && !h.equalTo(mLastHighlighted)) { mLastHighlighted = h; - mChart.highlightTouch(h); + mChart.highlightValue(h, true); } } @@ -397,36 +454,23 @@ private void performHighlightDrag(MotionEvent e) { */ /** DOING THE MATH BELOW ;-) */ - /** - * returns the distance between two points - * - * @param eventX - * @param startX - * @param eventY - * @param startY - * @return - */ - private static float distance(float eventX, float startX, float eventY, float startY) { - float dx = eventX - startX; - float dy = eventY - startY; - return (float) Math.sqrt(dx * dx + dy * dy); - } /** * 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 */ @@ -439,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 */ @@ -451,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 */ @@ -461,13 +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 e + * + * @param x + * @param y * @return */ - public PointF getTrans(float x, float y) { + public MPPointF getTrans(float x, float y) { ViewPortHandler vph = mChart.getViewPortHandler(); @@ -475,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())); } /** @@ -492,7 +547,7 @@ public PointF getTrans(float x, float y) { /** * returns the matrix object the listener holds - * + * * @return */ public Matrix getMatrix() { @@ -500,34 +555,45 @@ public Matrix getMatrix() { } /** - * returns the touch mode the listener is currently in - * - * @return + * Sets the minimum distance that will be interpreted as a "drag" by the chart in dp. + * Default: 3dp + * + * @param dragTriggerDistance */ - public int getTouchMode() { - return mTouchMode; + public void setDragTriggerDist(float dragTriggerDistance) { + this.mDragTriggerDist = Utils.convertDpToPixel(dragTriggerDistance); } @Override public boolean onDoubleTap(MotionEvent e) { + mLastGesture = ChartGesture.DOUBLE_TAP; + OnChartGestureListener l = mChart.getOnChartGestureListener(); if (l != null) { l.onChartDoubleTapped(e); - return super.onDoubleTap(e); } // check if double-tap zooming is enabled - if (mChart.isDoubleTapToZoomEnabled()) { + if (mChart.isDoubleTapToZoomEnabled() && mChart.getData().getEntryCount() > 0) { + + MPPointF trans = getTrans(e.getX(), e.getY()); - PointF trans = getTrans(e.getX(), e.getY()); + float scaleX = mChart.isScaleXEnabled() ? 1.4f : 1f; + float scaleY = mChart.isScaleYEnabled() ? 1.4f : 1f; - mChart.zoom(1.4f, 1.4f, trans.x, trans.y); + 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); @@ -536,6 +602,8 @@ public boolean onDoubleTap(MotionEvent e) { @Override public void onLongPress(MotionEvent e) { + mLastGesture = ChartGesture.LONG_PRESS; + OnChartGestureListener l = mChart.getOnChartGestureListener(); if (l != null) { @@ -547,13 +615,7 @@ public void onLongPress(MotionEvent e) { @Override public boolean onSingleTapUp(MotionEvent e) { - performHighlight(e); - - return super.onSingleTapUp(e); - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { + mLastGesture = ChartGesture.SINGLE_TAP; OnChartGestureListener l = mChart.getOnChartGestureListener(); @@ -561,22 +623,33 @@ public boolean onSingleTapConfirmed(MotionEvent e) { l.onChartSingleTapped(e); } - return super.onSingleTapConfirmed(e); + if (!mChart.isHighlightPerTapEnabled()) { + return false; + } + + Highlight h = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(h, e); + + return super.onSingleTapUp(e); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + mLastGesture = ChartGesture.FLING; + OnChartGestureListener l = mChart.getOnChartGestureListener(); - if (l != null) + if (l != null) { l.onChartFling(e1, e2, velocityX, velocityY); + } return super.onFling(e1, e2, velocityX, velocityY); } public void stopDeceleration() { - mDecelerationVelocity = new PointF(0.f, 0.f); + mDecelerationVelocity.x = 0; + mDecelerationVelocity.y = 0; } public void computeScroll() { @@ -589,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; @@ -597,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); @@ -606,7 +685,14 @@ public void computeScroll() { if (Math.abs(mDecelerationVelocity.x) >= 0.01 || Math.abs(mDecelerationVelocity.y) >= 0.01) Utils.postInvalidateOnAnimation(mChart); // This causes computeScroll to fire, recommended for this by Google - else + else { + // Range might have changed, which means that Y-axis labels + // could have changed in size, affecting Y-axis size. + // So we need to recalculate offsets. + mChart.calculateOffsets(); + mChart.postInvalidate(); + stopDeceleration(); + } } } diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java new file mode 100644 index 0000000000..75c8e864b4 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/ChartTouchListener.java @@ -0,0 +1,143 @@ +package com.github.mikephil.charting.listener; + +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.highlight.Highlight; + +/** + * Created by philipp on 12/06/15. + */ +public abstract class ChartTouchListener> extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener { + + public enum ChartGesture { + NONE, DRAG, X_ZOOM, Y_ZOOM, PINCH_ZOOM, ROTATE, SINGLE_TAP, DOUBLE_TAP, LONG_PRESS, FLING + } + + /** + * the last touch gesture that has been performed + **/ + protected ChartGesture mLastGesture = ChartGesture.NONE; + + // states + protected static final int NONE = 0; + protected static final int DRAG = 1; + protected static final int X_ZOOM = 2; + protected static final int Y_ZOOM = 3; + protected static final int PINCH_ZOOM = 4; + protected static final int POST_ZOOM = 5; + protected static final int ROTATE = 6; + + /** + * integer field that holds the current touch-state + */ + protected int mTouchMode = NONE; + + /** + * the last highlighted object (via touch) + */ + protected Highlight mLastHighlighted; + + /** + * the gesturedetector used for detecting taps and longpresses, ... + */ + protected GestureDetector mGestureDetector; + + /** + * the chart the listener represents + */ + protected T mChart; + + public ChartTouchListener(T chart) { + this.mChart = chart; + + mGestureDetector = new GestureDetector(chart.getContext(), this); + } + + /** + * Calls the OnChartGestureListener to do the start callback + * + * @param me + */ + public void startAction(MotionEvent me) { + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) + l.onChartGestureStart(me, mLastGesture); + } + + /** + * Calls the OnChartGestureListener to do the end callback + * + * @param me + */ + public void endAction(MotionEvent me) { + + OnChartGestureListener l = mChart.getOnChartGestureListener(); + + if (l != null) + l.onChartGestureEnd(me, mLastGesture); + } + + /** + * Sets the last value that was highlighted via touch. + * + * @param high + */ + public void setLastHighlighted(Highlight high) { + mLastHighlighted = high; + } + + /** + * returns the touch mode the listener is currently in + * + * @return + */ + public int getTouchMode() { + return mTouchMode; + } + + /** + * Returns the last gesture that has been performed on the chart. + * + * @return + */ + public ChartGesture getLastGesture() { + return mLastGesture; + } + + + /** + * Perform a highlight operation. + * + * @param e + */ + protected void performHighlight(Highlight h, MotionEvent e) { + + if (h == null || h.equalTo(mLastHighlighted)) { + mChart.highlightValue(null, true); + mLastHighlighted = null; + } else { + mChart.highlightValue(h, true); + mLastHighlighted = h; + } + } + + /** + * returns the distance between two points + * + * @param eventX + * @param startX + * @param eventY + * @param startY + * @return + */ + protected static float distance(float eventX, float startX, float eventY, float startY) { + float dx = eventX - startX; + float dy = eventY - startY; + return (float) Math.sqrt(dx * dx + dy * dy); + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java new file mode 100644 index 0000000000..da0c5ed180 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartGestureListener.java @@ -0,0 +1,76 @@ +package com.github.mikephil.charting.listener; + +import android.view.MotionEvent; + +/** + * Listener for callbacks when doing gestures on the chart. + * + * @author Philipp Jahoda + */ +public interface OnChartGestureListener { + + /** + * Callbacks when a touch-gesture has started on the chart (ACTION_DOWN) + * + * @param me + * @param lastPerformedGesture + */ + void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture); + + /** + * Callbacks when a touch-gesture has ended on the chart (ACTION_UP, ACTION_CANCEL) + * + * @param me + * @param lastPerformedGesture + */ + void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture); + + /** + * Callbacks when the chart is longpressed. + * + * @param me + */ + void onChartLongPressed(MotionEvent me); + + /** + * Callbacks when the chart is double-tapped. + * + * @param me + */ + void onChartDoubleTapped(MotionEvent me); + + /** + * Callbacks when the chart is single-tapped. + * + * @param me + */ + void onChartSingleTapped(MotionEvent me); + + /** + * Callbacks then a fling gesture is made on the chart. + * + * @param me1 + * @param me2 + * @param velocityX + * @param velocityY + */ + void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY); + + /** + * Callbacks when the chart is scaled / zoomed via pinch zoom / double-tap gesture. + * + * @param me + * @param scaleX scalefactor on the x-axis + * @param scaleY scalefactor on the y-axis + */ + void onChartScale(MotionEvent me, float scaleX, float scaleY); + + /** + * Callbacks when the chart is moved / translated via drag gesture. + * + * @param me + * @param dX translation distance on the x-axis + * @param dY translation distance on the y-axis + */ + void onChartTranslate(MotionEvent me, float dX, float dY); +} 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 50% 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 798d19f34f..7f50232b7e 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnChartValueSelectedListener.java @@ -1,30 +1,27 @@ - package com.github.mikephil.charting.listener; import com.github.mikephil.charting.data.Entry; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.highlight.Highlight; /** * 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, ... */ - public 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. */ - public void onNothingSelected(); + void onNothingSelected(); } 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 85% rename from MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java rename to MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java index 940d0ed18a..5890350bcd 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/OnDrawListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/OnDrawListener.java @@ -18,14 +18,14 @@ public interface OnDrawListener { * @param entry * the last drawn entry */ - public void onEntryAdded(Entry entry); + void onEntryAdded(Entry entry); /** * Called whenever an entry is moved by the user after beeing highlighted * * @param entry */ - public void onEntryMoved(Entry entry); + void onEntryMoved(Entry entry); /** * Called when drawing finger is lifted and the draw is finished. @@ -33,6 +33,6 @@ public interface OnDrawListener { * @param dataSet * the last drawn DataSet */ - public void onDrawFinished(DataSet dataSet); + void onDrawFinished(DataSet dataSet); } 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 64% 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 78bd9f269a..d3527f924a 100644 --- a/MPChartLib/src/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/listener/PieRadarChartTouchListener.java @@ -3,53 +3,38 @@ import android.annotation.SuppressLint; import android.graphics.PointF; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnTouchListener; 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.utils.Highlight; -import com.github.mikephil.charting.utils.SelInfo; +import com.github.mikephil.charting.highlight.Highlight; +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. - * + * * @author Philipp Jahoda */ -public class PieRadarChartTouchListener extends SimpleOnGestureListener implements OnTouchListener { +public class PieRadarChartTouchListener extends ChartTouchListener> { - private static final int NONE = 0; - private static final int ROTATE = 1; + private MPPointF mTouchStartPoint = MPPointF.getInstance(0,0); - private PointF mTouchStartPoint = new PointF(); - - private PieRadarChartBase mChart; - - /** the angle where the dragging started */ + /** + * the angle where the dragging started + */ private float mStartAngle = 0f; - private int mTouchMode = NONE; - - private GestureDetector mGestureDetector; - private ArrayList _velocitySamples = new ArrayList(); private long mDecelerationLastTime = 0; private float mDecelerationAngularVelocity = 0.f; - public PieRadarChartTouchListener(PieRadarChartBase ctx) { - this.mChart = ctx; - - mGestureDetector = new GestureDetector(ctx.getContext(), this); + public PieRadarChartTouchListener(PieRadarChartBase chart) { + super(chart); } @SuppressLint("ClickableViewAccessibility") @@ -60,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(); @@ -69,6 +55,8 @@ public boolean onTouch(View v, MotionEvent event) { case MotionEvent.ACTION_DOWN: + startAction(event); + stopDeceleration(); resetVelocity(); @@ -89,6 +77,7 @@ public boolean onTouch(View v, MotionEvent event) { if (mTouchMode == NONE && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) > Utils.convertDpToPixel(8f)) { + mLastGesture = ChartGesture.ROTATE; mTouchMode = ROTATE; mChart.disableScroll(); } else if (mTouchMode == ROTATE) { @@ -96,6 +85,8 @@ && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) mChart.invalidate(); } + endAction(event); + break; case MotionEvent.ACTION_UP: @@ -117,6 +108,8 @@ && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) mChart.enableScroll(); mTouchMode = NONE; + endAction(event); + break; } } @@ -126,6 +119,9 @@ && distance(x, mTouchStartPoint.x, y, mTouchStartPoint.y) @Override public void onLongPress(MotionEvent me) { + + mLastGesture = ChartGesture.LONG_PRESS; + OnChartGestureListener l = mChart.getOnChartGestureListener(); if (l != null) { @@ -138,114 +134,51 @@ public boolean onSingleTapConfirmed(MotionEvent e) { return true; } - /** reference to the last highlighted object */ - private Highlight mLastHighlight = null; - @Override public boolean onSingleTapUp(MotionEvent e) { + mLastGesture = ChartGesture.SINGLE_TAP; + OnChartGestureListener l = mChart.getOnChartGestureListener(); if (l != null) { l.onChartSingleTapped(e); } - 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 - mChart.highlightValues(null); - mLastHighlight = 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); - mLastHighlight = null; - - } else { - - List valsAtIndex = mChart.getYValsAtIndex(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); - } - - Highlight h = new Highlight(index, dataSetIndex); - - if (h.equalTo(mLastHighlight)) { - - mChart.highlightTouch(null); - mLastHighlight = null; - } else { - - mChart.highlightTouch(h); - mLastHighlight = h; - } - } + if(!mChart.isHighlightPerTapEnabled()) { + return false; } - return true; - } + Highlight high = mChart.getHighlightByTouchPoint(e.getX(), e.getY()); + performHighlight(high, e); - @Override - public boolean onDoubleTap(MotionEvent e) { - OnChartGestureListener l = mChart.getOnChartGestureListener(); - - if (l != null) { - l.onChartDoubleTapped(e); - } - return super.onDoubleTap(e); + return true; } - private void resetVelocity() - { + private void resetVelocity() { _velocitySamples.clear(); } - private void sampleVelocity(float touchLocationX, float touchLocationY) - { + private void sampleVelocity(float touchLocationX, float touchLocationY) { + long currentTime = AnimationUtils.currentAnimationTimeMillis(); _velocitySamples.add(new AngularVelocitySample(currentTime, mChart.getAngleForPoint(touchLocationX, touchLocationY))); // Remove samples older than our sample time - 1 seconds - for (int i = 0, count = _velocitySamples.size(); i < count - 2; i++) - { - if (currentTime - _velocitySamples.get(i).time > 1000) - { + for (int i = 0, count = _velocitySamples.size(); i < count - 2; i++) { + if (currentTime - _velocitySamples.get(i).time > 1000) { _velocitySamples.remove(0); i--; count--; - } - else - { + } else { break; } } } - private float calculateVelocity() - { + private float calculateVelocity() { + if (_velocitySamples.isEmpty()) return 0.f; @@ -254,37 +187,30 @@ private float calculateVelocity() // Look for a sample that's closest to the latest sample, but not the same, so we can deduce the direction AngularVelocitySample beforeLastSample = firstSample; - for (int i = _velocitySamples.size() - 1; i >= 0; i--) - { + for (int i = _velocitySamples.size() - 1; i >= 0; i--) { beforeLastSample = _velocitySamples.get(i); - if (beforeLastSample.angle != lastSample.angle) - { + if (beforeLastSample.angle != lastSample.angle) { break; } } // Calculate the sampling time float timeDelta = (lastSample.time - firstSample.time) / 1000.f; - if (timeDelta == 0.f) - { + if (timeDelta == 0.f) { timeDelta = 0.1f; } // Calculate clockwise/ccw by choosing two values that should be closest to each other, // so if the angles are two far from each other we know they are inverted "for sure" boolean clockwise = lastSample.angle >= beforeLastSample.angle; - if (Math.abs(lastSample.angle - beforeLastSample.angle) > 270.0) - { + if (Math.abs(lastSample.angle - beforeLastSample.angle) > 270.0) { clockwise = !clockwise; } // Now if the "gesture" is over a too big of an angle - then we know the angles are inverted, and we need to move them closer to each other from both sides of the 360.0 wrapping point - if (lastSample.angle - firstSample.angle > 180.0) - { + if (lastSample.angle - firstSample.angle > 180.0) { firstSample.angle += 360.0; - } - else if (firstSample.angle - lastSample.angle > 180.0) - { + } else if (firstSample.angle - lastSample.angle > 180.0) { lastSample.angle += 360.0; } @@ -292,8 +218,7 @@ else if (firstSample.angle - lastSample.angle > 180.0) float velocity = Math.abs((lastSample.angle - firstSample.angle) / timeDelta); // Direction? - if (!clockwise) - { + if (!clockwise) { velocity = -velocity; } @@ -308,9 +233,7 @@ else if (firstSample.angle - lastSample.angle > 180.0) * @param y */ public void setGestureStartAngle(float x, float y) { - mStartAngle = mChart.getAngleForPoint(x, y) - mChart.getRawRotationAngle(); - } /** @@ -321,26 +244,12 @@ public void setGestureStartAngle(float x, float y) { * @param y */ public void updateGestureRotation(float x, float y) { - mChart.setRotationAngle(mChart.getAngleForPoint(x, y) - mStartAngle); - } /** - * returns the distance between two points - * - * @param eventX - * @param startX - * @param eventY - * @param startY - * @return + * Sets the deceleration-angular-velocity to 0f */ - private static float distance(float eventX, float startX, float eventY, float startY) { - float dx = eventX - startX; - float dy = eventY - startY; - return (float) Math.sqrt(dx * dx + dy * dy); - } - public void stopDeceleration() { mDecelerationAngularVelocity = 0.f; } @@ -354,7 +263,7 @@ public void computeScroll() { mDecelerationAngularVelocity *= mChart.getDragDecelerationFrictionCoef(); - final float timeInterval = (float)(currentTime - mDecelerationLastTime) / 1000.f; + final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f; mChart.setRotationAngle(mChart.getRotationAngle() + mDecelerationAngularVelocity * timeInterval); @@ -366,13 +275,12 @@ public void computeScroll() { stopDeceleration(); } - private class AngularVelocitySample - { + private class AngularVelocitySample { + public long time; public float angle; - public AngularVelocitySample(long time, float angle) - { + public AngularVelocitySample(long time, float angle) { this.time = time; this.angle = angle; } 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 53% 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 3031b460b9..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,14 +4,15 @@ 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; -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.interfaces.BubbleDataProvider; -import com.github.mikephil.charting.utils.Highlight; +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; @@ -22,12 +23,12 @@ * 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; public BubbleChartRenderer(BubbleDataProvider chart, ChartAnimator animator, - ViewPortHandler viewPortHandler) { + ViewPortHandler viewPortHandler) { super(animator, viewPortHandler); mChart = chart; @@ -47,7 +48,7 @@ public void drawData(Canvas c) { BubbleData bubbleData = mChart.getBubbleData(); - for (BubbleDataSet set : bubbleData.getDataSets()) { + for (IBubbleDataSet set : bubbleData.getDataSets()) { if (set.isVisible()) drawDataSet(c, set); @@ -57,46 +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, BubbleDataSet dataSet) { + 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(); - List entries = dataSet.getYVals(); - - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); - - int minx = Math.max(dataSet.getEntryPosition(entryFrom), 0); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, entries.size()); + 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 = entries.get(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)) @@ -108,7 +108,7 @@ protected void drawDataSet(Canvas c, BubbleDataSet 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); @@ -124,41 +124,42 @@ 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(); + final List dataSets = bubbleData.getDataSets(); float lineHeight = Utils.calcTextHeight(mValuePaint, "1"); - for (BubbleDataSet dataSet : dataSets) { + for (int i = 0; i < dataSets.size(); i++) { - if (!dataSet.isDrawValuesEnabled()) - continue; + IBubbleDataSet dataSet = dataSets.get(i); - final float phaseX = mAnimator.getPhaseX(); - final float phaseY = mAnimator.getPhaseY(); + if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1) + continue; - final float alpha = phaseX == 1 ? phaseY : phaseX; - int valueTextColor = dataSet.getValueTextColor(); - valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), - Color.green(valueTextColor), Color.blue(valueTextColor)); + // apply the text-styling defined by the DataSet + applyValueTextStyle(dataSet); - mValuePaint.setColor(valueTextColor); + final float phaseX = Math.max(0.f, Math.min(1.f, mAnimator.getPhaseX())); + final float phaseY = mAnimator.getPhaseY(); - final List entries = dataSet.getYVals(); + mXBounds.set(mChart, dataSet); - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); + final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) + .generateTransformedValuesBubble(dataSet, phaseY, mXBounds.min, mXBounds.max); - int minx = dataSet.getEntryPosition(entryFrom); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, dataSet.getEntryCount()); + final float alpha = phaseX == 1 ? phaseY : phaseX; - final float[] positions = mChart.getTransformer(dataSet.getAxisDependency()) - .generateTransformedValuesBubble(entries, phaseX, phaseY, minx, maxx); + 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 + mXBounds.min); + valueTextColor = Color.argb(Math.round(255.f * alpha), Color.red(valueTextColor), + Color.green(valueTextColor), Color.blue(valueTextColor)); + float x = positions[j]; float y = positions[j + 1]; @@ -168,18 +169,30 @@ public void drawValues(Canvas c) { if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) continue; - final BubbleEntry entry = entries.get(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); + } - final float val = entry.getSize(); + if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) { - c.drawText(dataSet.getValueFormatter().getFormattedValue(val), - x, - y + (0.5f * lineHeight), - mValuePaint); + Drawable icon = entry.getIcon(); + + Utils.drawImage( + c, + icon, + (int)(x + iconsOffset.x), + (int)(y + iconsOffset.y), + icon.getIntrinsicWidth(), + icon.getIntrinsicHeight()); + } } + + MPPointF.recycleInstance(iconsOffset); } } - } @Override @@ -193,41 +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) { - BubbleDataSet dataSet = bubbleData.getDataSetByIndex(indice.getDataSetIndex()); + IBubbleDataSet set = bubbleData.getDataSetByIndex(high.getDataSetIndex()); - if (dataSet == null) + if (set == null || !set.isHighlightEnabled()) continue; - Entry entryFrom = dataSet.getEntryForXIndex(mMinX); - Entry entryTo = dataSet.getEntryForXIndex(mMaxX); + final BubbleEntry entry = set.getEntryForXValue(high.getX(), high.getY()); - int minx = dataSet.getEntryPosition(entryFrom); - int maxx = Math.min(dataSet.getEntryPosition(entryTo) + 1, dataSet.getEntryCount()); + if (entry.getY() != high.getY()) + continue; - final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice); + if (!isInBoundsX(entry, set)) + continue; + + Transformer trans = mChart.getTransformer(set.getAxisDependency()); - Transformer trans = mChart.getTransformer(dataSet.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)) @@ -239,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); @@ -250,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/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java new file mode 100644 index 0000000000..991b702117 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CandleStickChartRenderer.java @@ -0,0 +1,363 @@ + +package com.github.mikephil.charting.renderer; + +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; +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.highlight.Highlight; +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; + +import java.util.List; + +public class CandleStickChartRenderer extends LineScatterCandleRadarRenderer { + + protected CandleDataProvider mChart; + + private float[] mShadowBuffers = new float[8]; + private float[] mBodyBuffers = new float[4]; + private float[] mRangeBuffers = new float[4]; + private float[] mOpenBuffers = new float[4]; + private float[] mCloseBuffers = new float[4]; + + public CandleStickChartRenderer(CandleDataProvider chart, ChartAnimator animator, + ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + mChart = chart; + } + + @Override + public void initBuffers() { + + } + + @Override + public void drawData(Canvas c) { + + CandleData candleData = mChart.getCandleData(); + + for (ICandleDataSet set : candleData.getDataSets()) { + + if (set.isVisible()) + drawDataSet(c, set); + } + } + + @SuppressWarnings("ResourceAsColor") + protected void drawDataSet(Canvas c, ICandleDataSet dataSet) { + + Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); + + float phaseY = mAnimator.getPhaseY(); + float barSpace = dataSet.getBarSpace(); + boolean showCandleBar = dataSet.getShowCandleBar(); + + mXBounds.set(mChart, dataSet); + + mRenderPaint.setStrokeWidth(dataSet.getShadowWidth()); + + // draw the body + for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) { + + // get the entry + CandleEntry e = dataSet.getEntryForIndex(j); + + if (e == null) + continue; + + final float xPos = e.getX(); + + final float open = e.getOpen(); + final float close = e.getClose(); + final float high = e.getHigh(); + final float low = e.getLow(); + + if (showCandleBar) { + // calculate the shadow + + mShadowBuffers[0] = xPos; + mShadowBuffers[2] = xPos; + mShadowBuffers[4] = xPos; + mShadowBuffers[6] = xPos; + + if (open > close) { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = open * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = close * phaseY; + } else if (open < close) { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = close * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = open * phaseY; + } else { + mShadowBuffers[1] = high * phaseY; + mShadowBuffers[3] = open * phaseY; + mShadowBuffers[5] = low * phaseY; + mShadowBuffers[7] = mShadowBuffers[3]; + } + + trans.pointValuesToPixel(mShadowBuffers); + + // draw the shadows + + if (dataSet.getShadowColorSameAsCandle()) { + + if (open > close) + mRenderPaint.setColor( + dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getDecreasingColor() + ); + + else if (open < close) + mRenderPaint.setColor( + dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getIncreasingColor() + ); + + else + mRenderPaint.setColor( + dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getNeutralColor() + ); + + } else { + mRenderPaint.setColor( + dataSet.getShadowColor() == ColorTemplate.COLOR_NONE ? + dataSet.getColor(j) : + dataSet.getShadowColor() + ); + } + + mRenderPaint.setStyle(Paint.Style.STROKE); + + c.drawLines(mShadowBuffers, mRenderPaint); + + // calculate the body + + mBodyBuffers[0] = xPos - 0.5f + barSpace; + mBodyBuffers[1] = close * phaseY; + mBodyBuffers[2] = (xPos + 0.5f - barSpace); + mBodyBuffers[3] = open * phaseY; + + trans.pointValuesToPixel(mBodyBuffers); + + // draw body differently for increasing and decreasing entry + if (open > close) { // decreasing + + if (dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getDecreasingColor()); + } + + mRenderPaint.setStyle(dataSet.getDecreasingPaintStyle()); + + c.drawRect( + mBodyBuffers[0], mBodyBuffers[3], + mBodyBuffers[2], mBodyBuffers[1], + mRenderPaint); + + } else if (open < close) { + + if (dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getIncreasingColor()); + } + + mRenderPaint.setStyle(dataSet.getIncreasingPaintStyle()); + + c.drawRect( + mBodyBuffers[0], mBodyBuffers[1], + mBodyBuffers[2], mBodyBuffers[3], + mRenderPaint); + } else { // equal values + + if (dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE) { + mRenderPaint.setColor(dataSet.getColor(j)); + } else { + mRenderPaint.setColor(dataSet.getNeutralColor()); + } + + c.drawLine( + mBodyBuffers[0], mBodyBuffers[1], + mBodyBuffers[2], mBodyBuffers[3], + mRenderPaint); + } + } else { + + mRangeBuffers[0] = xPos; + mRangeBuffers[1] = high * phaseY; + mRangeBuffers[2] = xPos; + mRangeBuffers[3] = low * phaseY; + + mOpenBuffers[0] = xPos - 0.5f + barSpace; + mOpenBuffers[1] = open * phaseY; + mOpenBuffers[2] = xPos; + mOpenBuffers[3] = open * phaseY; + + mCloseBuffers[0] = xPos + 0.5f - barSpace; + mCloseBuffers[1] = close * phaseY; + mCloseBuffers[2] = xPos; + mCloseBuffers[3] = close * phaseY; + + trans.pointValuesToPixel(mRangeBuffers); + trans.pointValuesToPixel(mOpenBuffers); + trans.pointValuesToPixel(mCloseBuffers); + + // draw the ranges + int barColor; + + if (open > close) + barColor = dataSet.getDecreasingColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getDecreasingColor(); + else if (open < close) + barColor = dataSet.getIncreasingColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getIncreasingColor(); + else + barColor = dataSet.getNeutralColor() == ColorTemplate.COLOR_NONE + ? dataSet.getColor(j) + : dataSet.getNeutralColor(); + + mRenderPaint.setColor(barColor); + c.drawLine( + mRangeBuffers[0], mRangeBuffers[1], + mRangeBuffers[2], mRangeBuffers[3], + mRenderPaint); + c.drawLine( + mOpenBuffers[0], mOpenBuffers[1], + mOpenBuffers[2], mOpenBuffers[3], + mRenderPaint); + c.drawLine( + mCloseBuffers[0], mCloseBuffers[1], + mCloseBuffers[2], mCloseBuffers[3], + mRenderPaint); + } + } + } + + @Override + public void drawValues(Canvas c) { + + // if values are drawn + if (isDrawingValuesAllowed(mChart)) { + + List dataSets = mChart.getCandleData().getDataSets(); + + for (int i = 0; i < dataSets.size(); i++) { + + ICandleDataSet 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()); + + mXBounds.set(mChart, dataSet); + + float[] positions = trans.generateTransformedValuesCandle( + 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]; + float y = positions[j + 1]; + + if (!mViewPortHandler.isInBoundsRight(x)) + break; + + if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y)) + continue; + + 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(); + + 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) { + } + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + CandleData candleData = mChart.getCandleData(); + + for (Highlight high : indices) { + + ICandleDataSet set = candleData.getDataSetByIndex(high.getDataSetIndex()); + + if (set == null || !set.isHighlightEnabled()) + continue; + + CandleEntry e = set.getEntryForXValue(high.getX(), high.getY()); + + if (!isInBoundsX(e, set)) + continue; + + float lowValue = e.getLow() * mAnimator.getPhaseY(); + float highValue = e.getHigh() * mAnimator.getPhaseY(); + float y = (lowValue + highValue) / 2f; + + MPPointD pix = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(e.getX(), y); + + 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/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java new file mode 100644 index 0000000000..6d0d4d3da0 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/CombinedChartRenderer.java @@ -0,0 +1,167 @@ +package com.github.mikephil.charting.renderer; + +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; + +/** + * Renderer class that is responsible for rendering multiple different data-types. + */ +public class CombinedChartRenderer extends DataRenderer { + + /** + * all rederers for the different kinds of data this combined-renderer can draw + */ + protected List mRenderers = new ArrayList(5); + + protected WeakReference mChart; + + public CombinedChartRenderer(CombinedChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(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. + */ + public void createRenderers() { + + mRenderers.clear(); + + CombinedChart chart = (CombinedChart)mChart.get(); + if (chart == null) + return; + + DrawOrder[] orders = chart.getDrawOrder(); + + for (DrawOrder order : orders) { + + switch (order) { + case BAR: + if (chart.getBarData() != null) + mRenderers.add(new BarChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case BUBBLE: + if (chart.getBubbleData() != null) + mRenderers.add(new BubbleChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case LINE: + if (chart.getLineData() != null) + mRenderers.add(new LineChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case CANDLE: + if (chart.getCandleData() != null) + mRenderers.add(new CandleStickChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + case SCATTER: + if (chart.getScatterData() != null) + mRenderers.add(new ScatterChartRenderer(chart, mAnimator, mViewPortHandler)); + break; + } + } + } + + @Override + public void initBuffers() { + + for (DataRenderer renderer : mRenderers) + renderer.initBuffers(); + } + + @Override + public void drawData(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawData(c); + } + + @Override + public void drawValues(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawValues(c); + } + + @Override + public void drawExtras(Canvas c) { + + for (DataRenderer renderer : mRenderers) + renderer.drawExtras(c); + } + + protected List mHighlightBuffer = new ArrayList(); + + @Override + public void drawHighlighted(Canvas c, Highlight[] indices) { + + 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()])); + } + } + + /** + * Returns the sub-renderer object at the specified index. + * + * @param index + * @return + */ + public DataRenderer getSubRenderer(int index) { + if (index >= mRenderers.size() || index < 0) + return null; + else + return mRenderers.get(index); + } + + /** + * Returns all sub-renderers. + * + * @return + */ + public List getSubRenderers() { + return mRenderers; + } + + public void setSubRenderers(List renderers) { + this.mRenderers = renderers; + } +} 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 54% 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 68a9615005..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,22 +6,38 @@ 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.DataSet; -import com.github.mikephil.charting.utils.Highlight; +import com.github.mikephil.charting.data.Entry; +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; +/** + * Superclass of all render classes for the different data types (line, bar, ...). + * + * @author Philipp Jahoda + */ public abstract class DataRenderer extends Renderer { - /** the animator object used to perform animations on the chart data */ + /** + * the animator object used to perform animations on the chart data + */ protected ChartAnimator mAnimator; - /** main paint object used for rendering */ + /** + * main paint object used for rendering + */ protected Paint mRenderPaint; - /** paint used for highlighting values */ + /** + * paint used for highlighting values + */ protected Paint mHighlightPaint; protected Paint mDrawPaint; @@ -52,10 +68,15 @@ 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). - * + * * @return */ public Paint getPaintValues() { @@ -65,7 +86,7 @@ public Paint getPaintValues() { /** * Returns the Paint object this renderer uses for drawing highlight * indicators. - * + * * @return */ public Paint getPaintHighlight() { @@ -74,7 +95,7 @@ public Paint getPaintHighlight() { /** * Returns the Paint object used for rendering. - * + * * @return */ public Paint getPaintRender() { @@ -84,12 +105,11 @@ public Paint getPaintRender() { /** * Applies the required styling (provided by the DataSet) to the value-paint * object. - * + * * @param set */ - protected void applyValueTextStyle(DataSet set) { + protected void applyValueTextStyle(IDataSet set) { - mValuePaint.setColor(set.getValueTextColor()); mValuePaint.setTypeface(set.getValueTypeface()); mValuePaint.setTextSize(set.getValueTextSize()); } @@ -101,11 +121,49 @@ protected void applyValueTextStyle(DataSet set) { */ public abstract void initBuffers(); + /** + * Draws the actual data in form of lines, bars, ... depending on Renderer subclass. + * + * @param c + */ public abstract void drawData(Canvas c); + /** + * Loops over all Entrys and draws their values. + * + * @param c + */ public abstract void drawValues(Canvas c); + /** + * Draws the value of the given entry by using the provided IValueFormatter. + * + * @param c canvas + * @param formatter formatter for custom value-formatting + * @param value the value to be drawn + * @param entry the entry the value belongs to + * @param dataSetIndex the index of the DataSet the drawn Entry belongs to + * @param x position + * @param y position + * @param 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); + } + + /** + * Draws any kind of additional information (e.g. line-circles). + * + * @param c + */ public abstract void drawExtras(Canvas c); + /** + * Draws all highlight indicators for the values that are currently highlighted. + * + * @param c + * @param indices the highlighted values + */ public abstract void drawHighlighted(Canvas c, Highlight[] indices); } 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/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java new file mode 100644 index 0000000000..53f54cbfd9 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/LineScatterCandleRadarRenderer.java @@ -0,0 +1,63 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Path; + +import com.github.mikephil.charting.animation.ChartAnimator; +import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet; +import com.github.mikephil.charting.utils.ViewPortHandler; + +/** + * Created by Philipp Jahoda on 11/07/15. + */ +public abstract class LineScatterCandleRadarRenderer extends BarLineScatterCandleBubbleRenderer { + + /** + * path that is used for drawing highlight-lines (drawLines(...) cannot be used because of dashes) + */ + private Path mHighlightLinePath = new Path(); + + public LineScatterCandleRadarRenderer(ChartAnimator animator, ViewPortHandler viewPortHandler) { + super(animator, viewPortHandler); + } + + /** + * Draws vertical & horizontal highlight-lines if enabled. + * + * @param c + * @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 x, float y, ILineScatterCandleRadarDataSet set) { + + // set color and stroke-width + mHighlightPaint.setColor(set.getHighLightColor()); + mHighlightPaint.setStrokeWidth(set.getHighlightLineWidth()); + + // draw highlighted lines (if enabled) + mHighlightPaint.setPathEffect(set.getDashPathEffectHighlight()); + + // draw vertical highlight lines + if (set.isVerticalHighlightIndicatorEnabled()) { + + // create vertical path + mHighlightLinePath.reset(); + mHighlightLinePath.moveTo(x, mViewPortHandler.contentTop()); + mHighlightLinePath.lineTo(x, mViewPortHandler.contentBottom()); + + c.drawPath(mHighlightLinePath, mHighlightPaint); + } + + // draw horizontal highlight lines + if (set.isHorizontalHighlightIndicatorEnabled()) { + + // create horizontal path + mHighlightLinePath.reset(); + 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/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java new file mode 100644 index 0000000000..8adb56c73a --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -0,0 +1,401 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +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.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; + +import java.util.List; + +public class XAxisRenderer extends AxisRenderer { + + protected XAxis mXAxis; + + public XAxisRenderer(ViewPortHandler viewPortHandler, XAxis xAxis, Transformer trans) { + super(viewPortHandler, trans, xAxis); + + this.mXAxis = xAxis; + + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextAlign(Align.CENTER); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + } + + protected void setupGridPaint() { + mGridPaint.setColor(mXAxis.getGridColor()); + mGridPaint.setStrokeWidth(mXAxis.getGridLineWidth()); + mGridPaint.setPathEffect(mXAxis.getGridDashPathEffect()); + } + + @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()); + + if (inverted) { + + min = (float) p2.x; + max = (float) p1.x; + } else { + + min = (float) p1.x; + max = (float) p2.x; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + 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"); + + final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelWidth, + labelHeight, + mXAxis.getLabelRotationAngle()); + + + mXAxis.mLabelWidth = Math.round(labelWidth); + mXAxis.mLabelHeight = Math.round(labelHeight); + mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); + mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); + FSize.recycleInstance(labelSize); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) + return; + + float yoffset = mXAxis.getYOffset(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setColor(mXAxis.getTextColor()); + + MPPointF pointF = MPPointF.getInstance(0,0); + if (mXAxis.getPosition() == XAxisPosition.TOP) { + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() - yoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { + pointF.x = 0.5f; + pointF.y = 1.0f; + drawLabels(c, mViewPortHandler.contentTop() + yoffset + mXAxis.mLabelRotatedHeight, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() + yoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 0.5f; + pointF.y = 0.0f; + drawLabels(c, mViewPortHandler.contentBottom() - yoffset - mXAxis.mLabelRotatedHeight, pointF); + + } else { // BOTH SIDED + 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 + public void renderAxisLine(Canvas c) { + + if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) + return; + + mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect()); + + if (mXAxis.getPosition() == XAxisPosition.TOP + || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentTop(), mAxisLinePaint); + } + + if (mXAxis.getPosition() == XAxisPosition.BOTTOM + || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + /** + * draws the x-labels on the specified y-position + * + * @param pos + */ + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { + + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); + + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + + // only fill x values + if (centeringEnabled) { + positions[i] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i] = mXAxis.mEntries[i / 2]; + } + } + + mTrans.pointValuesToPixel(positions); + + for (int i = 0; i < positions.length; i += 2) { + + 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 / 2 == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { + float width = Utils.calcTextWidth(mAxisLabelPaint, label); + + if (width > mViewPortHandler.offsetRight() * 2 + && x + width > mViewPortHandler.getChartWidth()) + x -= width / 2; + + // avoid clipping of the first + } else if (i == 0) { + + float width = Utils.calcTextWidth(mAxisLabelPaint, label); + x += width / 2; + } + } + + drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees); + } + } + } + + 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; + + int clipRestoreCount = c.save(); + c.clipRect(getGridClippingRect()); + + if(mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2){ + mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; + } + float[] positions = mRenderGridLinesBuffer; + + for (int i = 0; i < positions.length; i += 2) { + positions[i] = mXAxis.mEntries[i / 2]; + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + + mTrans.pointValuesToPixel(positions); + + setupGridPaint(); + + Path gridLinePath = mRenderGridLinesPath; + gridLinePath.reset(); + + for (int i = 0; i < positions.length; i += 2) { + + drawGridLine(c, positions[i], positions[i + 1], gridLinePath); + } + + c.restoreToCount(clipRestoreCount); + } + + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; + } + + /** + * 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) { + + gridLinePath.moveTo(x, mViewPortHandler.contentBottom()); + gridLinePath.lineTo(x, mViewPortHandler.contentTop()); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); + + gridLinePath.reset(); + } + + protected float[] mRenderLimitLinesBuffer = new float[2]; + protected RectF mLimitLineClippingRect = new RectF(); + + /** + * 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; + + mTrans.pointValuesToPixel(position); + + 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) { + mLimitLineSegmentsBuffer[0] = position[0]; + mLimitLineSegmentsBuffer[1] = mViewPortHandler.contentTop(); + mLimitLineSegmentsBuffer[2] = position[0]; + mLimitLineSegmentsBuffer[3] = mViewPortHandler.contentBottom(); + + mLimitLinePath.reset(); + mLimitLinePath.moveTo(mLimitLineSegmentsBuffer[0], mLimitLineSegmentsBuffer[1]); + mLimitLinePath.lineTo(mLimitLineSegmentsBuffer[2], mLimitLineSegmentsBuffer[3]); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(limitLine.getLineColor()); + mLimitLinePaint.setStrokeWidth(limitLine.getLineWidth()); + mLimitLinePaint.setPathEffect(limitLine.getDashPathEffect()); + + c.drawPath(mLimitLinePath, mLimitLinePaint); + } + + public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { + String label = limitLine.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(limitLine.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(limitLine.getTextColor()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(limitLine.getTextSize()); + + + float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); + + final LimitLine.LimitLabelPosition labelPosition = limitLine.getLabelPosition(); + + if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); + } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, position[0] + xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } else if (labelPosition == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, + mLimitLinePaint); + } else { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, position[0] - xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java new file mode 100644 index 0000000000..86047cf1b8 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -0,0 +1,310 @@ + +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Path; +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.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 XAxisRenderer { + + protected BarChart mChart; + + public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, + Transformer trans, BarChart 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 + protected void computeSize() { + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + + String longest = mXAxis.getLongestLabel(); + + final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); + + final float labelWidth = (int)(labelSize.width + mXAxis.getXOffset() * 3.5f); + final float labelHeight = labelSize.height; + + final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelSize.width, + labelHeight, + mXAxis.getLabelRotationAngle()); + + mXAxis.mLabelWidth = Math.round(labelWidth); + mXAxis.mLabelHeight = Math.round(labelHeight); + mXAxis.mLabelRotatedWidth = (int)(labelRotatedSize.width + mXAxis.getXOffset() * 3.5f); + mXAxis.mLabelRotatedHeight = Math.round(labelRotatedSize.height); + + FSize.recycleInstance(labelRotatedSize); + } + + @Override + public void renderAxisLabels(Canvas c) { + + if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) + return; + + float xoffset = mXAxis.getXOffset(); + + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); + mAxisLabelPaint.setColor(mXAxis.getTextColor()); + + MPPointF pointF = MPPointF.getInstance(0,0); + + 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) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentRight() - xoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() - xoffset, pointF); + + } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 1.0f; + pointF.y = 0.5f; + drawLabels(c, mViewPortHandler.contentLeft() + xoffset, pointF); + + } else { // BOTH SIDED + 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); + } + + @Override + protected void drawLabels(Canvas c, float pos, MPPointF anchor) { + + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); + + float[] positions = new float[mXAxis.mEntryCount * 2]; + + for (int i = 0; i < positions.length; i += 2) { + + // only fill x values + if (centeringEnabled) { + positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; + } else { + positions[i + 1] = mXAxis.mEntries[i / 2]; + } + } + + mTrans.pointValuesToPixel(positions); + + for (int i = 0; i < positions.length; i += 2) { + + float y = positions[i + 1]; + + if (mViewPortHandler.isInBoundsY(y)) { + + String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); + drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); + } + } + } + + @Override + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; + } + + @Override + protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { + + gridLinePath.moveTo(mViewPortHandler.contentRight(), y); + gridLinePath.lineTo(mViewPortHandler.contentLeft(), y); + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, mGridPaint); + + gridLinePath.reset(); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) + return; + + mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); + + if (mXAxis.getPosition() == XAxisPosition.TOP + || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentRight(), + mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + + if (mXAxis.getPosition() == XAxisPosition.BOTTOM + || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE + || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { + c.drawLine(mViewPortHandler.contentLeft(), + mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + 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. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mXAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + + Path limitLinePath = mRenderLimitLinesPathBuffer; + limitLinePath.reset(); + + 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(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + + pts[1] = l.getLimit(); + + mTrans.pointValuesToPixel(pts); + + limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); + limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + + c.drawPath(limitLinePath, mLimitLinePaint); + limitLinePath.reset(); + // c.drawLines(pts, mLimitLinePaint); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(l.getTextSize()); + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); + float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.offsetLeft() + xOffset, + 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 60% 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 77eab01792..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; @@ -25,6 +26,9 @@ public void renderAxisLabels(Canvas c) { if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) return; + final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); + final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f); + mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); mAxisLabelPaint.setColor(mXAxis.getTextColor()); @@ -35,19 +39,24 @@ public void renderAxisLabels(Canvas c) { // pixels float factor = mChart.getFactor(); - PointF center = mChart.getCenterOffsets(); - - for (int i = 0; i < mXAxis.getValues().size(); i++) { + MPPointF center = mChart.getCenterOffsets(); + MPPointF pOut = MPPointF.getInstance(0,0); + for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { - String text = 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.mLabelWidth / 2f, angle); + Utils.getPosition(center, mChart.getYRange() * factor + + mXAxis.mLabelRotatedWidth / 2f, angle, pOut); - c.drawText(text, p.x, p.y + mXAxis.mLabelHeight / 2f, mAxisLabelPaint); + 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/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java new file mode 100644 index 0000000000..53cca7ee03 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -0,0 +1,352 @@ +package com.github.mikephil.charting.renderer; + +import android.graphics.Canvas; +import android.graphics.Color; +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.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 YAxisRenderer extends AxisRenderer { + + protected YAxis mYAxis; + + protected Paint mZeroLinePaint; + + public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer trans) { + super(viewPortHandler, trans, yAxis); + + this.mYAxis = yAxis; + + if(mViewPortHandler != null) { + + mAxisLabelPaint.setColor(Color.BLACK); + mAxisLabelPaint.setTextSize(Utils.convertDpToPixel(10f)); + + mZeroLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mZeroLinePaint.setColor(Color.GRAY); + mZeroLinePaint.setStrokeWidth(1f); + mZeroLinePaint.setStyle(Paint.Style.STROKE); + } + } + + /** + * draws the y-axis labels to the screen + */ + @Override + public void renderAxisLabels(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) + return; + + float[] positions = getTransformedPositions(); + + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); + mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); + mAxisLabelPaint.setColor(mYAxis.getTextColor()); + + float xoffset = mYAxis.getXOffset(); + float yoffset = Utils.calcTextHeight(mAxisLabelPaint, "A") / 2.5f + mYAxis.getYOffset(); + + AxisDependency dependency = mYAxis.getAxisDependency(); + YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); + + float xPos = 0f; + + if (dependency == AxisDependency.LEFT) { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + mAxisLabelPaint.setTextAlign(Align.RIGHT); + xPos = mViewPortHandler.offsetLeft() - xoffset; + } else { + mAxisLabelPaint.setTextAlign(Align.LEFT); + xPos = mViewPortHandler.offsetLeft() + xoffset; + } + + } else { + + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + mAxisLabelPaint.setTextAlign(Align.LEFT); + xPos = mViewPortHandler.contentRight() + xoffset; + } else { + mAxisLabelPaint.setTextAlign(Align.RIGHT); + xPos = mViewPortHandler.contentRight() - xoffset; + } + } + + drawYLabels(c, xPos, positions, yoffset); + } + + @Override + public void renderAxisLine(Canvas c) { + + if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) + return; + + mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); + mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); + + if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { + c.drawLine(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop(), mViewPortHandler.contentLeft(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } else { + c.drawLine(mViewPortHandler.contentRight(), mViewPortHandler.contentTop(), mViewPortHandler.contentRight(), + mViewPortHandler.contentBottom(), mAxisLinePaint); + } + } + + /** + * draws the y-labels on the specified x-position + * + * @param fixedPosition + * @param positions + */ + 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 = from; i < to; i++) { + + String text = mYAxis.getFormattedLabel(i); + + 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; + + 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 = mRenderGridLinesPath; + gridLinePath.reset(); + + // 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(linePath(gridLinePath, i, positions), mGridPaint); + gridLinePath.reset(); + } + + c.restoreToCount(clipRestoreCount); + } + + if (mYAxis.isDrawZeroLineEnabled()) { + drawZeroLine(c); + } + } + + protected RectF mGridClippingRect = new RectF(); + + public RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); + return mGridClippingRect; + } + + /** + * Calculates the path for a grid line. + * + * @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 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 = mDrawZeroLinePath; + zeroLinePath.reset(); + + 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. + * + * @param c + */ + @Override + public void renderLimitLines(Canvas c) { + + List limitLines = mYAxis.getLimitLines(); + + if (limitLines == null || limitLines.size() <= 0) + return; + + float[] pts = mRenderLimitLinesBuffer; + pts[0] = 0; + pts[1] = 0; + Path limitLinePath = mRenderLimitLines; + limitLinePath.reset(); + + 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(0.f, -l.getLineWidth()); + c.clipRect(mLimitLineClippingRect); + + mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setColor(l.getLineColor()); + mLimitLinePaint.setStrokeWidth(l.getLineWidth()); + mLimitLinePaint.setPathEffect(l.getDashPathEffect()); + + pts[1] = l.getLimit(); + + mTrans.pointValuesToPixel(pts); + + limitLinePath.moveTo(mViewPortHandler.contentLeft(), pts[1]); + limitLinePath.lineTo(mViewPortHandler.contentRight(), pts[1]); + + c.drawPath(limitLinePath, mLimitLinePaint); + limitLinePath.reset(); + // c.drawLines(pts, mLimitLinePaint); + + String label = l.getLabel(); + + // if drawing the limit-value label is enabled + if (label != null && !label.equals("")) { + + mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setPathEffect(null); + mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setTypeface(l.getTypeface()); + mLimitLinePaint.setStrokeWidth(0.5f); + mLimitLinePaint.setTextSize(l.getTextSize()); + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); + float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + c.drawText(label, + mViewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, mLimitLinePaint); + + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, mLimitLinePaint); + + } else { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, + mViewPortHandler.offsetLeft() + xOffset, + 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 50% 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 98c36276e8..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; @@ -21,36 +21,40 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer { public YAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, YAxis yAxis, - Transformer trans) { + Transformer trans) { super(viewPortHandler, yAxis, trans); - + mLimitLinePaint.setTextAlign(Align.LEFT); } /** * 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) { + @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,23 +69,15 @@ 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()); mAxisLabelPaint.setColor(mYAxis.getTextColor()); mAxisLabelPaint.setTextAlign(Align.CENTER); - float yoffset = Utils.calcTextHeight(mAxisLabelPaint, "A") + mYAxis.getYOffset(); + float baseYOffset = Utils.convertDpToPixel(2.5f); + float textHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); AxisDependency dependency = mYAxis.getAxisDependency(); YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); @@ -91,25 +87,21 @@ public void renderAxisLabels(Canvas c) { if (dependency == AxisDependency.LEFT) { if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yoffset = Utils.convertDpToPixel(3f); - yPos = mViewPortHandler.contentTop(); + yPos = mViewPortHandler.contentTop() - baseYOffset; } else { - yoffset = yoffset * -1f; - yPos = mViewPortHandler.contentTop(); + yPos = mViewPortHandler.contentTop() - baseYOffset; } } else { if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yoffset = yoffset * -1f; - yPos = mViewPortHandler.contentBottom(); + yPos = mViewPortHandler.contentBottom() + textHeight + baseYOffset; } else { - yoffset = Utils.convertDpToPixel(4f); - yPos = mViewPortHandler.contentBottom(); + yPos = mViewPortHandler.contentBottom() + textHeight + baseYOffset; } } - drawYLabels(c, yPos, positions, yoffset); + drawYLabels(c, yPos, positions, mYAxis.getYOffset()); } @Override @@ -134,55 +126,101 @@ public void renderAxisLine(Canvas c) { /** * draws the y-labels on the specified x-position - * + * * @param fixedPosition * @param positions */ @Override protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { - + mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); 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 RectF getGridClippingRect() { + mGridClippingRect.set(mViewPortHandler.getContentRect()); + mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); + return mGridClippingRect; } @Override - public void renderGridLines(Canvas c) { + protected Path linePath(Path p, int i, float[] positions) { - if (!mYAxis.isDrawGridLinesEnabled() || !mYAxis.isEnabled()) - return; + p.moveTo(positions[i], mViewPortHandler.contentTop()); + p.lineTo(positions[i], mViewPortHandler.contentBottom()); - // pre alloc - float[] position = new float[2]; + return p; + } - mGridPaint.setColor(mYAxis.getGridColor()); - mGridPaint.setStrokeWidth(mYAxis.getGridLineWidth()); + protected Path mDrawZeroLinePathBuffer = new Path(); - // draw the horizontal grid - for (int i = 0; i < mYAxis.mEntryCount; i++) { + @Override + protected void drawZeroLine(Canvas c) { - position[0] = mYAxis.mEntries[i]; - mTrans.pointValuesToPixel(position); + int clipRestoreCount = c.save(); + mZeroLineClippingRect.set(mViewPortHandler.getContentRect()); + mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth(), 0.f); + c.clipRect(mLimitLineClippingRect); - c.drawLine(position[0], mViewPortHandler.contentTop(), position[0], - mViewPortHandler.contentBottom(), - mGridPaint); - } + // draw zero line + MPPointD pos = mTrans.getPixelForValues(0f, 0f); + + mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); + mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); + + Path zeroLinePath = mDrawZeroLinePathBuffer; + zeroLinePath.reset(); + + zeroLinePath.moveTo((float) pos.x - 1, mViewPortHandler.contentTop()); + zeroLinePath.lineTo((float) pos.x - 1, 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. + * This is the standard XAxis renderer using the YAxis limit lines. * * @param c */ @@ -194,13 +232,26 @@ 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++) { 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); + pts[0] = l.getLimit(); pts[2] = l.getLimit(); @@ -208,11 +259,11 @@ public void renderLimitLines(Canvas c) { pts[1] = mViewPortHandler.contentTop(); pts[3] = mViewPortHandler.contentBottom(); - + limitLinePath.moveTo(pts[0], pts[1]); limitLinePath.lineTo(pts[2], pts[3]); - mLimitLinePaint.setStyle(Paint.Style.STROKE); + mLimitLinePaint.setStyle(Paint.Style.STROKE); mLimitLinePaint.setColor(l.getLineColor()); mLimitLinePaint.setPathEffect(l.getDashPathEffect()); mLimitLinePaint.setStrokeWidth(l.getLineWidth()); @@ -225,23 +276,40 @@ public void renderLimitLines(Canvas c) { // if drawing the limit-value label is enabled if (label != null && !label.equals("")) { - float xOffset = l.getLineWidth(); - float add = Utils.convertDpToPixel(4f); - - mLimitLinePaint.setStyle(l.getTextStyle()); + mLimitLinePaint.setStyle(l.getTextStyle()); mLimitLinePaint.setPathEffect(null); mLimitLinePaint.setColor(l.getTextColor()); + mLimitLinePaint.setTypeface(l.getTypeface()); mLimitLinePaint.setStrokeWidth(0.5f); mLimitLinePaint.setTextSize(l.getTextSize()); - - float yOffset = Utils.calcTextHeight(mLimitLinePaint, label) + add / 2f; - if (l.getLabelPosition() == LimitLabelPosition.POS_RIGHT) { - c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentBottom() - add, mLimitLinePaint); + float xOffset = l.getLineWidth() + l.getXOffset(); + float yOffset = Utils.convertDpToPixel(2f) + l.getYOffset(); + + final LimitLine.LimitLabelPosition position = l.getLabelPosition(); + + if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { + + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); + } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { + + mLimitLinePaint.setTextAlign(Align.LEFT); + c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentBottom() - yOffset, mLimitLinePaint); + } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { + + mLimitLinePaint.setTextAlign(Align.RIGHT); + final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); + c.drawText(label, pts[0] - xOffset, mViewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); } else { - c.drawText(label, pts[0] + xOffset, mViewPortHandler.contentTop() + yOffset, mLimitLinePaint); + + mLimitLinePaint.setTextAlign(Align.RIGHT); + 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 70% 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 809d93c4db..4d9c1de790 100644 --- a/MPChartLib/src/com/github/mikephil/charting/utils/ColorTemplate.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ColorTemplate.java @@ -11,12 +11,21 @@ * Class that holds predefined color integer arrays (e.g. * ColorTemplate.VORDIPLOM_COLORS) and convenience methods for loading colors * from resources. - * + * * @author Philipp Jahoda */ public class ColorTemplate { - public static final int COLOR_NONE = -1; + /** + * an "invalid" color that indicates that no color is set + */ + 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 = 0x00112234; /** * THE COLOR THEMES ARE PREDEFINED (predefined color integer arrays), FEEL @@ -42,20 +51,48 @@ 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. + * + * @param hex + * @return + */ + public static int rgb(String hex) { + int color = (int) Long.parseLong(hex.replace("#", ""), 16); + int r = (color >> 16) & 0xFF; + int g = (color >> 8) & 0xFF; + int b = (color >> 0) & 0xFF; + return Color.rgb(r, g, b); + } /** * Returns the Android ICS holo blue light color. - * + * * @return */ 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 - * + * * @param r * @param colors an integer array of resource id's of colors * @return @@ -74,7 +111,7 @@ public static List createColors(Resources r, int[] colors) { /** * Turns an array of colors (integer color values) into an ArrayList of * colors. - * + * * @param colors * @return */ 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/main/java/com/github/mikephil/charting/utils/Transformer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java new file mode 100644 index 0000000000..0496bd0673 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Transformer.java @@ -0,0 +1,459 @@ + +package com.github.mikephil.charting.utils; + +import android.graphics.Matrix; +import android.graphics.Path; +import android.graphics.RectF; + +import com.github.mikephil.charting.data.CandleEntry; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; +import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; + +import java.util.List; + +/** + * Transformer class that contains all matrices and is responsible for + * transforming values into pixels on the screen and backwards. + * + * @author Philipp Jahoda + */ +public class Transformer { + + /** + * matrix to map the values to the screen pixels + */ + protected Matrix mMatrixValueToPx = new Matrix(); + + /** + * matrix for handling the different offsets of the chart + */ + protected Matrix mMatrixOffset = new Matrix(); + + protected ViewPortHandler mViewPortHandler; + + public Transformer(ViewPortHandler viewPortHandler) { + this.mViewPortHandler = viewPortHandler; + } + + /** + * Prepares the matrix that transforms values to pixels. Calculates the + * scale factors from the charts size and offsets. + * + * @param xChartMin + * @param deltaX + * @param deltaY + * @param yChartMin + */ + public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) { + + float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); + float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); + + if (Float.isInfinite(scaleX)) { + scaleX = 0; + } + if (Float.isInfinite(scaleY)) { + scaleY = 0; + } + + // setup all matrices + mMatrixValueToPx.reset(); + mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); + mMatrixValueToPx.postScale(scaleX, -scaleY); + } + + /** + * Prepares the matrix that contains all offsets. + * + * @param inverted + */ + public void prepareMatrixOffset(boolean inverted) { + + mMatrixOffset.reset(); + + // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); + + if (!inverted) + mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), + mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); + else { + mMatrixOffset + .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); + mMatrixOffset.postScale(1.0f, -1.0f); + } + } + + 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. + * + * @param data + * @return + */ + public float[] generateTransformedValuesScatter(IScatterDataSet data, float phaseX, + float phaseY, int from, int to) { + + final int count = (int) ((to - from) * phaseX + 1) * 2; + + if (valuePointsForGenerateTransformedValuesScatter.length != count) { + valuePointsForGenerateTransformedValuesScatter = new float[count]; + } + float[] valuePoints = valuePointsForGenerateTransformedValuesScatter; + + for (int j = 0; j < count; j += 2) { + + Entry e = data.getEntryForIndex(j / 2 + from); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + 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. + * + * @param data + * @return + */ + public float[] generateTransformedValuesBubble(IBubbleDataSet data, float phaseY, int from, int to) { + + final int count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; + + 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] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + 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. + * + * @param data + * @return + */ + public float[] generateTransformedValuesLine(ILineDataSet data, + float phaseX, float phaseY, + int min, int max) { + + final int count = ((int) ((max - min) * phaseX) + 1) * 2; + + 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 + min); + + if (e != null) { + valuePoints[j] = e.getX(); + valuePoints[j + 1] = e.getY() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + 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. + * + * @param data + * @return + */ + public float[] generateTransformedValuesCandle(ICandleDataSet data, + float phaseX, float phaseY, int from, int to) { + + final int count = (int) ((to - from) * phaseX + 1) * 2; + + 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.getX(); + valuePoints[j + 1] = e.getHigh() * phaseY; + } else { + valuePoints[j] = 0; + valuePoints[j + 1] = 0; + } + } + + getValueToPixelMatrix().mapPoints(valuePoints); + + return valuePoints; + } + + /** + * transform a path with all the given matrices VERY IMPORTANT: keep order + * to value-touch-offset + * + * @param path + */ + public void pathValueToPixel(Path path) { + + path.transform(mMatrixValueToPx); + path.transform(mViewPortHandler.getMatrixTouch()); + path.transform(mMatrixOffset); + } + + /** + * Transforms multiple paths will all matrices. + * + * @param paths + */ + public void pathValuesToPixel(List paths) { + + for (int i = 0; i < paths.size(); i++) { + pathValueToPixel(paths.get(i)); + } + } + + /** + * Transform an array of points with all matrices. VERY IMPORTANT: Keep + * matrix order "value-touch-offset" when transforming. + * + * @param pts + */ + public void pointValuesToPixel(float[] pts) { + + mMatrixValueToPx.mapPoints(pts); + mViewPortHandler.getMatrixTouch().mapPoints(pts); + mMatrixOffset.mapPoints(pts); + } + + /** + * Transform a rectangle with all matrices. + * + * @param r + */ + public void rectValueToPixel(RectF r) { + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * Transform a rectangle with all matrices with potential animation phases. + * + * @param r + * @param phaseY + */ + public void rectToPixelPhase(RectF r, float phaseY) { + + // multiply the height of the rect with the phase + r.top *= phaseY; + r.bottom *= phaseY; + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + 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. + * + * @param r + */ + public void rectValueToPixelHorizontal(RectF r) { + + mMatrixValueToPx.mapRect(r); + mViewPortHandler.getMatrixTouch().mapRect(r); + mMatrixOffset.mapRect(r); + } + + /** + * Transform a rectangle with all matrices with potential animation phases. + * + * @param r + * @param phaseY + */ + public void rectValueToPixelHorizontal(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); + } + + /** + * transforms multiple rects with all matrices + * + * @param rects + */ + public void rectValuesToPixel(List rects) { + + Matrix m = getValueToPixelMatrix(); + + for (int i = 0; i < rects.size(); i++) + 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. + * + * @param pixels + */ + public void pixelsToValue(float[] pixels) { + + Matrix tmp = mPixelToValueMatrixBuffer; + tmp.reset(); + + // invert all matrixes to convert back to the original value + mMatrixOffset.invert(tmp); + tmp.mapPoints(pixels); + + mViewPortHandler.getMatrixTouch().invert(tmp); + tmp.mapPoints(pixels); + + mMatrixValueToPx.invert(tmp); + tmp.mapPoints(pixels); + } + + /** + * 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 + * 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 MPPointD getPixelForValues(float x, float y) { + + ptsBuffer[0] = x; + ptsBuffer[1] = y; + + pointValuesToPixel(ptsBuffer); + + double xPx = ptsBuffer[0]; + double yPx = ptsBuffer[1]; + + return MPPointD.getInstance(xPx, yPx); + } + + public Matrix getValueMatrix() { + return mMatrixValueToPx; + } + + public Matrix getOffsetMatrix() { + return mMatrixOffset; + } + + private Matrix mMBuffer1 = new Matrix(); + + public Matrix getValueToPixelMatrix() { + mMBuffer1.set(mMatrixValueToPx); + mMBuffer1.postConcat(mViewPortHandler.mMatrixTouch); + mMBuffer1.postConcat(mMatrixOffset); + return mMBuffer1; + } + + private Matrix mMBuffer2 = new Matrix(); + + public Matrix getPixelToValueMatrix() { + getValueToPixelMatrix().invert(mMBuffer2); + return mMBuffer2; + } +} 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/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java new file mode 100644 index 0000000000..c302673919 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java @@ -0,0 +1,779 @@ + +package com.github.mikephil.charting.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +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.formatter.DefaultValueFormatter; +import com.github.mikephil.charting.formatter.IValueFormatter; + +import java.util.List; + +/** + * Utilities class that has some helper methods. Needs to be initialized by + * calling Utils.init(...) before usage. Inside the Chart.init() method, this is + * done, if the Utils are used before that, Utils.init(...) needs to be called + * manually. + * + * @author Philipp Jahoda + */ +public abstract class Utils { + + private static DisplayMetrics mMetrics; + 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); + + @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. + * + * @param context + */ + @SuppressWarnings("deprecation") + public static void init(Context context) { + + if (context == null) { + // noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + // noinspection deprecation + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); + + Log.e("MPChartLib-Utils" + , "Utils.init(...) PROVIDED CONTEXT OBJECT IS NULL"); + + } else { + ViewConfiguration viewConfiguration = ViewConfiguration.get(context); + mMinimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); + + Resources res = context.getResources(); + mMetrics = res.getDisplayMetrics(); + } + } + + /** + * initialize method, called inside the Chart.init() method. backwards + * compatibility - to not break existing code + * + * @param res + */ + @Deprecated + public static void init(Resources res) { + + mMetrics = res.getDisplayMetrics(); + + // noinspection deprecation + mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); + // noinspection deprecation + mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity(); + } + + /** + * This method converts dp unit to equivalent pixels, depending on device + * density. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. + * + * @param dp A value in dp (density independent pixels) unit. Which we need + * to convert into pixels + * @return A float value to represent px equivalent to dp depending on + * device density + */ + 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."); + return dp; + } + + return dp * mMetrics.density; + } + + /** + * This method converts device specific pixels to density independent + * pixels. NEEDS UTILS TO BE INITIALIZED BEFORE USAGE. + * + * @param px A value in px (pixels) unit. Which we need to convert into db + * @return A float value to represent dp equivalent to px value + */ + 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."); + return px; + } + + return px / mMetrics.density; + } + + /** + * calculates the approximate width of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @return + */ + 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) + * + * @param paint + * @param demoText + * @return + */ + public static int calcTextHeight(Paint paint, String demoText) { + + 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) { + 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) { + 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 A Recyclable FSize instance + */ + public static FSize calcTextSize(Paint paint, String demoText) { + + 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); + outputFSize.width = r.width(); + outputFSize.height = r.height(); + + } + + + /** + * Math.pow(...) is very expensive, so avoid calling it and create it + * yourself. + */ + private static final int POW_10[] = { + 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 ("."). + * + * @param number + * @param digitCount + * @param separateThousands set this to true to separate thousands values + * @return + */ + public static String formatNumber(float number, int digitCount, boolean separateThousands) { + return formatNumber(number, digitCount, separateThousands, '.'); + } + + /** + * Formats the given number to the given number of decimals, and returns the + * number as a string, maximum 35 characters. + * + * @param number + * @param digitCount + * @param separateThousands set this to true to separate thousands values + * @param separateChar a caracter to be paced between the "thousands" + * @return + */ + public static String formatNumber(float number, int digitCount, boolean separateThousands, + char separateChar) { + + char[] out = new char[35]; + + boolean neg = false; + if (number == 0) { + return "0"; + } + + boolean zero = false; + if (number < 1 && number > -1) { + zero = true; + } + + if (number < 0) { + neg = true; + number = -number; + } + + if (digitCount > POW_10.length) { + digitCount = POW_10.length - 1; + } + + number *= POW_10[digitCount]; + long lval = Math.round(number); + int ind = out.length - 1; + int charCount = 0; + boolean decimalPointAdded = false; + + while (lval != 0 || charCount < (digitCount + 1)) { + int digit = (int) (lval % 10); + lval = lval / 10; + out[ind--] = (char) (digit + '0'); + charCount++; + + // add decimal point + if (charCount == digitCount) { + out[ind--] = ','; + charCount++; + decimalPointAdded = true; + + // add thousand separators + } else if (separateThousands && lval != 0 && charCount > digitCount) { + + if (decimalPointAdded) { + + if ((charCount - digitCount) % 4 == 0) { + out[ind--] = separateChar; + charCount++; + } + + } else { + + if ((charCount - digitCount) % 4 == 3) { + out[ind--] = separateChar; + charCount++; + } + } + } + } + + // if number around zero (between 1 and -1) + if (zero) { + out[ind--] = '0'; + charCount += 1; + } + + // if the number is negative + if (neg) { + out[ind--] = '-'; + charCount += 1; + } + + int start = out.length - charCount; + + // use this instead of "new String(...)" because of issue < Android 4.0 + return String.valueOf(out, start, out.length - start); + } + + /** + * rounds the given number to the next significant number + * + * @param number + * @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); + final long shifted = Math.round(number * magnitude); + return shifted / magnitude; + } + + /** + * Returns the appropriate number of decimals to be used for the provided + * number. + * + * @param number + * @return + */ + public static int getDecimals(float number) { + + float i = roundToNextSignificant(number); + + if (Float.isInfinite(i)) + return 0; + + return (int) Math.ceil(-Math.log10(i)) + 2; + } + + /** + * Converts the provided Integer List to an int array. + * + * @param integers + * @return + */ + public static int[] convertIntegers(List integers) { + + int[] ret = new int[integers.size()]; + + 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. + * + * @param strings + * @return + */ + public static String[] convertStrings(List strings) { + + String[] ret = new String[strings.size()]; + + for (int i = 0; i < ret.length; i++) { + ret[i] = strings.get(i); + } + + 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. + * + * @param d + * @return + */ + public static double nextUp(double d) { + if (d == Double.POSITIVE_INFINITY) + return d; + else { + d += 0.0d; + return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + + ((d >= 0.0d) ? +1L : -1L)); + } + } + + /** + * Returns a recyclable MPPointF instance. + * Calculates the position around a center point, depending on the distance + * from the center, and the angle of the position around the center. + * + * @param center + * @param dist + * @param angle in degrees, converted to radians internally + * @return + */ + public static MPPointF getPosition(MPPointF center, float dist, float 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) { + + // Check the dot product of current velocities. + // If the pointer that left was opposing another velocity vector, clear. + tracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); + final int upIndex = ev.getActionIndex(); + final int id1 = ev.getPointerId(upIndex); + final float x1 = tracker.getXVelocity(id1); + final float y1 = tracker.getYVelocity(id1); + for (int i = 0, count = ev.getPointerCount(); i < count; i++) { + if (i == upIndex) + continue; + + final int id2 = ev.getPointerId(i); + final float x = x1 * tracker.getXVelocity(id2); + final float y = y1 * tracker.getYVelocity(id2); + + final float dot = x + y; + if (dot < 0) { + tracker.clear(); + break; + } + } + } + + /** + * Original method view.postInvalidateOnAnimation() only supportd in API >= + * 16, This is a replica of the code from ViewCompat. + * + * @param view + */ + @SuppressLint("NewApi") + public static void postInvalidateOnAnimation(View view) { + if (Build.VERSION.SDK_INT >= 16) + view.postInvalidateOnAnimation(); + else + view.postInvalidateDelayed(10); + } + + public static int getMinimumFlingVelocity() { + return mMinimumFlingVelocity; + } + + public static int getMaximumFlingVelocity() { + return mMaximumFlingVelocity; + } + + /** + * returns an angle between 0.f < 360.f (not less than zero, less than 360) + */ + public static float getNormalizedAngle(float angle) { + while (angle < 0.f) + angle += 360.f; + + 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 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); + + // 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 += -mFontMetricsBuffer.ascent; + + // To have a consistent point of reference, we always draw left-aligned + Paint.Align originalTextAlign = paint.getTextAlign(); + paint.setTextAlign(Paint.Align.LEFT); + + if (angleDegrees != 0.f) { + + // Move the text drawing rect in a way that it always rotates around its center + drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f; + drawOffsetY -= lineHeight * 0.5f; + + float translateX = x; + float translateY = y; + + // Move the "outer" rect relative to the anchor, assuming its centered + if (anchor.x != 0.5f || anchor.y != 0.5f) { + final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees( + mDrawTextRectBuffer.width(), + lineHeight, + angleDegrees); + + translateX -= rotatedSize.width * (anchor.x - 0.5f); + translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); + } + + c.save(); + c.translate(translateX, translateY); + c.rotate(angleDegrees); + + c.drawText(text, drawOffsetX, drawOffsetY, paint); + + c.restore(); + } else { + if (anchor.x != 0.f || anchor.y != 0.f) { + + drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x; + drawOffsetY -= lineHeight * anchor.y; + } + + drawOffsetX += x; + drawOffsetY += y; + + c.drawText(text, drawOffsetX, drawOffsetY, paint); + } + + paint.setTextAlign(originalTextAlign); + } + + public static void drawMultilineText(Canvas c, StaticLayout textLayout, + float x, float y, + TextPaint paint, + MPPointF anchor, float angleDegrees) { + + float drawOffsetX = 0.f; + float drawOffsetY = 0.f; + float drawWidth; + float drawHeight; + + final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); + + drawWidth = textLayout.getWidth(); + drawHeight = textLayout.getLineCount() * lineHeight; + + // 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 += drawHeight; + + // To have a consistent point of reference, we always draw left-aligned + Paint.Align originalTextAlign = paint.getTextAlign(); + paint.setTextAlign(Paint.Align.LEFT); + + if (angleDegrees != 0.f) { + + // Move the text drawing rect in a way that it always rotates around its center + drawOffsetX -= drawWidth * 0.5f; + drawOffsetY -= drawHeight * 0.5f; + + float translateX = x; + float translateY = y; + + // Move the "outer" rect relative to the anchor, assuming its centered + if (anchor.x != 0.5f || anchor.y != 0.5f) { + final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees( + drawWidth, + drawHeight, + angleDegrees); + + translateX -= rotatedSize.width * (anchor.x - 0.5f); + translateY -= rotatedSize.height * (anchor.y - 0.5f); + FSize.recycleInstance(rotatedSize); + } + + c.save(); + c.translate(translateX, translateY); + c.rotate(angleDegrees); + + c.translate(drawOffsetX, drawOffsetY); + textLayout.draw(c); + + c.restore(); + } else { + if (anchor.x != 0.f || anchor.y != 0.f) { + + drawOffsetX -= drawWidth * anchor.x; + drawOffsetY -= drawHeight * anchor.y; + } + + drawOffsetX += x; + drawOffsetY += y; + + c.save(); + + c.translate(drawOffsetX, drawOffsetY); + textLayout.draw(c); + + c.restore(); + } + + paint.setTextAlign(originalTextAlign); + } + + public static void drawMultilineText(Canvas c, String text, + float x, float y, + TextPaint paint, + FSize constrainedToSize, + MPPointF anchor, float angleDegrees) { + + StaticLayout textLayout = new StaticLayout( + text, 0, text.length(), + paint, + (int) Math.max(Math.ceil(constrainedToSize.width), 1.f), + Layout.Alignment.ALIGN_NORMAL, 1.f, 0.f, false); + + + drawMultilineText(c, textLayout, x, y, paint, anchor, angleDegrees); + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java new file mode 100644 index 0000000000..71b4b9bf79 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/ViewPortHandler.java @@ -0,0 +1,759 @@ + +package com.github.mikephil.charting.utils; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.View; + +/** + * Class that contains information about the charts current viewport settings, including offsets, scale & translation + * levels, ... + * + * @author Philipp Jahoda + */ +public class ViewPortHandler { + + /** + * matrix used for touch events + */ + protected final Matrix mMatrixTouch = new Matrix(); + + /** + * this rectangle defines the area in which graph values can be drawn + */ + protected RectF mContentRect = new RectF(); + + protected float mChartWidth = 0f; + protected float mChartHeight = 0f; + + /** + * minimum scale value on the y-axis + */ + private float mMinScaleY = 1f; + + /** + * maximum scale value on the y-axis + */ + private float mMaxScaleY = Float.MAX_VALUE; + + /** + * minimum scale value on the x-axis + */ + private float mMinScaleX = 1f; + + /** + * maximum scale value on the x-axis + */ + private float mMaxScaleX = Float.MAX_VALUE; + + /** + * contains the current scale factor of the x-axis + */ + private float mScaleX = 1f; + + /** + * contains the current scale factor of the y-axis + */ + private float mScaleY = 1f; + + /** + * current translation (drag distance) on the x-axis + */ + private float mTransX = 0f; + + /** + * current translation (drag distance) on the y-axis + */ + private float mTransY = 0f; + + /** + * offset that allows the chart to be dragged over its bounds on the x-axis + */ + private float mTransOffsetX = 0f; + + /** + * offset that allows the chart to be dragged over its bounds on the x-axis + */ + private float mTransOffsetY = 0f; + + /** + * Constructor - don't forget calling setChartDimens(...) + */ + public ViewPortHandler() { + + } + + /** + * Sets the width and height of the chart. + * + * @param width + * @param height + */ + + public void setChartDimens(float width, float height) { + + float offsetLeft = this.offsetLeft(); + float offsetTop = this.offsetTop(); + float offsetRight = this.offsetRight(); + float offsetBottom = this.offsetBottom(); + + mChartHeight = height; + mChartWidth = width; + + restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom); + } + + public boolean hasChartDimens() { + if (mChartHeight > 0 && mChartWidth > 0) + return true; + else + return false; + } + + public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, + float offsetBottom) { + mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight + - offsetBottom); + } + + public float offsetLeft() { + return mContentRect.left; + } + + public float offsetRight() { + return mChartWidth - mContentRect.right; + } + + public float offsetTop() { + return mContentRect.top; + } + + public float offsetBottom() { + return mChartHeight - mContentRect.bottom; + } + + public float contentTop() { + return mContentRect.top; + } + + public float contentLeft() { + return mContentRect.left; + } + + public float contentRight() { + return mContentRect.right; + } + + public float contentBottom() { + return mContentRect.bottom; + } + + public float contentWidth() { + return mContentRect.width(); + } + + public float contentHeight() { + return mContentRect.height(); + } + + public RectF getContentRect() { + return mContentRect; + } + + public MPPointF getContentCenter() { + return MPPointF.getInstance(mContentRect.centerX(), mContentRect.centerY()); + } + + public float getChartHeight() { + return mChartHeight; + } + + 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()); + } + + /** + * ################ ################ ################ ################ + */ + /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */ + + /** + * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom + * center. + * + * @param x + * @param y + */ + public Matrix zoomIn(float x, float y) { + + Matrix save = new Matrix(); + 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. + */ + public Matrix zoomOut(float x, float y) { + + Matrix save = new Matrix(); + zoomOut(x, y, save); + return save; + } + + public void zoomOut(float x, float y, Matrix outputMatrix) { + outputMatrix.reset(); + outputMatrix.set(mMatrixTouch); + outputMatrix.postScale(0.7f, 0.7f, x, y); + } + + /** + * 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); + } + + /** + * Post-scales by the specified scale factors. + * + * @param scaleX + * @param scaleY + * @return + */ + public Matrix zoom(float scaleX, float scaleY) { + + Matrix save = new Matrix(); + 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. + * + * @param scaleX + * @param scaleY + * @param x + * @param y + * @return + */ + public Matrix zoom(float scaleX, float scaleY, float x, float y) { + + Matrix save = new Matrix(); + 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. + * + * @param scaleX + * @param scaleY + * @return + */ + public Matrix setZoom(float scaleX, float scaleY) { + + Matrix save = new Matrix(); + 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. + * + * @param scaleX + * @param scaleY + * @param x + * @param y + * @return + */ + public Matrix setZoom(float scaleX, float scaleY, float x, float y) { + + Matrix save = new Matrix(); + save.set(mMatrixTouch); + + save.setScale(scaleX, scaleY, x, 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; + + outputMatrix.set(mMatrixTouch); + + float[] vals = valsBufferForFitScreen; + for (int i = 0; i < 9; i++) { + vals[i] = 0; + } + + outputMatrix.getValues(vals); + + // reset all translations and scaling + vals[Matrix.MTRANS_X] = 0f; + vals[Matrix.MTRANS_Y] = 0f; + vals[Matrix.MSCALE_X] = 1f; + vals[Matrix.MSCALE_Y] = 1f; + + outputMatrix.setValues(vals); + } + + /** + * Post-translates to the specified points. Less Performant. + * + * @param transformedPts + * @return + */ + public Matrix translate(final float[] transformedPts) { + + Matrix save = new Matrix(); + 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(); + 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 + * not possible. Makes most sense in combination with the + * setScaleMinima(...) method. + * + * @param transformedPts the position to center view viewport to + * @param view + * @return save + */ + public void centerViewPort(final float[] transformedPts, final View view) { + + Matrix save = mCenterViewPortMatrixBuffer; + save.reset(); + save.set(mMatrixTouch); + + final float x = transformedPts[0] - offsetLeft(); + final float y = transformedPts[1] - offsetTop(); + + save.postTranslate(-x, -y); + + refresh(save, view, true); + } + + /** + * buffer for storing the 9 matrix values of a 3x3 matrix + */ + protected final float[] matrixBuffer = new float[9]; + + /** + * call this method to refresh the graph with a given matrix + * + * @param newMatrix + * @return + */ + public Matrix refresh(Matrix newMatrix, View chart, boolean invalidate) { + + mMatrixTouch.set(newMatrix); + + // make sure scale and translation are within their bounds + limitTransAndScale(mMatrixTouch, mContentRect); + + if (invalidate) + chart.invalidate(); + + newMatrix.set(mMatrixTouch); + return newMatrix; + } + + /** + * limits the maximum scale and X translation of the given matrix + * + * @param matrix + */ + public void limitTransAndScale(Matrix matrix, RectF content) { + + matrix.getValues(matrixBuffer); + + float curTransX = matrixBuffer[Matrix.MTRANS_X]; + float curScaleX = matrixBuffer[Matrix.MSCALE_X]; + + float curTransY = matrixBuffer[Matrix.MTRANS_Y]; + float curScaleY = matrixBuffer[Matrix.MSCALE_Y]; + + // min scale-x is 1f + mScaleX = Math.min(Math.max(mMinScaleX, curScaleX), mMaxScaleX); + + // min scale-y is 1f + mScaleY = Math.min(Math.max(mMinScaleY, curScaleY), mMaxScaleY); + + float width = 0f; + float height = 0f; + + if (content != null) { + width = content.width(); + height = content.height(); + } + + float maxTransX = -width * (mScaleX - 1f); + mTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX); + + float maxTransY = height * (mScaleY - 1f); + mTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY); + + matrixBuffer[Matrix.MTRANS_X] = mTransX; + matrixBuffer[Matrix.MSCALE_X] = mScaleX; + + matrixBuffer[Matrix.MTRANS_Y] = mTransY; + matrixBuffer[Matrix.MSCALE_Y] = mScaleY; + + matrix.setValues(matrixBuffer); + } + + /** + * Sets the minimum scale factor for the x-axis + * + * @param xScale + */ + public void setMinimumScaleX(float xScale) { + + if (xScale < 1f) + xScale = 1f; + + mMinScaleX = xScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the maximum scale factor for the x-axis + * + * @param xScale + */ + public void setMaximumScaleX(float xScale) { + + if (xScale == 0.f) + xScale = Float.MAX_VALUE; + + mMaxScaleX = xScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the minimum and maximum scale factors for the x-axis + * + * @param minScaleX + * @param maxScaleX + */ + public void setMinMaxScaleX(float minScaleX, float maxScaleX) { + + if (minScaleX < 1f) + minScaleX = 1f; + + if (maxScaleX == 0.f) + maxScaleX = Float.MAX_VALUE; + + mMinScaleX = minScaleX; + mMaxScaleX = maxScaleX; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the minimum scale factor for the y-axis + * + * @param yScale + */ + public void setMinimumScaleY(float yScale) { + + if (yScale < 1f) + yScale = 1f; + + mMinScaleY = yScale; + + limitTransAndScale(mMatrixTouch, mContentRect); + } + + /** + * Sets the maximum scale factor for the y-axis + * + * @param 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. + * + * @return + */ + public Matrix getMatrixTouch() { + return mMatrixTouch; + } + + /** + * ################ ################ ################ ################ + */ + /** + * BELOW METHODS FOR BOUNDS CHECK + */ + + public boolean isInBoundsX(float x) { + return isInBoundsLeft(x) && isInBoundsRight(x); + } + + public boolean isInBoundsY(float y) { + return isInBoundsTop(y) && isInBoundsBottom(y); + } + + public boolean isInBounds(float x, float y) { + return isInBoundsX(x) && isInBoundsY(y); + } + + public boolean isInBoundsLeft(float x) { + return mContentRect.left <= x + 1; + } + + public boolean isInBoundsRight(float x) { + x = (float) ((int) (x * 100.f)) / 100.f; + return mContentRect.right >= x - 1; + } + + public boolean isInBoundsTop(float y) { + return mContentRect.top <= y; + } + + public boolean isInBoundsBottom(float y) { + y = (float) ((int) (y * 100.f)) / 100.f; + return mContentRect.bottom >= y; + } + + /** + * returns the current x-scale factor + */ + public float getScaleX() { + return mScaleX; + } + + /** + * returns the current y-scale factor + */ + 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 + * + * @return + */ + public float getTransX() { + return mTransX; + } + + /** + * Returns the translation (drag / pan) distance on the y-axis + * + * @return + */ + public float getTransY() { + return mTransY; + } + + /** + * if the chart is fully zoomed out, return true + * + * @return + */ + public boolean isFullyZoomedOut() { + + return isFullyZoomedOutX() && isFullyZoomedOutY(); + } + + /** + * Returns true if the chart is fully zoomed out on it's y-axis (vertical). + * + * @return + */ + public boolean isFullyZoomedOutY() { + return !(mScaleY > mMinScaleY || mMinScaleY > 1f); + } + + /** + * Returns true if the chart is fully zoomed out on it's x-axis + * (horizontal). + * + * @return + */ + public boolean isFullyZoomedOutX() { + return !(mScaleX > mMinScaleX || mMinScaleX > 1f); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the x-axis. + * + * @param offset + */ + public void setDragOffsetX(float offset) { + mTransOffsetX = Utils.convertDpToPixel(offset); + } + + /** + * Set an offset in dp that allows the user to drag the chart over it's + * bounds on the y-axis. + * + * @param offset + */ + public void setDragOffsetY(float offset) { + mTransOffsetY = Utils.convertDpToPixel(offset); + } + + /** + * Returns true if both drag offsets (x and y) are zero or smaller. + * + * @return + */ + public boolean hasNoDragOffset() { + return mTransOffsetX <= 0 && mTransOffsetY <= 0; + } + + /** + * Returns true if the chart is not yet fully zoomed out on the x-axis + * + * @return + */ + public boolean canZoomOutMoreX() { + return mScaleX > mMinScaleX; + } + + /** + * Returns true if the chart is not yet fully zoomed in on the x-axis + * + * @return + */ + public boolean canZoomInMoreX() { + 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/Projects_using_MPAndroidChart.txt b/Projects_using_MPAndroidChart.txt deleted file mode 100644 index eeeb70f54b..0000000000 --- a/Projects_using_MPAndroidChart.txt +++ /dev/null @@ -1,34 +0,0 @@ -References - -Blood Pressure Companion -https://play.google.com/store/apps/details?id=de.medando.bloodpressurecompanion - -Smoke Free - Stop smoking help -https://play.google.com/store/apps/details?id=com.portablepixels.smokefree - -Drivvo - Vehicle Management -https://play.google.com/store/apps/details?id=br.com.ctncardoso.ctncar - -Phone Addiction -https://play.google.com/store/apps/details?id=com.tutorialsface.phoneaddiction - -AS Sales Management -https://play.google.com/store/apps/details?id=com.armsoft.mtrade - -Notification Analyser -https://play.google.com/store/apps/details?id=com.tierep.notificationanalyser - -Bluetooth Terminal/Graphics -https://play.google.com/store/apps/details?id=com.emrctn.BluetoothGraphics - -HI - Health & Fitness Tracker - All-in-one Diet, Health & Fitness Tracker -https://play.google.com/store/apps/details?id=com.droidinfinity.healthplus - -БИТ.Лидер -https://play.google.com/store/apps/details?id=com.firstbit.bitlider - -My Expenses -https://play.google.com/store/apps/details?id=org.totschnig.myexpenses - -H-Files -https://play.google.com/store/apps/details?id=com.hconnect.hfiles&hl=en diff --git a/README.md b/README.md index bcc93c190c..a3253e3ff9 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,188 @@ - [![Android Arsenal](http://img.shields.io/badge/Android%20Arsenal-MPAndroidChart-blue.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) +![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) +[![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/' } +} -Remember: *It's all about the looks.* +dependencies { + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' +} +``` +### Maven Setup -![alt tag](https://raw.github.com/PhilJay/MPChart/master/design/feature_graphic.png) +```xml + + + jitpack.io + https://jitpack.io + -**MPAndroidChart** is a powerful & easy to use chart library for Android, supporting line-, bar-, scatter-, candlestick-, bubble-, pie- and radarcharts (spider web), as well as scaling, dragging (panning), selecting and animations. Works on **Android 2.2 (API level 8)** and upwards. + + + com.github.PhilJay + MPAndroidChart + v3.1.0 + +``` -An **iOS** version of this library is now available, go check it out: [**ios-charts**](https://github.com/danielgindi/ios-charts) +
-Are you using this library? Add your project to the [**references**](https://github.com/PhilJay/MPAndroidChart/edit/master/Projects_using_MPAndroidChart.txt). +

Documentation :notebook_with_decorative_cover:

-Donations ------ +See the [**documentation**](https://weeklycoding.com/mpandroidchart/) for examples and general use of MPAndroidChart. -**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! +See the [**javadocs**](https://jitpack.io/com/github/PhilJay/MPAndroidChart/v3.1.0/javadoc/) for more advanced documentation. -**PayPal** +
- - [**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! - - [**Donate 300 $**] (https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PGW8CXUHZS2T2): I can afford it and want to assure the further development and maintenance of this project. Furthermore, I also want to show my gratitude to it's creator. - - 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! +

Examples :eyes:

-**Spread the word** +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) +

Questions & Issues :thinking:

- Download my [**iOS apps**](https://itunes.apple.com/us/artist/philipp-jahoda/id951309215). +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. -Demo ------ +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. -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**. +
-[![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) - - 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 **search known issues before**, do not create duplicate issues) - -Features -======= - -**Core features:** - - 7 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) Y-Axis - - Finger drawing (draw values into the chart with touch-gesture) - - Highlighting values (with customizeable popup-views) - - Multiple / Separate Axes - - Save chart to SD-Card (as image, or as .txt file) - - Predefined color templates - - Legends (generated automatically, customizeable) - - Customizeable Axes (both x- and y-axis) - - 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 - -**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) +**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! - - **LineChart (cubic lines)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/cubiclinechart.png) +**PayPal** - - **LineChart (single DataSet)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/linechart.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! - - **Combined-Chart (bar- and linechart in this case)** -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +
- - **BarChart (with legend, simple design)** +

Social Media :fire:

-![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/simpledesign_barchart3.png) +If you like this library, please tell others about it :two_hearts: :two_hearts: - - **BarChart (grouped DataSets)** +[![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) -![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/groupedbarchart.png) +If you like, you can follow me on Twitter [**@PhilippJahoda**](https://twitter.com/PhilippJahoda). - - **Horizontal-BarChart** +
-![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) +

More Examples :+1:

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

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

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

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

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

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

-Usage -======= +**Horizontal-BarChart** -In order to use the library, there are 4 options: +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/horizontal_barchart.png) +

-**1. Gradle dependency** +**Combined-Chart (bar- and linechart in this case)** - - Add the following to your `build.gradle`: - ```gradle -repositories { - maven { url "https://jitpack.io/" } -} +![alt tag](https://raw.github.com/PhilJay/MPChart/master/screenshots/combined_chart.png) +

-dependencies { - compile 'com.github.PhilJay:MPAndroidChart:v2.1.0' -} -``` +**PieChart (with selection, ...)** -**2. Maven** -- Add the following to your `pom.xml`: - ```xml - - jitpack.io - https://jitpack.io - +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/simpledesign_piechart1.png) +

- - com.github.PhilJay - MPAndroidChart - v2.1.0 - -``` +**ScatterChart** (with squares, triangles, circles, ... and more) -**3. jar file only** - - Download the [**latest .jar file**](https://github.com/PhilJay/MPAndroidChart/releases) from the releases section - - Copy the **mpandroidchartlibrary-.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 your desired destination folder (where you want to put 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 project as well as the example project) - - Import the library folder (`MPChartLib`) into 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) +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/scatterchart.png) +

-Proguard -======= +**CandleStickChart** (for financial data) -In case you are using Proguard, you will need to whitelist MPAndroidChart, which requires to add the following line to your Proguard configuration file. +![alt tag](https://raw.github.com/PhilJay/MPAndroidChart/master/screenshots/candlestickchart.png) +

-``` --keep class com.github.mikephil.charting.** { *; } -``` +**BubbleChart** (area covered by bubbles indicates the yValue) -Documentation -======= -For a **detailed documentation**, please have a look at the [**Wiki**](https://github.com/PhilJay/MPAndroidChart/wiki). +![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) -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). +
-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 :page_facing_up:

-License -======= -Copyright 2015 Philipp Jahoda +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, @@ -206,4 +190,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 8d6e95140e..ac31f01cf5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,16 @@ buildscript { repositories { - jcenter() + google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' - classpath 'com.github.dcendents:android-maven-plugin:1.2' + classpath 'com.android.tools.build:gradle:7.0.4' } } allprojects { repositories { - jcenter() + google() + mavenCentral() } } diff --git a/deploy.gradle b/deploy.gradle deleted file mode 100644 index b9382fe1f1..0000000000 --- a/deploy.gradle +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 Chris Banes - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin:'maven' -apply plugin:'signing' - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL - : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -def getSnapshotRepositoryUrl() { - return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL - : "https://oss.sonatype.org/content/repositories/snapshots/" -} - -def getRepositoryUsername() { - return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" -} - -def getRepositoryPassword() { - return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" -} - - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - snapshotRepository(url: getSnapshotRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description hasProperty('POM_DESCRIPTION') ? POM_DESCRIPTION : "" - url hasProperty('POM_URL') ? POM_URL : "" - - scm { - url hasProperty('POM_SCM_URL') ? POM_SCM_URL : "" - connection hasProperty('POM_SCM_CONNECTION') ? POM_SCM_CONNECTION : "" - developerConnection hasProperty('POM_SCM_DEV_CONNECTION') ? POM_SCM_DEV_CONNECTION : "" - } - - licenses { - license { - name hasProperty('POM_LICENCE_NAME') ? POM_LICENCE_NAME : "" - url hasProperty('POM_LICENCE_URL') ? POM_LICENCE_URL : "" - distribution hasProperty('POM_LICENCE_DIST') ? POM_LICENCE_DIST : "" - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - } - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") && hasProperty('signing.keyId') } - sign configurations.archives - } - - task androidJavadocs(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } - - task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir - } - - task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.sourceFiles - } - - artifacts { - archives androidSourcesJar - archives androidJavadocsJar - } -} - 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.jar b/gradle/wrapper/gradle-wrapper.jar index 8c0fb64a86..941144813d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 982af44537..ffed3a254e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Jan 11 01:47:43 CST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip diff --git a/gradlew b/gradlew index 91a7e269e1..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 ( ) { @@ -42,11 +42,6 @@ case "`uname`" in ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 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/performance.xlsx b/performance.xlsx deleted file mode 100644 index 041d4fea4e..0000000000 Binary files a/performance.xlsx and /dev/null differ diff --git a/screenshots/candlestickchart.png b/screenshots/candlestickchart.png index e12f8c2893..0dce18ab3e 100644 Binary files a/screenshots/candlestickchart.png and b/screenshots/candlestickchart.png differ diff --git a/screenshots/candlestickchart_old.png b/screenshots/candlestickchart_old.png new file mode 100644 index 0000000000..e12f8c2893 Binary files /dev/null and b/screenshots/candlestickchart_old.png differ 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/line_chart_gradient.png b/screenshots/line_chart_gradient.png new file mode 100644 index 0000000000..2891dcec5f Binary files /dev/null and b/screenshots/line_chart_gradient.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/realm_wiki.png b/screenshots/realm_wiki.png new file mode 100644 index 0000000000..3387c2e1b4 Binary files /dev/null and b/screenshots/realm_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/screenshots/zero_line_example_barchart.png b/screenshots/zero_line_example_barchart.png new file mode 100644 index 0000000000..514238569f Binary files /dev/null and b/screenshots/zero_line_example_barchart.png differ diff --git a/settings.gradle b/settings.gradle index 0d770eb38c..32efe09de1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,7 @@ include 'MPChartLib' +//include 'MPAndroidChart-Realm' include 'MPChartExample' +//include ':MPChartLib-Realm' +//project(':MPChartLib-Realm').projectDir = new File('../MPAndroidChart-Realm/MPChartLib-Realm') +