This commit is contained in:
coco
2026-07-03 15:56:07 +08:00
commit caef23209c
5767 changed files with 1004268 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/dictionaries
.idea/libraries
.idea/
# Keystore files
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
+50
View File
@@ -0,0 +1,50 @@
language: android
jdk: oraclejdk8
env:
global:
- ANDROID_API=28
- EMULATOR_API=26
- ANDROID_BUILD_TOOLS=27.0.3
- ADB_INSTALL_TIMEOUT=5 # minutes
android:
components:
- tools
- platform-tools
- build-tools-$ANDROID_BUILD_TOOLS
- android-$ANDROID_API
- android-$EMULATOR_API
- extra-google-m2repository
- extra-android-m2repository
- sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_API_LEVEL
- sys-img-armeabi-v7a-addon-google_apis-google-$EMULATOR_API_LEVEL
licenses:
- android-sdk-preview-license-.+
- android-sdk-license-.+
- google-gdk-license-.+
before_install:
- yes | sdkmanager "platforms;android-27"
# Emulator Management: Create, Start and Wait
#before_script:
# - echo no | android create avd --force -n test -t android-$$ANDROID_API --abi armeabi-v7a
# - emulator -avd test -no-skin -no-audio -no-window &
# - android-wait-for-emulator
# - adb shell input keyevent 82 &
script:
- "./gradlew clean build check -PdisablePreDex --stacktrace"
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 JingZhuanDuoYing Investment Consulting . Ltd.
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.
+35
View File
@@ -0,0 +1,35 @@
# JingZhuan Android Chart
[![Build Status](https://travis-ci.org/donglua/JZAndroidChart.svg?branch=master)](https://travis-ci.org/donglua/JZAndroidChart)
[ ![Download](https://api.bintray.com/packages/donglua/maven/chart2/images/download.svg) ](https://bintray.com/donglua/maven/chart2/_latestVersion)
## Usage
```gradle
implementation 'com.github.JingZhuanDuoYing:JZAndroidChart:1.5.10'
```
## Screenshots
![line-chart](https://github.com/donglua/JZAndroidChart/blob/master/screenshots/screenshot-minute-chart.png)
![minute-chart](https://github.com/donglua/JZAndroidChart/blob/master/screenshots/chart_minute.gif)
## License
```
Copyright 2017 JingZhuanDuoYing Investment Consulting . Ltd.
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.
```
+1
View File
@@ -0,0 +1 @@
/build
+41
View File
@@ -0,0 +1,41 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
setLibDefaultConfig(android)
defaultConfig {
dataBinding {
enabled true
}
applicationId "cn.jingzhuan.lib.chart.demo"
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
namespace 'cn.jingzhuan.lib.chart.demo'
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
testImplementation 'junit:junit:4.12'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.airbnb.android:epoxy:4.6.2'
implementation 'com.airbnb.android:epoxy-databinding:4.6.2'
// Add the annotation processor if you are using Epoxy's annotations (recommended)
kapt 'com.airbnb.android:epoxy-processor:4.6.2'
implementation rootProject.ext.dependencies["kotlinStdlibJdk8"]
implementation project(':chart')
}
+25
View File
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/Donglua/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# 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 *;
#}
# Uncomment this to preserve the lineDataSet number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the lineDataSet number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:name="cn.jingzhuan.lib.chart.demo.App"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,13 @@
package cn.jingzhuan.lib.chart.demo;
import android.app.Application;
/**
* Application
* Created by donglua on 12/27/17.
*/
public class App extends Application {
}
@@ -0,0 +1,68 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import cn.jingzhuan.lib.chart.base.Chart;
import cn.jingzhuan.lib.chart.data.BarDataSet;
import cn.jingzhuan.lib.chart.data.BarValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutBarChartClickableItemBinding;
import cn.jingzhuan.lib.chart.event.OnEntryClickListener;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by donglua on 11/20/17.
*/
@EpoxyModelClass(layout = R.layout.layout_bar_chart_clickable_item)
public abstract class BarChartClickableModel extends DataBindingEpoxyModel {
private BarDataSet barDataSet;
private List<BarValue> barValueList = new ArrayList<>();
private List<String> labels = Arrays.asList("data1", "data2", "data3", "data4", "data5", "data6");
public BarChartClickableModel() {
barValueList.add(new BarValue(11));
barValueList.add(new BarValue(4));
barValueList.add(new BarValue(6));
barValueList.add(new BarValue(13));
barValueList.add(new BarValue(8));
barValueList.add(new BarValue(9));
barDataSet = new BarDataSet(barValueList);
barDataSet.setMaxValueOffsetPercent(0.2f);
barDataSet.setDrawValueEnable(true);
barDataSet.setValueTextSize(24);
barDataSet.setAutoBarWidth(true);
}
@Override public View buildView(final ViewGroup parent) {
View rootView = super.buildView(parent);
LayoutBarChartClickableItemBinding barBinding = (LayoutBarChartClickableItemBinding) rootView.getTag();
barBinding.barChart.setDataSet(barDataSet);
barBinding.barChart.getAxisRight().setLabelTextColor(Color.BLACK);
barBinding.barChart.getAxisBottom().setLabels(labels);
barBinding.barChart.getAxisBottom().setLabelTextColor(Color.BLACK);
barBinding.barChart.setOnEntryClickListener(new OnEntryClickListener() {
@Override public void onEntryClick(Chart chart, int position) {
Toast.makeText(parent.getContext(),
"value = " + barValueList.get(position).getValues()[0],
Toast.LENGTH_SHORT).show();
}
});
return rootView;
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
}
}
@@ -0,0 +1,56 @@
package cn.jingzhuan.lib.chart.demo
import androidx.databinding.ViewDataBinding
import android.graphics.Color
import android.view.View
import android.view.ViewGroup
import cn.jingzhuan.lib.chart.data.BarDataSet
import cn.jingzhuan.lib.chart.data.BarValue
import cn.jingzhuan.lib.chart.demo.databinding.LayoutBarChartBinding
import com.airbnb.epoxy.DataBindingEpoxyModel
import com.airbnb.epoxy.EpoxyModelClass
import java.util.ArrayList
/**
* Created by Donglua on 17/8/2.
*/
@EpoxyModelClass(layout = R.layout.layout_bar_chart)
abstract class BarChartModel : DataBindingEpoxyModel() {
private val barDataSet: BarDataSet
init {
val barValueList = ArrayList<BarValue>()
barValueList.add(BarValue(11f))
barValueList.add(BarValue(10f))
barValueList.add(BarValue(11f))
barValueList.add(BarValue(13f))
barValueList.add(BarValue(11f))
barValueList.add(BarValue(12f))
barValueList.add(BarValue(12f))
barValueList.add(BarValue(13f).apply { setGradientColors(Color.WHITE, Color.BLACK) })
barValueList.add(BarValue(15f).apply { setGradientColors(Color.WHITE, Color.BLACK) })
barDataSet = BarDataSet(barValueList)
barDataSet.isAutoBarWidth = true
}
override fun buildView(parent: ViewGroup): View {
val rootView = super.buildView(parent)
val barBinding = rootView.tag as LayoutBarChartBinding
barBinding.barChart.setDataSet(barDataSet)
barBinding.barChart.axisRight.labelTextColor = Color.BLACK
return rootView
}
override fun setDataBindingVariables(binding: ViewDataBinding) {
binding as LayoutBarChartBinding
binding.barChart.animateY(500)
}
}
@@ -0,0 +1,166 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.Color;
import androidx.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import cn.jingzhuan.lib.chart.base.Chart;
import cn.jingzhuan.lib.chart.data.CandlestickDataSet;
import cn.jingzhuan.lib.chart.data.CandlestickValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutBarChartBinding;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutCombineChartBinding;
import cn.jingzhuan.lib.chart.event.HighlightStatusChangeListener;
import cn.jingzhuan.lib.chart.event.OnEntryClickListener;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyAttribute;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.List;
import static cn.jingzhuan.lib.chart.component.AxisY.*;
import static com.airbnb.epoxy.EpoxyAttribute.Option.DoNotHash;
/**
* Created by donglua on 8/29/17.
*/
@EpoxyModelClass(layout = R.layout.layout_combine_chart)
public abstract class CandlestickChartModel extends DataBindingEpoxyModel {
List<CandlestickValue> candlestickValues = new ArrayList<>();
@EpoxyAttribute(DoNotHash) HighlightStatusChangeListener highlightStatusChangeListener;
public CandlestickChartModel() {
candlestickValues.add(new CandlestickValue(3145.27f, 3117.44f, 3123.88f, 3134.57f));
candlestickValues.add(new CandlestickValue(3152.94f, 3131.41f, 3132.91f, 3140.85f));
candlestickValues.add(new CandlestickValue(3155.00f, 3097.33f, 3131.35f, 3152.18f));
candlestickValues.add(new CandlestickValue(3154.72f, 3136.58f, 3144.02f, 3154.65f));
candlestickValues.add(new CandlestickValue(3154.78f, 3136.54f, 3147.22f, 3143.70f));
candlestickValues.add(new CandlestickValue(3148.29f, 3123.75f, 3138.31f, 3135.35f));
candlestickValues.add(new CandlestickValue(3143.82f, 3111.38f, 3127.11f, 3127.37f));
candlestickValues.add(new CandlestickValue(3117.61f, 3092.09f, 3114.77f, 3103.04f));
candlestickValues.add(new CandlestickValue(3093.44f, 3067.68f, 3090.07f, 3078.61f));
candlestickValues.add(new CandlestickValue(3084.20f, 3056.56f, 3064.85f, 3080.53f));
candlestickValues.add(new CandlestickValue(3090.82f, 3051.59f, 3078.16f, 3052.78f));
candlestickValues.add(new CandlestickValue(3063.56f, 3016.53f, 3036.79f, 3061.50f));
candlestickValues.add(new CandlestickValue(3090.48f, 3051.87f, 3054.11f, 3083.51f));
candlestickValues.add(new CandlestickValue(3098.91f, 3085.93f, 3085.93f, 3090.22f));
candlestickValues.add(new CandlestickValue(3113.51f, 3060.53f, 3082.87f, 3112.95f));
candlestickValues.add(new CandlestickValue(3119.58f, 3101.30f, 3107.80f, 3104.43f));
candlestickValues.add(new CandlestickValue(3103.43f, 3077.95f, 3082.33f, 3090.13f));
candlestickValues.add(new CandlestickValue(3095.47f, 3081.28f, 3086.70f, 3090.62f));
candlestickValues.add(new CandlestickValue(3103.93f, 3063.14f, 3087.16f, 3075.67f));
candlestickValues.add(new CandlestickValue(3084.23f, 3050.84f, 3069.38f, 3061.94f));
candlestickValues.add(new CandlestickValue(3064.81f, 3022.30f, 3047.57f, 3064.08f));
candlestickValues.add(new CandlestickValue(3114.65f, 3052.83f, 3055.34f, 3107.83f));
candlestickValues.add(new CandlestickValue(3120.65f, 3100.38f, 3101.29f, 3110.06f));
candlestickValues.add(new CandlestickValue(3143.28f, 3111.56f, 3125.33f, 3117.17f));
candlestickValues.add(new CandlestickValue(3113.52f, 3097.67f, 3108.41f, 3102.62f));
candlestickValues.add(new CandlestickValue(3110.38f, 3081.84f, 3094.22f, 3105.54f));
candlestickValues.add(new CandlestickValue(3105.50f, 3084.83f, 3102.11f, 3091.65f));
candlestickValues.add(new CandlestickValue(3102.86f, 3078.79f, 3084.54f, 3102.12f));
candlestickValues.add(new CandlestickValue(3140.77f, 3098.94f, 3101.76f, 3140.32f));
candlestickValues.add(new CandlestickValue(3153.26f, 3132.82f, 3136.46f, 3150.33f));
candlestickValues.add(new CandlestickValue(3165.91f, 3146.10f, 3147.45f, 3158.39f));
candlestickValues.add(new CandlestickValue(3164.94f, 3135.31f, 3149.53f, 3139.87f));
candlestickValues.add(new CandlestickValue(3155.98f, 3131.04f, 3134.01f, 3153.73f));
candlestickValues.add(new CandlestickValue(3149.16f, 3125.35f, 3146.75f, 3130.66f));
candlestickValues.add(new CandlestickValue(3137.59f, 3117.08f, 3125.59f, 3132.48f));
candlestickValues.add(new CandlestickValue(3134.25f, 3117.85f, 3126.37f, 3123.16f));
candlestickValues.add(new CandlestickValue(3146.77f, 3121.77f, 3122.15f, 3144.37f));
candlestickValues.add(new CandlestickValue(3150.45f, 3134.61f, 3148.02f, 3140.01f));
candlestickValues.add(new CandlestickValue(3157.03f, 3132.62f, 3148.98f, 3156.20f));
candlestickValues.add(new CandlestickValue(3186.97f, 3146.63f, 3152.23f, 3147.44f));
candlestickValues.add(new CandlestickValue(3158.05f, 3118.09f, 3138.43f, 3157.87f));
candlestickValues.add(new CandlestickValue(3187.88f, 3156.97f, 3157.00f, 3185.43f));
candlestickValues.add(new CandlestickValue(3193.45f, 3172.45f, 3183.41f, 3191.19f));
candlestickValues.add(new CandlestickValue(3193.43f, 3170.78f, 3183.63f, 3173.20f));
candlestickValues.add(new CandlestickValue(3188.77f, 3174.28f, 3174.97f, 3188.06f));
candlestickValues.add(new CandlestickValue(3193.23f, 3171.57f, 3176.94f, 3192.42f));
candlestickValues.add(new CandlestickValue(3196.29f, 3177.02f, 3192.00f, 3195.90f));
candlestickValues.add(new CandlestickValue(3193.06f, 3174.31f, 3192.88f, 3182.80f));
candlestickValues.add(new CandlestickValue(3207.31f, 3174.70f, 3179.21f, 3207.12f));
candlestickValues.add(new CandlestickValue(3215.94f, 3188.77f, 3203.86f, 3212.43f));
candlestickValues.add(new CandlestickValue(3219.52f, 3195.29f, 3203.82f, 3217.95f));
candlestickValues.add(new CandlestickValue(3223.34f, 3203.20f, 3208.45f, 3212.62f));
candlestickValues.add(new CandlestickValue(3226.90f, 3199.21f, 3201.52f, 3203.04f));
candlestickValues.add(new CandlestickValue(3215.19f, 3177.92f, 3201.92f, 3197.54f));
candlestickValues.add(new CandlestickValue(3219.27f, 3190.34f, 3192.36f, 3218.15f));
candlestickValues.add(new CandlestickValue(3222.97f, 3204.85f, 3212.03f, 3222.41f));
candlestickValues.add(new CandlestickValue(3230.35f, 3139.50f, 3219.79f, 3176.45f));
candlestickValues.add(new CandlestickValue(3187.66f, 3150.12f, 3159.72f, 3187.57f));
candlestickValues.add(new CandlestickValue(3232.93f, 3179.72f, 3181.39f, 3230.97f));
candlestickValues.add(new CandlestickValue(3246.23f, 3225.42f, 3227.51f, 3244.86f));
candlestickValues.add(new CandlestickValue(3247.70f, 3231.95f, 3236.59f, 3237.97f));
candlestickValues.add(new CandlestickValue(3261.10f, 3230.07f, 3230.89f, 3250.60f));
candlestickValues.add(new CandlestickValue(3261.64f, 3233.13f, 3249.13f, 3243.68f));
candlestickValues.add(new CandlestickValue(3264.85f, 3228.04f, 3244.45f, 3247.66f));
candlestickValues.add(new CandlestickValue(3251.92f, 3220.63f, 3243.76f, 3249.78f));
candlestickValues.add(new CandlestickValue(3256.37f, 3232.95f, 3240.16f, 3253.23f));
candlestickValues.add(new CandlestickValue(3276.94f, 3251.18f, 3252.75f, 3273.03f));
candlestickValues.add(new CandlestickValue(3292.63f, 3273.50f, 3274.37f, 3292.63f));
candlestickValues.add(new CandlestickValue(3305.42f, 3282.04f, 3288.52f, 3285.06f));
candlestickValues.add(new CandlestickValue(3293.37f, 3262.15f, 3279.98f, 3272.92f));
candlestickValues.add(new CandlestickValue(3287.18f, 3261.31f, 3269.32f, 3262.08f));
candlestickValues.add(new CandlestickValue(3280.10f, 3243.71f, 3257.66f, 3279.45f));
candlestickValues.add(new CandlestickValue(3285.47f, 3269.65f, 3277.18f, 3281.87f));
candlestickValues.add(new CandlestickValue(3277.93f, 3263.85f, 3277.81f, 3275.57f));
candlestickValues.add(new CandlestickValue(3282.52f, 3236.17f, 3269.72f, 3261.75f));
candlestickValues.add(new CandlestickValue(3245.12f, 3200.75f, 3237.91f, 3208.54f));
candlestickValues.add(new CandlestickValue(3240.05f, 3206.04f, 3206.04f, 3237.36f));
candlestickValues.add(new CandlestickValue(3263.59f, 3235.10f, 3235.22f, 3251.26f));
candlestickValues.add(new CandlestickValue(3248.78f, 3228.87f, 3247.85f, 3246.44f));
candlestickValues.add(new CandlestickValue(3269.13f, 3251.45f, 3253.85f, 3268.42f));
candlestickValues.add(new CandlestickValue(3275.08f, 3248.08f, 3253.23f, 3268.71f));
candlestickValues.add(new CandlestickValue(3287.52f, 3270.47f, 3274.58f, 3286.90f));
candlestickValues.add(new CandlestickValue(3293.47f, 3274.93f, 3287.61f, 3290.22f));
candlestickValues.add(new CandlestickValue(3299.45f, 3274.43f, 3283.80f, 3287.69f));
candlestickValues.add(new CandlestickValue(3297.98f, 3266.36f, 3287.95f, 3271.51f));
candlestickValues.add(new CandlestickValue(3331.90f, 3271.45f, 3271.45f, 3331.52f));
candlestickValues.add(new CandlestickValue(3375.03f, 3336.12f, 3336.12f, 3362.64f));
candlestickValues.add(new CandlestickValue(3374.59f, 3354.45f, 3362.06f, 3365.22f));
candlestickValues.add(new CandlestickValue(3376.64f, 3357.08f, 3361.82f, 3363.62f));
candlestickValues.add(new CandlestickValue(3367.36f, 3341.14f, 3361.45f, 3349.57f));
}
@Override public View buildView(@NonNull ViewGroup parent) {
View rootView = super.buildView(parent);
final LayoutCombineChartBinding b = (LayoutCombineChartBinding) rootView.getTag();
CandlestickDataSet dataSet = new CandlestickDataSet(candlestickValues);
dataSet.setHighlightedVerticalEnable(true);
dataSet.setHighlightedHorizontalEnable(true);
b.combineChart.getAxisLeft().setAxisPosition(LEFT_OUTSIDE);
b.combineChart.getAxisRight().setAxisPosition(RIGHT_INSIDE);
b.combineChart.setMaxVisibleEntryCount(70);
b.combineChart.setMinVisibleEntryCount(10);
b.combineChart.setHighlightColor(Color.BLACK);
//b.combineChart.setOnHighlightStatusChangeListener(highlightStatusChangeListener);
//b.combineChart.setDataSet(new CandlestickDataSetArrowDecorator(dataSet));
b.combineChart.setDataSet(dataSet);
b.combineChart.setScaleGestureEnable(true);
b.combineChart.setScaleXEnable(true);
b.combineChart.setDraggingToMoveEnable(false);
b.combineChart.setDoubleTapToZoom(true);
b.combineChart.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
b.combineChart.setDraggingToMoveEnable(b.combineChart.isHighlightDisable());
}
});
b.combineChart.setOnEntryClickListener(new OnEntryClickListener() {
@Override public void onEntryClick(Chart chart, int position) {
b.combineChart.setHighlightDisable(!b.combineChart.isHighlightDisable());
b.combineChart.setDraggingToMoveEnable(b.combineChart.isHighlightDisable());
}
});
return rootView;
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
}
}
@@ -0,0 +1,89 @@
package cn.jingzhuan.lib.chart.demo;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import cn.jingzhuan.lib.chart.component.Highlight;
import cn.jingzhuan.lib.chart.event.HighlightStatusChangeListener;
import com.airbnb.epoxy.SimpleEpoxyAdapter;
/**
* Created by donglua on 9/7/17.
*/
public class ChartViewPagerAdapter extends PagerAdapter {
private RecyclerView view1;
private RecyclerView view2;
private RecyclerView view3;
private SimpleEpoxyAdapter epoxyAdapter1;
private SimpleEpoxyAdapter epoxyAdapter2;
private SimpleEpoxyAdapter epoxyAdapter3;
@SuppressLint("InflateParams")
ChartViewPagerAdapter(Context context) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
view1 = (RecyclerView) layoutInflater.inflate(R.layout.activity_main, null);
view2 = (RecyclerView) layoutInflater.inflate(R.layout.activity_main, null);
view3 = (RecyclerView) layoutInflater.inflate(R.layout.activity_main, null);
final HighlightStatusChangeListener highlightStatusListener = new HighlightStatusChangeListener() {
@Override public void onHighlightShow(Highlight[] highlights) {
view1.getParent().requestDisallowInterceptTouchEvent(true);
}
@Override public void onHighlightHide() {
view1.getParent().requestDisallowInterceptTouchEvent(false);
}
};
epoxyAdapter1 = new SimpleEpoxyAdapter();
epoxyAdapter1.addModels(new MinuteChartModel_().id(0).highlightStatusChangeListener(highlightStatusListener));
epoxyAdapter2 = new SimpleEpoxyAdapter();
epoxyAdapter2.addModels(new CandlestickChartModel_().id(1).highlightStatusChangeListener(highlightStatusListener));
epoxyAdapter3 = new SimpleEpoxyAdapter();
epoxyAdapter3.addModels(new CombineChartModel_().id(3));
view1.setAdapter(epoxyAdapter1);
view2.setAdapter(epoxyAdapter2);
view3.setAdapter(epoxyAdapter3);
}
@Override public int getCount() {
return 3;
}
@NonNull @Override
public Object instantiateItem(ViewGroup container, int position) {
View view = null;
switch (position) {
case 0:
view = view1;
break;
case 1:
view = view2;
break;
case 2:
view = view3;
break;
}
container.addView(view);
return view;
}
@Override public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
@@ -0,0 +1,104 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cn.jingzhuan.lib.chart.component.AxisY;
import cn.jingzhuan.lib.chart.data.BarDataSet;
import cn.jingzhuan.lib.chart.data.BarValue;
import cn.jingzhuan.lib.chart.data.LineDataSet;
import cn.jingzhuan.lib.chart.data.PointValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutCombineChartBinding;
/**
* Created by Donglua on 17/8/3.
*/
@EpoxyModelClass(layout = R.layout.layout_combine_chart)
public abstract class CombineChartModel extends DataBindingEpoxyModel {
private BarDataSet barDataSet;
private LineDataSet line;
public CombineChartModel() {
List<BarValue> barValueList = new ArrayList<>();
barValueList.add(new BarValue(1));
barValueList.add(new BarValue(-2));
barValueList.add(new BarValue(0));
barValueList.add(new BarValue(3));
barValueList.add(new BarValue(1));
barValueList.add(new BarValue(2));
barValueList.add(new BarValue(2));
barValueList.add(new BarValue(3));
barValueList.add(new BarValue(5));
barDataSet = new BarDataSet(barValueList, AxisY.DEPENDENCY_RIGHT);
barDataSet.setAutoBarWidth(true);
barDataSet.setColor(Color.DKGRAY);
final List<Float> floats = Arrays.asList(3134.55f, 3134.62f, 3134.34f, 3133.53f, 3133.37f,
3132.10f, 3131.55f, 3132.10f, 3133.30f, 3133.39f, 3133.02f, 3133.32f, 3132.60f,
3132.88f, 3132.46f, 3131.71f, 3132.14f, 3132.83f, 3132.40f, 3133.32f, 3134.26f,
3135.62f, 3136.88f, 3138.13f, 3138.51f, 3138.17f, 3138.73f, 3138.40f, 3138.65f,
3137.40f, 3137.05f, 3136.25f, 3136.70f, 3137.04f, 3136.28f, 3136.26f, 3135.62f,
3135.91f, 3135.85f, 3135.80f, 3136.21f, 3136.12f, 3136.41f, 3136.54f, 3136.30f,
3136.35f, 3135.62f, 3134.05f, 3133.15f, 3132.52f, 3132.28f, 3132.98f, 3133.08f,
3132.93f, 3133.18f, 3133.12f, 3134.12f, 3133.87f, 3133.84f, 3134.03f, 3134.16f,
3134.62f, 3135.23f, 3135.51f, 3135.59f, 3135.79f, 3136.02f, 3135.46f, 3135.90f,
3135.09f, 3135.05f, 3134.57f, 3135.03f, 3134.52f, 3134.82f, 3134.57f, 3134.78f,
3135.44f, 3135.13f, 3136.28f, 3136.62f, 3137.25f, 3137.16f, 3137.62f, 3138.21f,
3138.07f, 3138.09f, 3138.47f, 3139.63f, 3139.70f, 3140.38f, 3140.87f, 3142.22f,
3142.80f, 3143.58f, 3142.84f, 3143.13f, 3143.77f, 3143.77f, 3146.54f, 3145.62f,
3144.90f, 3144.80f, 3144.78f, 3144.76f, 3144.40f, 3144.15f, 3144.60f, 3145.46f,
3146.13f, 3145.82f, 3146.05f, 3144.65f, 3144.27f, 3144.29f, 3143.62f, 3143.67f,
3143.56f, 3142.93f, 3142.19f, 3142.72f, 3142.29f, 3142.39f, 3141.31f, 3141.92f,
3142.13f, 3141.65f, 3141.60f, 3140.42f, 3139.55f, 3139.94f, 3140.05f, 3139.12f,
3139.35f, 3138.90f, 3139.02f, 3138.87f, 3138.83f, 3138.53f, 3139.31f, 3139.36f,
3138.91f, 3139.06f, 3139.13f, 3139.52f, 3139.57f, 3138.82f, 3138.17f, 3138.5f,
3137.95f, 3138.55f, 3137.82f, 3138.25f, 3137.59f, 3137.75f, 3137.96f, 3138.37f,
3137.82f, 3138.22f, 3138.17f, 3137.31f, 3137.96f, 3137.22f, 3137.82f, 3137.19f,
3137.78f, 3137.93f, 3138.65f, 3138.70f, 3140.12f, 3140.35f, 3140.28f, 3140.46f,
3140.22f, 3140.06f, 3138.75f, 3139.31f, 3138.73f, 3137.54f, 3137.13f, 3136.23f,
3136.20f, 3136.53f, 3135.56f, 3135.71f, 3135.68f, 3135.89f, 3136.31f, 3135.81f,
3135.82f, 3135.5f, 3136.18f, 3138.01f, 3137.89f, 3138.09f, 3138.21f, 3138.52f,
3138.70f, 3138.55f, 3138.02f, 3137.73f, 3137.36f, 3137.59f, 3137.45f, 3137.89f,
3138.29f, 3138.63f, 3138.54f, 3139.09f, 3140.09f, 3140.89f, 3141.19f, 3141.57f,
3141.92f, 3142.10f, 3142.44f, 3143.38f, 3143.96f, 3144.77f, 3144.37f, 3148.02f,
3149.62f, 3149.79f, 3149.5f, 3148.58f, 3148.39f, 3148.43f, 3148.5f, 3148.12f,
3146.07f, 3144.87f, 3145.0f, 3144.67f, 3142.95f, 3143.63f, 3143.5f, 3144.13f,
3145.08f, 3145.06f, 3144.96f, 3143.86f);
List<PointValue> values = new ArrayList<>();
for (Float value: floats) {
values.add(new PointValue(value));
}
line = new LineDataSet(values, AxisY.DEPENDENCY_LEFT);
line.setHighlightedVerticalEnable(true);
}
@Override public View buildView(ViewGroup parent) {
View rootView = super.buildView(parent);
LayoutCombineChartBinding chartBinding = (LayoutCombineChartBinding) rootView.getTag();
chartBinding.combineChart.enableHighlightDashPathEffect(new float[] {10, 10}, 10);
chartBinding.combineChart.addDataSet(barDataSet);
chartBinding.combineChart.addDataSet(line);
return rootView;
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
}
}
@@ -0,0 +1,65 @@
package cn.jingzhuan.lib.chart.demo;
import android.content.Context;
import android.util.LayoutDirection;
import android.view.View;
import android.widget.Toast;
import com.airbnb.epoxy.AutoModel;
import com.airbnb.epoxy.EpoxyController;
/**
* Created by Donglua on 17/7/26.
*/
public class DemoAdapterController extends EpoxyController {
@AutoModel LineChartModel_ lineChartModel;
@AutoModel MinuteChartModel_ minuteChartModel;
@AutoModel BarChartModel_ barChartModel_;
@AutoModel BarChartClickableModel_ barChartClickableModel_;
@AutoModel CombineChartModel_ combineChartModel_;
@AutoModel CandlestickChartModel_ candlestickChartModel_;
@AutoModel ViewPagerModel_ viewPagerModel_;
@AutoModel ScatterChartModel_ scatterChartModel_;
@AutoModel ScatterChart2Model_ scatterChart2Model_;
private Context context;
public DemoAdapterController(Context context) {
this.context = context;
}
@Override
protected void buildModels() {
new LayoutDescTextBindingModel_().id("LineDataSet").text("LineDataSet Chart").addTo(this);
lineChartModel.addTo(this);
new LayoutDescTextBindingModel_().id("Minute").text("Minute Chart").addTo(this);
minuteChartModel.onClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
}
}).addTo(this);
new LayoutDescTextBindingModel_().id("Bar").text("Bar Chart").addTo(this);
barChartModel_.addTo(this);
barChartClickableModel_.addTo(this);
new LayoutDescTextBindingModel_().id("Combine").text("Combine Chart").addTo(this);
combineChartModel_.addTo(this);
new LayoutDescTextBindingModel_().id("Candlestick").text("Candlestick Chart").addTo(this);
candlestickChartModel_.addTo(this);
new LayoutDescTextBindingModel_().id("View Pager").text("ViewPager").addTo(this);
viewPagerModel_.pagerAdapter(new ChartViewPagerAdapter(context)).addTo(this);
new LayoutDescTextBindingModel_().id("Scatter Chart").text("Scatter Chart").addTo(this);
scatterChartModel_.addTo(this);
scatterChart2Model_.addTo(this);
}
}
@@ -0,0 +1,95 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import androidx.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import cn.jingzhuan.lib.chart.Viewport;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import cn.jingzhuan.lib.chart.data.LineDataSet;
import cn.jingzhuan.lib.chart.data.PointValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutLineChartBinding;
import static cn.jingzhuan.lib.chart.Viewport.*;
/**
* Created by Donglua on 17/7/26.
*/
@EpoxyModelClass(layout = R.layout.layout_line_chart)
public abstract class LineChartModel extends DataBindingEpoxyModel {
private LineDataSet line;
public LineChartModel() {
final List<Float> floats = Arrays.asList(3134.55f, 3134.62f, 3134.34f, 3133.53f, 3133.37f,
3132.10f, 3131.55f, 3132.10f, 3133.30f, 3133.39f, 3133.02f, 3133.32f, 3132.60f,
3132.88f, 3132.46f, 3131.71f, 3132.14f, 3132.83f, 3132.40f, 3133.32f, 3134.26f,
3135.62f, 3136.88f, 3138.13f, 3138.51f, 3138.17f, 3138.73f, 3138.40f, 3138.65f,
3137.40f, 3137.05f, 3136.25f, 3136.70f, 3137.04f, 3136.28f, 3136.26f, 3135.62f,
3135.91f, 3135.85f, 3135.80f, 3136.21f, 3136.12f, 3136.41f, 3136.54f, 3136.30f,
3136.35f, 3135.62f, 3134.05f, 3133.15f, 3132.52f, 3132.28f, 3132.98f, 3133.08f,
3132.93f, 3133.18f, 3133.12f, 3134.12f, 3133.87f, 3133.84f, 3134.03f, 3134.16f,
3134.62f, 3135.23f, 3135.51f, 3135.59f, 3135.79f, 3136.02f, 3135.46f, 3135.90f,
3135.09f, 3135.05f, 3134.57f, 3135.03f, 3134.52f, 3134.82f, 3134.57f, 3134.78f,
3135.44f, 3135.13f, 3136.28f, 3136.62f, 3137.25f, 3137.16f, 3137.62f, 3138.21f,
3138.07f, 3138.09f, 3138.47f, 3139.63f, 3139.70f, 3140.38f, 3140.87f, 3142.22f,
3142.80f, 3143.58f, 3142.84f, 3143.13f, 3143.77f, 3143.77f, 3146.54f, 3145.62f,
3144.90f, 3144.80f, 3144.78f, 3144.76f, 3144.40f, 3144.15f, 3144.60f, 3145.46f,
3146.13f, 3145.82f, 3146.05f, 3144.65f, 3144.27f, 3144.29f, 3143.62f, 3143.67f,
3143.56f, 3142.93f, 3142.19f, 3142.72f, 3142.29f, 3142.39f, 3141.31f, 3141.92f,
3142.13f, 3141.65f, 3141.60f, 3140.42f, 3139.55f, 3139.94f, 3140.05f, 3139.12f,
3139.35f, 3138.90f, 3139.02f, 3138.87f, 3138.83f, 3138.53f, 3139.31f, 3139.36f,
3138.91f, 3139.06f, 3139.13f, 3139.52f, 3139.57f, 3138.82f, 3138.17f, 3138.5f,
3137.95f, 3138.55f, 3137.82f, 3138.25f, 3137.59f, 3137.75f, 3137.96f, 3138.37f,
3137.82f, 3138.22f, 3138.17f, 3137.31f, 3137.96f, 3137.22f, 3137.82f, 3137.19f,
3137.78f, 3137.93f, 3138.65f, 3138.70f, 3140.12f, 3140.35f, 3140.28f, 3140.46f,
3140.22f, 3140.06f, 3138.75f, 3139.31f, 3138.73f, 3137.54f, 3137.13f, 3136.23f,
3136.20f, 3136.53f, 3135.56f, 3135.71f, 3135.68f, 3135.89f, 3136.31f, 3135.81f,
3135.82f, 3135.5f, 3136.18f, 3138.01f, 3137.89f, 3138.09f, 3138.21f, 3138.52f,
3138.70f, 3138.55f, 3138.02f, 3137.73f, 3137.36f, 3137.59f, 3137.45f, 3137.89f,
3138.29f, 3138.63f, 3138.54f, 3139.09f, 3140.09f, 3140.89f, 3141.19f, 3141.57f,
3141.92f, 3142.10f, 3142.44f, 3143.38f, 3143.96f, 3144.77f, 3144.37f, 3148.02f,
3149.62f, 3149.79f, 3149.5f, 3148.58f, 3148.39f, 3148.43f, 3148.5f, 3148.12f,
3146.07f, 3144.87f, 3145.0f, 3144.67f, 3142.95f, 3143.63f, 3143.5f, 3144.13f,
3145.08f, 3145.06f, 3144.96f, 3143.86f);
List<PointValue> values = new ArrayList<>();
for (Float value: floats) {
values.add(new PointValue(value));
}
line = new LineDataSet(values);
}
@Override public View buildView(@NonNull ViewGroup parent) {
View rootView = super.buildView(parent);
final LayoutLineChartBinding bd = (LayoutLineChartBinding) rootView.getTag();
bd.lineChart.setCurrentViewport(new Viewport(0.5f, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX));
bd.lineChart.setDoubleTapToZoom(true);
bd.lineChart.addLine(line);
bd.btMoveLeft.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
bd.lineChart.moveLeft();
}
});
bd.btMoveRight.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
bd.lineChart.moveRight();
}
});
return rootView;
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
}
}
@@ -0,0 +1,22 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DemoAdapterController controller = new DemoAdapterController(this);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setAdapter(controller.getAdapter());
controller.requestModelBuild();
}
}
@@ -0,0 +1,150 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.view.View;
import android.view.ViewGroup;
import cn.jingzhuan.lib.chart.data.MinuteLine;
import cn.jingzhuan.lib.chart.data.PointValue;
import cn.jingzhuan.lib.chart.data.ValueFormatter;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutMinuteChartBinding;
import cn.jingzhuan.lib.chart.event.HighlightStatusChangeListener;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyAttribute;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import static com.airbnb.epoxy.EpoxyAttribute.Option.DoNotHash;
/**
* Created by Donglua on 17/7/26.
*/
@EpoxyModelClass(layout = R.layout.layout_minute_chart)
public abstract class MinuteChartModel extends DataBindingEpoxyModel {
private final MinuteLine line;
private final float lastClose = 11.27f;
@EpoxyAttribute(DoNotHash) View.OnClickListener onClickListener;
@EpoxyAttribute(DoNotHash) HighlightStatusChangeListener highlightStatusChangeListener;
public MinuteChartModel() {
final List<Float> floats =
Arrays.asList(11.17449f, 11.15434f, 11.16595f, 11.18753f, 11.25440f, 11.27073f,
11.27655f, 11.28625f, 11.28847f, 11.29051f, 11.29548f, 11.29861f, 11.31060f,
11.3284f, 11.37816f, 11.55310f, 11.630815f, 11.67925f, 11.69136f, 11.70062f,
11.71826f, 11.7231f, 11.72815f, 11.73347f, 11.73745f, 11.73951f, 11.74206f,
11.74301f, 11.74439f, 11.748f, 11.75036f, 11.75095f, 11.75179f, 11.75248f,
11.75364f, 11.75408f, 11.75480f, 11.75523f, 11.75574f, 11.75635f, 11.75658f,
11.75653f, 11.75634f, 11.75630f, 11.75648f, 11.75646f, 11.75623f, 11.75595f,
11.75535f, 11.75532f, 11.75566f, 11.75635f, 11.7568f, 11.75707f, 11.75712f,
11.75716f, 11.75725f, 11.75740f, 11.75747f, 11.75758f, 11.75761f, 11.75753f,
11.75761f, 11.75843f, 11.75917f, 11.75963f, 11.75987f, 11.76059f, 11.76152f,
11.76369f, 11.76426f, 11.76446f, 11.76476f, 11.76524f, 11.76556f, 11.76582f,
11.76620f, 11.76672f, 11.76706f, 11.76722f, 11.76734f, 11.76747f, 11.76757f,
11.76773f, 11.76784f, 11.76798f, 11.768167f, 11.76890f, 11.77048f, 11.77456f,
11.77975f, 11.78389f, 11.78564f, 11.78687f, 11.78745f, 11.78922f, 11.79016f,
11.79115f, 11.7921f, 11.79493f, 11.79727f, 11.80075f, 11.80353f, 11.80501f,
11.80609f, 11.80723f, 11.80804f, 11.808804f, 11.80939f, 11.80992f, 11.81042f,
11.810842f, 11.81131f, 11.81160f, 11.81220f, 11.81263f, 11.81307f, 11.81345f,
11.81370f, 11.81391f, 11.81391f, 11.81438f, 11.815f, 11.8159f, 11.81694f, 11.81789f,
11.81858f, 11.81979f, 11.82164f, 11.83589f, 11.85183f, 11.86026f, 11.86534f,
11.869019f, 11.8747f, 11.87665f, 11.87817f, 11.879498f, 11.88067f, 11.88169f,
11.88243f, 11.88314f, 11.88457f, 11.88600f, 11.88785f, 11.89003f, 11.89152f,
11.89222f, 11.89326f, 11.89778f, 11.89976f, 11.90208f, 11.90356f, 11.90446f,
11.905189f, 11.90743f, 11.91124f, 11.91410f, 11.91525f, 11.91636f, 11.919332f,
11.9204f, 11.93025f, 11.97543f, 11.98504f, 11.98811f, 11.9973f, 11.99822f,
11.99884f, 11.99967f, 12.01106f, 12.01314f, 12.01556f, 12.01905f, 12.02244f,
12.02431f, 12.02863f, 12.03055f, 12.03342f, 12.03391f, 12.03451f, 12.03473f,
12.03511f, 12.03528f, 12.035439f, 12.03549f, 12.0356f, 12.03586f, 12.03604f,
12.03629f, 12.03680f, 12.03695f, 12.03699f, 12.03723f, 12.03739f, 12.03746f,
12.03752f, 12.037683f, 12.03771f, 12.03792f, 12.03796f, 12.03856f, 12.03860f,
12.03868f, 12.038836f, 12.03899f, 12.03905f, 12.03908f, 12.03914f, 12.03921f,
12.03924f, 12.03937f, 12.03948f, 12.03954f, 12.03958f, 12.03964f, 12.03966f,
12.0397f, 12.03979f, 12.03985f, 12.03988f, 12.03995f, 12.04000f, 12.04021f,
12.04030f, 12.04048f, 12.04051f, 12.040565f, 12.04060f, 12.04163f, 12.04201f,
12.04210f, 12.04233f, 12.04259f, 12.04264f, 12.04272f, 12.04276f, 12.04279f,
12.04279f, 12.04307f);
List<PointValue> values = new ArrayList<>();
for (Float value: floats) {
values.add(new PointValue(value));
}
line = new MinuteLine(values);
line.setHighlightedVerticalEnable(true);
line.setHighlightedHorizontalEnable(true);
line.setLastClose(lastClose);
}
@Override public View buildView(ViewGroup parent) {
View rootView = super.buildView(parent);
final LayoutMinuteChartBinding minuteBinding = (LayoutMinuteChartBinding) rootView.getTag();
minuteBinding.minuteChart.getAxisLeft().enableGridDashPathEffect(new float[] {10, 10}, 10);
minuteBinding.minuteChart.getAxisTop().enableGridDashPathEffect(new float[] {10, 10}, 10);
minuteBinding.minuteChart.getAxisTop().setGridLineEnable(true);
minuteBinding.minuteChart.getAxisRight().setLabelValueFormatter(new ValueFormatter() {
@Override
public String format(float value, int index) {
return String.format(Locale.ENGLISH, "%.2f%%",
(value - lastClose) / lastClose * 100);
}
});
minuteBinding.minuteChart.getAxisBottom().setGridCount(1);
minuteBinding.minuteChart.getAxisTop().setGridCount(3);
minuteBinding.minuteChart.getAxisRight().setGridCount(1);
minuteBinding.minuteChart.getAxisBottom().setLabelValueFormatter(new ValueFormatter() {
@Override
public String format(float value, int index) {
if (index == 0) {
return "9:30";
}
if (index == 1) {
return "11:30/13:00";
}
if (index == 2) {
return "15:00";
}
return "";
}
});
minuteBinding.minuteChart.setOnHighlightStatusChangeListener(highlightStatusChangeListener);
minuteBinding.minuteChart.setHighlightColor(Color.BLACK);
minuteBinding.minuteChart.setScaleXEnable(false);
line.setShader(new LinearGradient(0, 0, 0, 0, 0x10000000, 0x10000000, Shader.TileMode.REPEAT));
minuteBinding.minuteChart.addLine(line);
minuteBinding.minuteChart.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
minuteBinding.minuteChart.animateX(1000);
onClickListener.onClick(v);
}
});
minuteBinding.minuteChart.enableHighlightDashPathEffect(new float[] {10, 10}, 10);
return rootView;
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
}
}
@@ -0,0 +1,81 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import androidx.appcompat.content.res.AppCompatResources;
import android.widget.Toast;
import cn.jingzhuan.lib.chart.base.Chart;
import cn.jingzhuan.lib.chart.data.ScatterDataSet;
import cn.jingzhuan.lib.chart.data.ScatterValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutScatterChartBinding;
import cn.jingzhuan.lib.chart.event.OnEntryClickListener;
import cn.jingzhuan.lib.chart.renderer.TextValueRenderer;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by donglua on 10/19/17.
*/
@EpoxyModelClass(layout = R.layout.layout_scatter_chart)
public abstract class ScatterChart2Model extends DataBindingEpoxyModel {
private ScatterDataSet scatterDataSet;
private final List<String> textList =
Arrays.asList("data1", "data2", "data3", "data4", "data5", "data6");
public ScatterChart2Model() {
final List<ScatterValue> scatterValues = new ArrayList<>();
scatterValues.add(new ScatterValue(1f));
scatterValues.add(new ScatterValue(2f));
scatterValues.add(new ScatterValue(1f));
scatterValues.add(new ScatterValue(2f));
scatterValues.add(new ScatterValue(1f));
scatterValues.add(new ScatterValue(2f));
scatterDataSet = new ScatterDataSet(scatterValues);
scatterDataSet.setMinValueOffsetPercent(0.5f);
scatterDataSet.setMaxValueOffsetPercent(0.5f);
scatterDataSet.setAutoWidth(false);
scatterDataSet.addTextValueRenderer(new TextValueRenderer() {
@Override public void render(Canvas canvas, Paint textPaint, int index, float x, float y) {
textPaint.setTextSize(24f);
canvas.drawText(textList.get(index), x, y, textPaint);
}
});
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
if (binding instanceof LayoutScatterChartBinding) {
Drawable drawable = AppCompatResources.getDrawable(binding.getRoot().getContext(),
R.drawable.shape_circle);
scatterDataSet.setShape(drawable);
LayoutScatterChartBinding bd = (LayoutScatterChartBinding) binding;
bd.combineChart.getAxisBottom().setGridCount(1);
bd.combineChart.getAxisLeft().setGridCount(1);
bd.combineChart.addDataSet(scatterDataSet);
bd.combineChart.setOnEntryClickListener(new OnEntryClickListener() {
@Override public void onEntryClick(Chart chart, int position) {
if (position >= 0) {
Toast.makeText(chart.getContext(), textList.get(position), Toast.LENGTH_SHORT).show();
}
}
});
}
}
}
@@ -0,0 +1,63 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import android.graphics.drawable.Drawable;
import androidx.appcompat.content.res.AppCompatResources;
import cn.jingzhuan.lib.chart.data.ScatterDataSet;
import cn.jingzhuan.lib.chart.data.ScatterValue;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutBarChartBinding;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutCombineChartBinding;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyModelClass;
import java.util.ArrayList;
import java.util.List;
/**
* Created by donglua on 10/19/17.
*/
@EpoxyModelClass(layout = R.layout.layout_combine_chart)
public abstract class ScatterChartModel extends DataBindingEpoxyModel {
private ScatterDataSet scatterDataSet;
public ScatterChartModel() {
final List<ScatterValue> scatterValues = new ArrayList<>();
scatterValues.add(new ScatterValue(2));
scatterValues.add(new ScatterValue(3));
scatterValues.add(new ScatterValue(4));
scatterValues.add(new ScatterValue(6));
scatterValues.add(new ScatterValue(9));
scatterValues.add(new ScatterValue(2));
scatterValues.add(new ScatterValue(4));
scatterValues.add(new ScatterValue(6));
scatterValues.add(new ScatterValue(9));
scatterValues.add(new ScatterValue(0));
scatterValues.add(new ScatterValue(8));
scatterValues.add(new ScatterValue(9));
scatterValues.add(new ScatterValue(4));
scatterValues.add(new ScatterValue(1));
scatterValues.add(new ScatterValue(2));
scatterDataSet = new ScatterDataSet(scatterValues);
scatterDataSet.setAutoWidth(true);
}
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
if (binding instanceof LayoutCombineChartBinding) {
Drawable drawable = AppCompatResources.getDrawable(binding.getRoot().getContext(), R.drawable.ic_example);
scatterDataSet.setShape(drawable);
LayoutCombineChartBinding bd = (LayoutCombineChartBinding) binding;
bd.combineChart.addDataSet(scatterDataSet);
}
}
}
@@ -0,0 +1,29 @@
package cn.jingzhuan.lib.chart.demo;
import androidx.databinding.ViewDataBinding;
import androidx.viewpager.widget.PagerAdapter;
import cn.jingzhuan.lib.chart.demo.databinding.LayoutViewPagerBinding;
import com.airbnb.epoxy.DataBindingEpoxyModel;
import com.airbnb.epoxy.EpoxyAttribute;
import com.airbnb.epoxy.EpoxyModelClass;
/**
* Created by donglua on 9/7/17.
*/
@EpoxyModelClass(layout = R.layout.layout_view_pager)
public abstract class ViewPagerModel extends DataBindingEpoxyModel {
@EpoxyAttribute PagerAdapter pagerAdapter;
@Override protected void setDataBindingVariables(ViewDataBinding binding) {
LayoutViewPagerBinding b = (LayoutViewPagerBinding) binding;
b.viewPager.setAdapter(pagerAdapter);
}
@Override public boolean shouldSaveViewState() {
return true;
}
}
@@ -0,0 +1,13 @@
package cn.jingzhuan.lib.chart.demo;
import com.airbnb.epoxy.EpoxyDataBindingLayouts;
import com.airbnb.epoxy.PackageModelViewConfig;
@EpoxyDataBindingLayouts({
R.layout.layout_desc_text,
})
@PackageModelViewConfig(
rClass = R.class
)
interface EpoxyDataBindingConfig {
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="108.0"
android:viewportWidth="108.0">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#66FFFFFF"
android:strokeWidth="0.8" />
</vector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/colorPrimary" />
<size android:width="64dp" android:height="64dp" />
</shape>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recyclerView"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:context="cn.jingzhuan.lib.chart.demo.MainActivity" />
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layout>
<cn.jingzhuan.lib.chart.widget.BarChart
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/barChart"
android:layout_height="200dp" />
</layout>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="200dp"
>
<cn.jingzhuan.lib.chart.widget.BarChart
android:id="@+id/barChart"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<cn.jingzhuan.lib.chart.widget.CombineChart
android:id="@+id/combine_chart"
android:layout_width="match_parent"
android:layout_height="200dp" />
</layout>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<data>
<variable name="text" type="String" />
</data>
<TextView
android:padding="12dp"
android:text="@{text}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</layout>
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:ignore="HardcodedText"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<cn.jingzhuan.lib.chart.widget.LineChart
app:labelSeparation="4dp"
android:id="@+id/lineChart"
android:layout_width="match_parent"
android:layout_height="200dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
style="?android:attr/buttonBarButtonStyle"
android:id="@+id/bt_move_left"
android:text="move left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
style="?android:attr/buttonBarButtonStyle"
android:id="@+id/bt_move_right"
android:text="move right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>
</layout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<cn.jingzhuan.lib.chart.widget.LineChart
android:id="@+id/minute_chart"
android:layout_width="match_parent"
android:layout_height="200dp" />
</layout>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<cn.jingzhuan.lib.chart.widget.CombineChart
android:id="@+id/combine_chart"
android:layout_width="match_parent"
android:layout_height="160dp" />
</layout>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
>
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="240dp" />
</layout>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name">JingZhuanChart</string>
</resources>
@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
@@ -0,0 +1,17 @@
package cn.jingzhuan.lib.chart.demo;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
+48
View File
@@ -0,0 +1,48 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: "config.gradle"
repositories {
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public' }
maven { url 'https://maven.aliyun.com/repository/releases' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central' }
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.4.1'
// classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
// classpath 'com.novoda:bintray-release:0.9.1'
// classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$ver.kotlinVersion"
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.25.3'
}
}
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://mirrors.tencent.com/nexus/repository/maven-public' }
maven { url 'https://maven.aliyun.com/repository/releases' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central' }
google()
mavenCentral()
}
tasks.withType(Javadoc).configureEach {
enabled = false
}
}
tasks.register('clean', Delete) {
delete rootProject.buildDir
}
+1
View File
@@ -0,0 +1 @@
/build
+55
View File
@@ -0,0 +1,55 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
setLibDefaultConfig(android)
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
namespace 'cn.jingzhuan.lib.chart'
compileOptions.coreLibraryDesugaringEnabled true
libraryVariants.configureEach {
it.generateBuildConfig.enabled = false
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.appcompat:appcompat:1.6.1'
testImplementation 'junit:junit:4.13.2'
implementation 'androidx.annotation:annotation:1.7.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$ver.kotlinVersion"
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
apply plugin: 'com.vanniktech.maven.publish'
publishing {
repositories {
maven {
def releasesRepoUrl = "http://192.168.0.251:8081/repository/maven-releases"
def snapshotsRepoUrl = "http://192.168.0.251:8081/repository/maven-snapshots"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
allowInsecureProtocol = true
credentials {
username 'admin'
password 'admin123'
}
}
}
}
Binary file not shown.
@@ -0,0 +1,6 @@
#Tue Jul 25 16:13:11 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-milestone-1-all.zip
+160
View File
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
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`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+90
View File
@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-lineDataSet arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command lineDataSet arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command lineDataSet
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+25
View File
@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/donglua/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# 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 *;
#}
# Uncomment this to preserve the lineDataSet number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the lineDataSet number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package cn.jingzhuan.chart;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest {
@Test public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("cn.jingzhuan.lib.chart.test", appContext.getPackageName());
}
}
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
</application>
</manifest>
@@ -0,0 +1,11 @@
package cn.jingzhuan.lib.chart;
/**
* Created by Donglua on 17/7/17.
*/
public class AxisAutoValues {
public float[] values = new float[]{};
public int number;
public int decimals;
}
@@ -0,0 +1,58 @@
package cn.jingzhuan.lib.chart;
import android.graphics.Rect;
import android.graphics.RectF;
/**
* Created by Donglua on 17/7/17.
*/
public class Viewport extends RectF {
public static final float AXIS_X_MIN = 0f;
public static final float AXIS_X_MAX = 1f;
public static final float AXIS_Y_MIN = -1f;
public static final float AXIS_Y_MAX = 1f;
public Viewport() {
this(AXIS_X_MIN, AXIS_Y_MIN, AXIS_X_MAX, AXIS_Y_MAX);
}
public Viewport(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
}
public Viewport(RectF r) {
super(r);
}
public Viewport(Rect r) {
super(r);
}
/**
* Ensures that current viewport is inside the viewport extremes defined by {@link #AXIS_X_MIN},
* {@link #AXIS_X_MAX}, {@link #AXIS_Y_MIN} and {@link #AXIS_Y_MAX}.
*/
public void constrainViewport() {
left = Math.max(AXIS_X_MIN, left);
right = Math.max(Math.nextUp(left), right);
}
public boolean initialized() {
if (left == 0f && right == 1f && top == -1f && bottom == 1f) {
return false;
}
return true;
}
public Viewport moveToEnd() {
Viewport vp = new Viewport(this);
float width = this.width();
vp.right = 1f;
vp.left = right - width;
return vp;
}
}
@@ -0,0 +1,130 @@
/*
* Copyright 2013 The Android Open Source Project
*
* 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.
*/
package cn.jingzhuan.lib.chart;
import android.content.Context;
import android.os.SystemClock;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
/**
* A simple class that animates double-touch zoom gestures. Functionally similar to a {@link
* android.widget.Scroller}.
*/
public class Zoomer {
/**
* The interpolator, used for making zooms animate 'naturally.'
*/
private Interpolator mInterpolator;
/**
* The total animation duration for a zoom.
*/
private int mAnimationDurationMillis;
/**
* Whether or not the current zoom has finished.
*/
private boolean mFinished = true;
/**
* The current zoom value; computed by {@link #computeZoom()}.
*/
private float mCurrentZoom;
/**
* The time the zoom started, computed using {@link SystemClock#elapsedRealtime()}.
*/
private long mStartRTC;
/**
* The destination zoom factor.
*/
private float mEndZoom;
public Zoomer(Context context) {
mInterpolator = new DecelerateInterpolator();
mAnimationDurationMillis = context.getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
/**
* Forces the zoom finished state to the given value. Unlike {@link #abortAnimation()}, the
* current zoom value isn't set to the ending value.
*
* @see android.widget.Scroller#forceFinished(boolean)
*/
public void forceFinished(boolean finished) {
mFinished = finished;
}
/**
* Aborts the animation, setting the current zoom value to the ending value.
*
* @see android.widget.Scroller#abortAnimation()
*/
public void abortAnimation() {
mFinished = true;
mCurrentZoom = mEndZoom;
}
/**
* Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should
* by 0.25f.
*
* @see android.widget.Scroller#startScroll(int, int, int, int)
*/
public void startZoom(float endZoom) {
mStartRTC = SystemClock.elapsedRealtime();
mEndZoom = endZoom;
mFinished = false;
mCurrentZoom = 1f;
}
/**
* Computes the current zoom level, returning true if the zoom is still active and false if the
* zoom has finished.
*
* @see android.widget.Scroller#computeScrollOffset()
*/
public boolean computeZoom() {
if (mFinished) {
return false;
}
long tRTC = SystemClock.elapsedRealtime() - mStartRTC;
if (tRTC >= mAnimationDurationMillis) {
mFinished = true;
mCurrentZoom = mEndZoom;
return false;
}
float t = tRTC * 1f / mAnimationDurationMillis;
mCurrentZoom = mEndZoom * mInterpolator.getInterpolation(t);
return true;
}
/**
* Returns the current zoom level.
*
* @see android.widget.Scroller#getCurrX()
*/
public float getCurrZoom() {
return mCurrentZoom;
}
}
@@ -0,0 +1,205 @@
/*
Copyright 2018 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
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.
*/
package cn.jingzhuan.lib.chart.animation;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import static cn.jingzhuan.lib.chart.animation.Easing.*;
/**
* Object responsible for all animations in the Chart. Animations require API level 11.
*
* @author Philipp Jahoda
* @author Mick Ashton
*/
public class ChartAnimator {
/** object that is updated upon animation update */
private AnimatorUpdateListener mListener;
/** The phase of drawn values on the y-axis. 0 - 1 */
@SuppressWarnings("WeakerAccess")
protected float mPhaseY = 1f;
/** The phase of drawn values on the x-axis. 0 - 1 */
@SuppressWarnings("WeakerAccess")
protected float mPhaseX = 1f;
public ChartAnimator() { }
public ChartAnimator(AnimatorUpdateListener listener) {
mListener = listener;
}
private ObjectAnimator xAnimator(int duration, EasingFunction easing) {
ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f);
animatorX.setInterpolator(easing);
animatorX.setDuration(duration);
return animatorX;
}
private ObjectAnimator yAnimator(int duration, EasingFunction easing) {
ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f);
animatorY.setInterpolator(easing);
animatorY.setDuration(duration);
return animatorY;
}
/**
* Animates values along the X axis, in a linear fashion.
*
* @param durationMillis animation duration
*/
public void animateX(int durationMillis) {
animateX(durationMillis, Linear);
}
/**
* Animates values along the X axis.
*
* @param durationMillis animation duration
* @param easing EasingFunction
*/
public void animateX(int durationMillis, EasingFunction easing) {
ObjectAnimator animatorX = xAnimator(durationMillis, easing);
animatorX.addUpdateListener(mListener);
animatorX.start();
}
/**
* Animates values along both the X and Y axes, in a linear fashion.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
*/
public void animateXY(int durationMillisX, int durationMillisY) {
animateXY(durationMillisX, durationMillisY, Linear, Linear);
}
/**
* Animates values along both the X and Y axes.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
* @param easing EasingFunction for both axes
*/
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) {
ObjectAnimator xAnimator = xAnimator(durationMillisX, easing);
ObjectAnimator yAnimator = yAnimator(durationMillisY, easing);
if (durationMillisX > durationMillisY) {
xAnimator.addUpdateListener(mListener);
} else {
yAnimator.addUpdateListener(mListener);
}
xAnimator.start();
yAnimator.start();
}
/**
* Animates values along both the X and Y axes.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
* @param easingX EasingFunction for the X axis
* @param easingY EasingFunction for the Y axis
*/
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX,
EasingFunction easingY) {
ObjectAnimator xAnimator = xAnimator(durationMillisX, easingX);
ObjectAnimator yAnimator = yAnimator(durationMillisY, easingY);
if (durationMillisX > durationMillisY) {
xAnimator.addUpdateListener(mListener);
} else {
yAnimator.addUpdateListener(mListener);
}
xAnimator.start();
yAnimator.start();
}
/**
* Animates values along the Y axis, in a linear fashion.
*
* @param durationMillis animation duration
*/
public void animateY(int durationMillis) {
animateY(durationMillis, Linear);
}
/**
* Animates values along the Y axis.
*
* @param durationMillis animation duration
* @param easing EasingFunction
*/
public void animateY(int durationMillis, EasingFunction easing) {
ObjectAnimator animatorY = yAnimator(durationMillis, easing);
animatorY.addUpdateListener(mListener);
animatorY.start();
}
/**
* Gets the Y axis phase of the animation.
*
* @return float value of {@link #mPhaseY}
*/
public float getPhaseY() {
return mPhaseY;
}
/**
* Sets the Y axis phase of the animation.
*
* @param phase float value between 0 - 1
*/
public void setPhaseY(float phase) {
if (phase > 1f) {
phase = 1f;
} else if (phase < 0f) {
phase = 0f;
}
mPhaseY = phase;
}
/**
* Gets the X axis phase of the animation.
*
* @return float value of {@link #mPhaseX}
*/
public float getPhaseX() {
return mPhaseX;
}
/**
* Sets the X axis phase of the animation.
*
* @param phase float value between 0 - 1
*/
public void setPhaseX(float phase) {
if (phase > 1f) {
phase = 1f;
} else if (phase < 0f) {
phase = 0f;
}
mPhaseX = phase;
}
}
@@ -0,0 +1,316 @@
/*
Copyright 2018 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
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.
*/
package cn.jingzhuan.lib.chart.animation;
import android.animation.TimeInterpolator;
/**
* Easing options.
*
* @author Daniel Cohen Gindi
* @author Mick Ashton
*/
@SuppressWarnings("WeakerAccess")
public class Easing {
public interface EasingFunction extends TimeInterpolator {
@Override
float getInterpolation(float input);
}
private static final float DOUBLE_PI = 2f * (float) Math.PI;
@SuppressWarnings("unused")
public static final EasingFunction Linear = new EasingFunction() {
public float getInterpolation(float input) {
return input;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInQuad = new EasingFunction() {
public float getInterpolation(float input) {
return input * input;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutQuad = new EasingFunction() {
public float getInterpolation(float input) {
return -input * (input - 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutQuad = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * input * input;
}
return -0.5f * ((--input) * (input - 2f) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInCubic = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.pow(input, 3);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutCubic = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return (float) Math.pow(input, 3) + 1f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutCubic = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(input, 3);
}
input -= 2f;
return 0.5f * ((float) Math.pow(input, 3) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInQuart = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.pow(input, 4);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutQuart = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return -((float) Math.pow(input, 4) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutQuart = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(input, 4);
}
input -= 2f;
return -0.5f * ((float) Math.pow(input, 4) - 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInSine = new EasingFunction() {
public float getInterpolation(float input) {
return -(float) Math.cos(input * (Math.PI / 2f)) + 1f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutSine = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.sin(input * (Math.PI / 2f));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutSine = new EasingFunction() {
public float getInterpolation(float input) {
return -0.5f * ((float) Math.cos(Math.PI * input) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInExpo = new EasingFunction() {
public float getInterpolation(float input) {
return (input == 0) ? 0f : (float) Math.pow(2f, 10f * (input - 1f));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutExpo = new EasingFunction() {
public float getInterpolation(float input) {
return (input == 1f) ? 1f : (-(float) Math.pow(2f, -10f * (input + 1f)));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutExpo = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1f) {
return 1f;
}
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(2f, 10f * (input - 1f));
}
return 0.5f * (-(float) Math.pow(2f, -10f * --input) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInCirc = new EasingFunction() {
public float getInterpolation(float input) {
return -((float) Math.sqrt(1f - input * input) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutCirc = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return (float) Math.sqrt(1f - input * input);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutCirc = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return -0.5f * ((float) Math.sqrt(1f - input * input) - 1f);
}
return 0.5f * ((float) Math.sqrt(1f - (input -= 2f) * input) + 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1) {
return 1f;
}
float p = 0.3f;
float s = p / DOUBLE_PI * (float) Math.asin(1f);
return -((float) Math.pow(2f, 10f * (input -= 1f))
*(float) Math.sin((input - s) * DOUBLE_PI / p));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1) {
return 1f;
}
float p = 0.3f;
float s = p / DOUBLE_PI * (float) Math.asin(1f);
return 1f
+ (float) Math.pow(2f, -10f * input)
* (float) Math.sin((input - s) * DOUBLE_PI / p);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
}
input *= 2f;
if (input == 2) {
return 1f;
}
float p = 1f / 0.45f;
float s = 0.45f / DOUBLE_PI * (float) Math.asin(1f);
if (input < 1f) {
return -0.5f
* ((float) Math.pow(2f, 10f * (input -= 1f))
* (float) Math.sin((input * 1f - s) * DOUBLE_PI * p));
}
return 1f + 0.5f
* (float) Math.pow(2f, -10f * (input -= 1f))
* (float) Math.sin((input * 1f - s) * DOUBLE_PI * p);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInBack = new EasingFunction() {
public float getInterpolation(float input) {
final float s = 1.70158f;
return input * input * ((s + 1f) * input - s);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutBack = new EasingFunction() {
public float getInterpolation(float input) {
final float s = 1.70158f;
input--;
return (input * input * ((s + 1f) * input + s) + 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutBack = new EasingFunction() {
public float getInterpolation(float input) {
float s = 1.70158f;
input *= 2f;
if (input < 1f) {
return 0.5f * (input * input * (((s *= (1.525f)) + 1f) * input - s));
}
return 0.5f * ((input -= 2f) * input * (((s *= (1.525f)) + 1f) * input + s) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInBounce = new EasingFunction() {
public float getInterpolation(float input) {
return 1f - EaseOutBounce.getInterpolation(1f - input);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutBounce = new EasingFunction() {
public float getInterpolation(float input) {
float s = 7.5625f;
if (input < (1f / 2.75f)) {
return s * input * input;
} else if (input < (2f / 2.75f)) {
return s * (input -= (1.5f / 2.75f)) * input + 0.75f;
} else if (input < (2.5f / 2.75f)) {
return s * (input -= (2.25f / 2.75f)) * input + 0.9375f;
}
return s * (input -= (2.625f / 2.75f)) * input + 0.984375f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutBounce = new EasingFunction() {
public float getInterpolation(float input) {
if (input < 0.5f) {
return EaseInBounce.getInterpolation(input * 2f) * 0.5f;
}
return EaseOutBounce.getInterpolation(input * 2f - 1f) * 0.5f + 0.5f;
}
};
}
@@ -0,0 +1,309 @@
package cn.jingzhuan.lib.chart.base;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.MotionEvent;
import cn.jingzhuan.lib.chart.animation.ChartAnimator;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import cn.jingzhuan.lib.chart.component.Highlight;
import cn.jingzhuan.lib.chart.event.HighlightStatusChangeListener;
import cn.jingzhuan.lib.chart.event.OnHighlightListener;
import cn.jingzhuan.lib.chart.renderer.AbstractDataRenderer;
import cn.jingzhuan.lib.chart.renderer.AxisRenderer;
import cn.jingzhuan.lib.chart2.renderer.LineRenderer;
import static cn.jingzhuan.lib.chart.animation.Easing.*;
/**
* Created by Donglua on 17/7/17.
*/
public class BaseChart extends Chart {
protected AbstractDataRenderer mRenderer;
protected List<AxisRenderer> mAxisRenderers;
protected Highlight[] mHighlights;
private HighlightStatusChangeListener mHighlightStatusChangeListener;
private OnHighlightListener mHighlightListener;
protected WeakReference<Bitmap> mDrawBitmap;
protected Canvas mBitmapCanvas;
protected Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888;
private ChartAnimator mChartAnimator;
public BaseChart(Context context) {
super(context);
}
public BaseChart(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BaseChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BaseChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void initChart() {
mAxisRenderers = new ArrayList<>(4);
mAxisRenderers.add(new AxisRenderer(this, mAxisTop));
mAxisRenderers.add(new AxisRenderer(this, mAxisBottom));
mAxisRenderers.add(new AxisRenderer(this, mAxisLeft));
mAxisRenderers.add(new AxisRenderer(this, mAxisRight));
mChartAnimator = new ChartAnimator(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
postInvalidate();
}
});
}
@Override protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mBitmapCanvas != null) {
mBitmapCanvas.setBitmap(null);
mBitmapCanvas = null;
}
if (mDrawBitmap != null) {
if (mDrawBitmap.get() != null) mDrawBitmap.get().recycle();
mDrawBitmap.clear();
mDrawBitmap = null;
}
}
@Override protected void createBitmapCache(Canvas canvas) {
int width = getContentRect().width() + getContentRect().left;
int height = getContentRect().height();
if (mDrawBitmap == null
|| (mDrawBitmap.get().getWidth() != width)
|| (mDrawBitmap.get().getHeight() != height)) {
if (width > 0 && height > 0) {
mDrawBitmap = new WeakReference<>(Bitmap.createBitmap(width, height, mBitmapConfig));
mBitmapCanvas = new Canvas(mDrawBitmap.get());
} else
return;
}
mDrawBitmap.get().eraseColor(Color.TRANSPARENT);
}
@Override protected Bitmap getDrawBitmap() {
return mDrawBitmap.get();
}
@Override protected Paint getRenderPaint() {
return mRenderer.getRenderPaint();
}
@Override
public Canvas getBitmapCanvas() {
return mBitmapCanvas;
}
@Override
protected void drawAxis(Canvas canvas) {
for (AxisRenderer axisRenderer : mAxisRenderers) {
axisRenderer.renderer(canvas);
}
}
@Override protected void drawGridLine(Canvas canvas) {
for (AxisRenderer axisRenderer : mAxisRenderers) {
axisRenderer.drawGridLines(canvas);
}
}
@Override
protected void drawLabels(Canvas canvas) {
for (AxisRenderer axisRenderer : mAxisRenderers) {
axisRenderer.drawLabels(canvas);
}
}
@Override protected void onTouchPoint(MotionEvent e) {
if (e.getPointerCount() == 1) {
for (OnTouchPointChangeListener touchPointChangeListener : mTouchPointChangeListeners) {
touchPointChangeListener.touch(e.getX(), e.getY());
}
}
}
@Override
public void highlightValue(Highlight highlight) {
if (highlight == null) return;
final Highlight[] highlights = new Highlight[] { highlight };
if (mHighlightStatusChangeListener != null) {
mHighlightStatusChangeListener.onHighlightShow(highlights);
}
if (mHighlightListener != null) {
mHighlightListener.highlight(highlights);
}
mHighlights = highlights;
invalidate();
}
@Override
public void cleanHighlight() {
mHighlights = null;
if (mHighlightStatusChangeListener != null)
mHighlightStatusChangeListener.onHighlightHide();
invalidate();
}
public void setRenderer(AbstractDataRenderer renderer) {
this.mRenderer = renderer;
}
@Override
protected final void render(final Canvas canvas) {
if (mRenderer != null) {
mRenderer.renderer(canvas);
}
drawEdgeEffectsUnclipped(canvas);
renderHighlighted(canvas);
}
public void renderHighlighted(Canvas canvas) {
if (mRenderer != null && getHighlights() != null) {
mRenderer.renderHighlighted(canvas, getHighlights());
}
}
public void setHighlightColor(int color) {
mRenderer.setHighlightColor(color);
}
public int getHighlightColor() {
return mRenderer.getHighlightColor();
}
public Highlight[] getHighlights() {
return mHighlights;
}
public void setHighlights(Highlight[] highlights) {
this.mHighlights = highlights;
}
public void setOnHighlightStatusChangeListener(HighlightStatusChangeListener mHighlightStatusChangeListener) {
this.mHighlightStatusChangeListener = mHighlightStatusChangeListener;
}
public HighlightStatusChangeListener getOnHighlightStatusChangeListener() {
return mHighlightStatusChangeListener;
}
public void setOnHighlightListener(OnHighlightListener highlightListener) {
this.mHighlightListener = highlightListener;
}
public void enableHighlightDashPathEffect(float intervals[], float phase) {
this.mRenderer.enableHighlightDashPathEffect(intervals, phase);
}
public void setMinVisibleEntryCount(int minVisibleEntryCount) {
mRenderer.setMinVisibleEntryCount(minVisibleEntryCount);
}
public void setMaxVisibleEntryCount(int maxVisibleEntryCount) {
mRenderer.setMaxVisibleEntryCount(maxVisibleEntryCount);
}
public void setDefaultVisibleEntryCount(int defaultVisibleEntryCount) {
mRenderer.setDefaultVisibleEntryCount(defaultVisibleEntryCount);
}
@Override protected int getEntryIndexByCoordinate(float x, float y) {
return mRenderer.getEntryIndexByCoordinate(x, y);
}
public void setTypeface(Typeface tf) {
for (AxisRenderer mAxisRenderer : mAxisRenderers) {
mAxisRenderer.setTypeface(tf);
}
postInvalidate();
}
public void releaseBitmap() {
if (mBitmapCanvas != null) {
mBitmapCanvas.setBitmap(null);
mBitmapCanvas = null;
}
if (mDrawBitmap != null) {
if (mDrawBitmap.get() != null) mDrawBitmap.get().recycle();
mDrawBitmap.clear();
mDrawBitmap = null;
}
}
public ChartAnimator getChartAnimator() {
return mChartAnimator;
}
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX,
EasingFunction easingY) {
mChartAnimator.animateXY(durationMillisX, durationMillisY, easingX, easingY);
}
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) {
mChartAnimator.animateXY(durationMillisX, durationMillisY, easing);
}
public void animateX(int durationMillis, EasingFunction easing) {
mChartAnimator.animateX(durationMillis, easing);
}
public void animateY(int durationMillis, EasingFunction easing) {
mChartAnimator.animateY(durationMillis, easing);
}
public void animateX(int durationMillis) {
mChartAnimator.animateX(durationMillis);
}
public void animateY(int durationMillis) {
mChartAnimator.animateY(durationMillis);
}
public void animateXY(int durationMillisX, int durationMillisY) {
mChartAnimator.animateXY(durationMillisX, durationMillisY);
}
}
@@ -0,0 +1,157 @@
package cn.jingzhuan.lib.chart.base;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import java.lang.ref.WeakReference;
import cn.jingzhuan.lib.chart.Viewport;
public abstract class BitmapCachedChart extends View {
protected WeakReference<Bitmap> mDrawBitmap;
protected Canvas mBitmapCanvas;
protected Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888;
/**
* The current viewport. This rectangle represents the currently visible lib domain
* and range. The currently visible lib X values are from this rectangle's left to its right.
* The currently visible lib Y values are from this rectangle's top to its bottom.
* <p>
* Note that this rectangle's top is actually the smaller Y value, and its bottom is the larger
* Y value. Since the lib is drawn onscreen in such a way that lib Y values increase
* towards the top of the screen (decreasing pixel Y positions), this rectangle's "top" is drawn
* above this rectangle's "bottom" value.
*
* @see #mContentRect
*/
protected Viewport mCurrentViewport = new Viewport();
/**
* The current destination rectangle (in pixel coordinates) into which the lib data should
* be drawn. Chart labels are drawn outside this area.
*
* @see #mCurrentViewport
*/
protected Rect mContentRect = new Rect();
public BitmapCachedChart(Context context) {
super(context);
}
public BitmapCachedChart(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BitmapCachedChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BitmapCachedChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public Rect getContentRect() {
return mContentRect;
}
public Viewport getCurrentViewport() {
return mCurrentViewport;
}
public Canvas getBitmapCanvas() {
return mBitmapCanvas;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawAxis(canvas); // 坐标轴刻度在最底层
// Clips the next few drawing operations to the content area
int clipRestoreCount = canvas.save();
canvas.clipRect(mContentRect);
createBitmapCache(canvas);
if (getBitmapCanvas() != null) {
drawGridLine(getBitmapCanvas());
render(getBitmapCanvas());
canvas.drawBitmap(getDrawBitmap(), 0, 0, getRenderPaint());
}
// Removes clipping rectangle
canvas.restoreToCount(clipRestoreCount);
drawLabels(canvas); // 坐标轴刻度在最上层
}
protected void createBitmapCache(Canvas canvas) {
int width = getContentRect().width() + getContentRect().left;
int height = getContentRect().height();
if (mDrawBitmap == null
|| (mDrawBitmap.get() == null)
|| (mDrawBitmap.get().getWidth() != width)
|| (mDrawBitmap.get().getHeight() != height)) {
if (width > 0 && height > 0) {
mDrawBitmap = new WeakReference<>(Bitmap.createBitmap(width, height, mBitmapConfig));
mBitmapCanvas = new Canvas(mDrawBitmap.get());
} else
return;
}
mDrawBitmap.get().eraseColor(Color.TRANSPARENT);
}
protected Bitmap getDrawBitmap() {
return mDrawBitmap.get();
}
@Override protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mBitmapCanvas != null) {
mBitmapCanvas.setBitmap(null);
mBitmapCanvas = null;
}
if (mDrawBitmap != null) {
if (mDrawBitmap.get() != null) mDrawBitmap.get().recycle();
mDrawBitmap.clear();
mDrawBitmap = null;
}
}
public void releaseBitmap() {
if (mBitmapCanvas != null) {
mBitmapCanvas.setBitmap(null);
mBitmapCanvas = null;
}
if (mDrawBitmap != null) {
if (mDrawBitmap.get() != null) mDrawBitmap.get().recycle();
mDrawBitmap.clear();
mDrawBitmap = null;
}
}
protected abstract void drawAxis(Canvas canvas);
protected abstract void drawGridLine(Canvas canvas);
protected abstract void render(Canvas canvas);
protected abstract Paint getRenderPaint();
protected abstract void drawLabels(Canvas canvas);
}
@@ -0,0 +1,822 @@
package cn.jingzhuan.lib.chart.base;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.EdgeEffect;
import android.widget.OverScroller;
import cn.jingzhuan.lib.chart.R;
import cn.jingzhuan.lib.chart.Viewport;
import cn.jingzhuan.lib.chart.Zoomer;
import cn.jingzhuan.lib.chart.event.OnEntryClickListener;
import cn.jingzhuan.lib.chart.utils.ForceAlign;
import cn.jingzhuan.lib.chart.utils.ForceAlign.XForce;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import cn.jingzhuan.lib.chart.component.Axis;
import cn.jingzhuan.lib.chart.component.AxisX;
import cn.jingzhuan.lib.chart.component.AxisY;
import cn.jingzhuan.lib.chart.component.Highlight;
import cn.jingzhuan.lib.chart.event.OnViewportChangeListener;
/**
* Created by Donglua on 17/7/17.
*/
public abstract class Chart extends BitmapCachedChart {
protected AxisY mAxisLeft = new AxisY(AxisY.LEFT_INSIDE);
protected AxisY mAxisRight = new AxisY(AxisY.RIGHT_INSIDE);
protected AxisX mAxisTop = new AxisX(AxisX.TOP);
protected AxisX mAxisBottom = new AxisX(AxisX.BOTTOM);
// State objects and values related to gesture tracking.
private ScaleGestureDetector mScaleGestureDetector;
private GestureDetector mGestureDetector;
private OverScroller mScroller;
private Zoomer mZoomer;
private PointF mZoomFocalPoint = new PointF();
private RectF mScrollerStartViewport = new RectF(); // Used only for zooms and flings.
private boolean mScaleXEnable = true;
private boolean mDraggingToMoveEnable = true;
private boolean mDoubleTapToZoom = false;
private boolean mScaleGestureEnable = true;
private boolean mHighlightDisable = false;
protected List<OnTouchPointChangeListener> mTouchPointChangeListeners;
private List<OnViewportChangeListener> mOnViewportChangeListeners;
protected OnViewportChangeListener mInternalViewportChangeListener;
protected OnEntryClickListener onEntryClickListener;
/**
* The scaling factor for a single zoom 'step'.
*
* @see #zoomIn()
* @see #zoomOut()
*/
private static final float ZOOM_AMOUNT = 0.2f;
private final Point mSurfaceSizeBuffer = new Point();
// Edge effect / overscroll tracking objects.
private EdgeEffect mEdgeEffectLeft;
private EdgeEffect mEdgeEffectRight;
private boolean mEdgeEffectLeftActive;
private boolean mEdgeEffectRightActive;
private boolean isTouching = false;
public Chart(Context context) {
this(context, null, 0);
}
public Chart(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public Chart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public Chart(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.Chart, defStyleAttr, defStyleAttr);
mTouchPointChangeListeners = Collections.synchronizedList(new ArrayList<OnTouchPointChangeListener>());
mOnViewportChangeListeners = Collections.synchronizedList(new ArrayList<OnViewportChangeListener>());
mAxisTop.setGridLineEnable(false);
mAxisTop.setLabelEnable(false);
try {
List<Axis> axisList = new ArrayList<>(4);
axisList.add(mAxisLeft);
axisList.add(mAxisRight);
axisList.add(mAxisTop);
axisList.add(mAxisBottom);
float labelTextSize = a.getDimension(R.styleable.Chart_labelTextSize, 28);
float labelSeparation = a.getDimensionPixelSize(R.styleable.Chart_labelSeparation, 10);
float gridThickness = a.getDimension(R.styleable.Chart_gridThickness, 2);
float axisThickness = a.getDimension(R.styleable.Chart_axisThickness, 2);
int gridColor = a.getColor(R.styleable.Chart_gridColor, Color.GRAY);
int axisColor = a.getColor(R.styleable.Chart_axisColor, Color.GRAY);
int labelTextColor = a.getColor(R.styleable.Chart_labelTextColor, Color.GRAY);
for (Axis axis : axisList) {
axis.setLabelTextSize(labelTextSize);
axis.setLabelTextColor(labelTextColor);
axis.setLabelSeparation(labelSeparation);
axis.setGridColor(gridColor);
axis.setGridThickness(gridThickness);
axis.setAxisColor(axisColor);
axis.setAxisThickness(axisThickness);
}
} finally {
a.recycle();
}
initChart();
setupInteractions(context);
setupEdgeEffect(context);
}
public abstract void initChart();
public abstract void highlightValue(Highlight highlight);
public abstract void cleanHighlight();
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
getContentRect().set(
getPaddingLeft() + (mAxisLeft.isInside() ? 0 : mAxisLeft.getLabelWidth()),
getPaddingTop(),
getWidth() - getPaddingRight() - (mAxisRight.isInside() ? 0 : mAxisRight.getLabelWidth()),
getHeight() - getPaddingBottom() - mAxisBottom.getLabelHeight()
);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minChartSize = getResources().getDimensionPixelSize(R.dimen.jz_chart_min_size);
setMeasuredDimension(
Math.max(getSuggestedMinimumWidth(),
resolveSize(minChartSize + getPaddingLeft()
+ (mAxisLeft.isInside() ? 0 : mAxisLeft.getLabelWidth())
+ getPaddingRight(),
widthMeasureSpec)),
Math.max(getSuggestedMinimumHeight(),
resolveSize(minChartSize + getPaddingTop()
+ (mAxisBottom.isInside() ? 0 : mAxisBottom.getLabelHeight())
+ getPaddingBottom(),
heightMeasureSpec)));
}
protected abstract int getEntryIndexByCoordinate(float x, float y);
// -------- -------- --------
private void setupInteractions(Context context) {
mScaleGestureDetector = new ScaleGestureDetector(context, mScaleGestureListener);
mGestureDetector = new GestureDetector(context, mGestureListener);
mScroller = new OverScroller(context);
mZoomer = new Zoomer(context);
}
/**
* Finds the lib point (i.e. within the lib's domain and range) represented by the
* given pixel coordinates, if that pixel is within the lib region described by
* {@link #mContentRect}. If the point is found, the "dest" argument is set to the point and
* this function returns true. Otherwise, this function returns false and "dest" is unchanged.
*/
private boolean hitTest(float x, float y, PointF dest) {
if (!mContentRect.contains((int) x, (int) y)) {
return false;
}
dest.set(mCurrentViewport.left
+ mCurrentViewport.width()
* (x - mContentRect.left) / mContentRect.width(),
mCurrentViewport.top
+ mCurrentViewport.height()
* (y - mContentRect.bottom) / -mContentRect.height());
return true;
}
/**
* The scale listener, used for handling multi-finger scale gestures.
*/
private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener
= new ScaleGestureDetector.SimpleOnScaleGestureListener() {
/**
* This is the active focal point in terms of the viewport. Could be a local
* variable but kept here to minimize per-frame allocations.
*/
private PointF viewportFocus = new PointF();
private float lastSpanX;
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
if (!mScaleGestureEnable) return super.onScaleBegin(scaleGestureDetector);
lastSpanX = scaleGestureDetector.getCurrentSpanX();
return true;
}
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
if (!mScaleXEnable) return false;
if (!mScaleGestureEnable) return super.onScale(scaleGestureDetector);
float spanX = scaleGestureDetector.getCurrentSpanX();
float newWidth = lastSpanX / spanX * mCurrentViewport.width();
if (newWidth < mCurrentViewport.width() && mCurrentViewport.width() < 0.001) {
return true;
}
float focusX = scaleGestureDetector.getFocusX();
float focusY = scaleGestureDetector.getFocusY();
hitTest(focusX, focusY, viewportFocus);
mCurrentViewport.left = viewportFocus.x
- newWidth * (focusX - mContentRect.left)
/ mContentRect.width();
mCurrentViewport.right = mCurrentViewport.left + newWidth;
mCurrentViewport.constrainViewport();
triggerViewportChange();
lastSpanX = spanX;
return true;
}
};
protected abstract void onTouchPoint(MotionEvent e);
/**
* The gesture listener, used for handling simple gestures such as double touches, scrolls,
* and flings.
*/
private final GestureDetector.SimpleOnGestureListener mGestureListener
= new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
releaseEdgeEffects();
mScrollerStartViewport.set(mCurrentViewport);
mScroller.forceFinished(true);
postInvalidateOnAnimation();
return true;
}
@Override public void onLongPress(MotionEvent e) {
mGestureDetector.onTouchEvent(e);
mGestureDetector.setIsLongpressEnabled(false);
}
@Override
public void onShowPress(MotionEvent e) {
super.onShowPress(e);
onTouchPoint(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (onEntryClickListener != null) {
int index = getEntryIndexByCoordinate(e.getX(), e.getY());
if (index >= 0) {
onEntryClickListener.onEntryClick(Chart.this, index);
if (isHighlightDisable()) {
cleanHighlight();
} else {
onTouchPoint(e);
}
} else {
cleanHighlight();
performClick();
}
} else {
cleanHighlight();
performClick();
}
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
if (mDoubleTapToZoom) {
mZoomer.forceFinished(true);
if (hitTest(e.getX(), e.getY(), mZoomFocalPoint)) {
mZoomer.startZoom(ZOOM_AMOUNT);
}
postInvalidateOnAnimation();
}
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!isDraggingToMoveEnable()) {
onTouchPoint(e2);
return super.onScroll(e1, e2, distanceX, distanceY);
}
// Scrolling uses math based on the viewport (as opposed to math using pixels).
/**
* Pixel offset is the offset in screen pixels, while viewport offset is the
* offset within the current viewport. For additional information on surface sizes
* and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For
* additional information about the viewport, see the comments for
* {@link mCurrentViewport}.
*/
float viewportOffsetX = distanceX * mCurrentViewport.width() / mContentRect.width();
// float viewportOffsetY = -distanceY * mCurrentViewport.height() / mContentRect.height();
computeScrollSurfaceSize(mSurfaceSizeBuffer);
int scrolledX = (int) (mSurfaceSizeBuffer.x
* (mCurrentViewport.left + viewportOffsetX - Viewport.AXIS_X_MIN)
/ (Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN));
boolean canScrollX = mCurrentViewport.left > Viewport.AXIS_X_MIN
|| mCurrentViewport.right < Viewport.AXIS_X_MAX;
setViewportBottomLeft(
mCurrentViewport.left + viewportOffsetX
);
if (canScrollX && scrolledX < 0) {
mEdgeEffectLeft.onPull(scrolledX / (float) mContentRect.width());
mEdgeEffectLeftActive = true;
}
if (canScrollX && scrolledX > mSurfaceSizeBuffer.x - mContentRect.width()) {
mEdgeEffectRight.onPull((scrolledX - mSurfaceSizeBuffer.x + mContentRect.width())
/ (float) mContentRect.width());
mEdgeEffectRightActive = true;
}
onTouchPoint(e2);
//triggerViewportChange();
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (!isDraggingToMoveEnable()) return super.onFling(e1, e2, velocityX, velocityY);
fling((int) -velocityX);
onTouchPoint(e2);
return true;
}
};
protected void triggerViewportChange() {
postInvalidateOnAnimation();
if (mInternalViewportChangeListener != null) {
mInternalViewportChangeListener.onViewportChange(mCurrentViewport);
}
if (mOnViewportChangeListeners != null && !mOnViewportChangeListeners.isEmpty()) {
synchronized (this) {
for (OnViewportChangeListener mOnViewportChangeListener : mOnViewportChangeListeners) {
mOnViewportChangeListener.onViewportChange(mCurrentViewport);
}
}
}
}
private void fling(int velocityX) {
releaseEdgeEffects();
// Flings use math in pixels (as opposed to math based on the viewport).
computeScrollSurfaceSize(mSurfaceSizeBuffer);
mScrollerStartViewport.set(mCurrentViewport);
int startX = (int) (mSurfaceSizeBuffer.x * (mScrollerStartViewport.left - Viewport.AXIS_X_MIN) / (
Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN));
mScroller.forceFinished(true);
mScroller.fling(
startX,
0,
velocityX,
0,
0, mSurfaceSizeBuffer.x - mContentRect.width(),
0, mSurfaceSizeBuffer.y - mContentRect.height(),
mContentRect.width() / 2,
0);
postInvalidateOnAnimation();
}
/**
* Computes the current scrollable surface size, in pixels. For example, if the entire lib
* area is visible, this is simply the current size of {@link #mContentRect}. If the lib
* is zoomed in 200% in both directions, the returned size will be twice as large horizontally
* and vertically.
*/
private void computeScrollSurfaceSize(Point out) {
out.set((int) (mContentRect.width() * (Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN)
/ mCurrentViewport.width()),
(int) (mContentRect.height() * (Viewport.AXIS_Y_MAX - Viewport.AXIS_Y_MIN)
/ mCurrentViewport.height()));
}
/**
* Smoothly zooms the lib in one step.
*/
public void zoomIn() {
zoom(ZOOM_AMOUNT);
}
/**
* Smoothly zooms the lib out one step.
*/
public void zoomOut() {
zoom(-ZOOM_AMOUNT);
}
public void zoom(float scalingFactor) {
mScrollerStartViewport.set(mCurrentViewport);
mZoomer.forceFinished(true);
mZoomer.startZoom(scalingFactor);
mZoomFocalPoint.set(
(mCurrentViewport.right + mCurrentViewport.left) / 2,
(mCurrentViewport.bottom + mCurrentViewport.top) / 2);
triggerViewportChange();
}
public void zoomOut(@XForce int forceAlignX) {
mScrollerStartViewport.set(mCurrentViewport);
mZoomer.forceFinished(true);
mZoomer.startZoom(-ZOOM_AMOUNT);
float forceX;
switch (forceAlignX) {
case ForceAlign.LEFT:
forceX = mCurrentViewport.left;
break;
case ForceAlign.RIGHT:
forceX = mCurrentViewport.right;
break;
case ForceAlign.CENTER:
forceX = (mCurrentViewport.right + mCurrentViewport.left) / 2;
break;
default:
forceX = (mCurrentViewport.right + mCurrentViewport.left) / 2;
break;
}
mZoomFocalPoint.set(forceX,
(mCurrentViewport.bottom + mCurrentViewport.top) / 2);
postInvalidateOnAnimation();
}
/**
* Smoothly zooms the lib in one step.
*/
public void zoomIn(@XForce int forceAlignX) {
mScrollerStartViewport.set(mCurrentViewport);
mZoomer.forceFinished(true);
mZoomer.startZoom(ZOOM_AMOUNT);
float forceX;
switch (forceAlignX) {
case ForceAlign.LEFT:
forceX = mCurrentViewport.left;
break;
case ForceAlign.RIGHT:
forceX = mCurrentViewport.right;
break;
case ForceAlign.CENTER:
forceX = (mCurrentViewport.right + mCurrentViewport.left) / 2;
break;
default:
forceX = (mCurrentViewport.right + mCurrentViewport.left) / 2;
break;
}
mZoomFocalPoint.set(forceX,
(mCurrentViewport.bottom + mCurrentViewport.top) / 2);
triggerViewportChange();
}
@Override
public void computeScroll() {
super.computeScroll();
boolean needsInvalidate = false;
if (mScroller.computeScrollOffset()) {
// The scroller isn't finished, meaning a fling or programmatic pan operation is
// currently active.
computeScrollSurfaceSize(mSurfaceSizeBuffer);
int currX = mScroller.getCurrX();
boolean canScrollX = (mCurrentViewport.left > Viewport.AXIS_X_MIN
|| mCurrentViewport.right < Viewport.AXIS_X_MAX);
if (canScrollX
&& currX < 0
&& mEdgeEffectLeft.isFinished()
&& !mEdgeEffectLeftActive) {
mEdgeEffectLeft.onAbsorb((int) mScroller.getCurrVelocity());
mEdgeEffectLeftActive = true;
needsInvalidate = true;
} else if (canScrollX
&& currX > (mSurfaceSizeBuffer.x - mContentRect.width())
&& mEdgeEffectRight.isFinished()
&& !mEdgeEffectRightActive) {
mEdgeEffectRight.onAbsorb((int) mScroller.getCurrVelocity());
mEdgeEffectRightActive = true;
needsInvalidate = true;
}
float currXRange = Viewport.AXIS_X_MIN + (Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN)
* currX / mSurfaceSizeBuffer.x;
setViewportBottomLeft(currXRange);
}
if (mZoomer.computeZoom()) {
// Performs the zoom since a zoom is in progress (either programmatically or via
// double-touch).
float newWidth = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.width();
float newHeight = (1f - mZoomer.getCurrZoom()) * mScrollerStartViewport.height();
float pointWithinViewportX = (mZoomFocalPoint.x - mScrollerStartViewport.left)
/ mScrollerStartViewport.width();
float pointWithinViewportY = (mZoomFocalPoint.y - mScrollerStartViewport.top)
/ mScrollerStartViewport.height();
mCurrentViewport.set(
mZoomFocalPoint.x - newWidth * pointWithinViewportX,
mZoomFocalPoint.y - newHeight * pointWithinViewportY,
mZoomFocalPoint.x + newWidth * (1 - pointWithinViewportX),
mZoomFocalPoint.y + newHeight * (1 - pointWithinViewportY));
mCurrentViewport.constrainViewport();
needsInvalidate = true;
}
if (needsInvalidate) {
triggerViewportChange();
}
}
/**
* Sets the current viewport (defined by {@link #mCurrentViewport}) to the given
* X and Y positions. Note that the Y value represents the topmost pixel position, and thus
* the bottom of the {@link #mCurrentViewport} rectangle. For more details on why top and
* bottom are flipped, see {@link #mCurrentViewport}.
*/
private void setViewportBottomLeft(float x) {
/**
* Constrains within the scroll range. The scroll range is simply the viewport extremes
* (AXIS_X_MAX, etc.) minus the viewport size. For example, if the extrema were 0 and 10,
* and the viewport size was 2, the scroll range would be 0 to 8.
*/
float curWidth = mCurrentViewport.width();
x = Math.max(Viewport.AXIS_X_MIN, Math.min(x, Viewport.AXIS_X_MAX - curWidth));
mCurrentViewport.left = x;
mCurrentViewport.right = x + curWidth;
mCurrentViewport.constrainViewport();
triggerViewportChange();
}
/**
* Sets the lib's current viewport.
*
* @see #getCurrentViewport()
*/
public void setCurrentViewport(RectF viewport) {
mCurrentViewport.set(viewport.left, viewport.top, viewport.right, viewport.bottom);
mCurrentViewport.constrainViewport();
triggerViewportChange();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
isTouching = true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
isTouching = false;
}
boolean retVal = event.getPointerCount() > 1 && mScaleGestureDetector.onTouchEvent(event);
retVal = mGestureDetector.onTouchEvent(event) || retVal;
return retVal || super.onTouchEvent(event);
}
protected void setupEdgeEffect(Context context) {
// Sets up edge effects
mEdgeEffectLeft = new EdgeEffect(context);
mEdgeEffectRight = new EdgeEffect(context);
}
/**
* Draws the overscroll "glow" at the four edges of the lib region, if necessary. The edges
* of the lib region are stored in {@link #mContentRect}.
*
* @see EdgeEffect
*/
protected void drawEdgeEffectsUnclipped(Canvas canvas) {
// The methods below rotate and translate the canvas as needed before drawing the glow,
// since EdgeEffectCompat always draws a top-glow at 0,0.
boolean needsInvalidate = false;
if (!mEdgeEffectLeft.isFinished()) {
final int restoreCount = canvas.save();
canvas.translate(mContentRect.left, mContentRect.bottom);
canvas.rotate(-90, 0, 0);
mEdgeEffectLeft.setSize(mContentRect.height(), mContentRect.width());
if (mEdgeEffectLeft.draw(canvas)) {
needsInvalidate = true;
}
canvas.restoreToCount(restoreCount);
}
if (!mEdgeEffectRight.isFinished()) {
final int restoreCount = canvas.save();
canvas.translate(mContentRect.right, mContentRect.top);
canvas.rotate(90, 0, 0);
mEdgeEffectRight.setSize(mContentRect.height(), mContentRect.width());
if (mEdgeEffectRight.draw(canvas)) {
needsInvalidate = true;
}
canvas.restoreToCount(restoreCount);
}
if (needsInvalidate) {
triggerViewportChange();
}
}
private void releaseEdgeEffects() {
mEdgeEffectLeftActive
= mEdgeEffectRightActive
= false;
mEdgeEffectLeft.onRelease();
mEdgeEffectRight.onRelease();
}
public AxisY getAxisLeft() {
return mAxisLeft;
}
public AxisY getAxisRight() {
return mAxisRight;
}
public AxisX getAxisTop() {
return mAxisTop;
}
public AxisX getAxisBottom() {
return mAxisBottom;
}
public void addOnViewportChangeListener(OnViewportChangeListener onViewportChangeListener) {
synchronized (this) {
this.mOnViewportChangeListeners.add(onViewportChangeListener);
}
}
public boolean isScaleXEnable() {
return mScaleXEnable;
}
public void setScaleXEnable(boolean scaleXEnable) {
this.mScaleXEnable = scaleXEnable;
}
public void setDoubleTapToZoom(boolean doubleTapToZoom) {
this.mDoubleTapToZoom = doubleTapToZoom;
}
public interface OnTouchPointChangeListener {
void touch(float x, float y);
}
public void addOnTouchPointChangeListener(OnTouchPointChangeListener touchPointChangeListener) {
synchronized (this) {
this.mTouchPointChangeListeners.add(touchPointChangeListener);
}
}
public void removeOnTouchPointChangeListener(OnTouchPointChangeListener touchPointChangeListener) {
synchronized (this) {
this.mTouchPointChangeListeners.remove(touchPointChangeListener);
}
}
public void setDraggingToMoveEnable(boolean draggingToMoveEnable) {
this.mDraggingToMoveEnable = draggingToMoveEnable;
}
public boolean isDraggingToMoveEnable() {
return mDraggingToMoveEnable;
}
public void moveLeft() {
moveLeft(0.2f);
}
public void moveRight() {
moveRight(0.2f);
}
public void moveLeft(@FloatRange(from = 0f, to = 1.0f) float percent) {
releaseEdgeEffects();
computeScrollSurfaceSize(mSurfaceSizeBuffer);
mScrollerStartViewport.set(mCurrentViewport);
float moveDistance = mContentRect.width() * percent;
int startX = (int) (mSurfaceSizeBuffer.x * (mScrollerStartViewport.left - Viewport.AXIS_X_MIN)
/ (Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN));
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
mScroller.startScroll(startX, 0, (int) -moveDistance, 0, 300);
postInvalidateOnAnimation();
}
public void moveRight(@FloatRange(from = 0f, to = 1.0f) float percent) {
// releaseEdgeEffects();
computeScrollSurfaceSize(mSurfaceSizeBuffer);
mScrollerStartViewport.set(mCurrentViewport);
float moveDistance = mContentRect.width() * percent;
int startX = (int) (mSurfaceSizeBuffer.x * (mScrollerStartViewport.left - Viewport.AXIS_X_MIN)
/ (Viewport.AXIS_X_MAX - Viewport.AXIS_X_MIN));
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
mScroller.startScroll(startX, 0, (int) moveDistance, 0, 300);
postInvalidateOnAnimation();
}
public void setInternalViewportChangeListener(
OnViewportChangeListener mInternalViewportChangeListener) {
this.mInternalViewportChangeListener = mInternalViewportChangeListener;
}
public void setScaleGestureEnable(boolean mScaleGestureEnable) {
this.mScaleGestureEnable = mScaleGestureEnable;
}
public boolean isScaleGestureEnable() {
return mScaleGestureEnable;
}
public boolean isHighlightDisable() {
return mHighlightDisable;
}
public void setHighlightDisable(boolean highlightDisable) {
this.mHighlightDisable = highlightDisable;
}
public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
this.onEntryClickListener = onEntryClickListener;
}
public OnEntryClickListener getOnEntryClickListener() {
return onEntryClickListener;
}
public Zoomer getZoomer() {
return mZoomer;
}
public boolean isTouching() {
return isTouching;
}
}
@@ -0,0 +1,16 @@
package cn.jingzhuan.lib.chart.component;
public abstract class AbstractComponent implements Component {
private boolean isEnable = true;
@Override
public void setEnable(boolean enable) {
isEnable = enable;
}
@Override
public boolean isEnable() {
return isEnable;
}
}
@@ -0,0 +1,253 @@
package cn.jingzhuan.lib.chart.component;
import android.graphics.Color;
import android.graphics.Paint;
import cn.jingzhuan.lib.chart.AxisAutoValues;
import cn.jingzhuan.lib.chart.data.GirdLineColorSetter;
import cn.jingzhuan.lib.chart.data.LabelColorSetter;
import cn.jingzhuan.lib.chart.data.ValueFormatter;
import cn.jingzhuan.lib.chart.data.ValueIndexFormatter;
import java.util.List;
/**
* Created by Donglua on 17/7/17.
*/
public class Axis extends AbstractComponent {
private int mAxisPosition;
private int mGridColor = Color.GRAY;
private GirdLineColorSetter mGirdLineColorSetter = null;
private float mGridThickness = 1;
private int mGridCount = 3;
private float mLabelTextSize;
private float mLabelSeparation = 0;
private int mLabelTextColor = Color.GREEN;
private Paint mLabelTextPaint;
private int mLabelWidth = 100;
private int mLabelHeight = 0;
private int mAxisColor = Color.GRAY;
private float mAxisThickness = 2;
private List<String> mLabels;
private AxisAutoValues axisAutoValues = new AxisAutoValues();
private ValueFormatter mLabelValueFormatter;
private ValueIndexFormatter mValueIndexFormatter;
public float[] mLabelEntries = new float[]{};
private boolean gridLineEnable = true;
private boolean labelEnable = true;
private float mDashedGridIntervals[] = null;
private float mDashedGridPhase = -1;
private LabelColorSetter mLabelColorSetter;
private int isGridSlidIndex = -1;
Axis(int axisPosition) {
this.mAxisPosition = axisPosition;
}
public void setLabelTextSize(float mLabelTextSize) {
this.mLabelTextSize = mLabelTextSize;
}
public void setLabelSeparation(float mLabelSeparation) {
this.mLabelSeparation = mLabelSeparation;
}
public void setLabelTextColor(int mLabelTextColor) {
this.mLabelTextColor = mLabelTextColor;
}
public void setLabelTextPaint(Paint mLabelTextPaint) {
this.mLabelTextPaint = mLabelTextPaint;
}
public void setLabelWidth(int mLabelWidth) {
this.mLabelWidth = mLabelWidth;
}
public void setLabelHeight(int mLabelHeight) {
this.mLabelHeight = mLabelHeight;
}
public float getLabelTextSize() {
return mLabelTextSize;
}
public int getLabelSeparation() {
return Math.round(mLabelSeparation);
}
public int getLabelTextColor() {
return mLabelTextColor;
}
public Paint getLabelTextPaint() {
return mLabelTextPaint;
}
public int getLabelWidth() {
if (isInside()) {
return 0;
}
return mLabelWidth;
}
public boolean isInside() {
switch (getAxisPosition()) {
case AxisY.LEFT_INSIDE:
case AxisY.RIGHT_INSIDE:
case AxisX.BOTTOM_INSIDE:
case AxisX.TOP_INSIDE:
return true;
default:
return false;
}
}
public int getLabelHeight() {
return mLabelHeight;
}
public AxisAutoValues getAxisAutoValues() {
return axisAutoValues;
}
public int getAxisPosition() {
return mAxisPosition;
}
public void setAxisPosition(int mAxisPosition) {
this.mAxisPosition = mAxisPosition;
}
public int getGridColor() {
return mGridColor;
}
public void setGridColor(int mGridColor) {
this.mGridColor = mGridColor;
}
public float getGridThickness() {
return mGridThickness;
}
public void setGridThickness(float mGridThickness) {
this.mGridThickness = mGridThickness;
}
public int getAxisColor() {
return mAxisColor;
}
public void setAxisColor(int mAxisColor) {
this.mAxisColor = mAxisColor;
}
public float getAxisThickness() {
return mAxisThickness;
}
public void setAxisThickness(float mAxisThickness) {
this.mAxisThickness = mAxisThickness;
}
public int getGridCount() {
return mGridCount;
}
public void setGridCount(int mGridCount) {
this.mGridCount = mGridCount;
}
public void setGridLineEnable(boolean gridLineEnable) {
this.gridLineEnable = gridLineEnable;
}
public boolean isGridLineEnable() {
return gridLineEnable;
}
public boolean isLabelEnable() {
return labelEnable;
}
public void setLabelEnable(boolean labelEnable) {
this.labelEnable = labelEnable;
}
public ValueFormatter getLabelValueFormatter() {
return mLabelValueFormatter;
}
public void setLabelValueFormatter(ValueFormatter mValueFormatter) {
this.mLabelValueFormatter = mValueFormatter;
}
public ValueIndexFormatter getValueIndexFormatter() {
return mValueIndexFormatter;
}
public void setValueIndexFormatter(ValueIndexFormatter mValueIndexFormatter) {
this.mValueIndexFormatter = mValueIndexFormatter;
}
public float getDashedGridPhase() {
return mDashedGridPhase;
}
public float[] getDashedGridIntervals() {
return mDashedGridIntervals;
}
public void enableGridDashPathEffect(float intervals[], float phase) {
this.mDashedGridIntervals = intervals;
this.mDashedGridPhase = phase;
}
public void setGirdLineColorSetter(GirdLineColorSetter mGirdLineColorSetter) {
this.mGirdLineColorSetter = mGirdLineColorSetter;
}
public GirdLineColorSetter getGirdLineColorSetter() {
return mGirdLineColorSetter;
}
public List<String> getLabels() {
return mLabels;
}
public void setLabels(List<String> mLabels) {
this.mLabels = mLabels;
}
public LabelColorSetter getLabelColorSetter() {
return mLabelColorSetter;
}
public void setLabelColorSetter(LabelColorSetter mLabelColorSetter) {
this.mLabelColorSetter = mLabelColorSetter;
}
public int getIsGridSlidIndex() {
return isGridSlidIndex;
}
public void setIsGridSlidIndex(int isGridSlidIndex) {
this.isGridSlidIndex = isGridSlidIndex;
}
}
@@ -0,0 +1,30 @@
package cn.jingzhuan.lib.chart.component;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by Donglua on 17/7/17.
*/
public class AxisX extends Axis {
public final static int TOP = 101;
public final static int BOTTOM = 102;
public final static int TOP_INSIDE = 103;
public final static int BOTTOM_INSIDE = 104;
@Retention(RetentionPolicy.SOURCE)
@IntDef({TOP, BOTTOM, TOP_INSIDE, BOTTOM_INSIDE})
@interface AxisXPosition {}
public AxisX(@AxisXPosition int axisPosition) {
super(axisPosition);
}
}
@@ -0,0 +1,52 @@
package cn.jingzhuan.lib.chart.component;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Created by Donglua on 17/7/17.
*/
public class AxisY extends Axis {
public final static int LEFT_OUTSIDE = 111;
public final static int LEFT_INSIDE = 112;
public final static int RIGHT_OUTSIDE = 113;
public final static int RIGHT_INSIDE = 114;
public final static int DEPENDENCY_LEFT = 23;
public final static int DEPENDENCY_RIGHT = 24;
public final static int DEPENDENCY_BOTH = 25;
private float mYMin = Float.MAX_VALUE;
private float mYMax = -Float.MAX_VALUE;
@Retention(RetentionPolicy.SOURCE)
@IntDef({DEPENDENCY_LEFT, DEPENDENCY_RIGHT, DEPENDENCY_BOTH})
public @interface AxisDependency{}
@Retention(RetentionPolicy.SOURCE)
@IntDef({LEFT_OUTSIDE, LEFT_INSIDE, RIGHT_OUTSIDE, RIGHT_INSIDE})
public @interface AxisYPosition {}
public AxisY(int axisPosition) {
super(axisPosition);
}
public void setYMin(float value) {
this.mYMin = value;
}
public void setYMax(float value) {
this.mYMax = value;
}
public float getYMin() {
return mYMin;
}
public float getYMax() {
return mYMax;
}
}
@@ -0,0 +1,12 @@
package cn.jingzhuan.lib.chart.component;
/**
* Created by Donglua on 17/8/2.
*/
public interface Component {
void setEnable(boolean enable);
boolean isEnable();
}
@@ -0,0 +1,15 @@
package cn.jingzhuan.lib.chart.component;
/**
* Created by donglua on 11/22/17.
*/
public interface HasValueXOffset {
float getStartXOffset();
float getEndXOffset();
void setStartXOffset(float startXOffset);
void setEndXOffset(float endXOffset);
}
@@ -0,0 +1,21 @@
package cn.jingzhuan.lib.chart.component;
/**
* Created by donglua on 11/22/17.
*/
public interface HasValueYOffset {
float getMaxValueOffsetPercent();
float getMinValueOffsetPercent();
float getOffsetPercent();
void setMinValueOffsetPercent(float minValueOffsetPercent);
void setMaxValueOffsetPercent(float maxValueOffsetPercent);
void setOffsetPercent(float offsetPercent);
}
@@ -0,0 +1,65 @@
package cn.jingzhuan.lib.chart.component;
/**
* Created by donglua on 7/31/17.
*/
public class Highlight {
private float x = Float.NaN;
private float y = Float.NaN;
private int dataIndex = 0;
private float touchX = Float.NaN;
private float touchY = Float.NaN;
public Highlight() {
}
public Highlight(float x, float y, int dataIndex) {
this.x = x;
this.y = y;
this.dataIndex = dataIndex;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public void setDataIndex(int dataIndex) {
this.dataIndex = dataIndex;
}
public int getDataIndex() {
return dataIndex;
}
public float getTouchX() {
return touchX;
}
public void setTouchX(float touchX) {
this.touchX = touchX;
}
public float getTouchY() {
return touchY;
}
public void setTouchY(float touchY) {
this.touchY = touchY;
}
}
@@ -0,0 +1,260 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Color;
import android.graphics.Rect;
import cn.jingzhuan.lib.chart.Viewport;
import cn.jingzhuan.lib.chart.component.AxisY;
import cn.jingzhuan.lib.chart.component.AxisY.AxisDependency;
import cn.jingzhuan.lib.chart.component.HasValueYOffset;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract DataSet
* <p>
* Created by Donglua on 17/7/20.
*/
public abstract class AbstractDataSet<T extends Value> extends AbstractVisible implements IDataSet, HasValueYOffset {
protected float mViewportYMin = Float.MAX_VALUE;
protected float mViewportYMax = -Float.MAX_VALUE;
protected float minValueOffsetPercent = 0F;
protected float maxValueOffsetPercent = 0F;
protected float offsetPercent = 0F;
protected float startXOffset = 0f;
protected float endXOffset = 0f;
private int mAxisDependency = AxisY.DEPENDENCY_LEFT;
private int mColor = Color.GRAY;
private boolean mColorDynamic = false;
private int maxVisibleEntryCount = 500;
private int minVisibleEntryCount = 15;
private int defaultVisibleEntryCount = -1;
private boolean isHighlightedVerticalEnable = false;
private boolean isHighlightedHorizontalEnable = false;
private boolean enable = true;
private int minValueCount = -1;
private int drawIndex = -1;
private String tag;
private DataFormatter formatter; // 数据格式化器
public AbstractDataSet() {
}
public AbstractDataSet(String tag) {
this.tag = tag;
}
@Override
public void calcMinMax(Viewport viewport) {}
@Override
public void calcMinMax(Viewport viewport, Rect content, float max, float mix) {
calcMinMax(viewport);
}
public abstract List<T> getValues();
public abstract void setValues(List<T> values);
public abstract boolean addEntry(T e);
public abstract boolean removeEntry(T e);
public abstract int getEntryIndex(T e);
public abstract T getEntryForIndex(int index);
@AxisDependency
public int getAxisDependency() {
return mAxisDependency;
}
public void setAxisDependency(@AxisDependency int mAxisDependency) {
this.mAxisDependency = mAxisDependency;
}
public float getViewportYMin() {
return mViewportYMin;
}
public float getViewportYMax() {
return mViewportYMax;
}
public int getColor() {
return mColor;
}
public void setColor(int barColor) {
this.mColor = barColor;
}
public boolean getColorDynamic() {
return mColorDynamic;
}
public void setColorDynamic(boolean colorDynamic) {
this.mColorDynamic = colorDynamic;
}
public boolean isHighlightedVerticalEnable() {
return isHighlightedVerticalEnable;
}
public void setHighlightedVerticalEnable(boolean highlightedVerticalEnable) {
isHighlightedVerticalEnable = highlightedVerticalEnable;
}
public boolean isHighlightedHorizontalEnable() {
return isHighlightedHorizontalEnable;
}
public void setHighlightedHorizontalEnable(boolean highlightedHorizontalEnable) {
isHighlightedHorizontalEnable = highlightedHorizontalEnable;
}
public List<T> getVisiblePoints(Viewport viewport) {
ArrayList<T> allValue = new ArrayList<>(getValues());
int listSize = allValue.size();
int from = Math.round(viewport.left * listSize);
int to = Math.round(viewport.right * listSize);
if (listSize <= minVisibleEntryCount) {
return allValue;
}
return safeSubList(allValue, from, to);
}
private List<T> safeSubList(List<T> list, int from, int to) {
if (list == null || list.isEmpty()) return list;
int size = list.size();
int safeFrom = (from >= size) ? 0 : from;
int safeTo = Math.min(to, size);
return list.subList(safeFrom, safeTo);
}
public float getVisibleRange(Viewport viewport) {
return (viewport.right - viewport.left) * getEntryCount();
}
@Override
public void setMaxVisibleEntryCount(int maxVisibleEntryCount) {
this.maxVisibleEntryCount = maxVisibleEntryCount;
}
public int getMaxVisibleEntryCount() {
return maxVisibleEntryCount;
}
@Override
public void setMinVisibleEntryCount(int minVisibleEntryCount) {
this.minVisibleEntryCount = minVisibleEntryCount;
}
public int getMinVisibleEntryCount() {
return minVisibleEntryCount;
}
@Override
public void setDefaultVisibleEntryCount(int defaultVisibleEntryCount) {
this.defaultVisibleEntryCount = defaultVisibleEntryCount;
}
public int getDefaultVisibleEntryCount() {
return defaultVisibleEntryCount;
}
@Override
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
this.setVisible(enable);
}
@Override
public int getMinValueCount() {
return minValueCount;
}
@Override
public void setMinValueCount(int minValueCount) {
this.minValueCount = minValueCount;
}
public int getStartIndexOffset() {
int startIndex = 0;
// if (minValueCount > 0 && getValues() != null && getValues().size() > 0) {
// startIndex = getEntryCount() - getValues().size();
// }
return startIndex;
}
public int getDrawIndex() {
return drawIndex;
}
public void setDrawIndex(int drawIndex) {
this.drawIndex = drawIndex;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public DataFormatter getFormatter() {
return formatter;
}
public void setFormatter(DataFormatter formatter) {
this.formatter = formatter;
}
@Override
public float getMaxValueOffsetPercent() {
return maxValueOffsetPercent;
}
@Override
public float getMinValueOffsetPercent() {
return minValueOffsetPercent;
}
@Override
public void setMinValueOffsetPercent(float minValueOffsetPercent) {
this.minValueOffsetPercent = minValueOffsetPercent;
}
@Override
public void setMaxValueOffsetPercent(float maxValueOffsetPercent) {
this.maxValueOffsetPercent = maxValueOffsetPercent;
}
@Override
public float getOffsetPercent() {
return this.offsetPercent;
}
@Override
public void setOffsetPercent(float offsetPercent) {
this.offsetPercent = offsetPercent;
}
}
@@ -0,0 +1,19 @@
package cn.jingzhuan.lib.chart.data;
/**
* Created by Donglua on 17/8/2.
*/
abstract class AbstractVisible {
private boolean isVisible = true;
public boolean isVisible() {
return isVisible;
}
public void setVisible(boolean visible) {
this.isVisible = visible;
}
}
@@ -0,0 +1,9 @@
package cn.jingzhuan.lib.chart.data;
/**
* Created by Donglua on 17/8/2.
*/
public class BarData extends ChartData<BarDataSet> {
}
@@ -0,0 +1,208 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Color;
import cn.jingzhuan.lib.chart.Viewport;
import java.util.ArrayList;
import java.util.List;
import cn.jingzhuan.lib.chart.component.AxisY;
import cn.jingzhuan.lib.chart.component.AxisY.AxisDependency;
/**
* Bar Data Set
* Created by Donglua on 17/8/1.
*/
public class BarDataSet extends AbstractDataSet<BarValue> {
private List<BarValue> mBarValues;
private float mBarWidth = 20;
private float mMinBarWidth = 0.0f;
private boolean mAutoBarWidth = false;
private int mForceValueCount = -1;
private float strokeThickness = 2;
private float mBarWidthPercent = 0.8f;
private boolean drawValueEnable = false;
private int valueColor = Color.BLACK;
private float valueTextSize = 24F;
private ValueFormatter valueFormatter;
public BarDataSet(List<BarValue> barValues) {
this(barValues, AxisY.DEPENDENCY_BOTH);
}
public BarDataSet(List<BarValue> mBarValues, @AxisDependency int axisDependency) {
this.mBarValues = mBarValues;
setAxisDependency(axisDependency);
}
@Override
public int getEntryCount() {
if (mForceValueCount > 0) return mForceValueCount;
if (mBarValues != null) {
return Math.max(getMinValueCount(), mBarValues.size());
}
return 0;
}
@Override
public void calcMinMax(Viewport viewport) {
if (mBarValues == null || mBarValues.isEmpty())
return;
mViewportYMax = -Float.MAX_VALUE;
mViewportYMin = Float.MAX_VALUE;
for (BarValue e : getVisiblePoints(viewport)) {
calcMinMaxY(e);
}
float range = mViewportYMax - mViewportYMin;
if (Float.compare(getMinValueOffsetPercent(), 0f) > 0f) {
mViewportYMin = mViewportYMin - range * getMinValueOffsetPercent();
}
if (Float.compare(getMaxValueOffsetPercent(), 0f) > 0f) {
mViewportYMax = mViewportYMax + range * getMaxValueOffsetPercent();
}
}
protected void calcMinMaxY(BarValue e) {
if (e == null || !e.isEnable()) return;
for (float v : e.getValues()) {
if (!Float.isNaN(v) && !Float.isInfinite(v)) {
mViewportYMin = Math.min(mViewportYMin, v);
mViewportYMax = Math.max(mViewportYMax, v);
}
}
}
@Override
public void setValues(List<BarValue> values) {
this.mBarValues = values;
}
@Override
public List<BarValue> getValues() {
return mBarValues;
}
@Override
public boolean addEntry(BarValue e) {
if (e == null)
return false;
if (mBarValues == null) {
mBarValues = new ArrayList<>();
}
calcMinMaxY(e);
return mBarValues.add(e);
}
@Override
public boolean removeEntry(BarValue e) {
if (e == null) return false;
calcMinMaxY(e);
return mBarValues.remove(e);
}
@Override
public int getEntryIndex(BarValue e) {
return mBarValues.indexOf(e);
}
@Override
public BarValue getEntryForIndex(int index) {
return mBarValues.get(index);
}
public float getBarWidth() {
return mBarWidth;
}
public void setBarWidth(float mBarWidth) {
this.mBarWidth = mBarWidth;
}
public float getMinBarWidth() {
return mMinBarWidth;
}
public void setMinBarWidth(float minBarWidth) {
this.mMinBarWidth = minBarWidth;
}
public void setAutoBarWidth(boolean mAutoBarWidth) {
this.mAutoBarWidth = mAutoBarWidth;
}
public boolean isAutoBarWidth() {
return mAutoBarWidth;
}
public void setForceValueCount(int forceValueCount) {
this.mForceValueCount = forceValueCount;
}
public int getForceValueCount() {
return mForceValueCount;
}
public float getStrokeThickness() {
return strokeThickness;
}
public void setStrokeThickness(float strokeThickness) {
this.strokeThickness = strokeThickness;
}
public void setDrawValueEnable(boolean drawValueEnable) {
this.drawValueEnable = drawValueEnable;
}
public boolean isDrawValueEnable() {
return drawValueEnable;
}
public int getValueColor() {
return valueColor;
}
public void setValueColor(int valueColor) {
this.valueColor = valueColor;
}
public float getValueTextSize() {
return valueTextSize;
}
public void setValueTextSize(float valueTextSize) {
this.valueTextSize = valueTextSize;
}
public ValueFormatter getValueFormatter() {
return valueFormatter;
}
public void setValueFormatter(ValueFormatter valueFormatter) {
this.valueFormatter = valueFormatter;
}
public void setBarWidthPercent(float mBarWidthPercent) {
this.mBarWidthPercent = mBarWidthPercent;
}
public float getBarWidthPercent() {
return mBarWidthPercent;
}
}
@@ -0,0 +1,123 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Paint;
/**
* Created by Donglua on 17/8/1.
*/
public class BarValue extends Value {
private float[] yValues;
private int mColor = -2;
private Paint.Style mPaintStyle = Paint.Style.FILL;
private boolean isEnable = true;
private int[] gradientColors = null;
/**
* 这里tag比dataSet的优先级高
*/
private String tag = null;
public BarValue(float[] yValues) {
this.yValues = yValues;
}
public BarValue(float[] yValues, int color) {
this.yValues = yValues;
this.mColor = color;
}
public BarValue(float value1, float value2, int color) {
this.yValues = new float[] {value1, value2};
this.mColor = color;
}
public BarValue(float value1, float value2, int color, Paint.Style paintStyle) {
this.yValues = new float[] {value1, value2};
this.mColor = color;
this.mPaintStyle = paintStyle;
}
public BarValue(float value, int color, Paint.Style paintStyle) {
this.yValues = new float[] {value, 0f};
this.mColor = color;
this.mPaintStyle = paintStyle;
}
public BarValue(float[] values, int color, Paint.Style paintStyle) {
this.yValues = values;
this.mColor = color;
this.mPaintStyle = paintStyle;
}
public BarValue(float yValue) {
this.yValues = new float[] { yValue, 0f };
}
public BarValue(float yValue, int color) {
this.yValues = new float[] { yValue, 0f };
this.mColor = color;
}
public float[] getValues() {
return yValues;
}
public void setValues(float[] yValues) {
this.yValues = yValues;
}
public void setValues(float yValue) {
this.yValues = new float[] { yValue, 0f };
}
public int getValueCount() {
if (yValues == null) return 0;
return yValues.length;
}
public int getColor() {
return mColor;
}
public void setColor(int color) {
mColor = color;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getTag() {
return this.tag;
}
public void setPaintStyle(Paint.Style mPaintStyle) {
this.mPaintStyle = mPaintStyle;
}
public Paint.Style getPaintStyle() {
return mPaintStyle;
}
public void setEnable(boolean enable) {
isEnable = enable;
}
public boolean isEnable() {
return isEnable;
}
public void setGradientColors(int colorTop, int colorBottom) {
this.gradientColors = new int[] {colorTop, colorBottom};
}
public int[] getGradientColors() {
return gradientColors;
}
}
@@ -0,0 +1,9 @@
package cn.jingzhuan.lib.chart.data;
/**
* Created by donglua on 8/29/17.
*/
public class CandlestickData extends ChartData<CandlestickDataSet> {
}
@@ -0,0 +1,326 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Pair;
import android.util.SparseArray;
import cn.jingzhuan.lib.chart.Viewport;
import cn.jingzhuan.lib.chart.component.AxisY;
import java.util.ArrayList;
import java.util.List;
/**
* Created by donglua on 8/29/17.
*/
public class CandlestickDataSet extends AbstractDataSet<CandlestickValue> {
private List<CandlestickValue> candlestickValues;
private boolean mAutoWidth = true;
private float mCandleWidth = -1;
/**
* 阳线 颜色
*/
private int mIncreasingColor = 0xFFf84b4b;
/**
* 阴线 颜色
*/
private int mDecreasingColor = 0xFF1deb5b;
/**
* 十字光标线 颜色
*/
private int mNeutralColor = Color.WHITE;
private int mLimitUpColor = Color.TRANSPARENT;
private int mLimitUpColor20 = Color.TRANSPARENT;
/**
* 缺口 颜色
*/
private int mGapColor = Color.LTGRAY;
private Paint.Style mLimitUpPaintStyle = null;
private float strokeThickness = 4;
/**
* 阳线、阴线样式-> 实心或空心
*/
private Paint.Style mIncreasingPaintStyle = Paint.Style.FILL;
private Paint.Style mDecreasingPaintStyle = Paint.Style.FILL;
/**
* 是否显示缺口
*/
private boolean mEnableGap = false;
private SparseArray<Pair<Float, Float>> mLowGaps;
private SparseArray<Pair<Float, Float>> mHighGaps;
private float candleWidthPercent = 0.8f;
public CandlestickDataSet(List<CandlestickValue> candlestickValues) {
this(candlestickValues, AxisY.DEPENDENCY_BOTH);
}
public CandlestickDataSet(List<CandlestickValue> candlestickValues, @AxisY.AxisDependency int axisDependency) {
this.candlestickValues = candlestickValues;
setAxisDependency(axisDependency);
}
@Override
public void calcMinMax(Viewport viewport) {
if (candlestickValues == null || candlestickValues.isEmpty()) return;
mViewportYMax = -Float.MAX_VALUE;
mViewportYMin = Float.MAX_VALUE;
List<CandlestickValue> visiblePoints = getVisiblePoints(viewport);
for (int i = 0; i < visiblePoints.size(); i++) {
CandlestickValue e = visiblePoints.get(i);
calcViewportMinMax(e);
}
if (mEnableGap) {
float max = -Float.MAX_VALUE;
float min = Float.MAX_VALUE;
for (int i = candlestickValues.size() - 1; i >= 0; i--) {
CandlestickValue e = candlestickValues.get(i);
if (Float.isNaN(e.getLow())) continue;
if (Float.isNaN(e.getHigh())) continue;
if (Float.isInfinite(e.getLow())) continue;
if (Float.isInfinite(e.getHigh())) continue;
if (min != Float.MAX_VALUE && max != -Float.MAX_VALUE) {
if (min - e.getHigh() > 0.01f) { // 上涨缺口
mHighGaps.put(i, new Pair<>(e.getHigh(), min));
}
if (e.getLow() - max > 0.01f) { // 下跌缺口
mLowGaps.put(i, new Pair<>(e.getLow(), max));
}
}
if (e.getLow() < min) min = e.getLow();
if (e.getHigh() > max) max = e.getHigh();
}
}
float range = mViewportYMax - mViewportYMin;
if (Float.compare(getOffsetPercent(), 0f) > 0f) {
mViewportYMin = mViewportYMin - range * getOffsetPercent();
}
if (Float.compare(getOffsetPercent(), 0f) > 0f) {
mViewportYMax = mViewportYMax + range * getOffsetPercent();
}
}
public void setEnableGap(boolean enableGap) {
this.mEnableGap = enableGap;
if (mLowGaps == null) {
mLowGaps = new SparseArray<>();
}
if (mHighGaps == null) {
mHighGaps = new SparseArray<>();
}
}
private void calcViewportMinMax(CandlestickValue e) {
if (e == null || !e.isVisible()) return;
if (Float.isNaN(e.getLow())) return;
if (Float.isNaN(e.getHigh())) return;
if (Float.isInfinite(e.getLow())) return;
if (Float.isInfinite(e.getHigh())) return;
if (e.getLow() < mViewportYMin) {
mViewportYMin = e.getLow();
}
if (e.getHigh() > mViewportYMax) {
mViewportYMax = e.getHigh();
}
}
@Override
public int getEntryCount() {
if (candlestickValues == null) return 0;
return Math.max(getMinValueCount(), candlestickValues.size());
}
@Override
public void setValues(List<CandlestickValue> values) {
this.candlestickValues = values;
}
@Override
public List<CandlestickValue> getValues() {
return candlestickValues;
}
@Override
public boolean addEntry(CandlestickValue value) {
if (value == null)
return false;
if (candlestickValues == null) {
candlestickValues = new ArrayList<>();
}
calcViewportMinMax(value);
// add the entry
return candlestickValues.add(value);
}
@Override
public boolean removeEntry(CandlestickValue value) {
if (value == null) return false;
calcViewportMinMax(value);
return candlestickValues.remove(value);
}
@Override
public int getEntryIndex(CandlestickValue e) {
return candlestickValues.indexOf(e);
}
@Override
public CandlestickValue getEntryForIndex(int index) {
return candlestickValues.get(index);
}
public void setAutoWidth(boolean mAutoWidth) {
this.mAutoWidth = mAutoWidth;
}
public boolean isAutoWidth() {
return mAutoWidth;
}
public int getDecreasingColor() {
return mDecreasingColor;
}
public void setDecreasingColor(int decreasingColor) {
this.mDecreasingColor = decreasingColor;
}
public int getIncreasingColor() {
return mIncreasingColor;
}
public void setIncreasingColor(int increasingColor) {
this.mIncreasingColor = increasingColor;
}
public float getCandleWidth() {
return mCandleWidth;
}
public void setCandleWidth(float mCandleWidth) {
this.mCandleWidth = mCandleWidth;
}
public int getNeutralColor() {
return mNeutralColor;
}
public void setNeutralColor(int mNeutralColor) {
this.mNeutralColor = mNeutralColor;
}
public void setLimitUpColor(int mLimitUpColor) {
this.mLimitUpColor = mLimitUpColor;
}
public int getLimitUpColor() {
return mLimitUpColor;
}
public void setLimitUpColor20(int mLimitUpColor) {
this.mLimitUpColor20 = mLimitUpColor;
}
public int getLimitUpColor20() {
return mLimitUpColor20;
}
public float getStrokeThickness() {
return strokeThickness;
}
public void setStrokeThickness(float strokeThickness) {
this.strokeThickness = strokeThickness;
}
public Paint.Style getIncreasingPaintStyle() {
return mIncreasingPaintStyle;
}
public void setIncreasingPaintStyle(Paint.Style increasingPaintStyle) {
this.mIncreasingPaintStyle = increasingPaintStyle;
}
public Paint.Style getDecreasingPaintStyle() {
return mDecreasingPaintStyle;
}
public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) {
this.mDecreasingPaintStyle = decreasingPaintStyle;
}
public Paint.Style getLimitUpPaintStyle() {
return mLimitUpPaintStyle;
}
public void setLimitUpPaintStyle(Paint.Style limitUpPaintStyle) {
this.mLimitUpPaintStyle = limitUpPaintStyle;
}
public SparseArray<Pair<Float, Float>> getLowGaps() {
return mLowGaps;
}
public SparseArray<Pair<Float, Float>> getHighGaps() {
return mHighGaps;
}
public boolean isEnableGap() {
return mEnableGap;
}
public int getGapColor() {
return mGapColor;
}
public void setGapColor(int mGapColor) {
this.mGapColor = mGapColor;
}
public float getCandleWidthPercent() {
return candleWidthPercent;
}
public void setCandleWidthPercent(float candleWidthPercent) {
this.candleWidthPercent = candleWidthPercent;
}
}
@@ -0,0 +1,169 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import java.util.Arrays;
import java.util.Objects;
/**
* Created by donglua on 8/29/17.
*/
public class CandlestickValue extends Value {
public final static int COLOR_NONE = Color.TRANSPARENT;
private float high = 0f;
private float low = 0f;
private float open = 0f;
private float close = 0f;
private long time = -1;
private boolean visible = true;
private Paint.Style mPaintStyle = null;
private int color = COLOR_NONE;
private int fillBackgroundColor = COLOR_NONE;
public CandlestickValue(float high, float low, float open, float close) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
}
public CandlestickValue(float high, float low, float open, float close, long time) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
this.time = time;
}
public CandlestickValue(float high, float low, float open, float close, int color) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
this.color = color;
}
public CandlestickValue(float high, float low, float open, float close, long time, int color) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
this.color = color;
this.time = time;
}
public CandlestickValue(float high, float low, float open, float close, Paint.Style mPaintStyle,
int color) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
this.mPaintStyle = mPaintStyle;
this.color = color;
}
public CandlestickValue(float high, float low, float open, float close, Paint.Style mPaintStyle) {
this.high = high;
this.low = low;
this.open = open;
this.close = close;
this.mPaintStyle = mPaintStyle;
}
public float getHigh() {
return high;
}
public void setHigh(float high) {
this.high = high;
}
public float getLow() {
return low;
}
public void setLow(float low) {
this.low = low;
}
public float getOpen() {
return open;
}
public void setOpen(float open) {
this.open = open;
}
public float getClose() {
return close;
}
public void setClose(float close) {
this.close = close;
}
public void setPaintStyle(Paint.Style mPaintStyle) {
this.mPaintStyle = mPaintStyle;
}
public Paint.Style getPaintStyle() {
return mPaintStyle;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public void setTime(long time) {
this.time = time;
}
public long getTime() {
return time;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CandlestickValue that = (CandlestickValue) o;
return Float.compare(that.high, high) == 0 &&
Float.compare(that.low, low) == 0 &&
Float.compare(that.open, open) == 0 &&
Float.compare(that.close, close) == 0 &&
time == that.time &&
color == that.color &&
mPaintStyle == that.mPaintStyle;
}
@Override
public int hashCode() {
return Objects.hash(high, low, open, close, time, mPaintStyle, color, getX(), getY());
}
public void setFillBackgroundColor(int fillBackgroundColor) {
this.fillBackgroundColor = fillBackgroundColor;
}
public int getFillBackgroundColor() {
return fillBackgroundColor;
}
}
@@ -0,0 +1,235 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Rect;
import android.util.Log;
import cn.jingzhuan.lib.chart.base.Chart;
import cn.jingzhuan.lib.chart.Viewport;
import cn.jingzhuan.lib.chart.component.AxisY;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by Donglua on 17/8/2.
*/
public class ChartData<T extends IDataSet> {
private List<T> chartData;
protected float leftMin = Float.MAX_VALUE;
protected float leftMax = -Float.MAX_VALUE;
protected float rightMin = Float.MAX_VALUE;
protected float rightMax = -Float.MAX_VALUE;
private int entryCount = 0;
protected AxisY leftAxis;
protected AxisY rightAxis;
protected int maxVisibleEntryCount = 500;
protected int minVisibleEntryCount = 15;
protected int defaultVisibleEntryCount = -1;
private int minValueCount = -1;
public ChartData() {
this.chartData = Collections.synchronizedList(new ArrayList<T>());
}
public List<T> getDataSets() {
if (chartData == null) {
chartData = Collections.synchronizedList(new ArrayList<T>());
}
return chartData;
}
public boolean add(T e) {
if (e == null) return false;
e.setDefaultVisibleEntryCount(defaultVisibleEntryCount);
e.setMinVisibleEntryCount(minVisibleEntryCount);
e.setMaxVisibleEntryCount(maxVisibleEntryCount);
synchronized (this) {
return getDataSets().add(e);
}
}
public boolean remove(T e) {
synchronized (this) {
return e != null && getDataSets().remove(e);
}
}
public void clear() {
synchronized (this) {
getDataSets().clear();
}
leftMin = Float.MAX_VALUE;
leftMax = -Float.MAX_VALUE;
rightMin = Float.MAX_VALUE;
rightMax = -Float.MAX_VALUE;
}
public void setMinMax() {
if (leftAxis != null && leftMin != Float.MAX_VALUE) {
leftAxis.setYMin(leftMin);
leftAxis.setYMax(leftMax);
}
if (rightAxis != null && rightMin != Float.MAX_VALUE) {
rightAxis.setYMin(rightMin);
rightAxis.setYMax(rightMax);
}
}
public void calcMaxMin(Viewport viewport, Rect content) {
calcMaxMin(viewport, content, -Float.MAX_VALUE, Float.MAX_VALUE, -Float.MAX_VALUE, Float.MAX_VALUE);
}
public void calcMaxMin(Viewport viewport, Rect content, float lMax, float lMin, float rMax, float rMin) {
leftMin = lMin;
leftMax = lMax;
rightMin = rMin;
rightMax = rMax;
entryCount = 0;
if (!getDataSets().isEmpty()) {
synchronized (this) {
for (T t : getDataSets()) {
if (!t.isEnable() || t instanceof ScatterDataSet) continue;
t.calcMinMax(viewport, content, -Float.MAX_VALUE, Float.MAX_VALUE);
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_LEFT) {
leftMax = Math.max(leftMax, t.getViewportYMax());
leftMin = Math.min(leftMin, t.getViewportYMin());
}
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_RIGHT) {
rightMax = Math.max(rightMax, t.getViewportYMax());
rightMin = Math.min(rightMin, t.getViewportYMin());
}
if (t.getEntryCount() > entryCount) {
entryCount = t.getEntryCount();
}
}
// Log.d("ChartData", "calcMaxMin_0 leftMax:" + leftMax
// + ", leftMin:" + leftMin + ", rightMax:" + rightMax + ", rightMin:" + rightMin);
for (T t : getDataSets()) {
if (!t.isEnable() || !(t instanceof ScatterDataSet)) continue;
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_LEFT) {
t.calcMinMax(viewport, content, leftMax, leftMin);
}
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_RIGHT) {
t.calcMinMax(viewport, content, rightMax, rightMin);
}
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_LEFT) {
leftMax = Math.max(leftMax, t.getViewportYMax());
leftMin = Math.min(leftMin, t.getViewportYMin());
}
if (t.getAxisDependency() == AxisY.DEPENDENCY_BOTH || t.getAxisDependency() == AxisY.DEPENDENCY_RIGHT) {
rightMax = Math.max(rightMax, t.getViewportYMax());
rightMin = Math.min(rightMin, t.getViewportYMin());
}
if (t.getEntryCount() > entryCount) {
entryCount = t.getEntryCount();
}
}
}
// Log.d("ChartData", "calcMaxMin_1 leftMax:" + leftMax
// + ", leftMin:" + leftMin + ", rightMax:" + rightMax + ", rightMin:" + rightMin);
setMinMax();
}
}
public float getLeftMin() {
return leftMin;
}
public float getRightMax() {
return rightMax;
}
public float getRightMin() {
return rightMin;
}
public float getLeftMax() {
return leftMax;
}
public void setLeftMin(float leftMin) {
this.leftMin = leftMin;
}
public void setLeftMax(float leftMax) {
this.leftMax = leftMax;
}
public void setRightMin(float rightMin) {
this.rightMin = rightMin;
}
public void setRightMax(float rightMax) {
this.rightMax = rightMax;
}
public int getEntryCount() {
return entryCount;
}
public void setChart(Chart chart) {
this.leftAxis = chart.getAxisLeft();
this.rightAxis = chart.getAxisRight();
}
public void setChart(cn.jingzhuan.lib.chart2.base.Chart chart) {
this.leftAxis = chart.getAxisLeft();
this.rightAxis = chart.getAxisRight();
}
public void setMaxVisibleEntryCount(int maxVisibleEntryCount) {
this.maxVisibleEntryCount = maxVisibleEntryCount;
synchronized (getDataSets()) {
for (T t : getDataSets()) {
t.setMaxVisibleEntryCount(maxVisibleEntryCount);
}
}
}
public void setMinVisibleEntryCount(int minVisibleEntryCount) {
this.minVisibleEntryCount = minVisibleEntryCount;
synchronized (getDataSets()) {
for (T t : getDataSets()) {
t.setMinVisibleEntryCount(minVisibleEntryCount);
}
}
}
public void setDefaultVisibleEntryCount(int defaultVisibleEntryCount) {
this.defaultVisibleEntryCount = defaultVisibleEntryCount;
synchronized (getDataSets()) {
for (T t : getDataSets()) {
t.setDefaultVisibleEntryCount(defaultVisibleEntryCount);
}
}
}
public int getMinValueCount() {
return minValueCount;
}
public void setMinValueCount(int minValueCount) {
this.minValueCount = minValueCount;
synchronized (getDataSets()) {
for (T t : getDataSets()) {
t.setMinValueCount(minValueCount);
}
}
}
}
@@ -0,0 +1,334 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Rect;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import cn.jingzhuan.lib.chart.Viewport;
/**
* Created by Donglua on 17/8/2.
*/
public class CombineData extends ChartData<AbstractDataSet> {
private TreeData treeData;
private BarData barData;
private LineData lineData;
private CandlestickData candlestickData;
private ScatterData scatterData;
private PointLineData pointLineData;
private ScatterTextData scatterTextData;
public CombineData() {
treeData = new TreeData();
barData = new BarData();
lineData = new LineData();
candlestickData = new CandlestickData();
scatterData = new ScatterData();
pointLineData = new PointLineData();
scatterTextData = new ScatterTextData();
}
public List<BarDataSet> getBarData() {
return barData.getDataSets();
}
public List<LineDataSet> getLineData() {
return lineData.getDataSets();
}
public List<CandlestickDataSet> getCandlestickData() {
return candlestickData.getDataSets();
}
public List<ScatterDataSet> getScatterData() {
return scatterData.getDataSets();
}
public List<PointLineDataSet> getPointLineData() {
return pointLineData.getDataSets();
}
public List<ScatterTextDataSet> getScatterTextData() {
return scatterTextData.getDataSets();
}
public List<TreeDataSet> getTreeData() {
return treeData.getDataSets();
}
public BarData getBarChartData() {
return barData;
}
public LineData getLineChartData() {
return lineData;
}
public PointLineData getPointLineChartData() {
return pointLineData;
}
public CandlestickData getCandlestickChartData() {
return candlestickData;
}
public ScatterData getScatterChartData() {
return scatterData;
}
public ScatterTextData getScatterTextChartData() {
return scatterTextData;
}
public TreeData getTreeChartData() {
return treeData;
}
public boolean addDataSet(BarDataSet dataSet) {
return barData.add(dataSet);
}
public boolean addDataSet(LineDataSet dataSet) {
return lineData.add(dataSet);
}
public boolean addDataSet(CandlestickDataSet dataSet) {
return candlestickData.add(dataSet);
}
public boolean addDataSet(ScatterDataSet dataSet) {
return scatterData.add(dataSet);
}
public boolean addDataSet(PointLineDataSet dataSet) {
return pointLineData.add(dataSet);
}
public boolean addDataSet(ScatterTextDataSet dataSet) {
return scatterTextData.add(dataSet);
}
public boolean addDataSet(TreeDataSet dataSet) {
return treeData.add(dataSet);
}
public void setCombineData(CombineData combineData) {
this.leftMin = combineData.leftMin;
this.rightMin = combineData.rightMin;
this.leftMax = combineData.leftMax;
this.rightMax = combineData.rightMax;
treeData.setLeftMax(leftMax);
barData.setLeftMax(leftMax);
lineData.setLeftMax(leftMax);
candlestickData.setLeftMax(leftMax);
scatterData.setLeftMax(leftMax);
pointLineData.setLeftMax(leftMax);
scatterTextData.setLeftMax(leftMax);
treeData.setLeftMin(leftMin);
barData.setLeftMin(leftMin);
lineData.setLeftMin(leftMin);
candlestickData.setLeftMin(leftMin);
scatterData.setLeftMin(leftMin);
pointLineData.setLeftMin(leftMin);
scatterTextData.setLeftMin(leftMin);
treeData.setRightMax(rightMax);
barData.setRightMax(rightMax);
lineData.setRightMax(rightMax);
candlestickData.setRightMax(rightMax);
scatterData.setRightMax(rightMax);
pointLineData.setRightMax(rightMax);
scatterTextData.setRightMax(rightMax);
treeData.setRightMin(rightMin);
barData.setRightMin(rightMin);
lineData.setRightMin(rightMin);
candlestickData.setRightMin(rightMin);
scatterData.setRightMin(rightMin);
pointLineData.setRightMin(rightMin);
scatterTextData.setRightMin(rightMin);
treeData.getDataSets().addAll(combineData.getTreeData());
barData.getDataSets().addAll(combineData.getBarData());
lineData.getDataSets().addAll(combineData.getLineData());
candlestickData.getDataSets().addAll(combineData.getCandlestickData());
scatterData.getDataSets().addAll(combineData.getScatterData());
pointLineData.getDataSets().addAll(combineData.getPointLineData());
scatterTextData.getDataSets().addAll(combineData.getScatterTextData());
}
@Override
public void calcMaxMin(Viewport viewport, Rect content) {
leftMin = Float.MAX_VALUE;
leftMax = -Float.MAX_VALUE;
rightMin = Float.MAX_VALUE;
rightMax = -Float.MAX_VALUE;
if (!treeData.getDataSets().isEmpty()) {
treeData.calcMaxMin(viewport, content);
leftMin = Math.min(treeData.leftMin, leftMin);
leftMax = Math.max(treeData.leftMax, leftMax);
rightMin = Math.min(treeData.rightMin, rightMin);
rightMax = Math.max(treeData.rightMax, rightMax);
}
if (!candlestickData.getDataSets().isEmpty()) {
candlestickData.calcMaxMin(viewport, content);
leftMin = Math.min(candlestickData.leftMin, leftMin);
leftMax = Math.max(candlestickData.leftMax, leftMax);
rightMin = Math.min(candlestickData.rightMin, rightMin);
rightMax = Math.max(candlestickData.rightMax, rightMax);
}
if (!lineData.getDataSets().isEmpty()) {
lineData.calcMaxMin(viewport, content);
leftMin = Math.min(lineData.leftMin, leftMin);
leftMax = Math.max(lineData.leftMax, leftMax);
rightMin = Math.min(lineData.rightMin, rightMin);
rightMax = Math.max(lineData.rightMax, rightMax);
}
if (!barData.getDataSets().isEmpty()) {
barData.calcMaxMin(viewport, content);
leftMin = Math.min(barData.leftMin, leftMin);
leftMax = Math.max(barData.leftMax, leftMax);
rightMin = Math.min(barData.rightMin, rightMin);
rightMax = Math.max(barData.rightMax, rightMax);
}
if (!pointLineData.getDataSets().isEmpty()) {
pointLineData.calcMaxMin(viewport, content);
leftMin = Math.min(pointLineData.leftMin, leftMin);
leftMax = Math.max(pointLineData.leftMax, leftMax);
rightMin = Math.min(pointLineData.rightMin, rightMin);
rightMax = Math.max(pointLineData.rightMax, rightMax);
}
if (!scatterTextData.getDataSets().isEmpty()) {
scatterTextData.calcMaxMin(viewport, content);
leftMin = Math.min(scatterTextData.leftMin, leftMin);
leftMax = Math.max(scatterTextData.leftMax, leftMax);
rightMin = Math.min(scatterTextData.rightMin, rightMin);
rightMax = Math.max(scatterTextData.rightMax, rightMax);
}
// Log.d("CombineData", "calcMaxMin_0 leftMax:" + leftMax
// + ", leftMin:" + leftMin + ", rightMax:" + rightMax + ", rightMin:" + rightMin);
/** ScatterData的calcMaxMin放在其它Data后面调用,利用其它Data计算好的leftMax, leftMin, rightMax, rightMin,计算要扩展的数值。**/
if (!scatterData.getDataSets().isEmpty()) {
scatterData.calcMaxMin(viewport, content, leftMax, leftMin, rightMax, rightMin);
leftMin = Math.min(scatterData.leftMin, leftMin);
leftMax = Math.max(scatterData.leftMax, leftMax);
rightMin = Math.min(scatterData.rightMin, rightMin);
rightMax = Math.max(scatterData.rightMax, rightMax);
}
// Log.d("CombineData", "calcMaxMin_1 leftMax:" + leftMax
// + ", leftMin:" + leftMin + ", rightMax:" + rightMax + ", rightMin:" + rightMin);
treeData.setLeftMax(leftMax);
barData.setLeftMax(leftMax);
lineData.setLeftMax(leftMax);
candlestickData.setLeftMax(leftMax);
pointLineData.setLeftMax(leftMax);
scatterTextData.setLeftMax(leftMax);
scatterData.setLeftMax(leftMax);
treeData.setLeftMin(leftMin);
barData.setLeftMin(leftMin);
lineData.setLeftMin(leftMin);
candlestickData.setLeftMin(leftMin);
pointLineData.setLeftMin(leftMin);
scatterTextData.setLeftMin(leftMin);
scatterData.setLeftMin(leftMin);
treeData.setRightMax(rightMax);
barData.setRightMax(rightMax);
lineData.setRightMax(rightMax);
candlestickData.setRightMax(rightMax);
pointLineData.setRightMax(rightMax);
scatterTextData.setRightMax(rightMax);
scatterData.setRightMax(rightMax);
treeData.setRightMin(rightMin);
barData.setRightMin(rightMin);
lineData.setRightMin(rightMin);
candlestickData.setRightMin(rightMin);
pointLineData.setRightMin(rightMin);
scatterTextData.setRightMin(rightMin);
scatterData.setRightMin(rightMin);
setMinMax();
}
@Override
public boolean add(AbstractDataSet e) {
if (e instanceof TreeDataSet) {
return addDataSet((TreeDataSet) e);
}
if (e instanceof CandlestickDataSet) {
return addDataSet((CandlestickDataSet) e);
}
if (e instanceof LineDataSet) {
return addDataSet((LineDataSet) e);
}
if (e instanceof BarDataSet) {
return addDataSet((BarDataSet) e);
}
if (e instanceof ScatterDataSet) {
return addDataSet((ScatterDataSet) e);
}
if (e instanceof PointLineDataSet) {
return addDataSet((PointLineDataSet) e);
}
if (e instanceof ScatterTextDataSet) {
return addDataSet((ScatterTextDataSet) e);
}
return super.add(e);
}
@Override
public void setMinMax() {
if (leftAxis != null) {
leftAxis.setYMin(leftMin);
leftAxis.setYMax(leftMax);
}
if (rightAxis != null) {
rightAxis.setYMin(rightMin);
rightAxis.setYMax(rightMax);
}
}
public <T extends AbstractDataSet> void addAll(List<T> dataSets) {
for (AbstractDataSet dataSet : dataSets) {
add(dataSet);
}
}
public List<AbstractDataSet<?>> getAllDataSet() {
List allDataSet = Collections.synchronizedList(new ArrayList());
// 按分类顺序添加,当drawIndex是默认值-1时,按下列顺序绘制
allDataSet.addAll(treeData.getDataSets());
allDataSet.addAll(barData.getDataSets());
allDataSet.addAll(candlestickData.getDataSets());
allDataSet.addAll(lineData.getDataSets());
allDataSet.addAll(scatterData.getDataSets());
allDataSet.addAll(pointLineData.getDataSets());
allDataSet.addAll(scatterTextData.getDataSets());
Collections.sort(allDataSet, new Comparator<AbstractDataSet>() {
@Override
public int compare(AbstractDataSet dataSet1, AbstractDataSet dataSet2) {
return dataSet1.getDrawIndex() - dataSet2.getDrawIndex();
}
});
return allDataSet;
}
}
@@ -0,0 +1,186 @@
package cn.jingzhuan.lib.chart.data;
public class DataFormatter {
public static final String HIDE = "HIDE";
public static final String UNIT_TRILLION = "万亿";
public static final String UNIT_TEN_THOUSAND = "";
public static final String UNIT_BILLION = "亿";
public static final String UNIT_PERCENT = "%"; // 数值先*100,再添加%
public static final String UNIT_PERCENT_DIRECT = "+%"; // 数值不变,直接添加%
public static final String UNIT_CALC = "*"; // 不指定单位,由客户端根据数值范围决定基本单位。这时精度默认为2
public static final String UNIT_CALC_SUFFIX = "*+"; // 指定附加单位,由客户端根据数值范围决定基本单位。这时精度默认为2
public static final String UNIT_EMPTY = "";
private int precision = 2; // 数据精度
private String unit = ""; // 数据基本单位
private String unitSuffix = ""; // 数据附加单位
private boolean isHide = false;
public static DataFormatter TRILLION() {
return new DataFormatter(UNIT_TRILLION);
}
public static DataFormatter TEN_THOUSAND() {
return new DataFormatter(UNIT_TEN_THOUSAND);
}
public static DataFormatter BILLION() {
return new DataFormatter(UNIT_BILLION);
}
public static DataFormatter PERCENT() {
return new DataFormatter(UNIT_PERCENT);
}
public static DataFormatter PERCENT_DIRECT() {
return new DataFormatter(UNIT_PERCENT_DIRECT);
}
public static DataFormatter CALC() {
return new DataFormatter(UNIT_CALC);
}
public static DataFormatter CALC_SUFFIX(String unitSuffix) {
return new DataFormatter(UNIT_CALC_SUFFIX, unitSuffix);
}
public static DataFormatter EMPTY() {
return new DataFormatter(UNIT_EMPTY);
}
public static DataFormatter HIDE() {
return new DataFormatter(true);
}
public DataFormatter() {}
public DataFormatter(boolean isHide) {
this.isHide = isHide;
}
public DataFormatter(String unit) {
this(unit, "");
}
public DataFormatter(String unit, String unitSuffix) {
if (unit == null || (!unit.equals(UNIT_TRILLION)
&& !unit.equals(UNIT_TEN_THOUSAND) && !unit.equals(UNIT_BILLION)
&& !unit.equals(UNIT_PERCENT)&& !unit.equals(UNIT_PERCENT_DIRECT)
&& !unit.equals(UNIT_CALC) && !unit.equals(UNIT_CALC_SUFFIX)
&& !unit.equals(UNIT_EMPTY))) {
unit = "";
}
this.precision = 2;
this.unit = unit;
this.unitSuffix = unitSuffix;
}
public DataFormatter(int precision, String unit) {
if (unit == null || (!unit.equals(UNIT_TRILLION)
&& !unit.equals(UNIT_TEN_THOUSAND) && !unit.equals(UNIT_BILLION)
&& !unit.equals(UNIT_PERCENT)&& !unit.equals(UNIT_PERCENT_DIRECT)
&& !unit.equals(UNIT_CALC) && !unit.equals(UNIT_EMPTY))) {
unit = "";
}
if (precision < 0) {
precision = 2;
}
this.precision = precision;
this.unit = unit;
}
public DataFormatter(int unit) {
switch (unit) {
case 1:
this.unit = UNIT_TEN_THOUSAND;
break;
case 2:
this.unit = UNIT_BILLION;
break;
case 3:
this.unit = UNIT_PERCENT;
break;
case 4:
this.unit = UNIT_CALC; // 不指定单位,由客户端根据数值范围决定单位。这时精度默认为2
this.precision = 2;
break;
case 5:
this.unit = UNIT_PERCENT_DIRECT; // 数值不变,直接添加%
break;
default:
this.unit = UNIT_EMPTY;
this.precision = 2;
break;
}
}
public DataFormatter(int precision, int unit) {
this(unit);
if (precision < 0) {
precision = 2;
}
this.precision = precision;
}
// public DataFormatter(String formatterString) {
// if (formatterString == null || formatterString.length() == 0) {
// return;
// }
// if (formatterString.equals(HIDE)) {
// isHide = true;
// } else {
// String[] formatterList = formatterString.toUpperCase(Locale.ROOT).split("_");
// for (String formatter : formatterList) {
// if (formatter.contains("W")) {
// unit = UNIT_TEN_THOUSAND;
// String precisionString = formatter.replace("W", "");
// precision = parsePrecision(precisionString);
// } else if (formatter.contains("Y")) {
// unit = UNIT_BILLION;
// String precisionString = formatter.replace("Y", "");
// precision = parsePrecision(precisionString);
// } else if (formatter.contains("F")) {
//// unit = "%"; // 在 PERCENT 分支再决定
// String precisionString = formatter.replace("F", "");
// precision = parsePrecision(precisionString);
// } else if (formatter.contains("PERCENT")) {
// unit = UNIT_PERCENT;
// }
// }
//
// }
// }
//
// private int parsePrecision(String precisionString) {
// int result = 2;
// if (precisionString != null && precisionString.length() != 0) {
// try {
// result = Integer.parseInt(precisionString);
// } catch (NumberFormatException e) {
// }
// }
// return result;
// }
public int getPrecision() {
return precision;
}
public void setPrecision(int precision) {
this.precision = precision;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public String getUnitSuffix() {
return unitSuffix;
}
public void setUnitSuffix(String unitSuffix) {
this.unitSuffix = unitSuffix;
}
public boolean isHide() {
return isHide;
}
public void setHide(boolean hide) {
isHide = hide;
}
}
@@ -0,0 +1,12 @@
package cn.jingzhuan.lib.chart.data;
import androidx.annotation.ColorInt;
/**
* Created by donglua on 11/13/17.
*/
public interface GirdLineColorSetter {
@ColorInt int getColorByIndex(int color, int position);
}
@@ -0,0 +1,37 @@
package cn.jingzhuan.lib.chart.data;
import android.graphics.Rect;
import cn.jingzhuan.lib.chart.Viewport;
import cn.jingzhuan.lib.chart.component.AxisY.AxisDependency;
/**
* Created by Donglua on 17/7/19.
*/
public interface IDataSet {
void calcMinMax(Viewport viewport);
void calcMinMax(Viewport viewport, Rect content, float max, float mix);
int getEntryCount();
float getViewportYMin();
float getViewportYMax();
void setMaxVisibleEntryCount(int maxVisibleEntryCount);
void setMinVisibleEntryCount(int minVisibleEntryCount);
void setDefaultVisibleEntryCount(int defaultVisibleEntryCount);
@AxisDependency int getAxisDependency();
boolean isEnable();
int getMinValueCount();
void setMinValueCount(int minValueCount);
}
@@ -0,0 +1,12 @@
package cn.jingzhuan.lib.chart.data;
import androidx.annotation.ColorInt;
/**
* Created by Donglua on 17/7/30.
*/
public interface LabelColorSetter {
@ColorInt int getColorByIndex(int position);
}
@@ -0,0 +1,13 @@
package cn.jingzhuan.lib.chart.data
data class Leaf(
val high: Float,
val leftValue: Float,
val rightValue: Float,
val sumValue: Float
) {
override fun toString(): String {
return "${String.format("%.2f", leftValue/100f)}手(${String.format("%.2f", leftValue / sumValue * 100f)}%) <- $high -> ${String.format("%.2f", rightValue/100f)}手(${String.format("%.2f", rightValue / sumValue * 100f)}%)"
// return "${leftValue/100f}(${String.format("%.2f", leftValue / sumValue * 100f)}%) <- $high -> ${rightValue/100f}(${String.format("%.2f", rightValue / sumValue * 100f)}%)"
}
}
@@ -0,0 +1,9 @@
package cn.jingzhuan.lib.chart.data;
/**
* Created by Donglua on 17/8/2.
*/
public class LineData extends ChartData<LineDataSet> {
}

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