├── .gitignore ├── .gradle ├── 4.1 │ ├── fileChanges │ │ └── last-build.bin │ └── fileContent │ │ └── fileContent.lock └── buildOutputCleanup │ └── cache.properties ├── KelloCharts.iml ├── LICENSE ├── PrivacyPolicy.html ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kellocharts-sample ├── .gitignore ├── build.gradle ├── kellocharts-library.iml ├── kellocharts-sample.iml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── co │ │ └── csadev │ │ └── kellocharts │ │ └── sample │ │ ├── AboutActivity.kt │ │ ├── BubbleChartActivity.kt │ │ ├── ColumnChartActivity.kt │ │ ├── ComboLineColumnChartActivity.kt │ │ ├── GoodBadChartActivity.kt │ │ ├── LineChartActivity.kt │ │ ├── LineColumnDependencyActivity.kt │ │ ├── MainActivity.kt │ │ ├── PieChartActivity.kt │ │ ├── PreviewColumnChartActivity.kt │ │ ├── PreviewLineChartActivity.kt │ │ ├── SpeedChartActivity.kt │ │ ├── TempoChartActivity.kt │ │ └── ViewPagerChartsActivity.kt │ └── res │ ├── color │ └── selector_text_link.xml │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── drawable-xxxhdpi │ └── ic_launcher.png │ ├── layout │ ├── activity_about.xml │ ├── activity_bubble_chart.xml │ ├── activity_column_chart.xml │ ├── activity_combo_line_column_chart.xml │ ├── activity_good_bad.xml │ ├── activity_line_chart.xml │ ├── activity_line_column_dependency.xml │ ├── activity_main.xml │ ├── activity_pie_chart.xml │ ├── activity_preview_column_chart.xml │ ├── activity_preview_line_chart.xml │ ├── activity_tempo_chart.xml │ ├── activity_view_pager_charts.xml │ ├── fragment_about.xml │ ├── fragment_bubble_chart.xml │ ├── fragment_column_chart.xml │ ├── fragment_combo_line_column_chart.xml │ ├── fragment_good_bad.xml │ ├── fragment_line_chart.xml │ ├── fragment_line_column_dependency.xml │ ├── fragment_main.xml │ ├── fragment_pie_chart.xml │ ├── fragment_preview_column_chart.xml │ ├── fragment_preview_line_chart.xml │ ├── fragment_tempo_chart.xml │ ├── fragment_view_pager_charts.xml │ └── list_item_sample.xml │ ├── menu │ ├── bubble_chart.xml │ ├── column_chart.xml │ ├── combo_line_column_chart.xml │ ├── line_chart.xml │ ├── main.xml │ ├── pie_chart.xml │ ├── preview_column_chart.xml │ ├── preview_line_chart.xml │ └── tempo_chart.xml │ ├── values-land │ └── dimens.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── kellocharts ├── .gitignore ├── build.gradle ├── kellocharts.iml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── co │ │ └── csadev │ │ └── kellocharts │ │ ├── animation │ │ ├── AnimatorInterfaces.kt │ │ ├── ChartDataAnimatorV14.kt │ │ ├── ChartViewportAnimatorV14.kt │ │ ├── DummyChartAnimationListener.kt │ │ └── PieChartRotationAnimatorV14.kt │ │ ├── computator │ │ ├── ChartComputator.kt │ │ └── PreviewChartComputator.kt │ │ ├── formatter │ │ ├── DateFormatters.kt │ │ ├── ValueFormatterHelper.kt │ │ ├── ValueFormatterInterfaces.kt │ │ └── ValueFormatters.kt │ │ ├── gesture │ │ ├── ChartScroller.kt │ │ ├── ChartTouchHandler.kt │ │ ├── ChartZoomer.kt │ │ ├── ContainerScrollType.kt │ │ ├── PieChartTouchHandler.kt │ │ ├── PreviewChartTouchHandler.kt │ │ ├── ZoomType.kt │ │ └── ZoomerCompat.kt │ │ ├── listener │ │ ├── ChartListeners.kt │ │ └── DummyChartListeners.kt │ │ ├── model │ │ ├── AbstractChartData.kt │ │ ├── Axis.kt │ │ ├── AxisValue.kt │ │ ├── BubbleChartData.kt │ │ ├── BubbleValue.kt │ │ ├── ChartData.kt │ │ ├── Column.kt │ │ ├── ColumnChartData.kt │ │ ├── ComboLineColumnChartData.kt │ │ ├── Line.kt │ │ ├── LineChartData.kt │ │ ├── PieChartData.kt │ │ ├── PointValue.kt │ │ ├── SelectedValue.kt │ │ ├── SliceValue.kt │ │ ├── SubcolumnValue.kt │ │ ├── ValueShape.kt │ │ ├── Viewport.kt │ │ └── dsl │ │ │ ├── DataDslProviders.kt │ │ │ ├── LayoutDslProviders.kt │ │ │ └── ValueDslProviders.kt │ │ ├── provider │ │ └── ChartDataProviders.kt │ │ ├── renderer │ │ ├── AbstractChartRenderer.kt │ │ ├── AxesRenderer.kt │ │ ├── BubbleChartRenderer.kt │ │ ├── ChartRenderer.kt │ │ ├── ColumnChartRenderer.kt │ │ ├── ComboChartRenderer.kt │ │ ├── ComboLineColumnChartRenderer.kt │ │ ├── LineChartRenderer.kt │ │ ├── PieChartRenderer.kt │ │ ├── PreviewColumnChartRenderer.kt │ │ └── PreviewLineChartRenderer.kt │ │ ├── util │ │ ├── AxisAutoValues.kt │ │ ├── ChartUtils.kt │ │ └── FloatUtils.kt │ │ └── view │ │ ├── AbstractChartView.kt │ │ ├── BubbleChartView.kt │ │ ├── Chart.kt │ │ ├── ColumnChartView.kt │ │ ├── ComboLineColumnChartView.kt │ │ ├── LineChartView.kt │ │ ├── PieChartView.kt │ │ ├── PreviewColumnChartView.kt │ │ ├── PreviewLineChartView.kt │ │ └── hack │ │ ├── HackyDrawerLayout.kt │ │ └── HackyViewPager.kt │ └── res │ └── values │ └── strings.xml ├── libs └── gradle-play-publisher-1.2.3.jar ├── logo_design.xcf ├── play_feature.png ├── play_icon.png ├── screenshots ├── bar_chart_horizontal.png ├── bar_chart_vertical.png ├── bubble_chart_horizontal.png ├── bubble_chart_vertical.png ├── icon_screenshot.png ├── line_chart_horizontal.png ├── line_chart_vertical.png ├── pie_chart_horizontal.png ├── pie_chart_vertical_menu.png ├── preview_line_chart_vertical.png └── preview_list.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .idea 3 | kellocharts-sample/release 4 | local.properties 5 | .gradle 6 | -------------------------------------------------------------------------------- /.gradle/4.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/4.1/fileContent/fileContent.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/.gradle/4.1/fileContent/fileContent.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Fri Oct 12 23:47:04 CEST 2018 2 | gradle.version=4.6 3 | -------------------------------------------------------------------------------- /KelloCharts.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KelloCharts for Android 2 | Kotlin Charts/graphs library for Android compatible with API 21+ (Android 5.0), several chart types with support for scaling, scrolling and animations 3 | Works best when hardware acceleration is available, so API 14+(Android 4.0) is recommended. 4 | Apache License 2.0. 5 | 6 | # Build badges and gradle availability coming soon 7 | 8 | ## Features 9 | 10 | - Line chart(cubic lines, filled lines, scattered points) 11 | - Column chart(grouped, stacked, negative values) 12 | - Pie chart 13 | - Bubble chart 14 | - Combo chart(columns/lines) 15 | - Preview charts(for column chart and line chart) 16 | - Zoom(pinch to zoom, double tap zoom), scroll and fling 17 | - Custom and auto-generated axes(top, bottom, left, right, inside) 18 | - Animations 19 | 20 | ## Screens and Demos 21 | 22 | - Code of a demo application is in `kellocharts-sample` directory 23 | - Demo App coming soon 24 | 25 | ## Usage 26 | 27 | Every chart view can be defined in layout xml file: 28 | 29 | ```xml 30 | 34 | ``` 35 | 36 | or created in code and added to layout later: 37 | 38 | ```Kotlin 39 | LineChartView chart = new LineChartView(context) 40 | layout.addView(chart) 41 | ``` 42 | 43 | Use methods from *Chart classes to define chart behaviour, example methods: 44 | 45 | ```Kotlin 46 | Chart.isInteractive = isInteractive 47 | Chart.zoomType = zoomType 48 | Chart.setContainerScrollEnabled(boolean isEnabled, ContainerScrollType type) 49 | ``` 50 | 51 | Use methods from data models to define how chart looks like, example methods: 52 | 53 | ```Kotlin 54 | ChartData.axisXBottom = axisX 55 | ColumnChartData.isStacked = isStacked 56 | Line.strokeWidth = strokeWidthDp 57 | ``` 58 | 59 | Every chart has its own method to set chart data and its own data model, example for line chart: 60 | 61 | ```Kotlin 62 | arrayListOf(PointValue(0, 2), PointValue(1, 4), PointValue(2, 3), PointValue(3, 4)) 63 | 64 | val line = Line(values, color = Color.BLUE, isCubic = true) 65 | val lines = arrayListOf(line) 66 | 67 | val data = LineChartData(lines) 68 | 69 | val chart = LineChartView(context) 70 | chart.lineChartData = data 71 | ``` 72 | 73 | Also, available as a DSL, same example as above: 74 | 75 | ```Kotlin 76 | lineData { 77 | lines { 78 | line { 79 | color = Color.BLUE 80 | isCubic = true 81 | pointValues { 82 | point { 83 | x = 0f 84 | y = 2f 85 | } 86 | point { 87 | x = 1f 88 | y = 4f 89 | } 90 | point { 91 | x = 2f 92 | y = 3f 93 | } 94 | point { 95 | x = 3f 96 | y = 4f 97 | } 98 | } 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | After the chart data has been set you can still modify its attributes but right after that you should call 105 | `*.*ChartData` setter again to let chart recalculate and redraw data. There is also an option to use copy constructor for deep copy of 106 | chart data. You can safely modify copy in other threads and pass it to `*.*ChartData` setter later. 107 | 108 | 109 | ## Contributing 110 | 111 | Yes :) If you found a bug, have an idea how to improve library or have a question, please create new issue or comment existing one. If you would like to contribute code fork the repository and send a pull request. 112 | 113 | ## License 114 | 115 | KelloCharts 116 | Copyright 2018 Charles Anderson 117 | 118 | Licensed under the Apache License, Version 2.0 (the "License"); 119 | you may not use this file except in compliance with the License. 120 | You may obtain a copy of the License at 121 | 122 | http://www.apache.org/licenses/LICENSE-2.0 123 | 124 | Unless required by applicable law or agreed to in writing, software 125 | distributed under the License is distributed on an "AS IS" BASIS, 126 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 127 | See the License for the specific language governing permissions and 128 | limitations under the License. 129 | 130 | --- 131 | KelloCharts library is developed from the original HelloCharts library available: 132 | https://github.com/lecho/hellocharts-android 133 | 134 | --- 135 | KelloCharts library uses code from InteractiveChart sample available 136 | on Android Developers page: 137 | 138 | http://developer.android.com/training/gestures/scale.html 139 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.2.71' 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.2.1' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 13 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 14 | classpath 'com.github.triplet.gradle:play-publisher:1.2.2' 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | android.enableJetifier=true 13 | android.useAndroidX=true 14 | org.gradle.jvmargs=-Xmx1536m 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed May 23 18:35:54 EDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /kellocharts-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /kellocharts-sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-android-extensions' 5 | apply plugin: 'com.github.triplet.play' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "co.csadev.kellocharts" 11 | minSdkVersion 21 12 | targetSdkVersion 28 13 | versionCode 3 14 | versionName "2.0.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: 'libs', include: ['*.jar']) 26 | implementation(project(':kellocharts')) 27 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 28 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 29 | implementation 'androidx.appcompat:appcompat:1.0.0' 30 | } 31 | -------------------------------------------------------------------------------- /kellocharts-sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 26 | 27 | 30 | 31 | 34 | 35 | 38 | 39 | 42 | 43 | 46 | 47 | 50 | 51 | 54 | 55 | 58 | 59 | 62 | 63 | 66 | 67 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/java/co/csadev/kellocharts/sample/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.sample 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.net.Uri 8 | import android.os.Bundle 9 | import android.util.Log 10 | import android.util.Pair 11 | import android.view.LayoutInflater 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import android.widget.TextView 15 | import androidx.appcompat.app.AppCompatActivity 16 | import androidx.fragment.app.Fragment 17 | 18 | class AboutActivity : AppCompatActivity() { 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | setContentView(R.layout.activity_about) 23 | if (savedInstanceState == null) { 24 | supportFragmentManager.beginTransaction().add(R.id.container, PlaceholderFragment()).commit() 25 | } 26 | } 27 | 28 | /** 29 | * A placeholder fragment containing a simple view. 30 | */ 31 | class PlaceholderFragment : Fragment() { 32 | 33 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 34 | val rootView = inflater.inflate(R.layout.fragment_about, container, false) 35 | 36 | val version = rootView.findViewById(R.id.version) 37 | version.text = getAppVersionAndBuild(activity).first 38 | 39 | val gotToGithub = rootView.findViewById(R.id.go_to_github) 40 | gotToGithub.setOnClickListener { launchWebBrowser(activity, GITHUB_URL) } 41 | 42 | return rootView 43 | } 44 | } 45 | 46 | companion object { 47 | val TAG = AboutActivity::class.java.simpleName 48 | val GITHUB_URL = "github.com/gtcompscientist/kellocharts" 49 | 50 | fun getAppVersionAndBuild(context: Context?): Pair { 51 | try { 52 | val pInfo = context!!.packageManager.getPackageInfo(context.packageName, 0) 53 | return Pair(pInfo.versionName, pInfo.versionCode) 54 | } catch (e: Exception) { 55 | Log.e(TAG, "Could not get version number") 56 | return Pair("", 0) 57 | } 58 | 59 | } 60 | 61 | @SuppressLint("DefaultLocale") 62 | fun launchWebBrowser(context: Context?, url: String): Boolean { 63 | var url = url 64 | try { 65 | url = url.toLowerCase() 66 | if (!url.startsWith("http://") || !url.startsWith("https://")) { 67 | url = "http://" + url 68 | } 69 | 70 | val intent = Intent(Intent.ACTION_VIEW) 71 | intent.data = Uri.parse(url) 72 | val resolveInfo = context!!.packageManager.resolveActivity(intent, 73 | PackageManager.MATCH_DEFAULT_ONLY) 74 | if (null == resolveInfo) { 75 | Log.e(TAG, "No activity to handle web intent") 76 | return false 77 | } 78 | context.startActivity(intent) 79 | Log.i(TAG, "Launching browser with url: " + url) 80 | return true 81 | } catch (e: Exception) { 82 | Log.e(TAG, "Could not start web browser", e) 83 | return false 84 | } 85 | 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/java/co/csadev/kellocharts/sample/GoodBadChartActivity.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.sample 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.fragment.app.Fragment 9 | import co.csadev.kellocharts.model.Line 10 | import co.csadev.kellocharts.model.LineChartData 11 | import co.csadev.kellocharts.model.PointValue 12 | import co.csadev.kellocharts.util.ChartUtils 13 | import co.csadev.kellocharts.view.LineChartView 14 | 15 | class GoodBadChartActivity : AppCompatActivity() { 16 | 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContentView(R.layout.activity_good_bad) 20 | if (savedInstanceState == null) { 21 | supportFragmentManager.beginTransaction().add(R.id.container, PlaceholderFragment()).commit() 22 | } 23 | } 24 | 25 | /** 26 | * A placeholder fragment containing a simple view. 27 | */ 28 | class PlaceholderFragment : Fragment() { 29 | 30 | private var chart: LineChartView? = null 31 | private var data: LineChartData? = null 32 | 33 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 34 | val rootView = inflater.inflate(R.layout.fragment_good_bad, container, false) 35 | 36 | chart = rootView.findViewById(R.id.chart) as LineChartView 37 | 38 | generateDefaultData() 39 | chart?.lineChartData = data!! 40 | 41 | // Increase viewport height for better look 42 | val v = chart!!.maximumViewport 43 | val dy = v.height() * 0.2f 44 | v.inset(0f, -dy) 45 | chart!!.maximumViewport = v 46 | chart!!.currentViewport = v 47 | 48 | return rootView 49 | } 50 | 51 | private fun generateDefaultData() { 52 | 53 | // Generate data, every line has 3 points to form filled triangle. Point radius is set to 1 to be almost 54 | // invisible but it has to be there because without points there is not labels. Area transparency is set to 55 | // 255(full opacity). 56 | 57 | // Important note. This example uses negative values, to properly fill area below 0 chart base value have to 58 | // be set to 0. That is default base value but if you want to be sure you can call data.setBaseValue(0) 59 | // method. 60 | 61 | var line: Line 62 | var values: MutableList 63 | val lines = ArrayList() 64 | 65 | // First good triangle 66 | values = ArrayList() 67 | values.add(PointValue(0, 0, "".toCharArray())) 68 | values.add(PointValue(1, 1, "Very Good:)".toCharArray())) 69 | values.add(PointValue(2, 0, "".toCharArray())) 70 | 71 | line = Line(values) 72 | line.color = ChartUtils.COLOR_GREEN 73 | line.areaTransparency = 255 74 | line.isFilled = true 75 | line.pointRadius = 1 76 | line.hasLabels = true 77 | lines.add(line) 78 | 79 | // Second good triangle 80 | values = ArrayList() 81 | values.add(PointValue(3, 0, "".toCharArray())) 82 | values.add(PointValue(4f, 0.5f, "Good Enough".toCharArray())) 83 | values.add(PointValue(5, 0, "".toCharArray())) 84 | 85 | line = Line(values) 86 | line.color = ChartUtils.COLOR_GREEN 87 | line.areaTransparency = 255 88 | line.isFilled = true 89 | line.pointRadius = 1 90 | line.hasLabels = true 91 | lines.add(line) 92 | 93 | // Bad triangle 94 | values = ArrayList() 95 | values.add(PointValue(1, 0, "".toCharArray())) 96 | values.add(PointValue(2, -1, "Very Bad".toCharArray())) 97 | values.add(PointValue(3, 0, "".toCharArray())) 98 | 99 | line = Line(values) 100 | line.color = ChartUtils.COLOR_RED 101 | line.areaTransparency = 255 102 | line.isFilled = true 103 | line.pointRadius = 1 104 | line.hasLabels = true 105 | lines.add(line) 106 | 107 | data = LineChartData(lines) 108 | 109 | // *** Important, set base value to 0 to fill negative part of chart. 110 | // data.setBaseValue(0); 111 | 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/color/selector_text_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/kellocharts-sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/kellocharts-sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/kellocharts-sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/kellocharts-sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/kellocharts-sample/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_bubble_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_column_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_combo_line_column_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_good_bad.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_line_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_line_column_dependency.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_pie_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_preview_column_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_preview_line_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_tempo_chart.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/activity_view_pager_charts.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_about.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 24 | 25 | 33 | 34 | 41 | 42 | 47 | 48 | 52 | 53 | 54 | 61 | 62 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_bubble_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_column_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_combo_line_column_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_good_bad.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_line_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_line_column_dependency.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | 25 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_pie_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_preview_column_chart.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | 25 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_preview_line_chart.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | 25 | 26 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_tempo_chart.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/fragment_view_pager_charts.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/layout/list_item_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | 26 | 27 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/bubble_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/column_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 58 | 62 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/combo_line_column_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 43 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/line_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 58 | 62 | 66 | 70 | 74 | 78 | 82 | 83 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/pie_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 43 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/preview_column_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/preview_line_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 14 | 18 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/menu/tempo_chart.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 38sp 4 | 12dp 5 | 6 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 54sp 9 | 24dp 10 | 11 | 12 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 64dp 9 | 10 | 11 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #33B5E5 5 | #8033B5E5 6 | #0099CC 7 | #FF8800 8 | #FFBB33 9 | 10 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 42sp 7 | 16dp 8 | 9 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KelloCharts 5 | About 6 | LineChartActivity 7 | ColumnChartActivity 8 | PieChartActivity 9 | BubbleChartActivity 10 | PreviewLineChartActivity 11 | PreviewColumnChartActivity 12 | ComboLineColumnChartActivity 13 | LineColumnDependency 14 | GoodBadActivity 15 | TempoChartActivity 16 | SpeedChartActivity 17 | ViewPagerChartsActivity 18 | Section 1 19 | Section 2 20 | Section 3 21 | AboutActivity 22 | "Library version " 23 | Charles Anderson 2018 24 | App icon 25 | Go to GitHub 26 | 27 | -------------------------------------------------------------------------------- /kellocharts-sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /kellocharts/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /kellocharts/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 28 6 | 7 | defaultConfig { 8 | minSdkVersion 21 9 | targetSdkVersion 28 10 | versionCode 4 11 | versionName "2.0.0" 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'androidx.appcompat:appcompat:1.0.0' 26 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 27 | } 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | ext { 33 | bintrayRepo = 'KelloCharts' 34 | bintrayName = 'KelloCharts' 35 | 36 | publishedGroupId = 'co.csadev' 37 | libraryName = 'KelloCharts' 38 | artifact = 'kellocharts' 39 | 40 | libraryDescription = 'Kotlin Charts/graphs library for Android compatible with API 21+, several chart types with support for scaling, scrolling and animations' 41 | 42 | siteUrl = 'https://github.com/gtcompscientist/KelloCharts' 43 | gitUrl = 'https://github.com/gtcompscientist/KelloCharts.git' 44 | 45 | libraryVersion = android.defaultConfig.versionName 46 | 47 | developerId = 'gtcompscientist' 48 | developerName = 'Charles Anderson' 49 | developerEmail = 'csadevapps@gmail.com' 50 | 51 | licenseName = 'The Apache Software License, Version 2.0' 52 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 53 | allLicenses = ["Apache-2.0"] 54 | } 55 | 56 | tasks.withType(Javadoc) { 57 | // Ignores errors from mavenAndroidJavadocs task 58 | // (reference: github.com/novoda/bintray-release/issues/71#issuecomment-164324255) 59 | options.addStringOption('Xdoclint:none', '-quiet') 60 | options.addStringOption('encoding', 'UTF-8') 61 | excludes = ['**/*.kt'] // < ---- Exclude all kotlin files from javadoc file. 62 | } 63 | 64 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' 65 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' -------------------------------------------------------------------------------- /kellocharts/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /kellocharts/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/animation/AnimatorInterfaces.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.animation 2 | 3 | import co.csadev.kellocharts.model.Viewport 4 | import java.util.* 5 | 6 | val FAST_ANIMATION_DURATION = 200L 7 | 8 | interface ChartAnimationListener : EventListener { 9 | fun onAnimationStarted() 10 | fun onAnimationFinished() 11 | } 12 | 13 | interface ChartDataAnimator { 14 | val isAnimationStarted: Boolean 15 | fun startAnimation(duration: Long) 16 | fun cancelAnimation() 17 | fun setChartAnimationListener(animationListener: ChartAnimationListener?) 18 | companion object { 19 | val DEFAULT_ANIMATION_DURATION: Long = 500 20 | } 21 | } 22 | 23 | interface ChartViewportAnimator { 24 | val isAnimationStarted: Boolean 25 | fun startAnimation(startViewport: Viewport, targetViewport: Viewport) 26 | fun startAnimation(startViewport: Viewport, targetViewport: Viewport, duration: Long) 27 | fun cancelAnimation() 28 | fun setChartAnimationListener(animationListener: ChartAnimationListener?) 29 | } 30 | 31 | interface PieChartRotationAnimator { 32 | val isAnimationStarted: Boolean 33 | fun startAnimation(startAngle: Float, angleToRotate: Float) 34 | fun cancelAnimation() 35 | fun setChartAnimationListener(animationListener: ChartAnimationListener?) 36 | } 37 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/animation/ChartDataAnimatorV14.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.Animator.AnimatorListener 5 | import android.animation.ValueAnimator 6 | import android.animation.ValueAnimator.AnimatorUpdateListener 7 | import android.annotation.SuppressLint 8 | 9 | import co.csadev.kellocharts.view.Chart 10 | 11 | @SuppressLint("NewApi") 12 | class ChartDataAnimatorV14(private val chart: Chart) : ChartDataAnimator, AnimatorListener, AnimatorUpdateListener { 13 | private val animator: ValueAnimator 14 | private var animationListener: ChartAnimationListener? = DummyChartAnimationListener() 15 | 16 | override val isAnimationStarted: Boolean 17 | get() = animator.isStarted 18 | 19 | init { 20 | animator = ValueAnimator.ofFloat(0.0f, 1.0f) 21 | animator.addListener(this) 22 | animator.addUpdateListener(this) 23 | } 24 | 25 | override fun startAnimation(duration: Long) { 26 | if (duration >= 0) { 27 | animator.duration = duration 28 | } else { 29 | animator.duration = ChartDataAnimator.Companion.DEFAULT_ANIMATION_DURATION 30 | } 31 | animator.start() 32 | } 33 | 34 | override fun cancelAnimation() { 35 | animator.cancel() 36 | } 37 | 38 | override fun onAnimationUpdate(animation: ValueAnimator) { 39 | chart.animationDataUpdate(animation.animatedFraction) 40 | } 41 | 42 | override fun onAnimationCancel(animation: Animator) {} 43 | 44 | override fun onAnimationEnd(animation: Animator) { 45 | chart.animationDataFinished() 46 | animationListener?.onAnimationFinished() 47 | } 48 | 49 | override fun onAnimationRepeat(animation: Animator) {} 50 | 51 | override fun onAnimationStart(animation: Animator) { 52 | animationListener?.onAnimationStarted() 53 | } 54 | 55 | override fun setChartAnimationListener(animationListener: ChartAnimationListener?) { 56 | this.animationListener = animationListener 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/animation/ChartViewportAnimatorV14.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.Animator.AnimatorListener 5 | import android.animation.ValueAnimator 6 | import android.animation.ValueAnimator.AnimatorUpdateListener 7 | import android.annotation.SuppressLint 8 | 9 | import co.csadev.kellocharts.model.Viewport 10 | import co.csadev.kellocharts.model.set 11 | import co.csadev.kellocharts.view.Chart 12 | 13 | @SuppressLint("NewApi") 14 | class ChartViewportAnimatorV14(private val chart: Chart) : ChartViewportAnimator, AnimatorListener, AnimatorUpdateListener { 15 | private val animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f) 16 | private val startViewport = Viewport() 17 | private val targetViewport = Viewport() 18 | private val newViewport = Viewport() 19 | private var animationListener: ChartAnimationListener? = DummyChartAnimationListener() 20 | 21 | override val isAnimationStarted: Boolean 22 | get() = animator.isStarted 23 | 24 | init { 25 | animator.addListener(this) 26 | animator.addUpdateListener(this) 27 | animator.duration = FAST_ANIMATION_DURATION 28 | } 29 | 30 | override fun startAnimation(startViewport: Viewport, targetViewport: Viewport) { 31 | this.startViewport.set(startViewport) 32 | this.targetViewport.set(targetViewport) 33 | animator.duration = FAST_ANIMATION_DURATION 34 | animator.start() 35 | } 36 | 37 | override fun startAnimation(startViewport: Viewport, targetViewport: Viewport, duration: Long) { 38 | this.startViewport.set(startViewport) 39 | this.targetViewport.set(targetViewport) 40 | animator.duration = duration 41 | animator.start() 42 | } 43 | 44 | override fun cancelAnimation() { 45 | animator.cancel() 46 | } 47 | 48 | override fun onAnimationUpdate(animation: ValueAnimator) { 49 | val scale = animation.animatedFraction 50 | val diffLeft = (targetViewport.left - startViewport.left) * scale 51 | val diffTop = (targetViewport.top - startViewport.top) * scale 52 | val diffRight = (targetViewport.right - startViewport.right) * scale 53 | val diffBottom = (targetViewport.bottom - startViewport.bottom) * scale 54 | newViewport.set(startViewport.left + diffLeft, startViewport.top + diffTop, startViewport.right + diffRight, startViewport.bottom + diffBottom) 55 | chart.currentViewport = newViewport 56 | } 57 | 58 | override fun onAnimationCancel(animation: Animator) {} 59 | 60 | override fun onAnimationEnd(animation: Animator) { 61 | chart.currentViewport = targetViewport 62 | animationListener?.onAnimationFinished() 63 | } 64 | 65 | override fun onAnimationRepeat(animation: Animator) {} 66 | 67 | override fun onAnimationStart(animation: Animator) { 68 | animationListener?.onAnimationStarted() 69 | } 70 | 71 | override fun setChartAnimationListener(animationListener: ChartAnimationListener?) { 72 | this.animationListener = animationListener 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/animation/DummyChartAnimationListener.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.animation 2 | 3 | class DummyChartAnimationListener : ChartAnimationListener { 4 | override fun onAnimationStarted() { } 5 | override fun onAnimationFinished() { } 6 | } 7 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/animation/PieChartRotationAnimatorV14.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.Animator.AnimatorListener 5 | import android.animation.ValueAnimator 6 | import android.animation.ValueAnimator.AnimatorUpdateListener 7 | import android.annotation.SuppressLint 8 | 9 | import co.csadev.kellocharts.view.PieChartView 10 | 11 | @SuppressLint("NewApi") 12 | class PieChartRotationAnimatorV14 @JvmOverloads constructor(private val chart: PieChartView, duration: Long = 200L) : PieChartRotationAnimator, AnimatorListener, AnimatorUpdateListener { 13 | private val animator: ValueAnimator = ValueAnimator.ofFloat(0.0f, 1.0f) 14 | private var startRotation = 0f 15 | private var targetRotation = 0f 16 | private var animationListener: ChartAnimationListener = DummyChartAnimationListener() 17 | 18 | override val isAnimationStarted: Boolean 19 | get() = animator.isStarted 20 | 21 | init { 22 | animator.duration = duration 23 | animator.addListener(this) 24 | animator.addUpdateListener(this) 25 | } 26 | 27 | override fun startAnimation(startAngle: Float, angleToRotate: Float) { 28 | this.startRotation = (startAngle % 360 + 360) % 360 29 | this.targetRotation = (angleToRotate % 360 + 360) % 360 30 | animator.start() 31 | } 32 | 33 | override fun cancelAnimation() { 34 | animator.cancel() 35 | } 36 | 37 | override fun onAnimationUpdate(animation: ValueAnimator) { 38 | val scale = animation.animatedFraction 39 | var rotation = startRotation + (targetRotation - startRotation) * scale 40 | rotation = (rotation % 360 + 360) % 360 41 | chart.setChartRotation(rotation.toInt(), false) 42 | } 43 | 44 | override fun onAnimationCancel(animation: Animator) {} 45 | 46 | override fun onAnimationEnd(animation: Animator) { 47 | chart.setChartRotation(targetRotation.toInt(), false) 48 | animationListener.onAnimationFinished() 49 | } 50 | 51 | override fun onAnimationRepeat(animation: Animator) {} 52 | 53 | override fun onAnimationStart(animation: Animator) { 54 | animationListener.onAnimationStarted() 55 | } 56 | 57 | override fun setChartAnimationListener(animationListener: ChartAnimationListener?) { 58 | if (null == animationListener) { 59 | this.animationListener = DummyChartAnimationListener() 60 | } else { 61 | this.animationListener = animationListener 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/computator/PreviewChartComputator.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.computator 2 | 3 | import co.csadev.kellocharts.model.Viewport 4 | 5 | /** 6 | * Version of ChartComputator for preview charts. It always uses maxViewport as visible viewport and currentViewport as 7 | * preview area. 8 | */ 9 | class PreviewChartComputator : ChartComputator() { 10 | 11 | override fun computeRawX(valueX: Float): Float { 12 | val pixelOffset = (valueX - maximumViewport.left) * (contentRectMinusAllMargins.width() / maximumViewport 13 | .width()) 14 | return contentRectMinusAllMargins.left + pixelOffset 15 | } 16 | 17 | override fun computeRawY(valueY: Float): Float { 18 | val pixelOffset = (valueY - maximumViewport.bottom) * (contentRectMinusAllMargins.height() / maximumViewport 19 | .height()) 20 | return contentRectMinusAllMargins.bottom - pixelOffset 21 | } 22 | 23 | override var visibleViewport: Viewport 24 | get() = maximumViewport 25 | set(value) { maximumViewport = value } 26 | 27 | override fun constrainViewport(left: Float, top: Float, right: Float, bottom: Float) { 28 | super.constrainViewport(left, top, right, bottom) 29 | viewportChangeListener?.onViewportChanged(currentViewport) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/formatter/DateFormatters.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package co.csadev.kellocharts.formatter 4 | 5 | import co.csadev.kellocharts.model.* 6 | import java.text.DateFormat 7 | import java.util.* 8 | import kotlin.math.roundToLong 9 | 10 | private const val nullTerminator = '\u0000' 11 | 12 | private fun DateFormat.formatValue(formattedValue: CharArray, value: Float): Int { 13 | val dateResult = format(Date(value.roundToLong())) 14 | val dateLength = dateResult.length 15 | dateResult.mapIndexed { index, c -> 16 | formattedValue[index] = c 17 | } 18 | for (i in dateLength until formattedValue.size) { 19 | formattedValue[i] = nullTerminator 20 | } 21 | return dateLength 22 | } 23 | 24 | 25 | 26 | class DateAxisValueFormatter(private val dateFormat: DateFormat) : AxisValueFormatter { 27 | override fun formatValueForManualAxis(formattedValue: CharArray, axisValue: AxisValue) 28 | = dateFormat.formatValue(formattedValue, axisValue.value) 29 | 30 | override fun formatValueForAutoGeneratedAxis(formattedValue: CharArray, value: Float, autoDecimalDigits: Int) 31 | = dateFormat.formatValue(formattedValue, value) 32 | 33 | } 34 | 35 | class DateBubbleChartValueFormatterX(private val dateFormat: DateFormat) : BubbleChartValueFormatter { 36 | override fun formatChartValue(formattedValue: CharArray, value: BubbleValue) 37 | = dateFormat.formatValue(formattedValue, value.x) 38 | } 39 | 40 | class DateBubbleChartValueFormatterY(private val dateFormat: DateFormat) : BubbleChartValueFormatter { 41 | override fun formatChartValue(formattedValue: CharArray, value: BubbleValue) 42 | = dateFormat.formatValue(formattedValue, value.y) 43 | } 44 | 45 | class DateBubbleChartValueFormatterZ(private val dateFormat: DateFormat) : BubbleChartValueFormatter { 46 | override fun formatChartValue(formattedValue: CharArray, value: BubbleValue) 47 | = dateFormat.formatValue(formattedValue, value.z) 48 | } 49 | 50 | class DateColumnChartValueFormatter(private val dateFormat: DateFormat) : ColumnChartValueFormatter { 51 | override fun formatChartValue(formattedValue: CharArray, value: SubcolumnValue) 52 | = dateFormat.formatValue(formattedValue, value.value) 53 | } 54 | 55 | class DateLineChartValueFormatterX(private val dateFormat: DateFormat) : LineChartValueFormatter { 56 | override fun formatChartValue(formattedValue: CharArray, value: PointValue) 57 | = dateFormat.formatValue(formattedValue, value.x) 58 | } 59 | 60 | class DateLineChartValueFormatterY(private val dateFormat: DateFormat) : LineChartValueFormatter { 61 | override fun formatChartValue(formattedValue: CharArray, value: PointValue) 62 | = dateFormat.formatValue(formattedValue, value.y) 63 | } 64 | 65 | class DatePieChartValueFormatter(private val dateFormat: DateFormat) : PieChartValueFormatter { 66 | override fun formatChartValue(formattedValue: CharArray, value: SliceValue) 67 | = dateFormat.formatValue(formattedValue, value.value) 68 | } 69 | 70 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/formatter/ValueFormatterHelper.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.formatter 2 | 3 | import android.util.Log 4 | import co.csadev.kellocharts.util.FloatUtils 5 | import java.text.DecimalFormat 6 | import java.text.NumberFormat 7 | 8 | class ValueFormatterHelper(var decimalDigitsNumber: Int = Int.MIN_VALUE, var appendedText: CharArray = CharArray(0), var prependedText: CharArray = CharArray(0), var decimalSeparator: Char = '.') { 9 | 10 | fun determineDecimalSeparator() { 11 | val numberFormat = NumberFormat.getInstance() 12 | if (numberFormat is DecimalFormat) { 13 | decimalSeparator = numberFormat.decimalFormatSymbols.decimalSeparator 14 | } 15 | } 16 | 17 | fun setDecimalSeparator(decimalSeparator: Char): ValueFormatterHelper { 18 | val nullChar = '\u0000' 19 | if (nullChar != decimalSeparator) { 20 | this.decimalSeparator = decimalSeparator 21 | } 22 | return this 23 | } 24 | 25 | /** 26 | * Formats float value. Result is stored in (output) formattedValue array. Method 27 | * returns number of chars of formatted value. The formatted value starts at index [formattedValue.length - 28 | * charsNumber] and ends at index [formattedValue.length-1]. 29 | * Note: If label is not null it will be used as formattedValue instead of float value. 30 | * Note: Parameter defaultDigitsNumber is used only if you didn't change decimalDigintsNumber value using 31 | * method [.setDecimalDigitsNumber]. 32 | */ 33 | @JvmOverloads 34 | fun formatFloatValueWithPrependedAndAppendedText(formattedValue: CharArray, value: Float, defaultDigitsNumber: Int, label: CharArray? = null): Int { 35 | if (null != label) { 36 | // If custom label is not null use only name characters as formatted value. 37 | // Copy label into formatted value array. 38 | var labelLength = label.size 39 | if (labelLength > formattedValue.size) { 40 | Log.w(TAG, "Label length is larger than buffer size(64chars), some chars will be skipped!") 41 | labelLength = formattedValue.size 42 | } 43 | System.arraycopy(label, 0, formattedValue, formattedValue.size - labelLength, labelLength) 44 | return labelLength 45 | } 46 | 47 | val appliedDigitsNumber = getAppliedDecimalDigitsNumber(defaultDigitsNumber) 48 | val charsNumber = formatFloatValue(formattedValue, value, appliedDigitsNumber) 49 | appendText(formattedValue) 50 | prependText(formattedValue, charsNumber) 51 | return charsNumber + prependedText.size + appendedText.size 52 | } 53 | 54 | /** 55 | * @see .formatFloatValueWithPrependedAndAppendedText 56 | */ 57 | fun formatFloatValueWithPrependedAndAppendedText(formattedValue: CharArray, value: Float, label: CharArray?): Int { 58 | return formatFloatValueWithPrependedAndAppendedText(formattedValue, value, DEFAULT_DIGITS_NUMBER, label) 59 | } 60 | 61 | fun formatFloatValue(formattedValue: CharArray, value: Float, decimalDigitsNumber: Int): Int { 62 | return FloatUtils.formatFloat(formattedValue, value, formattedValue.size - appendedText.size, 63 | decimalDigitsNumber, 64 | decimalSeparator) 65 | } 66 | 67 | fun appendText(formattedValue: CharArray) { 68 | if (appendedText.isNotEmpty()) { 69 | System.arraycopy(appendedText, 0, formattedValue, formattedValue.size - appendedText.size, 70 | appendedText.size) 71 | } 72 | } 73 | 74 | fun prependText(formattedValue: CharArray, charsNumber: Int) { 75 | if (prependedText.isNotEmpty()) { 76 | System.arraycopy(prependedText, 0, formattedValue, formattedValue.size - charsNumber - appendedText.size 77 | - prependedText.size, prependedText.size) 78 | } 79 | } 80 | 81 | fun getAppliedDecimalDigitsNumber(defaultDigitsNumber: Int): Int { 82 | val appliedDecimalDigitsNumber: Int 83 | if (decimalDigitsNumber < 0) { 84 | //When decimalDigitsNumber < 0 that means that user didn't set that value and defaultDigitsNumber should 85 | // be used. 86 | appliedDecimalDigitsNumber = defaultDigitsNumber 87 | } else { 88 | appliedDecimalDigitsNumber = decimalDigitsNumber 89 | } 90 | return appliedDecimalDigitsNumber 91 | } 92 | 93 | companion object { 94 | val DEFAULT_DIGITS_NUMBER = 0 95 | private val TAG = "ValueFormatterHelper" 96 | } 97 | 98 | } 99 | /** 100 | * @see .formatFloatValueWithPrependedAndAppendedText 101 | */ 102 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/formatter/ValueFormatterInterfaces.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.formatter 2 | 3 | import co.csadev.kellocharts.model.* 4 | 5 | interface AxisValueFormatter { 6 | fun formatValueForManualAxis(formattedValue: CharArray, axisValue: AxisValue): Int 7 | fun formatValueForAutoGeneratedAxis(formattedValue: CharArray, value: Float, autoDecimalDigits: Int): Int 8 | } 9 | 10 | interface BubbleChartValueFormatter { 11 | fun formatChartValue(formattedValue: CharArray, value: BubbleValue): Int 12 | } 13 | 14 | interface ColumnChartValueFormatter { 15 | fun formatChartValue(formattedValue: CharArray, value: SubcolumnValue): Int 16 | } 17 | 18 | interface LineChartValueFormatter { 19 | fun formatChartValue(formattedValue: CharArray, value: PointValue): Int 20 | } 21 | 22 | interface PieChartValueFormatter { 23 | fun formatChartValue(formattedValue: CharArray, value: SliceValue): Int 24 | } 25 | 26 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/formatter/ValueFormatters.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.formatter 2 | 3 | import co.csadev.kellocharts.model.* 4 | 5 | open class ValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') { 6 | internal val valueFormatterHelper = ValueFormatterHelper(decimalDigitsNumber, appendedText, prependedText, decimalSeparator) 7 | 8 | open var decimalDigitsNumber: Int 9 | get() = valueFormatterHelper.decimalDigitsNumber 10 | set(value) { valueFormatterHelper.decimalDigitsNumber = value } 11 | 12 | open var appendedText: CharArray 13 | get() = valueFormatterHelper.appendedText 14 | set(value) { valueFormatterHelper.appendedText = value } 15 | 16 | open var prependedText: CharArray 17 | get() = valueFormatterHelper.prependedText 18 | set(value) { valueFormatterHelper.prependedText = value } 19 | 20 | open var decimalSeparator: Char 21 | get() = valueFormatterHelper.decimalSeparator 22 | set(value) { valueFormatterHelper.decimalSeparator = value } 23 | 24 | init { 25 | valueFormatterHelper.determineDecimalSeparator() 26 | } 27 | } 28 | 29 | open class SimplePieChartValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') : ValueFormatter(decimalDigitsNumber, appendedText, prependedText, decimalSeparator), PieChartValueFormatter { 30 | override fun formatChartValue(formattedValue: CharArray, value: SliceValue) 31 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.value, value.label) 32 | } 33 | 34 | open class SimpleLineChartValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') : ValueFormatter(decimalDigitsNumber, appendedText, prependedText, decimalSeparator), LineChartValueFormatter { 35 | override fun formatChartValue(formattedValue: CharArray, value: PointValue) 36 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.y, value.label) 37 | } 38 | 39 | open class SimpleColumnChartValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') : ValueFormatter(decimalDigitsNumber, appendedText, prependedText, decimalSeparator), ColumnChartValueFormatter { 40 | override fun formatChartValue(formattedValue: CharArray, value: SubcolumnValue) 41 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.value, value.label) 42 | } 43 | 44 | open class SimpleBubbleChartValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') : ValueFormatter(decimalDigitsNumber, appendedText, prependedText, decimalSeparator), BubbleChartValueFormatter { 45 | override fun formatChartValue(formattedValue: CharArray, value: BubbleValue) 46 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value.z, value.label) 47 | } 48 | 49 | open class SimpleAxisValueFormatter(decimalDigitsNumber: Int = Int.MIN_VALUE, appendedText: CharArray = CharArray(0), prependedText: CharArray = CharArray(0), decimalSeparator: Char = '.') : ValueFormatter(decimalDigitsNumber, appendedText, prependedText, decimalSeparator), AxisValueFormatter { 50 | override fun formatValueForManualAxis(formattedValue: CharArray, axisValue: AxisValue) 51 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, axisValue.value, axisValue.label) 52 | override fun formatValueForAutoGeneratedAxis(formattedValue: CharArray, value: Float, autoDecimalDigits: Int) 53 | = valueFormatterHelper.formatFloatValueWithPrependedAndAppendedText(formattedValue, value, autoDecimalDigits) 54 | } 55 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/ChartScroller.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.gesture 2 | 3 | import android.content.Context 4 | import android.graphics.Point 5 | import androidx.core.widget.ScrollerCompat 6 | 7 | import co.csadev.kellocharts.computator.ChartComputator 8 | import co.csadev.kellocharts.model.Viewport 9 | import co.csadev.kellocharts.model.set 10 | 11 | /** 12 | * Encapsulates scrolling functionality. 13 | */ 14 | class ChartScroller(context: Context) { 15 | 16 | private val scrollerStartViewport = Viewport() // Used only for zooms and flings 17 | private val surfaceSizeBuffer = Point()// Used for scroll and flings 18 | private val scroller: ScrollerCompat 19 | 20 | init { 21 | scroller = ScrollerCompat.create(context) 22 | } 23 | 24 | fun startScroll(computator: ChartComputator): Boolean { 25 | scroller.abortAnimation() 26 | scrollerStartViewport.set(computator.currentViewport) 27 | return true 28 | } 29 | 30 | fun scroll(computator: ChartComputator, distanceX: Float, distanceY: Float, scrollResult: ScrollResult): Boolean { 31 | 32 | // Scrolling uses math based on the viewport (as opposed to math using pixels). Pixel offset is the offset in 33 | // screen pixels, while viewport offset is the offset within the current viewport. For additional 34 | // information on 35 | // surface sizes and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For additional 36 | // information about the viewport, see the comments for {@link mCurrentViewport}. 37 | 38 | val maxViewport = computator.maximumViewport 39 | val visibleViewport = computator.visibleViewport 40 | val currentViewport = computator.currentViewport 41 | val contentRect = computator.contentRectMinusAllMargins 42 | 43 | val canScrollLeft = currentViewport.left > maxViewport.left 44 | val canScrollRight = currentViewport.right < maxViewport.right 45 | val canScrollTop = currentViewport.top < maxViewport.top 46 | val canScrollBottom = currentViewport.bottom > maxViewport.bottom 47 | 48 | var canScrollX = false 49 | var canScrollY = false 50 | 51 | if (canScrollLeft && distanceX <= 0) { 52 | canScrollX = true 53 | } else if (canScrollRight && distanceX >= 0) { 54 | canScrollX = true 55 | } 56 | 57 | if (canScrollTop && distanceY <= 0) { 58 | canScrollY = true 59 | } else if (canScrollBottom && distanceY >= 0) { 60 | canScrollY = true 61 | } 62 | 63 | if (canScrollX || canScrollY) { 64 | 65 | computator.computeScrollSurfaceSize(surfaceSizeBuffer) 66 | 67 | val viewportOffsetX = distanceX * visibleViewport.width() / contentRect.width() 68 | val viewportOffsetY = -distanceY * visibleViewport.height() / contentRect.height() 69 | 70 | computator 71 | .setViewportTopLeft(currentViewport.left + viewportOffsetX, currentViewport.top + viewportOffsetY) 72 | } 73 | 74 | scrollResult.canScrollX = canScrollX 75 | scrollResult.canScrollY = canScrollY 76 | 77 | return canScrollX || canScrollY 78 | } 79 | 80 | fun computeScrollOffset(computator: ChartComputator): Boolean { 81 | if (scroller.computeScrollOffset()) { 82 | // The scroller isn't finished, meaning a fling or programmatic pan operation is 83 | // currently active. 84 | 85 | val maxViewport = computator.maximumViewport 86 | 87 | computator.computeScrollSurfaceSize(surfaceSizeBuffer) 88 | 89 | val currXRange = maxViewport.left + maxViewport.width() * scroller.currX / surfaceSizeBuffer.x 90 | val currYRange = maxViewport.top - maxViewport.height() * scroller.currY / surfaceSizeBuffer.y 91 | 92 | computator.setViewportTopLeft(currXRange, currYRange) 93 | 94 | return true 95 | } 96 | 97 | return false 98 | } 99 | 100 | fun fling(velocityX: Int, velocityY: Int, computator: ChartComputator): Boolean { 101 | // Flings use math in pixels (as opposed to math based on the viewport). 102 | computator.computeScrollSurfaceSize(surfaceSizeBuffer) 103 | scrollerStartViewport.set(computator.currentViewport) 104 | 105 | val startX = (surfaceSizeBuffer.x * (scrollerStartViewport.left - computator.maximumViewport.left) / computator.maximumViewport.width()).toInt() 106 | val startY = (surfaceSizeBuffer.y * (computator.maximumViewport.top - scrollerStartViewport.top) / computator.maximumViewport.height()).toInt() 107 | 108 | // TODO probably should be mScroller.forceFinish but ScrollerCompat doesn't have that method. 109 | scroller.abortAnimation() 110 | 111 | val width = computator.contentRectMinusAllMargins.width() 112 | val height = computator.contentRectMinusAllMargins.height() 113 | scroller.fling(startX, startY, velocityX, velocityY, 0, surfaceSizeBuffer.x - width + 1, 0, 114 | surfaceSizeBuffer.y - height + 1) 115 | return true 116 | } 117 | 118 | class ScrollResult { 119 | var canScrollX: Boolean = false 120 | var canScrollY: Boolean = false 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/ChartZoomer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.gesture 2 | 3 | import android.content.Context 4 | import android.graphics.PointF 5 | import android.view.MotionEvent 6 | 7 | import co.csadev.kellocharts.computator.ChartComputator 8 | import co.csadev.kellocharts.model.Viewport 9 | import co.csadev.kellocharts.model.set 10 | 11 | /** 12 | * Encapsulates zooming functionality. 13 | */ 14 | class ChartZoomer(context: Context, var zoomType: ZoomType?) { 15 | private val zoomer = ZoomerCompat(context) 16 | private val zoomFocalPoint = PointF()// Used for double tap zoom 17 | private val viewportFocus = PointF() 18 | private val scrollerStartViewport = Viewport() // Used only for zooms and flings 19 | 20 | fun startZoom(e: MotionEvent, computator: ChartComputator): Boolean { 21 | zoomer.forceFinished(true) 22 | scrollerStartViewport.set(computator.currentViewport) 23 | if (!computator.rawPixelsToDataPoint(e.x, e.y, zoomFocalPoint)) { 24 | // Focus point is not within content area. 25 | return false 26 | } 27 | zoomer.startZoom(ZOOM_AMOUNT) 28 | return true 29 | } 30 | 31 | fun computeZoom(computator: ChartComputator): Boolean { 32 | if (zoomer.computeZoom()) { 33 | // Performs the zoom since a zoom is in progress. 34 | val newWidth = (1.0f - zoomer.currZoom) * scrollerStartViewport.width() 35 | val newHeight = (1.0f - zoomer.currZoom) * scrollerStartViewport.height() 36 | val pointWithinViewportX = (zoomFocalPoint.x - scrollerStartViewport.left) / scrollerStartViewport.width() 37 | val pointWithinViewportY = (zoomFocalPoint.y - scrollerStartViewport.bottom) / scrollerStartViewport.height() 38 | 39 | val left = zoomFocalPoint.x - newWidth * pointWithinViewportX 40 | val top = zoomFocalPoint.y + newHeight * (1 - pointWithinViewportY) 41 | val right = zoomFocalPoint.x + newWidth * (1 - pointWithinViewportX) 42 | val bottom = zoomFocalPoint.y - newHeight * pointWithinViewportY 43 | setCurrentViewport(computator, left, top, right, bottom) 44 | return true 45 | } 46 | return false 47 | } 48 | 49 | fun scale(computator: ChartComputator, focusX: Float, focusY: Float, scale: Float): Boolean { 50 | /** 51 | * Smaller viewport means bigger zoom so for zoomIn scale should have value <1, for zoomOout >1 52 | */ 53 | val newWidth = scale * computator.currentViewport.width() 54 | val newHeight = scale * computator.currentViewport.height() 55 | if (!computator.rawPixelsToDataPoint(focusX, focusY, viewportFocus)) { 56 | // Focus point is not within content area. 57 | return false 58 | } 59 | 60 | val left = viewportFocus.x - (focusX - computator.contentRectMinusAllMargins.left) * (newWidth / computator.contentRectMinusAllMargins.width()) 61 | val top = viewportFocus.y + (focusY - computator.contentRectMinusAllMargins.top) * (newHeight / computator.contentRectMinusAllMargins.height()) 62 | val right = left + newWidth 63 | val bottom = top - newHeight 64 | setCurrentViewport(computator, left, top, right, bottom) 65 | return true 66 | } 67 | 68 | private fun setCurrentViewport(computator: ChartComputator, left: Float, top: Float, right: Float, bottom: Float) { 69 | val currentViewport = computator.currentViewport 70 | when (zoomType ?: return) { 71 | ZoomType.HORIZONTAL -> computator.setCurrentViewport(left, currentViewport.top, right, currentViewport.bottom) 72 | ZoomType.VERTICAL -> computator.setCurrentViewport(currentViewport.left, top, currentViewport.right, bottom) 73 | ZoomType.HORIZONTAL_AND_VERTICAL -> computator.setCurrentViewport(left, top, right, bottom) 74 | } 75 | } 76 | 77 | companion object { 78 | val ZOOM_AMOUNT = 0.25f 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/ContainerScrollType.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.gesture 2 | 3 | /** 4 | * Enum used to inform chart in which type of container it exists. 5 | */ 6 | enum class ContainerScrollType { 7 | HORIZONTAL, VERTICAL 8 | } 9 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/PreviewChartTouchHandler.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.gesture 2 | 3 | import android.content.Context 4 | import android.view.GestureDetector 5 | import android.view.MotionEvent 6 | import android.view.ScaleGestureDetector 7 | 8 | import co.csadev.kellocharts.view.Chart 9 | 10 | /** 11 | * Touch Handler for preview charts. It scroll and zoom only preview area, not all preview chart data. 12 | */ 13 | class PreviewChartTouchHandler(context: Context, chart: Chart) : ChartTouchHandler(context, chart) { 14 | 15 | init { 16 | gestureDetector = GestureDetector(context, PreviewChartGestureListener()) 17 | scaleGestureDetector = ScaleGestureDetector(context, ChartScaleGestureListener()) 18 | 19 | // Disable value touch and selection mode, by default not needed for preview chart. 20 | isValueTouchEnabled = false 21 | isValueSelectionEnabled = false 22 | } 23 | 24 | protected inner class ChartScaleGestureListener : ScaleGestureDetector.SimpleOnScaleGestureListener() { 25 | 26 | override fun onScale(detector: ScaleGestureDetector): Boolean { 27 | if (isZoomEnabled) { 28 | var scale = detector.currentSpan / detector.previousSpan 29 | if (java.lang.Float.isInfinite(scale)) { 30 | scale = 1f 31 | } 32 | return chartZoomer.scale(computator, detector.focusX, detector.focusY, scale) 33 | } 34 | 35 | return false 36 | } 37 | } 38 | 39 | protected inner class PreviewChartGestureListener : ChartTouchHandler.ChartGestureListener() { 40 | 41 | override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean { 42 | return super.onScroll(e1, e2, -distanceX, -distanceY) 43 | } 44 | 45 | override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { 46 | return super.onFling(e1, e2, -velocityX, -velocityY) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/ZoomType.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.gesture 2 | 3 | enum class ZoomType { 4 | HORIZONTAL, VERTICAL, HORIZONTAL_AND_VERTICAL 5 | } 6 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/gesture/ZoomerCompat.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package co.csadev.kellocharts.gesture 18 | 19 | import android.content.Context 20 | import android.os.SystemClock 21 | import android.view.animation.DecelerateInterpolator 22 | import android.view.animation.Interpolator 23 | 24 | /** 25 | * A simple class that animates double-touch zoom gestures. Functionally similar to a [android.widget.Scroller]. 26 | */ 27 | class ZoomerCompat(context: Context) { 28 | /** 29 | * The interpolator, used for making zooms animate 'naturally.' 30 | */ 31 | private val mInterpolator: Interpolator 32 | 33 | /** 34 | * The total animation duration for a zoom. 35 | */ 36 | private val mAnimationDurationMillis: Long 37 | 38 | /** 39 | * Whether or not the current zoom has finished. 40 | */ 41 | private var mFinished = true 42 | 43 | /** 44 | * The current zoom value; computed by [.computeZoom]. 45 | */ 46 | /** 47 | * Returns the current zoom level. 48 | * 49 | * @see android.widget.Scroller.getCurrX 50 | */ 51 | var currZoom: Float = 0.toFloat() 52 | private set 53 | 54 | /** 55 | * The time the zoom started, computed using [SystemClock.elapsedRealtime]. 56 | */ 57 | private var mStartRTC: Long = 0 58 | 59 | /** 60 | * The destination zoom factor. 61 | */ 62 | private var mEndZoom: Float = 0.toFloat() 63 | 64 | init { 65 | mInterpolator = DecelerateInterpolator() 66 | // TODO: use constant 67 | mAnimationDurationMillis = DEFAULT_SHORT_ANIMATION_DURATION.toLong() 68 | } 69 | 70 | /** 71 | * Forces the zoom finished state to the given value. Unlike [.abortAnimation], the current zoom value isn't 72 | * set to the ending value. 73 | * 74 | * @see android.widget.Scroller.forceFinished 75 | */ 76 | fun forceFinished(finished: Boolean) { 77 | mFinished = finished 78 | } 79 | 80 | /** 81 | * Aborts the animation, setting the current zoom value to the ending value. 82 | * 83 | * @see android.widget.Scroller.abortAnimation 84 | */ 85 | fun abortAnimation() { 86 | mFinished = true 87 | currZoom = mEndZoom 88 | } 89 | 90 | /** 91 | * Starts a zoom from 1.0 to (1.0 + endZoom). That is, to zoom from 100% to 125%, endZoom should by 0.25f. 92 | * 93 | * @see android.widget.Scroller.startScroll 94 | */ 95 | fun startZoom(endZoom: Float) { 96 | mStartRTC = SystemClock.elapsedRealtime() 97 | mEndZoom = endZoom 98 | 99 | mFinished = false 100 | currZoom = 1f 101 | } 102 | 103 | /** 104 | * Computes the current zoom level, returning true if the zoom is still active and false if the zoom has finished. 105 | * 106 | * @see android.widget.Scroller.computeScrollOffset 107 | */ 108 | fun computeZoom(): Boolean { 109 | if (mFinished) { 110 | return false 111 | } 112 | 113 | val tRTC = SystemClock.elapsedRealtime() - mStartRTC 114 | if (tRTC >= mAnimationDurationMillis) { 115 | mFinished = true 116 | currZoom = mEndZoom 117 | return false 118 | } 119 | 120 | val t = tRTC * 1f / mAnimationDurationMillis 121 | currZoom = mEndZoom * mInterpolator.getInterpolation(t) 122 | return true 123 | } 124 | 125 | companion object { 126 | private val DEFAULT_SHORT_ANIMATION_DURATION = 200 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/listener/ChartListeners.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.listener 2 | 3 | import co.csadev.kellocharts.model.* 4 | 5 | interface BubbleChartOnValueSelectListener : OnValueDeselectListener { 6 | fun onValueSelected(bubbleIndex: Int, value: BubbleValue) 7 | } 8 | 9 | interface ColumnChartOnValueSelectListener : OnValueDeselectListener { 10 | fun onValueSelected(columnIndex: Int, subcolumnIndex: Int, value: SubcolumnValue) 11 | } 12 | 13 | interface ComboLineColumnChartOnValueSelectListener : OnValueDeselectListener { 14 | fun onColumnValueSelected(columnIndex: Int, subcolumnIndex: Int, value: SubcolumnValue) 15 | fun onPointValueSelected(lineIndex: Int, pointIndex: Int, value: PointValue) 16 | } 17 | 18 | interface LineChartOnValueSelectListener : OnValueDeselectListener { 19 | fun onValueSelected(lineIndex: Int, pointIndex: Int, value: PointValue) 20 | } 21 | 22 | interface OnValueDeselectListener { 23 | fun onValueDeselected() 24 | } 25 | 26 | interface PieChartOnValueSelectListener : OnValueDeselectListener { 27 | fun onValueSelected(arcIndex: Int, value: SliceValue) 28 | } 29 | 30 | interface ViewportChangeListener { 31 | fun onViewportChanged(viewport: Viewport) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/listener/DummyChartListeners.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.listener 2 | 3 | import co.csadev.kellocharts.model.* 4 | 5 | class DummyBubbleChartOnValueSelectListener : BubbleChartOnValueSelectListener { 6 | override fun onValueSelected(bubbleIndex: Int, value: BubbleValue) { } 7 | override fun onValueDeselected() { } 8 | } 9 | 10 | class DummyColumnChartOnValueSelectListener : ColumnChartOnValueSelectListener { 11 | override fun onValueSelected(columnIndex: Int, subcolumnIndex: Int, value: SubcolumnValue) {} 12 | override fun onValueDeselected() {} 13 | } 14 | 15 | 16 | class DummyCompoLineColumnChartOnValueSelectListener : ComboLineColumnChartOnValueSelectListener { 17 | override fun onColumnValueSelected(columnIndex: Int, subcolumnIndex: Int, value: SubcolumnValue) { } 18 | override fun onPointValueSelected(lineIndex: Int, pointIndex: Int, value: PointValue) { } 19 | override fun onValueDeselected() { } 20 | } 21 | 22 | class DummyLineChartOnValueSelectListener : LineChartOnValueSelectListener { 23 | override fun onValueSelected(lineIndex: Int, pointIndex: Int, value: PointValue) { } 24 | override fun onValueDeselected() { } 25 | } 26 | 27 | class DummyPieChartOnValueSelectListener : PieChartOnValueSelectListener { 28 | override fun onValueSelected(arcIndex: Int, value: SliceValue) { } 29 | override fun onValueDeselected() { } 30 | } 31 | 32 | class DummyViewportChangeListener : ViewportChangeListener { 33 | override fun onViewportChanged(viewport: Viewport) { } 34 | } 35 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/AbstractChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import android.graphics.Color 4 | import android.graphics.Typeface 5 | 6 | import co.csadev.kellocharts.util.ChartUtils 7 | 8 | /** 9 | * Base class for most chart data models. 10 | */ 11 | abstract class AbstractChartData : ChartData { 12 | override var axisXBottom: Axis? = null 13 | override var axisYLeft: Axis? = null 14 | override var axisXTop: Axis? = null 15 | override var axisYRight: Axis? = null 16 | override var valueLabelTextColor = Color.WHITE 17 | override var valueLabelTextSize = DEFAULT_TEXT_SIZE_SP 18 | override var valueLabelTypeface: Typeface? = null 19 | 20 | /** 21 | * If true each value label will have background rectangle 22 | */ 23 | override var isValueLabelBackgroundEnabled = true 24 | 25 | /** 26 | * If true and [.isValueLabelBackgroundEnabled] is true each label will have background rectangle and that 27 | * rectangle will be filled with color specified for given value. 28 | */ 29 | override var isValueLabelBackgroundAuto = true 30 | 31 | /** 32 | * If [.isValueLabelBackgroundEnabled] is true and [.isValueLabelBackgrountAuto] is false each label 33 | * will have background rectangle and that rectangle will be filled with this color. Helpful if you want all labels 34 | * to have the same background color. 35 | */ 36 | override var valueLabelBackgroundColor = ChartUtils.darkenColor(ChartUtils.DEFAULT_DARKEN_COLOR) 37 | 38 | fun deepCopy(newItem: AbstractChartData) { 39 | newItem.axisXBottom = axisXBottom 40 | newItem.axisXTop = axisXTop 41 | newItem.axisYLeft = axisYLeft 42 | newItem.axisYRight = axisYRight 43 | newItem.valueLabelTextColor = valueLabelTextColor 44 | newItem.valueLabelTextSize = valueLabelTextSize 45 | newItem.valueLabelTypeface = valueLabelTypeface 46 | } 47 | 48 | fun withData(original: AbstractChartData) : AbstractChartData { 49 | axisXBottom = original.axisXBottom 50 | axisXTop = original.axisXTop 51 | axisYLeft = original.axisYLeft 52 | axisYRight = original.axisYRight 53 | valueLabelTextColor = original.valueLabelTextColor 54 | valueLabelTextSize = original.valueLabelTextSize 55 | valueLabelTypeface = original.valueLabelTypeface 56 | return this 57 | } 58 | 59 | companion object { 60 | val DEFAULT_TEXT_SIZE_SP = 12 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/Axis.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import android.graphics.Color 4 | import android.graphics.Typeface 5 | import co.csadev.kellocharts.formatter.AxisValueFormatter 6 | import co.csadev.kellocharts.formatter.SimpleAxisValueFormatter 7 | import co.csadev.kellocharts.util.ChartUtils 8 | import java.util.* 9 | 10 | /** 11 | * Single axis model. By default axis is auto-generated. Use [.setAutoGenerated] to disable axis values 12 | * generation and set values manually using [.setValues]. If Axis is auto-generated [.setValues] 13 | * will be ignored but if you set some values Axis will switch to manual mode. Change how axis labels are displayed by 14 | * changing formatter [.setFormatter]. Axis can have a name 15 | * that should be displayed next to 16 | * labels(that depends on renderer implementation), you can change name using [.setName], by default axis 17 | * name is empty and therefore not displayed. 18 | */ 19 | class Axis(values: MutableList = ArrayList(), 20 | var name: String? = null, 21 | var isAutoGenerated: Boolean = true, 22 | var hasLines: Boolean = false, 23 | var isInside: Boolean = false, 24 | var textColor: Int = Color.LTGRAY, 25 | var lineColor: Int = ChartUtils.DEFAULT_DARKEN_COLOR, 26 | var textSize: Int = DEFAULT_TEXT_SIZE_SP, 27 | maxLabelChars: Int = DEFAULT_MAX_AXIS_LABEL_CHARS, 28 | var typeface: Typeface? = null, 29 | var formatter: AxisValueFormatter = SimpleAxisValueFormatter(), 30 | var hasSeparationLine: Boolean = true, 31 | var hasTiltedLabels: Boolean = false, 32 | var isReversed: Boolean = false, 33 | var maxLabels: Int = -1) { 34 | 35 | var maxLabelChars: Int = maxLabelChars 36 | set(value) { 37 | field = Math.min(32, Math.max(0, value)) 38 | } 39 | 40 | var values: MutableList = values 41 | get() { 42 | //Copy and transfer to ensure that other value logic stays intact 43 | if (isReversed) { 44 | return field.indices.reversed().mapTo(ArrayList()) { field[it] } 45 | } 46 | return field 47 | } 48 | set(value) { 49 | field = value 50 | this.isAutoGenerated = false 51 | } 52 | 53 | fun copy() = Axis(values.map { it.copy() }.toMutableList(), name, isAutoGenerated, hasLines, isInside, textColor, lineColor, textSize, maxLabelChars, typeface, formatter, hasSeparationLine) 54 | 55 | companion object { 56 | const val DEFAULT_TEXT_SIZE_SP = 12 57 | const val DEFAULT_MAX_AXIS_LABEL_CHARS = 3 58 | 59 | /** 60 | * Generates Axis with values from start to stop inclusive. 61 | */ 62 | fun generateAxisFromRange(start: Float, stop: Float, step: Float): Axis { 63 | 64 | val values = ArrayList() 65 | var value = start 66 | while (value <= stop) { 67 | val axisValue = AxisValue(value) 68 | values.add(axisValue) 69 | value += step 70 | } 71 | 72 | return Axis(values = values) 73 | } 74 | 75 | /** 76 | * Generates Axis with values from given list. 77 | */ 78 | fun generateAxisFromCollection(axisValues: List): Axis { 79 | val values = ArrayList() 80 | var index = 0 81 | for (value in axisValues) { 82 | val axisValue = AxisValue(value) 83 | values.add(axisValue) 84 | ++index 85 | } 86 | 87 | return Axis(values = values) 88 | } 89 | 90 | /** 91 | * Generates Axis with values and labels from given lists, both lists must have the same size. 92 | */ 93 | fun generateAxisFromCollection(axisValues: List, axisValuesLabels: List): Axis { 94 | if (axisValues.size != axisValuesLabels.size) { 95 | throw IllegalArgumentException("Values and labels lists must have the same size!") 96 | } 97 | return Axis(values = axisValues.mapIndexedTo(ArrayList()) { index, value -> AxisValue(value, axisValuesLabels[index].toCharArray()) }) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/AxisValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import java.util.* 4 | 5 | /** 6 | * Single axis value, use it to manually set axis labels position. You can use label attribute to display text instead 7 | * of number but value formatter implementation have to handle it. 8 | */ 9 | class AxisValue(var value: Float = 0f, var label: CharArray? = null) { 10 | constructor(value: Int, label: CharArray? = null) : this(value.toFloat(), label) 11 | constructor(value: Int, label: String) : this(value.toFloat(), label.toCharArray()) 12 | 13 | fun copy() = AxisValue(this.value, this.label) 14 | 15 | override fun equals(other: Any?): Boolean { 16 | if (this === other) return true 17 | if (other == null || javaClass != other.javaClass) return false 18 | 19 | val axisValue = other as AxisValue? 20 | 21 | if (java.lang.Float.compare(axisValue!!.value, value) != 0) return false 22 | return Arrays.equals(label, axisValue.label) 23 | 24 | } 25 | 26 | override fun hashCode(): Int { 27 | var result = if (value != +0.0f) java.lang.Float.floatToIntBits(value) else 0 28 | result = 31 * result + if (label != null) Arrays.hashCode(label) else 0 29 | return result 30 | } 31 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/BubbleChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.formatter.BubbleChartValueFormatter 4 | import co.csadev.kellocharts.formatter.SimpleBubbleChartValueFormatter 5 | import co.csadev.kellocharts.model.dsl.bubbleData 6 | import co.csadev.kellocharts.view.Chart 7 | import java.util.* 8 | 9 | /** 10 | * Data for BubbleChart. 11 | */ 12 | class BubbleChartData(var values: MutableList = ArrayList(), var formatter: BubbleChartValueFormatter = SimpleBubbleChartValueFormatter(), hasLabels: Boolean = false, var hasLabelsOnlyForSelected: Boolean = false, var minBubbleRadius: Int = DEFAULT_MIN_BUBBLE_RADIUS_DP, var bubbleScale: Float = DEFAULT_BUBBLE_SCALE) : AbstractChartData() { 13 | var hasLabels = hasLabels 14 | set(value) { 15 | field = value 16 | if (field) hasLabelsOnlyForSelected = false 17 | } 18 | 19 | fun copy() = BubbleChartData(values.map { it.copy() }.toMutableList(), formatter, hasLabels, hasLabelsOnlyForSelected, minBubbleRadius, bubbleScale) 20 | 21 | override fun update(scale: Float) { 22 | for (value in values) { 23 | value.update(scale) 24 | } 25 | } 26 | 27 | override fun finish() { 28 | for (value in values) { 29 | value.finish() 30 | } 31 | } 32 | 33 | /** 34 | * Set true if you want to show value labels only for selected value, works best when chart has 35 | * isValueSelectionEnabled set to true [Chart.setValueSelectionEnabled]. 36 | */ 37 | fun setHasLabelsOnlyForSelected(hasLabelsOnlyForSelected: Boolean): BubbleChartData { 38 | this.hasLabelsOnlyForSelected = hasLabelsOnlyForSelected 39 | if (hasLabelsOnlyForSelected) { 40 | this.hasLabels = false 41 | } 42 | return this 43 | } 44 | 45 | companion object { 46 | val DEFAULT_MIN_BUBBLE_RADIUS_DP = 6 47 | val DEFAULT_BUBBLE_SCALE = 1f 48 | 49 | fun generateDummyData() = 50 | bubbleData { 51 | bubbles { 52 | bubble { 53 | x = 0f 54 | y = 20f 55 | z = 15000f 56 | } 57 | bubble { 58 | x = 3f 59 | y = 22f 60 | z = 20000f 61 | } 62 | bubble { 63 | x = 5f 64 | y = 25f 65 | z = 5000f 66 | } 67 | bubble { 68 | x = 7f 69 | y = 30f 70 | z = 30000f 71 | } 72 | bubble { 73 | x = 11f 74 | y = 22f 75 | z = 10f 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/BubbleValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.util.ChartUtils 4 | import co.csadev.kellocharts.view.Chart 5 | import java.util.* 6 | 7 | /** 8 | * Single value drawn as bubble on BubbleChart. 9 | */ 10 | class BubbleValue(x: Float = 0f, y: Float = 0f, z: Float = 0f, color: Int = ChartUtils.DEFAULT_COLOR, var label: CharArray? = null, var shape: ValueShape? = ValueShape.CIRCLE) { 11 | /** 12 | * Current X value. 13 | */ 14 | var x: Float = x 15 | private set 16 | /** 17 | * Current Y value. 18 | */ 19 | var y: Float = y 20 | private set 21 | /** 22 | * Current Z value , third bubble value interpreted as bubble area. 23 | */ 24 | var z: Float = z 25 | private set 26 | 27 | var color = color 28 | set(value) { 29 | field = value 30 | darkenColor = ChartUtils.darkenColor(field) 31 | } 32 | var darkenColor = ChartUtils.DEFAULT_DARKEN_COLOR 33 | private set 34 | 35 | /** 36 | * Origin X value, used during value animation. 37 | */ 38 | private var originX: Float = x 39 | /** 40 | * Origin Y value, used during value animation. 41 | */ 42 | private var originY: Float = y 43 | /** 44 | * Origin Z value, used during value animation. 45 | */ 46 | private var originZ: Float = z 47 | 48 | /** 49 | * Difference between originX value and target X value. 50 | */ 51 | private var diffX: Float = x - originX 52 | 53 | /** 54 | * Difference between originX value and target X value. 55 | */ 56 | private var diffY: Float = y - originY 57 | 58 | /** 59 | * Difference between originX value and target X value. 60 | */ 61 | private var diffZ: Float = z - originZ 62 | 63 | fun copy() = BubbleValue(x, y, z, color, label) 64 | 65 | fun update(scale: Float) { 66 | x = originX + diffX * scale 67 | y = originY + diffY * scale 68 | z = originZ + diffZ * scale 69 | } 70 | 71 | fun finish() { 72 | set(originX + diffX, originY + diffY, originZ + diffZ) 73 | } 74 | 75 | operator fun set(x: Float, y: Float, z: Float): BubbleValue { 76 | this.x = x 77 | this.y = y 78 | this.z = z 79 | this.originX = x 80 | this.originY = y 81 | this.originZ = z 82 | this.diffX = 0f 83 | this.diffY = 0f 84 | this.diffZ = 0f 85 | return this 86 | } 87 | 88 | /** 89 | * Set target values that should be reached when data animation finish then call [Chart.startDataAnimation] 90 | */ 91 | fun setTarget(targetX: Float, targetY: Float, targetZ: Float): BubbleValue { 92 | set(x, y, z) 93 | this.diffX = targetX - originX 94 | this.diffY = targetY - originY 95 | this.diffZ = targetZ - originZ 96 | return this 97 | } 98 | 99 | override fun toString(): String { 100 | return "BubbleValue [x=$x, y=$y, z=$z]" 101 | } 102 | 103 | override fun equals(o: Any?): Boolean { 104 | if (this === o) return true 105 | if (o == null || javaClass != o.javaClass) return false 106 | 107 | val that = o as BubbleValue? 108 | 109 | if (color != that!!.color) return false 110 | if (darkenColor != that.darkenColor) return false 111 | if (java.lang.Float.compare(that.diffX, diffX) != 0) return false 112 | if (java.lang.Float.compare(that.diffY, diffY) != 0) return false 113 | if (java.lang.Float.compare(that.diffZ, diffZ) != 0) return false 114 | if (java.lang.Float.compare(that.originX, originX) != 0) return false 115 | if (java.lang.Float.compare(that.originY, originY) != 0) return false 116 | if (java.lang.Float.compare(that.originZ, originZ) != 0) return false 117 | if (java.lang.Float.compare(that.x, x) != 0) return false 118 | if (java.lang.Float.compare(that.y, y) != 0) return false 119 | if (java.lang.Float.compare(that.z, z) != 0) return false 120 | if (!Arrays.equals(label, that.label)) return false 121 | return shape === that.shape 122 | 123 | } 124 | 125 | override fun hashCode(): Int { 126 | var result = if (x != +0.0f) java.lang.Float.floatToIntBits(x) else 0 127 | result = 31 * result + if (y != +0.0f) java.lang.Float.floatToIntBits(y) else 0 128 | result = 31 * result + if (z != +0.0f) java.lang.Float.floatToIntBits(z) else 0 129 | result = 31 * result + if (originX != +0.0f) java.lang.Float.floatToIntBits(originX) else 0 130 | result = 31 * result + if (originY != +0.0f) java.lang.Float.floatToIntBits(originY) else 0 131 | result = 31 * result + if (originZ != +0.0f) java.lang.Float.floatToIntBits(originZ) else 0 132 | result = 31 * result + if (diffX != +0.0f) java.lang.Float.floatToIntBits(diffX) else 0 133 | result = 31 * result + if (diffY != +0.0f) java.lang.Float.floatToIntBits(diffY) else 0 134 | result = 31 * result + if (diffZ != +0.0f) java.lang.Float.floatToIntBits(diffZ) else 0 135 | result = 31 * result + color 136 | result = 31 * result + darkenColor 137 | result = 31 * result + if (shape != null) shape!!.hashCode() else 0 138 | result = 31 * result + if (label != null) Arrays.hashCode(label) else 0 139 | return result 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/ChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import android.graphics.Typeface 4 | 5 | /** 6 | * Base interface for all chart data models. 7 | */ 8 | interface ChartData { 9 | 10 | /** 11 | * @see .setAxisXBottom 12 | */ 13 | /** 14 | * Set horizontal axis at the bottom of the chart. Pass null to remove that axis. 15 | * 16 | * @param axisX 17 | */ 18 | var axisXBottom: Axis? 19 | 20 | /** 21 | * @see .setAxisYLeft 22 | */ 23 | /** 24 | * Set vertical axis on the left of the chart. Pass null to remove that axis. 25 | * 26 | * @param axisY 27 | */ 28 | var axisYLeft: Axis? 29 | 30 | /** 31 | * @see .setAxisXTop 32 | */ 33 | /** 34 | * Set horizontal axis at the top of the chart. Pass null to remove that axis. 35 | * 36 | * @param axisX 37 | */ 38 | var axisXTop: Axis? 39 | 40 | /** 41 | * @see .setAxisYRight 42 | */ 43 | /** 44 | * Set vertical axis on the right of the chart. Pass null to remove that axis. 45 | * 46 | * @param axisY 47 | */ 48 | var axisYRight: Axis? 49 | 50 | /** 51 | * Returns color used to draw value label text. 52 | */ 53 | var valueLabelTextColor: Int 54 | 55 | /** 56 | * Returns text size for value label in SP units. 57 | */ 58 | /** 59 | * Set text size for value label in SP units. 60 | */ 61 | var valueLabelTextSize: Int 62 | 63 | /** 64 | * Returns Typeface for value labels. 65 | * 66 | * @return Typeface or null if Typeface is not set. 67 | */ 68 | /** 69 | * Set Typeface for all values labels. 70 | * 71 | * @param typeface 72 | */ 73 | var valueLabelTypeface: Typeface? 74 | 75 | /** 76 | * @see .setValueLabelBackgroundEnabled 77 | */ 78 | /** 79 | * Set whether labels should have rectangle background. Default is true. 80 | */ 81 | var isValueLabelBackgroundEnabled: Boolean 82 | 83 | /** 84 | * @see .setValueLabelBackgroundAuto 85 | */ 86 | /** 87 | * Set false if you want to set custom color for all value labels. Default is true. 88 | */ 89 | var isValueLabelBackgroundAuto: Boolean 90 | 91 | /** 92 | * @see .setValueLabelBackgroundColor 93 | */ 94 | /** 95 | * Set value labels background. This value is used only if isValueLabelBackgroundAuto returns false. Default is 96 | * green. 97 | */ 98 | var valueLabelBackgroundColor: Int 99 | 100 | /** 101 | * Updates data by scale during animation. 102 | * 103 | * @param scale value from 0 to 1.0 104 | */ 105 | fun update(scale: Float) 106 | 107 | /** 108 | * Inform data that animation finished(data should be update with scale 1.0f). 109 | */ 110 | fun finish() 111 | } 112 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/Column.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.formatter.ColumnChartValueFormatter 4 | import co.csadev.kellocharts.formatter.SimpleColumnChartValueFormatter 5 | import java.util.* 6 | 7 | /** 8 | * Single column for ColumnChart. One column can be divided into multiple sub-columns(ColumnValues) especially for 9 | * stacked ColumnChart. 10 | * Note: you can set X value for columns or sub-columns, columns are by default indexed from 0 to numOfColumns-1 and 11 | * column index is used as column X value, so first column has X value 0, second clumn has X value 1 etc. 12 | * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column. 13 | */ 14 | class Column(var values: MutableList = ArrayList(), hasLabels: Boolean = false, hasLabelsOnlyForSelected: Boolean = false, var formatter: ColumnChartValueFormatter = SimpleColumnChartValueFormatter()) { 15 | var hasLabels: Boolean = hasLabels 16 | set(value) { 17 | field = value 18 | if (field) hasLabelsOnlyForSelected = false 19 | } 20 | 21 | var hasLabelsOnlyForSelected: Boolean = hasLabelsOnlyForSelected 22 | set(value) { 23 | field = value 24 | if (field) hasLabels = false 25 | } 26 | 27 | fun update(scale: Float) = values.forEach { it.update(scale) } 28 | 29 | fun finish() = values.forEach { it.finish() } 30 | 31 | fun copy() = Column(values.map { it.copy() }.toMutableList(), hasLabels, hasLabelsOnlyForSelected, formatter) 32 | } 33 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/ColumnChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.model.dsl.columnData 4 | import java.util.* 5 | 6 | /** 7 | * Data model for column chart. Note: you can set X value for columns or sub-columns, columns are by default indexed 8 | * from 0 to numOfColumns-1 and 9 | * column index is used as column X value, so first column has X value 0, second clumn has X value 1 etc. 10 | * If you want to display AxisValue for given column you should initialize AxisValue with X value of that column. 11 | */ 12 | class ColumnChartData(var columns: MutableList = ArrayList(), var isStacked: Boolean = false, var isHorizontal: Boolean = true) : AbstractChartData() { 13 | var fillRatio = DEFAULT_FILL_RATIO 14 | set(value) { 15 | field = Math.min(1f, Math.max(0f, value)) 16 | } 17 | var baseValue = DEFAULT_BASE_VALUE 18 | 19 | 20 | var _axisXTop: Axis? = null 21 | override var axisXTop: Axis? 22 | get() { 23 | return if (isHorizontal) { 24 | _axisYRight 25 | } else _axisXTop 26 | } 27 | set(value) { _axisXTop = value } 28 | var _axisXBottom: Axis? = null 29 | override var axisXBottom: Axis? 30 | get() { 31 | return if (isHorizontal) { 32 | _axisYLeft 33 | } else _axisXBottom 34 | } 35 | set(value) { _axisXBottom = value } 36 | var _axisYLeft: Axis? = null 37 | override var axisYLeft: Axis? 38 | get() { 39 | return if (isHorizontal) { 40 | _axisXBottom 41 | } else _axisYLeft 42 | } 43 | set(value) { _axisYLeft = value } 44 | var _axisYRight: Axis? = null 45 | override var axisYRight: Axis? 46 | get() { 47 | return if (isHorizontal) { 48 | _axisXTop 49 | } else _axisYRight 50 | } 51 | set(value) { _axisYRight = value } 52 | 53 | fun copy() = ColumnChartData(columns.map { it.copy() }.toMutableList(), isStacked, isHorizontal).withData(this) as ColumnChartData 54 | 55 | override fun update(scale: Float) { 56 | for (column in columns) { 57 | column.update(scale) 58 | } 59 | 60 | } 61 | 62 | override fun finish() = columns.forEach { it.finish() } 63 | 64 | companion object { 65 | const val DEFAULT_FILL_RATIO = 0.75f 66 | const val DEFAULT_BASE_VALUE = 0.0f 67 | 68 | fun generateDummyData() = 69 | columnData { 70 | columns { 71 | column { 72 | columnValues { 73 | subcolumn { 74 | value = 4f 75 | } 76 | subcolumn { 77 | value = 3f 78 | } 79 | subcolumn { 80 | value = 2f 81 | } 82 | subcolumn { 83 | value = 1f 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/ComboLineColumnChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | /** 4 | * Data model for combo line-column chart. It uses ColumnChartData and LineChartData internally. 5 | */ 6 | class ComboLineColumnChartData(var columnChartData: ColumnChartData = ColumnChartData(), var lineChartData: LineChartData = LineChartData()) : AbstractChartData() { 7 | override fun update(scale: Float) { 8 | columnChartData.update(scale) 9 | lineChartData.update(scale) 10 | } 11 | 12 | override fun finish() { 13 | columnChartData.finish() 14 | lineChartData.finish() 15 | } 16 | 17 | companion object { 18 | fun generateDummyData() = ComboLineColumnChartData(ColumnChartData.generateDummyData(), LineChartData.generateDummyData()) 19 | fun fromComboData(data: ComboLineColumnChartData) = ComboLineColumnChartData(data.columnChartData, data.lineChartData).withData(data) as ComboLineColumnChartData 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/Line.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import android.graphics.PathEffect 4 | import co.csadev.kellocharts.formatter.LineChartValueFormatter 5 | import co.csadev.kellocharts.formatter.SimpleLineChartValueFormatter 6 | import co.csadev.kellocharts.util.ChartUtils 7 | import java.util.* 8 | 9 | /** 10 | * Single line for line chart. 11 | */ 12 | class Line(var values: MutableList = ArrayList(), 13 | color: Int = ChartUtils.DEFAULT_COLOR, 14 | pointColor: Int = color, 15 | darkenColor: Int = ChartUtils.darkenColor(color), 16 | var formatter: LineChartValueFormatter = SimpleLineChartValueFormatter(), 17 | var shape: ValueShape = ValueShape.CIRCLE, 18 | var isFilled: Boolean = false, 19 | isSquare: Boolean = false, 20 | isCubic: Boolean = false, 21 | var pointRadius: Int = DEFAULT_POINT_RADIUS_DP, 22 | var areaTransparency: Int = DEFAULT_AREA_TRANSPARENCY, 23 | var strokeWidth: Int = DEFAULT_LINE_STROKE_WIDTH_DP, 24 | var hasPoints: Boolean = true, 25 | var hasLines: Boolean = true, 26 | hasLabels: Boolean = true, 27 | hasLabelsOnlyForSelected: Boolean = true, 28 | var pathEffect: PathEffect? = null) { 29 | var color = color 30 | set(value) { 31 | field = value 32 | if (pointColor == UNINITIALIZED) darkenColor = ChartUtils.darkenColor(field) 33 | } 34 | 35 | var pointColor = pointColor 36 | get() = if (field == UNINITIALIZED) color else field 37 | set(value) { 38 | field = value 39 | darkenColor = ChartUtils.darkenColor(if (field == UNINITIALIZED) color else field) 40 | } 41 | 42 | var darkenColor = darkenColor 43 | private set 44 | 45 | var isSquare = isSquare 46 | set(value) { 47 | field = value 48 | if (field) isCubic = false 49 | } 50 | var isCubic = isCubic 51 | set(value) { 52 | field = value 53 | if (field) isSquare = false 54 | } 55 | 56 | var hasLabels = hasLabels 57 | set(value) { 58 | field = value 59 | if (field) hasLabelsOnlyForSelected = false 60 | } 61 | var hasLabelsOnlyForSelected = hasLabelsOnlyForSelected 62 | set(value) { 63 | field = value 64 | if (field) hasLabels = false 65 | } 66 | 67 | fun copy() = Line(values.map { it.copy() }.toMutableList(), color, pointColor, darkenColor, formatter, shape, isFilled, isSquare, isCubic, pointRadius, areaTransparency, strokeWidth, hasPoints, hasLines, hasLabels, hasLabelsOnlyForSelected, pathEffect) 68 | 69 | fun update(scale: Float) = values.forEach { it.update(scale) } 70 | 71 | fun finish() = values.forEach { it.finish() } 72 | 73 | companion object { 74 | internal val DEFAULT_LINE_STROKE_WIDTH_DP = 3 75 | internal val DEFAULT_POINT_RADIUS_DP = 6 76 | internal val DEFAULT_AREA_TRANSPARENCY = 64 77 | val UNINITIALIZED = 0 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/LineChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.model.dsl.lineData 4 | import java.util.* 5 | 6 | /** 7 | * Data model for LineChartView. 8 | */ 9 | class LineChartData(var lines: MutableList = ArrayList(), var baseValue: Float = DEFAULT_BASE_VALUE) : AbstractChartData() { 10 | 11 | override fun update(scale: Float) { 12 | for (line in lines) { 13 | line.update(scale) 14 | } 15 | } 16 | 17 | override fun finish() { 18 | for (line in lines) { 19 | line.finish() 20 | } 21 | } 22 | 23 | fun copy() = LineChartData(lines.map { it.copy() }.toMutableList(), baseValue).withData(this) as LineChartData 24 | 25 | companion object { 26 | const val DEFAULT_BASE_VALUE = 0.0f 27 | 28 | fun generateDummyData() = 29 | lineData { 30 | lines { 31 | line { 32 | pointValues { 33 | point { 34 | x = 0f 35 | y = 2f 36 | } 37 | point { 38 | x = 1f 39 | y = 4f 40 | } 41 | point { 42 | x = 2f 43 | y = 3f 44 | } 45 | point { 46 | x = 3f 47 | y = 4f 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/PieChartData.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import android.graphics.Color 4 | import android.graphics.Typeface 5 | import co.csadev.kellocharts.formatter.PieChartValueFormatter 6 | import co.csadev.kellocharts.formatter.SimplePieChartValueFormatter 7 | import co.csadev.kellocharts.model.dsl.pieData 8 | import co.csadev.kellocharts.model.dsl.sliceValue 9 | import java.util.* 10 | 11 | /** 12 | * Data for PieChart, by default it doesn't have axes. 13 | */ 14 | class PieChartData(var values: MutableList = ArrayList(), 15 | override var axisXBottom: Axis? = null, 16 | override var axisYLeft: Axis? = null, 17 | hasLabels: Boolean = false, 18 | hasLabelsOnlyForSelected: Boolean = false, 19 | var hasLabelsOutside: Boolean = false, 20 | var hasCenterCircle: Boolean = false, 21 | var centerCircleColor: Int = Color.TRANSPARENT, 22 | var centerCircleScale: Float = DEFAULT_CENTER_CIRCLE_SCALE, 23 | var centerText1Color: Int = Color.BLACK, 24 | var centerText1FontSize: Int = DEFAULT_CENTER_TEXT1_SIZE_SP, 25 | var centerText1Typeface: Typeface? = null, 26 | var centerText1: String? = null, 27 | var centerText2Color: Int = Color.BLACK, 28 | var centerText2FontSize: Int = DEFAULT_CENTER_TEXT2_SIZE_SP, 29 | var centerText2Typeface: Typeface? = null, 30 | var centerText2: String? = null, 31 | var sliceSpacing: Int = DEFAULT_SLICE_SPACING_DP, 32 | var formatter: PieChartValueFormatter = SimplePieChartValueFormatter()) : AbstractChartData() { 33 | var hasLabels = hasLabels 34 | set(value) { 35 | field = value 36 | if (field) hasLabelsOnlyForSelected = false 37 | } 38 | var hasLabelsOnlyForSelected = hasLabelsOnlyForSelected 39 | set(value) { 40 | field = value 41 | if (field) hasLabels = false 42 | } 43 | 44 | override fun update(scale: Float) { 45 | for (value in values) { 46 | value.update(scale) 47 | } 48 | } 49 | 50 | override fun finish() { 51 | for (value in values) { 52 | value.finish() 53 | } 54 | } 55 | 56 | fun copy() = PieChartData(values.map { it.copy() }.toMutableList(), axisXBottom, axisYLeft, hasLabels, hasLabelsOnlyForSelected, hasLabelsOutside, hasCenterCircle, centerCircleColor, centerCircleScale, centerText1Color, centerText1FontSize, centerText1Typeface, centerText1, centerText2Color, centerText2FontSize, centerText2Typeface, centerText2, sliceSpacing, formatter).withData(this) 57 | 58 | companion object { 59 | const val DEFAULT_CENTER_TEXT1_SIZE_SP = 42 60 | const val DEFAULT_CENTER_TEXT2_SIZE_SP = 16 61 | const val DEFAULT_CENTER_CIRCLE_SCALE = 0.6f 62 | internal const val DEFAULT_SLICE_SPACING_DP = 2 63 | 64 | fun generateDummyData() = 65 | pieData { 66 | sliceValues { 67 | sliceValue { 68 | slice { value = 40f } 69 | } 70 | sliceValue { 71 | slice { value = 20f } 72 | } 73 | sliceValue { 74 | slice { value = 30f } 75 | } 76 | sliceValue { 77 | slice { value = 50f } 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/PointValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.view.Chart 4 | import java.util.* 5 | 6 | /** 7 | * Single point coordinates, used for LineChartData. 8 | */ 9 | class PointValue(x: Float = 0f, y: Float = 0f, var label: CharArray? = null) { 10 | constructor(x: Int, y: Int, label: CharArray? = null) : this(x.toFloat(), y.toFloat(), label) 11 | constructor(x: Int, y: Int, label: String) : this(x.toFloat(), y.toFloat(), label.toCharArray()) 12 | constructor(x: Float, y: Float, label: String) : this(x, y, label.toCharArray()) 13 | 14 | var x: Float = x 15 | private set 16 | var y: Float = y 17 | private set 18 | private var originX: Float = x 19 | private var originY: Float = y 20 | private var diffX: Float = x - originX 21 | private var diffY: Float = y - originY 22 | 23 | fun copy() = PointValue(x, y, label) 24 | 25 | fun update(scale: Float) { 26 | x = originX + diffX * scale 27 | y = originY + diffY * scale 28 | } 29 | 30 | fun finish() { 31 | set(originX + diffX, originY + diffY) 32 | } 33 | 34 | operator fun set(x: Float, y: Float): PointValue { 35 | this.x = x 36 | this.y = y 37 | this.originX = x 38 | this.originY = y 39 | this.diffX = 0f 40 | this.diffY = 0f 41 | return this 42 | } 43 | 44 | /** 45 | * Set target values that should be reached when data animation finish then call [Chart.startDataAnimation] 46 | */ 47 | fun setTarget(targetX: Float, targetY: Float): PointValue { 48 | set(x, y) 49 | this.diffX = targetX - originX 50 | this.diffY = targetY - originY 51 | return this 52 | } 53 | 54 | override fun toString(): String { 55 | return "PointValue [x=$x, y=$y]" 56 | } 57 | 58 | override fun equals(other: Any?): Boolean { 59 | if (this === other) return true 60 | if (other == null || javaClass != other.javaClass) return false 61 | 62 | val that = other as PointValue? 63 | 64 | if (that!!.diffX.compareTo(diffX) != 0) return false 65 | if (that.diffY.compareTo(diffY) != 0) return false 66 | if (that.originX.compareTo(originX) != 0) return false 67 | if (that.originY.compareTo(originY) != 0) return false 68 | if (that.x.compareTo(x) != 0) return false 69 | if (that.y.compareTo(y) != 0) return false 70 | return Arrays.equals(label, that.label) 71 | 72 | } 73 | 74 | override fun hashCode(): Int { 75 | var result = if (x != +0.0f) java.lang.Float.floatToIntBits(x) else 0 76 | result = 31 * result + if (y != +0.0f) java.lang.Float.floatToIntBits(y) else 0 77 | result = 31 * result + if (originX != +0.0f) java.lang.Float.floatToIntBits(originX) else 0 78 | result = 31 * result + if (originY != +0.0f) java.lang.Float.floatToIntBits(originY) else 0 79 | result = 31 * result + if (diffX != +0.0f) java.lang.Float.floatToIntBits(diffX) else 0 80 | result = 31 * result + if (diffY != +0.0f) java.lang.Float.floatToIntBits(diffY) else 0 81 | result = 31 * result + if (label != null) Arrays.hashCode(label) else 0 82 | return result 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/SelectedValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | /** 4 | * Holds selected values indexes, i.e. for LineChartModel it will be firstIndex=lineIndex; secondIndex=valueIndex. 5 | */ 6 | class SelectedValue(var firstIndex: Int = 0, var secondIndex: Int = 0, var type: SelectedValueType? = SelectedValueType.NONE) { 7 | 8 | /** 9 | * Return true if selected value have meaningful value. 10 | */ 11 | val isSet: Boolean 12 | get() = firstIndex >= 0 && secondIndex >= 0 13 | 14 | operator fun set(firstIndex: Int, secondIndex: Int, type: SelectedValueType?) { 15 | this.firstIndex = firstIndex 16 | this.secondIndex = secondIndex 17 | if (null != type) { 18 | this.type = type 19 | } else { 20 | this.type = SelectedValueType.NONE 21 | } 22 | } 23 | 24 | fun set(selectedValue: SelectedValue) { 25 | this.firstIndex = selectedValue.firstIndex 26 | this.secondIndex = selectedValue.secondIndex 27 | this.type = selectedValue.type 28 | } 29 | 30 | fun clear() { 31 | set(Integer.MIN_VALUE, Integer.MIN_VALUE, SelectedValueType.NONE) 32 | } 33 | 34 | override fun hashCode(): Int { 35 | val prime = 31 36 | var result = 1 37 | result = prime * result + firstIndex 38 | result = prime * result + secondIndex 39 | result = prime * result + if (type == null) 0 else type!!.hashCode() 40 | return result 41 | } 42 | 43 | override fun equals(other: Any?): Boolean { 44 | if (this === other) 45 | return true 46 | if (other == null) 47 | return false 48 | if (javaClass != other.javaClass) 49 | return false 50 | val other = other as SelectedValue? 51 | if (firstIndex != other!!.firstIndex) 52 | return false 53 | if (secondIndex != other.secondIndex) 54 | return false 55 | return type == other.type 56 | } 57 | 58 | override fun toString(): String { 59 | return "SelectedValue [firstIndex=$firstIndex, secondIndex=$secondIndex, type=$type]" 60 | } 61 | 62 | /** 63 | * Used in combo chart to determine if selected value is used for line or column selection. 64 | */ 65 | enum class SelectedValueType { 66 | NONE, LINE, COLUMN 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/SliceValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.util.ChartUtils 4 | import co.csadev.kellocharts.view.Chart 5 | import java.util.* 6 | 7 | /** 8 | * Model representing single slice on PieChart. 9 | */ 10 | class SliceValue(value: Float = 0f, private var originValue: Float = value, private var diff: Float = 0f, color: Int = ChartUtils.DEFAULT_COLOR, var label: CharArray? = null) { 11 | var darkenColor = ChartUtils.darkenColor(color) 12 | private set 13 | 14 | fun update(scale: Float) { 15 | value = originValue + diff * scale 16 | } 17 | 18 | fun finish() { 19 | value = originValue + diff 20 | } 21 | 22 | var value: Float = value 23 | set(value) { 24 | field = value 25 | this.originValue = value 26 | this.diff = 0f 27 | } 28 | 29 | var color: Int = color 30 | set(value) { 31 | field = value 32 | this.darkenColor = ChartUtils.darkenColor(field) 33 | } 34 | 35 | /** 36 | * Set target value that should be reached when data animation finish then call [Chart.startDataAnimation] 37 | * 38 | * @param target 39 | * @return 40 | */ 41 | fun setTarget(target: Float): SliceValue { 42 | value = value 43 | this.diff = target - originValue 44 | return this 45 | } 46 | 47 | override fun toString(): String { 48 | return "SliceValue [value=$value]" 49 | } 50 | 51 | override fun equals(o: Any?): Boolean { 52 | if (this === o) return true 53 | if (o == null || javaClass != o.javaClass) return false 54 | 55 | val that = o as SliceValue? 56 | 57 | if (color != that!!.color) return false 58 | if (darkenColor != that.darkenColor) return false 59 | if (that.diff.compareTo(diff) != 0) return false 60 | if (that.originValue.compareTo(originValue) != 0) return false 61 | if (that.value.compareTo(value) != 0) return false 62 | return Arrays.equals(label, that.label) 63 | 64 | } 65 | 66 | override fun hashCode(): Int { 67 | var result = if (value != +0.0f) java.lang.Float.floatToIntBits(value) else 0 68 | result = 31 * result + if (originValue != +0.0f) java.lang.Float.floatToIntBits(originValue) else 0 69 | result = 31 * result + if (diff != +0.0f) java.lang.Float.floatToIntBits(diff) else 0 70 | result = 31 * result + color 71 | result = 31 * result + darkenColor 72 | result = 31 * result + if (label != null) Arrays.hashCode(label) else 0 73 | return result 74 | } 75 | 76 | fun copy() = SliceValue(this.value, color = this.color, label = this.label) 77 | } 78 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/SubcolumnValue.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | import co.csadev.kellocharts.util.ChartUtils 4 | import co.csadev.kellocharts.view.Chart 5 | import java.util.* 6 | 7 | /** 8 | * Single sub-column value for ColumnChart. 9 | */ 10 | class SubcolumnValue(value: Float = 0f, color: Int = ChartUtils.DEFAULT_COLOR, var label: CharArray? = null) { 11 | 12 | var value: Float = value 13 | set (value) { 14 | field = value 15 | this.originValue = value 16 | this.diff = 0f 17 | } 18 | private var originValue: Float = 0.toFloat() 19 | private var diff: Float = 0.toFloat() 20 | var color: Int = color 21 | set (value) { 22 | field = value 23 | this.darkenColor = ChartUtils.darkenColor(field) 24 | } 25 | var darkenColor = ChartUtils.darkenColor(color) 26 | private set 27 | 28 | fun update(scale: Float) { 29 | value = originValue + diff * scale 30 | } 31 | 32 | fun finish() { 33 | value = originValue + diff 34 | } 35 | 36 | /** 37 | * Set target value that should be reached when data animation finish then call [Chart.startDataAnimation] 38 | * 39 | * @param target 40 | * @return 41 | */ 42 | fun setTarget(target: Float): SubcolumnValue { 43 | value = value 44 | this.diff = target - originValue 45 | return this 46 | } 47 | 48 | override fun toString(): String { 49 | return "ColumnValue [value=$value]" 50 | } 51 | 52 | override fun equals(other: Any?): Boolean { 53 | if (this === other) return true 54 | if (other == null || javaClass != other.javaClass) return false 55 | 56 | val that = other as SubcolumnValue? 57 | 58 | if (color != that!!.color) return false 59 | if (darkenColor != that.darkenColor) return false 60 | if (java.lang.Float.compare(that.diff, diff) != 0) return false 61 | if (java.lang.Float.compare(that.originValue, originValue) != 0) return false 62 | if (java.lang.Float.compare(that.value, value) != 0) return false 63 | return Arrays.equals(label, that.label) 64 | 65 | } 66 | 67 | override fun hashCode(): Int { 68 | var result = if (value != +0.0f) java.lang.Float.floatToIntBits(value) else 0 69 | result = 31 * result + if (originValue != +0.0f) java.lang.Float.floatToIntBits(originValue) else 0 70 | result = 31 * result + if (diff != +0.0f) java.lang.Float.floatToIntBits(diff) else 0 71 | result = 31 * result + color 72 | result = 31 * result + darkenColor 73 | result = 31 * result + if (label != null) Arrays.hashCode(label) else 0 74 | return result 75 | } 76 | 77 | fun copy() = SubcolumnValue(value, color, label) 78 | } 79 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/ValueShape.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model 2 | 3 | enum class ValueShape { 4 | CIRCLE, SQUARE, DIAMOND 5 | } 6 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/dsl/DataDslProviders.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model.dsl 2 | 3 | import android.graphics.Color 4 | import android.graphics.Typeface 5 | import co.csadev.kellocharts.formatter.BubbleChartValueFormatter 6 | import co.csadev.kellocharts.formatter.PieChartValueFormatter 7 | import co.csadev.kellocharts.formatter.SimpleBubbleChartValueFormatter 8 | import co.csadev.kellocharts.formatter.SimplePieChartValueFormatter 9 | import co.csadev.kellocharts.model.* 10 | import co.csadev.kellocharts.model.BubbleChartData.Companion.DEFAULT_BUBBLE_SCALE 11 | import co.csadev.kellocharts.model.BubbleChartData.Companion.DEFAULT_MIN_BUBBLE_RADIUS_DP 12 | import co.csadev.kellocharts.model.LineChartData.Companion.DEFAULT_BASE_VALUE 13 | import java.util.* 14 | 15 | @DslMarker 16 | annotation class BubbleDataDsl 17 | 18 | fun bubbleData(block: BubbleChartDataBuilder.() -> Unit): BubbleChartData = BubbleChartDataBuilder().apply(block).build() 19 | @BubbleDataDsl 20 | class BubbleChartDataBuilder { 21 | private var values: MutableList = ArrayList() 22 | var formatter: BubbleChartValueFormatter = SimpleBubbleChartValueFormatter() 23 | var hasLabels: Boolean = false 24 | var hasLabelsOnlyForSelected: Boolean = false 25 | var minBubbleRadius: Int = DEFAULT_MIN_BUBBLE_RADIUS_DP 26 | var bubbleScale: Float = DEFAULT_BUBBLE_SCALE 27 | 28 | fun bubbles(block: BUBBLEVALUES.() -> Unit) { 29 | values.addAll(BUBBLEVALUES().apply(block)) 30 | } 31 | 32 | fun build(): BubbleChartData = BubbleChartData( 33 | values, 34 | formatter, 35 | hasLabels, 36 | hasLabelsOnlyForSelected, 37 | minBubbleRadius, 38 | bubbleScale 39 | ) 40 | } 41 | 42 | @DslMarker 43 | annotation class ColumnDataDsl 44 | 45 | fun columnData(block: ColumnChartDataBuilder.() -> Unit): ColumnChartData = ColumnChartDataBuilder().apply(block).build() 46 | @ColumnDataDsl 47 | class ColumnChartDataBuilder { 48 | private var columns: MutableList = ArrayList() 49 | var isStacked: Boolean = false 50 | var isHorizontal: Boolean = true 51 | 52 | fun columns(block: COLUMNS.() -> Unit) { 53 | columns.addAll(COLUMNS().apply(block)) 54 | } 55 | 56 | fun build(): ColumnChartData = ColumnChartData(columns, isStacked, isHorizontal) 57 | } 58 | 59 | @DslMarker 60 | annotation class LineDataDsl 61 | 62 | fun lineData(block: LineChartDataBuilder.() -> Unit): LineChartData = LineChartDataBuilder().apply(block).build() 63 | @LineDataDsl 64 | class LineChartDataBuilder { 65 | private var lines: MutableList = ArrayList() 66 | var baseValue: Float = DEFAULT_BASE_VALUE 67 | 68 | fun lines(block: LINES.() -> Unit) { 69 | lines.addAll(LINES().apply(block)) 70 | } 71 | 72 | fun build(): LineChartData = LineChartData(lines, baseValue) 73 | } 74 | 75 | @DslMarker 76 | annotation class PieDataDsl 77 | 78 | fun pieData(block: PieChartDataBuilder.() -> Unit): PieChartData = PieChartDataBuilder().apply(block).build() 79 | @PieDataDsl 80 | class PieChartDataBuilder { 81 | private var values: MutableList = ArrayList() 82 | var axisXBottom: Axis? = null 83 | var axisYLeft: Axis? = null 84 | var hasLabels: Boolean = false 85 | var hasLabelsOnlyForSelected: Boolean = false 86 | var hasLabelsOutside: Boolean = false 87 | var hasCenterCircle: Boolean = false 88 | var centerCircleColor: Int = Color.TRANSPARENT 89 | var centerCircleScale: Float = PieChartData.DEFAULT_CENTER_CIRCLE_SCALE 90 | var centerText1Color: Int = Color.BLACK 91 | var centerText1FontSize: Int = PieChartData.DEFAULT_CENTER_TEXT1_SIZE_SP 92 | var centerText1Typeface: Typeface? = null 93 | var centerText1: String? = null 94 | var centerText2Color: Int = Color.BLACK 95 | var centerText2FontSize: Int = PieChartData.DEFAULT_CENTER_TEXT2_SIZE_SP 96 | var centerText2Typeface: Typeface? = null 97 | var centerText2: String? = null 98 | var sliceSpacing: Int = PieChartData.DEFAULT_SLICE_SPACING_DP 99 | var formatter: PieChartValueFormatter = SimplePieChartValueFormatter() 100 | 101 | fun sliceValues(block: SLICEVALUES.() -> Unit) { 102 | values.addAll(SLICEVALUES().apply(block)) 103 | } 104 | 105 | fun build(): PieChartData = PieChartData( 106 | values, 107 | axisXBottom, 108 | axisYLeft, 109 | hasLabels, 110 | hasLabelsOnlyForSelected, 111 | hasLabelsOutside, 112 | hasCenterCircle, 113 | centerCircleColor, 114 | centerCircleScale, 115 | centerText1Color, 116 | centerText1FontSize, 117 | centerText1Typeface, 118 | centerText1, 119 | centerText2Color, 120 | centerText2FontSize, 121 | centerText2Typeface, 122 | centerText2, 123 | sliceSpacing, 124 | formatter 125 | ) 126 | } 127 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/dsl/LayoutDslProviders.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model.dsl 2 | 3 | import android.graphics.Color 4 | import android.graphics.PathEffect 5 | import android.graphics.Typeface 6 | import co.csadev.kellocharts.formatter.* 7 | import co.csadev.kellocharts.model.* 8 | import co.csadev.kellocharts.model.Axis.Companion.DEFAULT_MAX_AXIS_LABEL_CHARS 9 | import co.csadev.kellocharts.util.ChartUtils 10 | import java.util.* 11 | 12 | @DslMarker 13 | annotation class AxisDsl 14 | 15 | fun axis(block: AxisBuilder.() -> Unit): Axis = AxisBuilder().apply(block).build() 16 | @AxisDsl 17 | class AxisBuilder { 18 | private var values: MutableList = ArrayList() 19 | var name: String? = null 20 | var isAutoGenerated: Boolean = true 21 | var hasLines: Boolean = false 22 | var isInside: Boolean = false 23 | var textColor: Int = Color.LTGRAY 24 | var lineColor: Int = ChartUtils.DEFAULT_DARKEN_COLOR 25 | var textSize: Int = Axis.DEFAULT_TEXT_SIZE_SP 26 | var maxLabelChars: Int = DEFAULT_MAX_AXIS_LABEL_CHARS 27 | var typeface: Typeface? = null 28 | var formatter: AxisValueFormatter = SimpleAxisValueFormatter() 29 | var hasSeparationLine: Boolean = true 30 | var hasTiltedLabels: Boolean = false 31 | var isReversed: Boolean = false 32 | var maxLabels: Int = -1 33 | 34 | fun values(block: AXISVALUES.() -> Unit) { 35 | values.addAll(AXISVALUES().apply(block)) 36 | } 37 | 38 | fun build(): Axis = Axis( 39 | values, 40 | name, 41 | isAutoGenerated, 42 | hasLines, 43 | isInside, 44 | textColor, 45 | lineColor, 46 | textSize, 47 | maxLabelChars, 48 | typeface, 49 | formatter, 50 | hasSeparationLine, 51 | hasTiltedLabels, 52 | isReversed, 53 | maxLabels 54 | ) 55 | } 56 | 57 | @DslMarker 58 | annotation class ColumnDsl 59 | 60 | fun column(block: ColumnBuilder.() -> Unit): Column = ColumnBuilder().apply(block).build() 61 | class ColumnBuilder { 62 | private var values: MutableList = ArrayList() 63 | var hasLabels: Boolean = false 64 | var hasLabelsOnlyForSelected: Boolean = false 65 | var formatter: ColumnChartValueFormatter = SimpleColumnChartValueFormatter() 66 | 67 | fun columnValues(block: SUBCOLUMNVALUES.() -> Unit) { 68 | values.addAll(SUBCOLUMNVALUES().apply(block)) 69 | } 70 | 71 | fun build(): Column = Column(values, hasLabels, hasLabelsOnlyForSelected, formatter) 72 | } 73 | @ColumnDsl 74 | class COLUMNS: ArrayList() { 75 | fun column(block: ColumnBuilder.() -> Unit) { 76 | add(ColumnBuilder().apply(block).build()) 77 | } 78 | } 79 | 80 | @DslMarker 81 | annotation class LineDsl 82 | 83 | fun line(block: LineBuilder.() -> Unit): Line = LineBuilder().apply(block).build() 84 | @LineDsl 85 | class LineBuilder { 86 | 87 | private var values: MutableList = ArrayList() 88 | 89 | var color: Int = ChartUtils.DEFAULT_COLOR 90 | var pointColor: Int = color 91 | var darkenColor: Int = ChartUtils.darkenColor(color) 92 | var formatter: LineChartValueFormatter = SimpleLineChartValueFormatter() 93 | var shape: ValueShape = ValueShape.CIRCLE 94 | var isFilled: Boolean = false 95 | var isSquare: Boolean = false 96 | var isCubic: Boolean = false 97 | var pointRadius: Int = Line.DEFAULT_POINT_RADIUS_DP 98 | var areaTransparency: Int = Line.DEFAULT_AREA_TRANSPARENCY 99 | var strokeWidth: Int = Line.DEFAULT_LINE_STROKE_WIDTH_DP 100 | var hasPoints: Boolean = true 101 | var hasLines: Boolean = true 102 | var hasLabels: Boolean = true 103 | var hasLabelsOnlyForSelected: Boolean = true 104 | var pathEffect: PathEffect? = null 105 | 106 | fun pointValues(block: POINTVALUES.() -> Unit) { 107 | values.addAll(POINTVALUES().apply(block)) 108 | } 109 | 110 | fun build(): Line = Line( 111 | values, 112 | color, 113 | pointColor, 114 | darkenColor, 115 | formatter, 116 | shape, 117 | isFilled, 118 | isSquare, 119 | isCubic, 120 | pointRadius, 121 | areaTransparency, 122 | strokeWidth, 123 | hasPoints, 124 | hasLines, 125 | hasLabels, 126 | hasLabelsOnlyForSelected, 127 | pathEffect 128 | ) 129 | } 130 | @LineDsl 131 | class LINES: ArrayList() { 132 | fun line(block: LineBuilder.() -> Unit) { 133 | add(LineBuilder().apply(block).build()) 134 | } 135 | } 136 | 137 | fun viewport(block: Viewport.() -> Unit): Viewport = Viewport().apply(block) 138 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/model/dsl/ValueDslProviders.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.model.dsl 2 | 3 | import co.csadev.kellocharts.model.* 4 | import co.csadev.kellocharts.util.ChartUtils 5 | 6 | @DslMarker 7 | annotation class AxisValueDsl 8 | 9 | @AxisValueDsl 10 | class AXISVALUES: ArrayList() { 11 | fun axis(block: AxisValue.() -> Unit) { 12 | add(AxisValue().apply(block)) 13 | } 14 | } 15 | 16 | @DslMarker 17 | annotation class BubbleValueDsl 18 | 19 | @BubbleValueDsl 20 | class BubbleValueBuilder { 21 | var x: Float = 0f 22 | var y: Float = 0f 23 | var z: Float = 0f 24 | var color: Int = ChartUtils.DEFAULT_COLOR 25 | var label: CharArray? = null 26 | var shape: ValueShape? = ValueShape.CIRCLE 27 | 28 | fun build() = BubbleValue(x, y, z, color, label, shape) 29 | } 30 | @BubbleValueDsl 31 | class BUBBLEVALUES: ArrayList() { 32 | fun bubble(block: BubbleValueBuilder.() -> Unit) { 33 | add(BubbleValueBuilder().apply(block).build()) 34 | } 35 | } 36 | 37 | @DslMarker 38 | annotation class PointValueDsl 39 | 40 | @PointValueDsl 41 | class PointValueBuilder { 42 | var x: Float = 0f 43 | var y: Float = 0f 44 | var label: CharArray? = null 45 | 46 | fun build() = PointValue(x, y, label) 47 | } 48 | @PointValueDsl 49 | class POINTVALUES: ArrayList() { 50 | fun point(block: PointValueBuilder.() -> Unit) { 51 | add(PointValueBuilder().apply(block).build()) 52 | } 53 | } 54 | 55 | @DslMarker 56 | annotation class SelectedValueDsl 57 | 58 | @SelectedValueDsl 59 | class SELECTEDVALUES: ArrayList() { 60 | fun point(block: SelectedValue.() -> Unit) { 61 | add(SelectedValue().apply(block)) 62 | } 63 | } 64 | 65 | @DslMarker 66 | annotation class SliceValueDsl 67 | 68 | fun sliceValue(block: SliceValue.() -> Unit): SliceValue = SliceValue().apply(block) 69 | @SliceValueDsl 70 | class SLICEVALUES: ArrayList() { 71 | fun slice(block: SliceValue.() -> Unit) { 72 | add(SliceValue().apply(block)) 73 | } 74 | } 75 | 76 | @DslMarker 77 | annotation class SubcolumnValueDsl 78 | 79 | @SubcolumnValueDsl 80 | class SUBCOLUMNVALUES: ArrayList() { 81 | fun subcolumn(block: SubcolumnValue.() -> Unit) { 82 | add(SubcolumnValue().apply(block)) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/provider/ChartDataProviders.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.provider 2 | 3 | import co.csadev.kellocharts.model.* 4 | 5 | interface BubbleChartDataProvider { 6 | var bubbleChartData: BubbleChartData 7 | } 8 | 9 | interface ColumnChartDataProvider { 10 | var columnChartData: ColumnChartData 11 | } 12 | 13 | interface ComboLineColumnChartDataProvider { 14 | var comboLineColumnChartData: ComboLineColumnChartData 15 | } 16 | 17 | interface LineChartDataProvider { 18 | var lineChartData: LineChartData 19 | } 20 | 21 | interface PieChartDataProvider { 22 | var pieChartData: PieChartData 23 | } 24 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/AbstractChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.content.Context 4 | import android.graphics.* 5 | import android.graphics.Paint.Align 6 | import android.graphics.Paint.FontMetricsInt 7 | import co.csadev.kellocharts.computator.ChartComputator 8 | import co.csadev.kellocharts.model.SelectedValue 9 | import co.csadev.kellocharts.model.Viewport 10 | import co.csadev.kellocharts.util.ChartUtils 11 | import co.csadev.kellocharts.view.Chart 12 | 13 | /** 14 | * Abstract renderer implementation, every chart renderer extends this class(although it is not required it helps). 15 | */ 16 | abstract class AbstractChartRenderer(context: Context, protected var chart: Chart) : ChartRenderer { 17 | var DEFAULT_LABEL_MARGIN_DP = 4 18 | protected var computator: ChartComputator 19 | /** 20 | * Paint for value labels. 21 | */ 22 | protected var labelPaint = Paint() 23 | /** 24 | * Paint for labels background. 25 | */ 26 | protected var labelBackgroundPaint = Paint() 27 | /** 28 | * Holds coordinates for label background rect. 29 | */ 30 | protected var labelBackgroundRect = RectF() 31 | /** 32 | * Font metrics for label paint, used to determine text height. 33 | */ 34 | protected var fontMetrics = FontMetricsInt() 35 | /** 36 | * If true maximum and current viewport will be calculated when chart data change or during data animations. 37 | */ 38 | override var isViewportCalculationEnabled = true 39 | protected var density: Float = 0.toFloat() 40 | protected var scaledDensity: Float = 0.toFloat() 41 | override var selectedValue = SelectedValue() 42 | protected var labelBuffer = CharArray(64) 43 | protected var labelOffset: Int = 0 44 | protected var labelMargin: Int = 0 45 | protected var isValueLabelBackgroundEnabled: Boolean = false 46 | protected var isValueLabelBackgroundAuto: Boolean = false 47 | override val isTouched: Boolean 48 | get() = selectedValue.isSet 49 | override var currentViewport: Viewport 50 | get() = computator.currentViewport 51 | set(value) { computator.currentViewport = value } 52 | override var maximumViewport: Viewport 53 | get() = computator.maximumViewport 54 | set(value) { computator.maximumViewport = value } 55 | 56 | init { 57 | this.density = context.resources.displayMetrics.density 58 | this.scaledDensity = context.resources.displayMetrics.scaledDensity 59 | this.computator = chart.chartComputator 60 | 61 | labelMargin = ChartUtils.dp2px(density, DEFAULT_LABEL_MARGIN_DP) 62 | labelOffset = labelMargin 63 | 64 | labelPaint.isAntiAlias = true 65 | labelPaint.style = Paint.Style.FILL 66 | labelPaint.textAlign = Align.LEFT 67 | labelPaint.typeface = Typeface.defaultFromStyle(Typeface.BOLD) 68 | labelPaint.color = Color.WHITE 69 | 70 | labelBackgroundPaint.isAntiAlias = true 71 | labelBackgroundPaint.style = Paint.Style.FILL 72 | } 73 | 74 | override fun resetRenderer() { 75 | this.computator = chart.chartComputator 76 | } 77 | 78 | override fun onChartDataChanged() { 79 | val data = chart.chartData 80 | 81 | val typeface = chart.chartData.valueLabelTypeface 82 | if (null != typeface) { 83 | labelPaint.typeface = typeface 84 | } 85 | 86 | labelPaint.color = data.valueLabelTextColor 87 | labelPaint.textSize = ChartUtils.sp2px(scaledDensity, data.valueLabelTextSize).toFloat() 88 | labelPaint.getFontMetricsInt(fontMetrics) 89 | 90 | this.isValueLabelBackgroundEnabled = data.isValueLabelBackgroundEnabled 91 | this.isValueLabelBackgroundAuto = data.isValueLabelBackgroundAuto 92 | this.labelBackgroundPaint.color = data.valueLabelBackgroundColor 93 | 94 | // Important - clear selection when data changed. 95 | selectedValue.clear() 96 | 97 | } 98 | 99 | /** 100 | * Draws label text and label background if isValueLabelBackgroundEnabled is true. 101 | */ 102 | protected fun drawLabelTextAndBackground(canvas: Canvas, labelBuffer: CharArray, startIndex: Int, numChars: Int, 103 | autoBackgroundColor: Int) { 104 | val textX: Float 105 | val textY: Float 106 | 107 | if (isValueLabelBackgroundEnabled) { 108 | 109 | if (isValueLabelBackgroundAuto) { 110 | labelBackgroundPaint.color = autoBackgroundColor 111 | } 112 | 113 | canvas.drawRect(labelBackgroundRect, labelBackgroundPaint) 114 | 115 | textX = labelBackgroundRect.left + labelMargin 116 | textY = labelBackgroundRect.bottom - labelMargin 117 | } else { 118 | textX = labelBackgroundRect.left 119 | textY = labelBackgroundRect.bottom 120 | } 121 | 122 | canvas.drawText(labelBuffer, startIndex, numChars, textX, textY, labelPaint) 123 | } 124 | 125 | override fun clearTouch() { 126 | selectedValue.clear() 127 | } 128 | 129 | } 130 | 131 | class InternalChartRendererBase(context: Context, chart: Chart) : AbstractChartRenderer(context, chart) { 132 | override fun onChartSizeChanged() { } 133 | override fun onChartViewportChanged() { } 134 | override fun draw(canvas: Canvas) { } 135 | override fun drawUnclipped(canvas: Canvas) { } 136 | override fun checkTouch(touchX: Float, touchY: Float): Boolean { return false } 137 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/ChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.graphics.Canvas 4 | 5 | import co.csadev.kellocharts.model.SelectedValue 6 | import co.csadev.kellocharts.model.Viewport 7 | 8 | /** 9 | * Interface for all chart renderer. 10 | */ 11 | interface ChartRenderer { 12 | 13 | /** 14 | * Returns true if there is value selected. 15 | */ 16 | val isTouched: Boolean 17 | 18 | var maximumViewport: Viewport 19 | 20 | var currentViewport: Viewport 21 | 22 | var isViewportCalculationEnabled: Boolean 23 | 24 | var selectedValue: SelectedValue 25 | 26 | fun onChartSizeChanged() 27 | 28 | fun onChartDataChanged() 29 | 30 | fun onChartViewportChanged() 31 | 32 | fun resetRenderer() 33 | 34 | /** 35 | * Draw chart data. 36 | */ 37 | fun draw(canvas: Canvas) 38 | 39 | /** 40 | * Draw chart data that should not be clipped to contentRect area. 41 | */ 42 | fun drawUnclipped(canvas: Canvas) 43 | 44 | /** 45 | * Checks if given pixel coordinates corresponds to any chart value. If yes return true and set selectedValue, if 46 | * not selectedValue should be *cleared* and method should return false. 47 | */ 48 | fun checkTouch(touchX: Float, touchY: Float): Boolean 49 | 50 | /** 51 | * Clear value selection. 52 | */ 53 | fun clearTouch() 54 | } 55 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/ComboChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import co.csadev.kellocharts.model.Viewport 6 | import co.csadev.kellocharts.model.set 7 | import co.csadev.kellocharts.view.Chart 8 | import java.util.* 9 | 10 | open class ComboChartRenderer(context: Context, chart: Chart) : AbstractChartRenderer(context, chart) { 11 | 12 | internal var renderers: MutableList = ArrayList() 13 | protected var unionViewport = Viewport() 14 | 15 | override fun onChartSizeChanged() { 16 | for (renderer in renderers) { 17 | renderer.onChartSizeChanged() 18 | } 19 | } 20 | 21 | override fun onChartDataChanged() { 22 | super.onChartDataChanged() 23 | for (renderer in renderers) { 24 | renderer.onChartDataChanged() 25 | } 26 | onChartViewportChanged() 27 | } 28 | 29 | override fun onChartViewportChanged() { 30 | if (isViewportCalculationEnabled) { 31 | var rendererIndex = 0 32 | for (renderer in renderers) { 33 | renderer.onChartViewportChanged() 34 | if (rendererIndex == 0) { 35 | unionViewport.set(renderer.maximumViewport) 36 | } else { 37 | unionViewport.union(renderer.maximumViewport) 38 | } 39 | ++rendererIndex 40 | } 41 | computator.maximumViewport = unionViewport 42 | computator.currentViewport = unionViewport 43 | } 44 | 45 | 46 | } 47 | 48 | override fun draw(canvas: Canvas) { 49 | for (renderer in renderers) { 50 | renderer.draw(canvas) 51 | } 52 | } 53 | 54 | override fun drawUnclipped(canvas: Canvas) { 55 | for (renderer in renderers) { 56 | renderer.drawUnclipped(canvas) 57 | } 58 | } 59 | 60 | override fun checkTouch(touchX: Float, touchY: Float): Boolean { 61 | selectedValue.clear() 62 | var rendererIndex = renderers.size - 1 63 | while (rendererIndex >= 0) { 64 | val renderer = renderers[rendererIndex] 65 | if (renderer.checkTouch(touchX, touchY)) { 66 | selectedValue.set(renderer.selectedValue) 67 | break 68 | } 69 | rendererIndex-- 70 | } 71 | 72 | //clear the rest of renderers if value was selected, if value was not selected this loop 73 | // will not be executed. 74 | rendererIndex-- 75 | while (rendererIndex >= 0) { 76 | val renderer = renderers[rendererIndex] 77 | renderer.clearTouch() 78 | rendererIndex-- 79 | } 80 | 81 | return isTouched 82 | } 83 | 84 | override fun clearTouch() { 85 | for (renderer in renderers) { 86 | renderer.clearTouch() 87 | } 88 | selectedValue.clear() 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/ComboLineColumnChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.content.Context 4 | 5 | import co.csadev.kellocharts.provider.ColumnChartDataProvider 6 | import co.csadev.kellocharts.provider.LineChartDataProvider 7 | import co.csadev.kellocharts.view.Chart 8 | 9 | class ComboLineColumnChartRenderer(context: Context, chart: Chart, private val columnChartRenderer: ColumnChartRenderer, 10 | private val lineChartRenderer: LineChartRenderer) : ComboChartRenderer(context, chart) { 11 | 12 | constructor(context: Context, chart: Chart, columnChartDataProvider: ColumnChartDataProvider, lineChartDataProvider: LineChartDataProvider) 13 | : this(context, chart, ColumnChartRenderer(context, chart, columnChartDataProvider), LineChartRenderer(context, chart, lineChartDataProvider)) 14 | 15 | constructor(context: Context, chart: Chart, columnChartRenderer: ColumnChartRenderer, lineChartDataProvider: LineChartDataProvider) 16 | : this(context, chart, columnChartRenderer, LineChartRenderer(context, chart, lineChartDataProvider)) 17 | 18 | constructor(context: Context, chart: Chart, columnChartDataProvider: ColumnChartDataProvider, lineChartRenderer: LineChartRenderer) 19 | : this(context, chart, ColumnChartRenderer(context, chart, columnChartDataProvider), lineChartRenderer) 20 | 21 | init { 22 | renderers.add(this.columnChartRenderer) 23 | renderers.add(this.lineChartRenderer) 24 | } 25 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/PreviewColumnChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.graphics.Color 6 | import android.graphics.Paint 7 | import co.csadev.kellocharts.provider.ColumnChartDataProvider 8 | import co.csadev.kellocharts.util.ChartUtils 9 | import co.csadev.kellocharts.view.Chart 10 | 11 | /** 12 | * Renderer for preview chart based on ColumnChart. In addition to drawing chart data it also draw current viewport as 13 | * preview area. 14 | */ 15 | class PreviewColumnChartRenderer(context: Context, chart: Chart, dataProvider: ColumnChartDataProvider) : ColumnChartRenderer(context, chart, dataProvider) { 16 | 17 | private val previewPaint = Paint() 18 | 19 | var previewColor: Int 20 | get() = previewPaint.color 21 | set(color) { 22 | previewPaint.color = color 23 | } 24 | 25 | init { 26 | previewPaint.isAntiAlias = true 27 | previewPaint.color = Color.LTGRAY 28 | previewPaint.strokeWidth = ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP).toFloat() 29 | } 30 | 31 | override fun drawUnclipped(canvas: Canvas) { 32 | super.drawUnclipped(canvas) 33 | val currentViewport = computator.currentViewport 34 | val left = computator.computeRawX(currentViewport.left) 35 | val top = computator.computeRawY(currentViewport.top) 36 | val right = computator.computeRawX(currentViewport.right) 37 | val bottom = computator.computeRawY(currentViewport.bottom) 38 | previewPaint.alpha = DEFAULT_PREVIEW_TRANSPARENCY 39 | previewPaint.style = Paint.Style.FILL 40 | canvas.drawRect(left, top, right, bottom, previewPaint) 41 | previewPaint.style = Paint.Style.STROKE 42 | previewPaint.alpha = FULL_ALPHA 43 | canvas.drawRect(left, top, right, bottom, previewPaint) 44 | } 45 | 46 | companion object { 47 | private val DEFAULT_PREVIEW_TRANSPARENCY = 64 48 | private val FULL_ALPHA = 255 49 | private val DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/renderer/PreviewLineChartRenderer.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.renderer 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.graphics.Color 6 | import android.graphics.Paint 7 | import co.csadev.kellocharts.provider.LineChartDataProvider 8 | import co.csadev.kellocharts.util.ChartUtils 9 | import co.csadev.kellocharts.view.Chart 10 | 11 | /** 12 | * Renderer for preview chart based on LineChart. In addition to drawing chart data it also draw current viewport as 13 | * preview area. 14 | */ 15 | class PreviewLineChartRenderer(context: Context, chart: Chart, dataProvider: LineChartDataProvider) : LineChartRenderer(context, chart, dataProvider) { 16 | 17 | private val previewPaint = Paint() 18 | 19 | var previewColor: Int 20 | get() = previewPaint.color 21 | set(color) { 22 | previewPaint.color = color 23 | } 24 | 25 | init { 26 | previewPaint.isAntiAlias = true 27 | previewPaint.color = Color.LTGRAY 28 | previewPaint.strokeWidth = ChartUtils.dp2px(density, DEFAULT_PREVIEW_STROKE_WIDTH_DP).toFloat() 29 | } 30 | 31 | override fun drawUnclipped(canvas: Canvas) { 32 | super.drawUnclipped(canvas) 33 | val currentViewport = computator.currentViewport 34 | val left = computator.computeRawX(currentViewport.left) 35 | val top = computator.computeRawY(currentViewport.top) 36 | val right = computator.computeRawX(currentViewport.right) 37 | val bottom = computator.computeRawY(currentViewport.bottom) 38 | previewPaint.alpha = DEFAULT_PREVIEW_TRANSPARENCY 39 | previewPaint.style = Paint.Style.FILL 40 | canvas.drawRect(left, top, right, bottom, previewPaint) 41 | previewPaint.style = Paint.Style.STROKE 42 | previewPaint.alpha = FULL_ALPHA 43 | canvas.drawRect(left, top, right, bottom, previewPaint) 44 | } 45 | 46 | companion object { 47 | private val DEFAULT_PREVIEW_TRANSPARENCY = 64 48 | private val FULL_ALPHA = 255 49 | private val DEFAULT_PREVIEW_STROKE_WIDTH_DP = 2 50 | } 51 | } -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/util/AxisAutoValues.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.util 2 | 3 | /** 4 | * A simple class representing axis label values used only for auto generated axes. 5 | */ 6 | class AxisAutoValues { 7 | var values = floatArrayOf() 8 | var valuesNumber: Int = 0 9 | var decimals: Int = 0 10 | } 11 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/util/ChartUtils.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.util 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.util.TypedValue 6 | 7 | object ChartUtils { 8 | 9 | val DEFAULT_COLOR = Color.parseColor("#DFDFDF") 10 | val DEFAULT_DARKEN_COLOR = Color.parseColor("#DDDDDD") 11 | val COLOR_BLUE = Color.parseColor("#33B5E5") 12 | val COLOR_VIOLET = Color.parseColor("#AA66CC") 13 | val COLOR_GREEN = Color.parseColor("#99CC00") 14 | val COLOR_ORANGE = Color.parseColor("#FFBB33") 15 | val COLOR_RED = Color.parseColor("#FF4444") 16 | val COLORS = intArrayOf(COLOR_BLUE, COLOR_VIOLET, COLOR_GREEN, COLOR_ORANGE, COLOR_RED) 17 | private val DARKEN_SATURATION = 1.1f 18 | private val DARKEN_INTENSITY = 0.9f 19 | private var COLOR_INDEX = 0 20 | 21 | fun pickColor(): Int { 22 | return COLORS[Math.round(Math.random() * (COLORS.size - 1)).toInt()] 23 | } 24 | 25 | fun nextColor(): Int { 26 | if (COLOR_INDEX >= COLORS.size) { 27 | COLOR_INDEX = 0 28 | } 29 | return COLORS[COLOR_INDEX++] 30 | } 31 | 32 | fun dp2px(density: Float, dp: Int): Int { 33 | return if (dp == 0) { 34 | 0 35 | } else (dp * density + 0.5f).toInt() 36 | 37 | } 38 | 39 | fun px2dp(density: Float, px: Int): Int { 40 | return Math.ceil((px / density).toDouble()).toInt() 41 | } 42 | 43 | fun sp2px(scaledDensity: Float, sp: Int): Int { 44 | return if (sp == 0) { 45 | 0 46 | } else (sp * scaledDensity + 0.5f).toInt() 47 | } 48 | 49 | fun px2sp(scaledDensity: Float, px: Int): Int { 50 | return Math.ceil((px / scaledDensity).toDouble()).toInt() 51 | } 52 | 53 | fun mm2px(context: Context, mm: Int): Int { 54 | return (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, mm.toFloat(), context.resources 55 | .displayMetrics) + 0.5f).toInt() 56 | } 57 | 58 | fun darkenColor(color: Int): Int { 59 | val hsv = FloatArray(3) 60 | val alpha = Color.alpha(color) 61 | Color.colorToHSV(color, hsv) 62 | hsv[1] = Math.min(hsv[1] * DARKEN_SATURATION, 1.0f) 63 | hsv[2] = hsv[2] * DARKEN_INTENSITY 64 | val tempColor = Color.HSVToColor(hsv) 65 | return Color.argb(alpha, Color.red(tempColor), Color.green(tempColor), Color.blue(tempColor)) 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/BubbleChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import androidx.core.view.ViewCompat 7 | import co.csadev.kellocharts.BuildConfig 8 | import co.csadev.kellocharts.listener.BubbleChartOnValueSelectListener 9 | import co.csadev.kellocharts.listener.DummyBubbleChartOnValueSelectListener 10 | import co.csadev.kellocharts.model.BubbleChartData 11 | import co.csadev.kellocharts.model.ChartData 12 | import co.csadev.kellocharts.provider.BubbleChartDataProvider 13 | import co.csadev.kellocharts.renderer.BubbleChartRenderer 14 | 15 | /** 16 | * BubbleChart, supports circle bubbles and square bubbles. 17 | * 18 | * @author lecho 19 | */ 20 | class BubbleChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : AbstractChartView(context, attrs, defStyle), BubbleChartDataProvider { 21 | var onValueTouchListener: BubbleChartOnValueSelectListener = DummyBubbleChartOnValueSelectListener() 22 | 23 | var bubbleChartRenderer: BubbleChartRenderer = BubbleChartRenderer(context, this, this) 24 | 25 | override var bubbleChartData: BubbleChartData = BubbleChartData.generateDummyData() 26 | set(value) { 27 | if (BuildConfig.DEBUG) { 28 | Log.d(TAG, "Setting data for BubbleChartView") 29 | } 30 | field = value 31 | super.onChartDataChange() 32 | } 33 | 34 | override val chartData: ChartData 35 | get() = bubbleChartData 36 | 37 | init { 38 | chartRenderer = bubbleChartRenderer 39 | bubbleChartData = BubbleChartData.generateDummyData() 40 | } 41 | 42 | override fun callTouchListener() { 43 | val selectedValue = chartRenderer.selectedValue 44 | 45 | if (selectedValue.isSet) { 46 | val value = bubbleChartData.values[selectedValue.firstIndex] 47 | onValueTouchListener.onValueSelected(selectedValue.firstIndex, value) 48 | } else { 49 | onValueTouchListener.onValueDeselected() 50 | } 51 | } 52 | 53 | /** 54 | * Removes empty spaces, top-bottom for portrait orientation and left-right for landscape. This method has to be 55 | * called after view View#onSizeChanged() method is called and chart data is set. This method may be inaccurate. 56 | * 57 | * @see BubbleChartRenderer.removeMargins 58 | */ 59 | fun removeMargins() { 60 | bubbleChartRenderer.removeMargins() 61 | ViewCompat.postInvalidateOnAnimation(this) 62 | } 63 | 64 | companion object { 65 | private val TAG = "BubbleChartView" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/ColumnChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import co.csadev.kellocharts.BuildConfig 7 | import co.csadev.kellocharts.listener.ColumnChartOnValueSelectListener 8 | import co.csadev.kellocharts.listener.DummyColumnChartOnValueSelectListener 9 | import co.csadev.kellocharts.model.ChartData 10 | import co.csadev.kellocharts.model.ColumnChartData 11 | import co.csadev.kellocharts.provider.ColumnChartDataProvider 12 | import co.csadev.kellocharts.renderer.ColumnChartRenderer 13 | 14 | /** 15 | * ColumnChart/BarChart, supports subcolumns, stacked columns, horizontal mode, and negative values. 16 | * 17 | * @author Leszek Wach 18 | */ 19 | open class ColumnChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : AbstractChartView(context, attrs, defStyle), ColumnChartDataProvider { 20 | override var columnChartData: ColumnChartData = ColumnChartData.generateDummyData() 21 | set(value) { 22 | if (BuildConfig.DEBUG) { 23 | Log.d(TAG, "Setting data for ColumnChartView") 24 | } 25 | field = value 26 | super.onChartDataChange() 27 | 28 | } 29 | 30 | override val chartData: ChartData 31 | get() = columnChartData 32 | 33 | var onValueTouchListener: ColumnChartOnValueSelectListener? = DummyColumnChartOnValueSelectListener() 34 | set(touchListener) { 35 | if (null != touchListener) { 36 | field = touchListener 37 | } 38 | } 39 | 40 | init { 41 | chartRenderer = ColumnChartRenderer(context, this, this) 42 | } 43 | 44 | override fun callTouchListener() { 45 | val selectedValue = chartRenderer.selectedValue 46 | 47 | if (selectedValue.isSet) { 48 | val value = columnChartData.columns[selectedValue.firstIndex].values[selectedValue.secondIndex] 49 | this.onValueTouchListener?.onValueSelected(selectedValue.firstIndex, selectedValue.secondIndex, value) 50 | } else { 51 | this.onValueTouchListener?.onValueDeselected() 52 | } 53 | } 54 | 55 | companion object { 56 | private val TAG = "ColumnChartView" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/ComboLineColumnChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import co.csadev.kellocharts.BuildConfig 7 | import co.csadev.kellocharts.listener.ComboLineColumnChartOnValueSelectListener 8 | import co.csadev.kellocharts.listener.DummyCompoLineColumnChartOnValueSelectListener 9 | import co.csadev.kellocharts.model.ChartData 10 | import co.csadev.kellocharts.model.ColumnChartData 11 | import co.csadev.kellocharts.model.ComboLineColumnChartData 12 | import co.csadev.kellocharts.model.LineChartData 13 | import co.csadev.kellocharts.model.SelectedValue.SelectedValueType 14 | import co.csadev.kellocharts.provider.ColumnChartDataProvider 15 | import co.csadev.kellocharts.provider.ComboLineColumnChartDataProvider 16 | import co.csadev.kellocharts.provider.LineChartDataProvider 17 | import co.csadev.kellocharts.renderer.ColumnChartRenderer 18 | import co.csadev.kellocharts.renderer.ComboLineColumnChartRenderer 19 | import co.csadev.kellocharts.renderer.LineChartRenderer 20 | 21 | /** 22 | * ComboChart, supports ColumnChart combined with LineChart. Lines are always drawn on top. 23 | * 24 | * @author Leszek Wach 25 | */ 26 | class ComboLineColumnChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : AbstractChartView(context, attrs, defStyle), ComboLineColumnChartDataProvider { 27 | var columnChartDataProvider: ColumnChartDataProvider = ComboColumnChartDataProvider() 28 | var lineChartDataProvider: LineChartDataProvider = ComboLineChartDataProvider() 29 | var onValueTouchListener: ComboLineColumnChartOnValueSelectListener = DummyCompoLineColumnChartOnValueSelectListener() 30 | 31 | override// generateDummyData(); 32 | var comboLineColumnChartData: ComboLineColumnChartData = ComboLineColumnChartData.generateDummyData() 33 | set(value) { 34 | if (BuildConfig.DEBUG) { 35 | Log.d(TAG, "Setting data for ComboLineColumnChartView") 36 | } 37 | field = value 38 | super.onChartDataChange() 39 | } 40 | 41 | override val chartData: ChartData 42 | get() = comboLineColumnChartData 43 | 44 | init { 45 | chartRenderer = ComboLineColumnChartRenderer(context, this, columnChartDataProvider, lineChartDataProvider) 46 | } 47 | 48 | override fun callTouchListener() { 49 | val selectedValue = chartRenderer.selectedValue 50 | 51 | if (selectedValue.isSet) { 52 | 53 | if (SelectedValueType.COLUMN == selectedValue.type) { 54 | 55 | val value = comboLineColumnChartData.columnChartData.columns[selectedValue.firstIndex].values[selectedValue.secondIndex] 56 | onValueTouchListener.onColumnValueSelected(selectedValue.firstIndex, 57 | selectedValue.secondIndex, value) 58 | 59 | } else if (SelectedValueType.LINE == selectedValue.type) { 60 | 61 | val value = comboLineColumnChartData.lineChartData.lines[selectedValue.firstIndex].values[selectedValue.secondIndex] 62 | onValueTouchListener.onPointValueSelected(selectedValue.firstIndex, selectedValue.secondIndex, 63 | value) 64 | 65 | } else { 66 | throw IllegalArgumentException("Invalid selected value type " + selectedValue.type!!.name) 67 | } 68 | } else { 69 | onValueTouchListener.onValueDeselected() 70 | } 71 | } 72 | 73 | fun setColumnChartRenderer(context: Context, columnChartRenderer: ColumnChartRenderer) { 74 | chartRenderer = ComboLineColumnChartRenderer(context, this, columnChartRenderer, lineChartDataProvider) 75 | } 76 | 77 | fun setLineChartRenderer(context: Context, lineChartRenderer: LineChartRenderer) { 78 | chartRenderer = ComboLineColumnChartRenderer(context, this, columnChartDataProvider, lineChartRenderer) 79 | } 80 | 81 | private inner class ComboLineChartDataProvider : LineChartDataProvider { 82 | 83 | override var lineChartData: LineChartData 84 | get() = comboLineColumnChartData.lineChartData 85 | set(data) { comboLineColumnChartData.lineChartData = data } 86 | 87 | } 88 | 89 | private inner class ComboColumnChartDataProvider : ColumnChartDataProvider { 90 | 91 | override var columnChartData: ColumnChartData 92 | get() = comboLineColumnChartData.columnChartData 93 | set(data) { comboLineColumnChartData.columnChartData = data } 94 | 95 | } 96 | 97 | companion object { 98 | private val TAG = "ComboLCChartView" 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/LineChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import co.csadev.kellocharts.BuildConfig 7 | import co.csadev.kellocharts.listener.DummyLineChartOnValueSelectListener 8 | import co.csadev.kellocharts.listener.LineChartOnValueSelectListener 9 | import co.csadev.kellocharts.model.ChartData 10 | import co.csadev.kellocharts.model.LineChartData 11 | import co.csadev.kellocharts.provider.LineChartDataProvider 12 | import co.csadev.kellocharts.renderer.LineChartRenderer 13 | 14 | /** 15 | * LineChart, supports cubic lines, filled lines, circle and square points. Point radius and stroke width can be 16 | * adjusted using LineChartData attributes. 17 | * 18 | * @author Leszek Wach 19 | */ 20 | open class LineChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : AbstractChartView(context, attrs, defStyle), LineChartDataProvider { 21 | var onValueTouchListener: LineChartOnValueSelectListener = DummyLineChartOnValueSelectListener() 22 | 23 | override var lineChartData: LineChartData = LineChartData.generateDummyData() 24 | set(value) { 25 | if (BuildConfig.DEBUG) { 26 | Log.d(TAG, "Setting data for LineChartView") 27 | } 28 | field = value 29 | super.onChartDataChange() 30 | } 31 | 32 | override val chartData: ChartData 33 | get() = lineChartData 34 | 35 | init { 36 | chartRenderer = LineChartRenderer(context, this, this) 37 | } 38 | 39 | override fun callTouchListener() { 40 | val selectedValue = chartRenderer.selectedValue 41 | 42 | if (selectedValue.isSet) { 43 | val point = lineChartData.lines[selectedValue.firstIndex].values[selectedValue.secondIndex] 44 | onValueTouchListener.onValueSelected(selectedValue.firstIndex, selectedValue.secondIndex, point) 45 | } else { 46 | onValueTouchListener.onValueDeselected() 47 | } 48 | } 49 | 50 | companion object { 51 | private val TAG = "LineChartView" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/PreviewColumnChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import androidx.core.view.ViewCompat 7 | import co.csadev.kellocharts.BuildConfig 8 | import co.csadev.kellocharts.computator.ChartComputator 9 | import co.csadev.kellocharts.computator.PreviewChartComputator 10 | import co.csadev.kellocharts.gesture.PreviewChartTouchHandler 11 | import co.csadev.kellocharts.model.ColumnChartData 12 | import co.csadev.kellocharts.renderer.PreviewColumnChartRenderer 13 | 14 | /** 15 | * Preview chart that can be used as overview for other ColumnChart. When you change Viewport of this chart, visible 16 | * area of other chart will change. For that you need also to use 17 | * [Chart.setViewportChangeListener] 18 | * 19 | * @author Leszek Wach 20 | */ 21 | class PreviewColumnChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : ColumnChartView(context, attrs, defStyle) { 22 | override val chartComputator: ChartComputator = PreviewChartComputator() 23 | protected var previewChartRenderer: PreviewColumnChartRenderer = PreviewColumnChartRenderer(context, this, this) 24 | 25 | var previewColor: Int 26 | get() = previewChartRenderer.previewColor 27 | set(color) { 28 | if (BuildConfig.DEBUG) { 29 | Log.d(TAG, "Changing preview area color") 30 | } 31 | 32 | previewChartRenderer.previewColor = color 33 | ViewCompat.postInvalidateOnAnimation(this) 34 | } 35 | 36 | init { 37 | touchHandler = PreviewChartTouchHandler(context, this) 38 | chartRenderer = previewChartRenderer 39 | columnChartData = ColumnChartData.generateDummyData() 40 | } 41 | 42 | override fun canScrollHorizontally(direction: Int): Boolean { 43 | val offset = computeHorizontalScrollOffset() 44 | val range = computeHorizontalScrollRange() - computeHorizontalScrollExtent() 45 | if (range == 0) return false 46 | return if (direction < 0) { 47 | offset > 0 48 | } else { 49 | offset < range - 1 50 | } 51 | } 52 | 53 | companion object { 54 | private val TAG = "ColumnChartView" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/PreviewLineChartView.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.Log 6 | import androidx.core.view.ViewCompat 7 | import co.csadev.kellocharts.BuildConfig 8 | import co.csadev.kellocharts.computator.ChartComputator 9 | import co.csadev.kellocharts.computator.PreviewChartComputator 10 | import co.csadev.kellocharts.gesture.PreviewChartTouchHandler 11 | import co.csadev.kellocharts.model.LineChartData 12 | import co.csadev.kellocharts.renderer.PreviewLineChartRenderer 13 | 14 | /** 15 | * Preview chart that can be used as overview for other LineChart. When you change Viewport of this chart, visible area 16 | * of other chart will change. For that you need also to use 17 | * [Chart.setViewportChangeListener] 18 | * 19 | * @author Leszek Wach 20 | */ 21 | class PreviewLineChartView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : LineChartView(context, attrs, defStyle) { 22 | override val chartComputator: ChartComputator = PreviewChartComputator() 23 | protected var previewChartRenderer: PreviewLineChartRenderer = PreviewLineChartRenderer(context, this, this) 24 | 25 | var previewColor: Int 26 | get() = previewChartRenderer.previewColor 27 | set(color) { 28 | if (BuildConfig.DEBUG) { 29 | Log.d(TAG, "Changing preview area color") 30 | } 31 | 32 | previewChartRenderer.previewColor = color 33 | ViewCompat.postInvalidateOnAnimation(this) 34 | } 35 | 36 | init { 37 | touchHandler = PreviewChartTouchHandler(context, this) 38 | chartRenderer = previewChartRenderer 39 | lineChartData = LineChartData.generateDummyData() 40 | } 41 | 42 | override fun canScrollHorizontally(direction: Int): Boolean { 43 | val offset = computeHorizontalScrollOffset() 44 | val range = computeHorizontalScrollRange() - computeHorizontalScrollExtent() 45 | if (range == 0) return false 46 | return if (direction < 0) { 47 | offset > 0 48 | } else { 49 | offset < range - 1 50 | } 51 | } 52 | 53 | companion object { 54 | private val TAG = "PreviewLineChartView" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/hack/HackyDrawerLayout.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view.hack 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import androidx.drawerlayout.widget.DrawerLayout 7 | 8 | /** 9 | * Hacky fix for issue with DrawerLayout https://github.com/chrisbanes/PhotoView/issues/72 10 | */ 11 | class HackyDrawerLayout : DrawerLayout { 12 | 13 | constructor(context: Context) : super(context) {} 14 | 15 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {} 16 | 17 | constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {} 18 | 19 | override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { 20 | try { 21 | return super.onInterceptTouchEvent(ev) 22 | } catch (e: Exception) { 23 | e.printStackTrace() 24 | return false 25 | } 26 | 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /kellocharts/src/main/java/co/csadev/kellocharts/view/hack/HackyViewPager.kt: -------------------------------------------------------------------------------- 1 | package co.csadev.kellocharts.view.hack 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import androidx.viewpager.widget.ViewPager 7 | 8 | /** 9 | * ScaleGestureDetector seems to mess up the touch events, which means that ViewGroups which make use of 10 | * onInterceptTouchEvent throw a lot of IllegalArgumentException: pointerIndex out of range.There's not much I can do 11 | * in my code for now, but we can mask the result by just catching the problem and ignoring 12 | * it. 13 | * 14 | * @author Chris Banes 15 | */ 16 | class HackyViewPager : ViewPager { 17 | 18 | constructor(context: Context) : super(context) {} 19 | 20 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {} 21 | 22 | override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { 23 | try { 24 | return super.onInterceptTouchEvent(ev) 25 | } catch (e: Exception) { 26 | e.printStackTrace() 27 | return false 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /kellocharts/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | KelloCharts 3 | 4 | -------------------------------------------------------------------------------- /libs/gradle-play-publisher-1.2.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/libs/gradle-play-publisher-1.2.3.jar -------------------------------------------------------------------------------- /logo_design.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/logo_design.xcf -------------------------------------------------------------------------------- /play_feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/play_feature.png -------------------------------------------------------------------------------- /play_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/play_icon.png -------------------------------------------------------------------------------- /screenshots/bar_chart_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/bar_chart_horizontal.png -------------------------------------------------------------------------------- /screenshots/bar_chart_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/bar_chart_vertical.png -------------------------------------------------------------------------------- /screenshots/bubble_chart_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/bubble_chart_horizontal.png -------------------------------------------------------------------------------- /screenshots/bubble_chart_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/bubble_chart_vertical.png -------------------------------------------------------------------------------- /screenshots/icon_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/icon_screenshot.png -------------------------------------------------------------------------------- /screenshots/line_chart_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/line_chart_horizontal.png -------------------------------------------------------------------------------- /screenshots/line_chart_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/line_chart_vertical.png -------------------------------------------------------------------------------- /screenshots/pie_chart_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/pie_chart_horizontal.png -------------------------------------------------------------------------------- /screenshots/pie_chart_vertical_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/pie_chart_vertical_menu.png -------------------------------------------------------------------------------- /screenshots/preview_line_chart_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/preview_line_chart_vertical.png -------------------------------------------------------------------------------- /screenshots/preview_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gtcompscientist/KelloCharts/c78ce8c4ba1991f30f379ec47cf4c38749341c57/screenshots/preview_list.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':kellocharts', ':kellocharts-sample' 2 | --------------------------------------------------------------------------------