的
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -0,0 +1,35 @@
|
||||
# JingZhuan Android Chart
|
||||
[](https://travis-ci.org/donglua/JZAndroidChart)
|
||||
[  ](https://bintray.com/donglua/maven/chart2/_latestVersion)
|
||||
|
||||
## Usage
|
||||
|
||||
```gradle
|
||||
|
||||
implementation 'com.github.JingZhuanDuoYing:JZAndroidChart:1.5.10'
|
||||
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## 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.
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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')
|
||||
}
|
||||
@@ -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 {
|
||||
}
|
||||
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 495 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
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>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 "$@"
|
||||
@@ -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
|
||||
@@ -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> {
|
||||
|
||||
}
|
||||