├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wangshijia │ │ └── www │ │ └── radachartview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wangshijia │ │ │ └── www │ │ │ └── radachartview │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wangshijia │ └── www │ └── radachartview │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── radarchartview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wangshijia │ │ └── www │ │ └── radarchartview │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wangshijia │ │ │ └── www │ │ │ └── radarchartview │ │ │ └── RadarChartView.java │ └── res │ │ └── values │ │ ├── radar_attrs.xml │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── wangshijia │ └── www │ └── radarchartview │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 92 | 102 | 103 | 104 | 105 | 106 | 107 | 109 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RadaChartView 2 | 一个可定制的雷达网状图 3 | 4 | **效果图如下:** 5 | 6 | ![](https://ws4.sinaimg.cn/large/006tKfTcly1ftf11k735xj307n0fw0sr.jpg) ![](https://ws4.sinaimg.cn/large/006tKfTcly1ftf131v36hj307n0fwjrd.jpg) ![](https://ws4.sinaimg.cn/large/006tKfTcly1ftf131r926j307n0fwt8l.jpg) 7 | 8 | ``` 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | */build 7 | /captures 8 | .bin 9 | local.properties 10 | build/ 11 | gradle/ 12 | 13 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.wangshijia.www.radachartview" 7 | minSdkVersion 21 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | buildToolsVersion '27.0.3' 20 | compileOptions { 21 | sourceCompatibility JavaVersion.VERSION_1_8 22 | targetCompatibility JavaVersion.VERSION_1_8 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 29 | testImplementation 'junit:junit:4.12' 30 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 32 | implementation 'com.android.support:appcompat-v7:27.1.1' 33 | implementation project(':radarchartview') 34 | } 35 | -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wangshijia/www/radachartview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radachartview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wangshijia.www.radachartview", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/wangshijia/www/radachartview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radachartview; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.SeekBar; 6 | 7 | import com.wangshijia.www.radarchartview.RadarChartView; 8 | 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | private RadarChartView radarChartView; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | radarChartView = findViewById(R.id.radarCharView); 19 | // radarCharView.setDefaultShader(Color.parseColor("#ff8f4c"), Color.parseColor("#ff4c51")); 20 | radarChartView.addValues("市场", 60); 21 | radarChartView.addValues("题材", 60); 22 | radarChartView.addValues("财务", 60); 23 | radarChartView.addValues("主力", 60); 24 | radarChartView.addValues("技术", 60); 25 | radarChartView.addValues("技术1", 60); 26 | radarChartView.addValues("技术2", 60); 27 | radarChartView.addValues("技术3", 60); 28 | radarChartView.addValues("技术4", 60); 29 | radarChartView.invalidate(); 30 | 31 | final SeekBar seekBar = findViewById(R.id.seekBar); 32 | final SeekBar seekBar1 = findViewById(R.id.seekBar1); 33 | seekBar1.setMax(30); 34 | seekBar1.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 35 | @Override 36 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 37 | updateSum(progress); 38 | } 39 | 40 | @Override 41 | public void onStartTrackingTouch(SeekBar seekBar) { 42 | 43 | } 44 | 45 | @Override 46 | public void onStopTrackingTouch(SeekBar seekBar) { 47 | 48 | } 49 | }); 50 | seekBar.setMax(30); 51 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 52 | @Override 53 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 54 | updateData((int) (progress)); 55 | } 56 | 57 | @Override 58 | public void onStartTrackingTouch(SeekBar seekBar) { 59 | 60 | } 61 | 62 | @Override 63 | public void onStopTrackingTouch(SeekBar seekBar) { 64 | 65 | } 66 | }); 67 | } 68 | 69 | private void updateSum(int progress) { 70 | radarChartView.setSideNum(progress); 71 | } 72 | 73 | private void updateData(int size) { 74 | radarChartView.clear(); 75 | for (int i = 0; i < size; i++) { 76 | radarChartView.addValues("Text" + i, 60); 77 | } 78 | radarChartView.invalidate(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 35 | 36 | 37 | 47 | 48 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RadaChartView 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/wangshijia/www/radachartview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radachartview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.3' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImportEffort/RadaChartView/d9bd63f7883b2036d9c62f82dc45c1640dae67d3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 19 11:25:04 CST 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.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /radarchartview/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /radarchartview/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | 6 | defaultConfig { 7 | minSdkVersion 21 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | compileOnly 'com.android.support:appcompat-v7:27.1.1' 28 | testImplementation 'junit:junit:4.12' 29 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 30 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 31 | } 32 | -------------------------------------------------------------------------------- /radarchartview/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 | -------------------------------------------------------------------------------- /radarchartview/src/androidTest/java/com/wangshijia/www/radarchartview/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radarchartview; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wangshijia.www.radarchartview.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /radarchartview/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /radarchartview/src/main/java/com/wangshijia/www/radarchartview/RadarChartView.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radarchartview; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.LinearGradient; 8 | import android.graphics.Paint; 9 | import android.graphics.Path; 10 | import android.graphics.PointF; 11 | import android.graphics.Shader; 12 | import android.support.annotation.NonNull; 13 | import android.support.annotation.Nullable; 14 | import android.util.AttributeSet; 15 | import android.view.View; 16 | 17 | import java.util.HashMap; 18 | import java.util.Iterator; 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | 22 | /*** 23 | * 自定义网格雷达View 24 | * @author wangshijia 编辑于 2018/7/19 25 | */ 26 | public class RadarChartView extends View { 27 | 28 | /** 29 | * 多边形边个数 30 | */ 31 | private int sideNum = 5; 32 | /** 33 | * 刻度绘制网格圈的个数 34 | */ 35 | private int gridNum = 4; 36 | /** 37 | * 360 / sideNum 得出的每一份的角度 38 | */ 39 | private double angle; 40 | 41 | /** 42 | * 雷达图圆的半径 43 | */ 44 | private float radius; 45 | /** 46 | * 数据区域参考最大值用于计算设置数据相对于半径的比例 47 | */ 48 | private float axisMax = 100f; 49 | /** 50 | * 字体间距 51 | */ 52 | private float textSpacing = 8f; 53 | 54 | private PointF pointF = new PointF(); 55 | private Path gridPath = new Path(); 56 | private Path connPath = new Path(); 57 | private Path dataPath = new Path(); 58 | private Path dataOverLinePath = new Path(); 59 | 60 | private HashMap valueHash; 61 | 62 | private Paint gridPathPaint; 63 | private Paint valueStrikePaint; 64 | private Paint valueFillPaint; 65 | private Paint valueOverPaint; 66 | private Paint textPaint; 67 | private Paint circleValueTextPaint; 68 | private Paint valueCirclePaint; 69 | private Paint valueCircleFillPaint; 70 | 71 | /** 72 | * 数据覆盖层线条是否绘制的宽度 颜色 73 | */ 74 | private boolean drawValueOverStrike = false; 75 | private float dataOverStrikeWidth = 1; 76 | private int colorDataOverStrike = Color.TRANSPARENT; 77 | /** 78 | * 网格线的颜色 和宽度 79 | */ 80 | private int colorGrid = Color.GRAY; 81 | private float gridStrikeWidth = 2; 82 | /** 83 | * 数据描边的是否绘制颜色宽度 84 | */ 85 | private boolean drawValueStrike = false; 86 | private int colorDataStrike = Color.TRANSPARENT; 87 | private float dataStrikeWidth = 1; 88 | /** 89 | * 数据填充的颜色 90 | */ 91 | private boolean drawValueFill = true; 92 | private int colorDataFill = Color.TRANSPARENT; 93 | /** 94 | * 数据文字颜色大小 95 | */ 96 | private int colorValueText = Color.BLACK; 97 | private int textSize = 16; 98 | 99 | /** 100 | * 渐变颜色色值 101 | */ 102 | private int shaderStartColor = -1; 103 | private int shaderEndColor = -1; 104 | /** 105 | * 是否绘制内容部分尾部的圆圈 106 | */ 107 | private boolean drawEndCircle = false; 108 | /** 109 | * 是否绘制内容部分的数值 valueHash.entry.getValue 110 | */ 111 | private boolean drawEndValue = true; 112 | private int colorValueCircle = Color.BLACK; 113 | private float valueCircleStrikeWidth = 1; 114 | private int colorValueCircleFill = Color.TRANSPARENT; 115 | /** 116 | * 文字大小 117 | */ 118 | private int circleValueTextSize = 8; 119 | private int circleValueTextColor = Color.BLACK; 120 | /** 121 | * 文字与圆圈的间距 122 | */ 123 | private float circleTextSpacing = 8; 124 | 125 | private int maxTextNum = 0; 126 | 127 | 128 | public RadarChartView(Context context) { 129 | this(context, null); 130 | } 131 | 132 | public RadarChartView(Context context, @Nullable AttributeSet attrs) { 133 | this(context, attrs, -1); 134 | } 135 | 136 | public RadarChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 137 | super(context, attrs, defStyleAttr); 138 | 139 | TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.RadarChartView, defStyleAttr, 0); 140 | //内容及网格线属性 141 | this.colorGrid = values.getColor(R.styleable.RadarChartView_colorGrid, colorGrid); 142 | this.gridStrikeWidth = values.getDimension(R.styleable.RadarChartView_gridStrikeWidth, gridStrikeWidth); 143 | //填充数据线条颜色 144 | this.colorDataStrike = values.getColor(R.styleable.RadarChartView_colorDataStrike, colorDataStrike); 145 | this.dataStrikeWidth = values.getDimension(R.styleable.RadarChartView_dataStrikeWidth, dataStrikeWidth); 146 | 147 | //填充颜色 148 | this.drawValueFill = values.getBoolean(R.styleable.RadarChartView_drawValueFill, drawValueFill); 149 | this.colorDataFill = values.getColor(R.styleable.RadarChartView_colorDataFill, colorDataFill); 150 | this.shaderStartColor = values.getColor(R.styleable.RadarChartView_shaderStartColor, shaderStartColor); 151 | this.shaderEndColor = values.getColor(R.styleable.RadarChartView_shaderEndColor, shaderEndColor); 152 | 153 | //填充颜色上边覆盖的线条颜色 154 | this.colorDataOverStrike = values.getColor(R.styleable.RadarChartView_colorDataOverStrike, colorDataOverStrike); 155 | this.dataOverStrikeWidth = values.getDimension(R.styleable.RadarChartView_dataOverStrikeWidth, dataOverStrikeWidth); 156 | 157 | //外圈文字属性 158 | this.textSize = values.getDimensionPixelSize(R.styleable.RadarChartView_textSize, textSize); 159 | this.colorValueText = values.getColor(R.styleable.RadarChartView_colorValueText, colorValueText); 160 | this.textSpacing = dp2px(getContext(), (int) textSpacing); 161 | this.textSpacing = values.getDimension(R.styleable.RadarChartView_textSpacing, textSpacing); 162 | 163 | //边数 164 | this.sideNum = values.getInt(R.styleable.RadarChartView_sideNum, sideNum); 165 | this.gridNum = values.getInt(R.styleable.RadarChartView_gridNum, gridNum); 166 | 167 | // 内部圆圈属性 168 | this.drawEndValue = values.getBoolean(R.styleable.RadarChartView_drawEndValue, drawEndValue); 169 | this.drawEndCircle = values.getBoolean(R.styleable.RadarChartView_drawEndCircle, drawEndCircle); 170 | this.colorValueCircle = values.getColor(R.styleable.RadarChartView_colorValueCircle, colorValueCircle); 171 | this.valueCircleStrikeWidth = values.getDimension(R.styleable.RadarChartView_valueCircleStrikeWidth, valueCircleStrikeWidth); 172 | this.circleTextSpacing = dp2px(getContext(), (int) circleTextSpacing); 173 | this.circleTextSpacing = values.getDimension(R.styleable.RadarChartView_circleTextSpacing, circleTextSpacing); 174 | this.colorValueCircleFill = values.getColor(R.styleable.RadarChartView_colorValueCircleFill, colorValueCircleFill); 175 | this.circleValueTextColor = values.getColor(R.styleable.RadarChartView_circleValueTextColor, circleValueTextColor); 176 | this.circleValueTextSize = values.getDimensionPixelSize(R.styleable.RadarChartView_circleValueTextSize, circleValueTextSize); 177 | 178 | this.drawValueStrike = values.getBoolean(R.styleable.RadarChartView_drawValueStrike, drawValueStrike); 179 | this.drawValueOverStrike = values.getBoolean(R.styleable.RadarChartView_drawValueOverStrike, drawValueOverStrike); 180 | 181 | this.axisMax = values.getFloat(R.styleable.RadarChartView_axisMax, axisMax); 182 | 183 | values.recycle(); 184 | valueHash = new LinkedHashMap<>(sideNum); 185 | initPaint(); 186 | } 187 | 188 | 189 | private void initPaint() { 190 | gridPathPaint = createPaint(colorGrid, 0, gridStrikeWidth, 0); 191 | 192 | valueStrikePaint = createPaint(colorDataStrike, 0, dataStrikeWidth, 0); 193 | valueFillPaint = createPaint(colorDataFill, 1, 1, 0); 194 | 195 | valueOverPaint = createPaint(colorDataOverStrike, 0, dataOverStrikeWidth, 0); 196 | textPaint = createPaint(colorValueText, 2, 1, textSize); 197 | 198 | valueCirclePaint = createPaint(colorValueCircle, 0, valueCircleStrikeWidth, 0); 199 | valueCircleFillPaint = createPaint(colorValueCircleFill, 1, 1, 0); 200 | circleValueTextPaint = createPaint(circleValueTextColor, 1, 1, circleValueTextSize); 201 | } 202 | 203 | /** 204 | * 创建画笔 205 | */ 206 | private Paint createPaint(int color, int style, float width, int textSize) { 207 | Paint paint = new Paint(); 208 | paint.setColor(color); 209 | paint.setFlags(Paint.ANTI_ALIAS_FLAG); 210 | switch (style) { 211 | case 0: 212 | paint.setStyle(Paint.Style.STROKE); 213 | break; 214 | case 1: 215 | paint.setStyle(Paint.Style.FILL); 216 | break; 217 | case 2: 218 | paint.setStyle(Paint.Style.FILL_AND_STROKE); 219 | break; 220 | default: 221 | break; 222 | } 223 | paint.setStrokeWidth(width); 224 | paint.setTextSize(textSize); 225 | return paint; 226 | } 227 | 228 | 229 | @Override 230 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 231 | super.onSizeChanged(w, h, oldw, oldh); 232 | radius = Math.min(w, h) / 2 - maxTextNum * textSize - textSpacing; 233 | pointF.x = w / 2; 234 | pointF.y = h / 2; 235 | postInvalidate(); 236 | } 237 | 238 | 239 | @Override 240 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 241 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 242 | 243 | int widthMode = MeasureSpec.getMode(widthMeasureSpec); 244 | int heightMode = MeasureSpec.getMode(heightMeasureSpec); 245 | int widthSpec = MeasureSpec.getSize(widthMeasureSpec); 246 | int heightSpec = MeasureSpec.getSize(heightMeasureSpec); 247 | 248 | 249 | int width = getResultMeasureDimension(widthSpec, widthMode); 250 | int height = getResultMeasureDimension(heightSpec, heightMode); 251 | setMeasuredDimension(width, height); 252 | } 253 | 254 | private int getResultMeasureDimension(int size, int mode) { 255 | int result; 256 | //View 设置为 wrap_content 的时候 257 | if (mode == MeasureSpec.AT_MOST) { 258 | result = Math.min(size, dp2px(getContext(), 400)); 259 | } else { 260 | result = size; 261 | } 262 | return result; 263 | } 264 | 265 | @Override 266 | protected void onDraw(Canvas canvas) { 267 | super.onDraw(canvas); 268 | // 2 π r = C 假设 r=1 angle 就是 360 度的多少份 269 | angle = (Math.PI * 2 / sideNum); 270 | 271 | canvas.translate(pointF.x, pointF.y); 272 | 273 | drawGrids(canvas); 274 | 275 | if (!valueHash.isEmpty()) { 276 | drawData(canvas); 277 | drawText(canvas); 278 | } 279 | } 280 | 281 | private void drawText(Canvas canvas) { 282 | Iterator> iterator = valueHash.entrySet().iterator(); 283 | 284 | for (int i = 0; i < valueHash.size(); i++) { 285 | Map.Entry next = iterator.next(); 286 | double degree = angle * i; 287 | 288 | double x = radius * Math.sin(degree); 289 | double y = -radius * Math.cos(degree); 290 | 291 | PointF position = getRelativePositionByText(next.getKey(), textPaint, textSpacing, Math.toDegrees(degree), x, y); 292 | 293 | canvas.drawText(next.getKey(), position.x, position.y, textPaint); 294 | } 295 | } 296 | 297 | /** 298 | * 画网格线 299 | * 300 | * @param canvas 301 | */ 302 | private void drawGrids(Canvas canvas) { 303 | float perR = radius / gridNum; 304 | for (int i = 1; i <= gridNum; i++) { 305 | float r = perR * i; 306 | this.gridPath.reset(); 307 | this.connPath.reset(); 308 | //绘制开始的时 path 起点在原点,需要lineTo第一个点的坐标 309 | for (int j = 0; j < sideNum; j++) { 310 | float x = (float) (r * Math.sin(angle * j)); 311 | float y = (float) (-r * Math.cos(angle * j)); 312 | if (j == 0) { 313 | gridPath.moveTo(x, y); 314 | } else { 315 | gridPath.lineTo(x, y); 316 | } 317 | connPath.moveTo(0, 0); 318 | connPath.lineTo(x, y); 319 | } 320 | gridPath.close(); 321 | canvas.drawPath(gridPath, gridPathPaint); 322 | canvas.drawPath(connPath, gridPathPaint); 323 | } 324 | } 325 | 326 | /** 327 | * 画内部填充区域 328 | * 329 | * @param canvas 330 | */ 331 | private void drawData(Canvas canvas) { 332 | Iterator> iterator = valueHash.entrySet().iterator(); 333 | dataPath.reset(); 334 | dataOverLinePath.reset(); 335 | 336 | //坐标缩放系数 337 | float dataRadius = radius / axisMax; 338 | for (int i = 0; i < valueHash.size(); i++) { 339 | Map.Entry data = iterator.next(); 340 | 341 | float x = (float) (Math.sin(angle * i) * dataRadius * Math.min(data.getValue(), axisMax)); 342 | float y = -(float) (Math.cos(angle * i) * dataRadius * Math.min(data.getValue(), axisMax)); 343 | 344 | if (i == 0) { 345 | dataPath.moveTo(x, y); 346 | } else { 347 | dataPath.lineTo(x, y); 348 | } 349 | 350 | dataOverLinePath.moveTo(0, 0); 351 | dataOverLinePath.lineTo(x, y); 352 | } 353 | dataPath.close(); 354 | if (drawValueStrike) { 355 | //画线 356 | canvas.drawPath(dataPath, valueStrikePaint); 357 | } 358 | 359 | if (shaderStartColor != -1 && shaderEndColor != -1) { 360 | valueFillPaint.setShader(new LinearGradient(0, -radius, 0, radius, shaderStartColor, shaderEndColor, Shader.TileMode.CLAMP)); 361 | canvas.drawPath(dataPath, valueFillPaint); 362 | } 363 | 364 | //是否绘制填充颜色 365 | if (drawValueFill) { 366 | canvas.drawPath(dataPath, valueFillPaint); 367 | } 368 | 369 | 370 | //填充颜色上边是否添加额外的线条覆盖 371 | if (drawValueOverStrike) { 372 | canvas.drawPath(dataOverLinePath, valueOverPaint); 373 | } 374 | //是否绘制数据末尾的源泉 375 | if (drawEndCircle) { 376 | drawCircle(canvas, dataRadius); 377 | } 378 | 379 | } 380 | 381 | private void drawCircle(Canvas canvas, float dataRadius) { 382 | 383 | Iterator> entryIterator = valueHash.entrySet().iterator(); 384 | for (int i = 0; i < valueHash.size(); i++) { 385 | 386 | Map.Entry data = entryIterator.next(); 387 | 388 | double x = (Math.sin(angle * i) * dataRadius * Math.min(data.getValue(), axisMax)); 389 | double y = -(Math.cos(angle * i) * dataRadius * Math.min(data.getValue(), axisMax)); 390 | 391 | canvas.drawCircle((float) x, (float) y, 10, valueCircleFillPaint); 392 | canvas.drawCircle((float) x, (float) y, 10, valueCirclePaint); 393 | 394 | if (drawEndValue) { 395 | PointF pointF = getRelativePositionByText(String.valueOf(data.getValue()), circleValueTextPaint, circleTextSpacing, Math.toDegrees(angle * i), x, y); 396 | canvas.drawText(String.valueOf(data.getValue()), pointF.x, pointF.y, circleValueTextPaint); 397 | } 398 | } 399 | } 400 | 401 | /** 402 | * 原来的坐标和文字以及间距计算出应该放置的点的位置 403 | * 一共八个方向 404 | */ 405 | private PointF getRelativePositionByText(String text, Paint paint, float textSpacing, double degree, double pointX, double pointY) { 406 | 407 | PointF resultPoint = new PointF(); 408 | float tW = paint.measureText(text); 409 | 410 | Paint.FontMetrics fontMetrics = paint.getFontMetrics(); 411 | float tH = fontMetrics.descent - fontMetrics.ascent; 412 | 413 | //第一象限 414 | if (pointX > 0 && pointY < 0 && degree != 0) { 415 | resultPoint = new PointF((float) pointX + textSpacing, (float) pointY); 416 | } 417 | 418 | //第=象限 419 | if (pointX < 0 && pointY < 0 && degree != 90.0) { 420 | resultPoint = new PointF((float) pointX - textSpacing - tW, (float) (pointY)); 421 | } 422 | //第三象限 423 | if (pointX < 0 && pointY > 0 && degree != 180.0) { 424 | resultPoint = new PointF((float) pointX - textSpacing - tW, (float) (pointY + tH / 2)); 425 | } 426 | //第四象限 427 | if (pointX > 0 && pointY > 0 && degree != 270 && degree != 180.0) { 428 | resultPoint = new PointF((float) pointX + textSpacing, (float) (pointY + tH / 2)); 429 | } 430 | 431 | //X轴正 432 | if (degree == 0) { 433 | resultPoint = new PointF((float) pointX - tW / 2, (float) (pointY - textSpacing)); 434 | } 435 | //X轴负 436 | if (degree == 180) { 437 | resultPoint = new PointF((float) pointX - tW / 2, (float) (pointY + textSpacing + tH / 2)); 438 | } 439 | //y轴负 440 | if (degree == 90) { 441 | resultPoint = new PointF((float) pointX + textSpacing, (float) (pointY + tH / 4));//修正为除 4 442 | } 443 | //y轴正 444 | if (degree == 270) { 445 | resultPoint = new PointF((float) pointX - textSpacing - tW, (float) (pointY + tH / 4)); 446 | } 447 | return resultPoint; 448 | } 449 | 450 | 451 | public int getSideNum() { 452 | return sideNum; 453 | } 454 | 455 | public void setSideNum(int sideNum) { 456 | this.sideNum = sideNum; 457 | invalidate(); 458 | } 459 | 460 | public int getGridNum() { 461 | return gridNum; 462 | } 463 | 464 | public void setGridNum(int gridNum) { 465 | this.gridNum = gridNum; 466 | } 467 | 468 | 469 | public int getColorGrid() { 470 | return colorGrid; 471 | } 472 | 473 | public void setColorGrid(int colorGrid) { 474 | this.colorGrid = colorGrid; 475 | } 476 | 477 | public int getColorDataStrike() { 478 | return colorDataStrike; 479 | } 480 | 481 | public void setColorDataStrike(int colorDataStrike) { 482 | this.colorDataStrike = colorDataStrike; 483 | } 484 | 485 | public int getColorDataFill() { 486 | return colorDataFill; 487 | } 488 | 489 | public void setColorDataFill(int colorDataFill) { 490 | this.colorDataFill = colorDataFill; 491 | } 492 | 493 | public int getColorDataOverStrike() { 494 | return colorDataOverStrike; 495 | } 496 | 497 | public void setColorDataOverStrike(int colorDataOverStrike) { 498 | this.colorDataOverStrike = colorDataOverStrike; 499 | } 500 | 501 | public int getColorValueText() { 502 | return colorValueText; 503 | } 504 | 505 | public void setColorValueText(int colorValueText) { 506 | this.colorValueText = colorValueText; 507 | } 508 | 509 | public boolean isDrawValueStrike() { 510 | return drawValueStrike; 511 | } 512 | 513 | public void setDrawValueStrike(boolean drawValueStrike) { 514 | this.drawValueStrike = drawValueStrike; 515 | } 516 | 517 | public boolean isDrawValueOverStrike() { 518 | return drawValueOverStrike; 519 | } 520 | 521 | public void setDrawValueOverStrike(boolean drawValueOverStrike) { 522 | this.drawValueOverStrike = drawValueOverStrike; 523 | } 524 | 525 | public float getAxisMax() { 526 | return axisMax; 527 | } 528 | 529 | public void setAxisMax(float axisMax) { 530 | this.axisMax = axisMax; 531 | } 532 | 533 | public int getTextSize() { 534 | return textSize; 535 | } 536 | 537 | public void setTextSize(int textSize) { 538 | this.textSize = textSize; 539 | } 540 | 541 | public float getTextSpacing() { 542 | return textSpacing; 543 | } 544 | 545 | public void setTextSpacing(float textSpacing) { 546 | this.textSpacing = textSpacing; 547 | } 548 | 549 | public void addValues(@NonNull String key, @NonNull float presentValue) { 550 | maxTextNum = Math.max(key.length(), maxTextNum); 551 | valueHash.put(key, presentValue); 552 | } 553 | 554 | public void clear() { 555 | valueHash.clear(); 556 | invalidate(); 557 | } 558 | 559 | public float getGridStrikeWidth() { 560 | return gridStrikeWidth; 561 | } 562 | 563 | public void setGridStrikeWidth(float gridStrikeWidth) { 564 | this.gridStrikeWidth = gridStrikeWidth; 565 | } 566 | 567 | public float getDataOverStrikeWidth() { 568 | return dataOverStrikeWidth; 569 | } 570 | 571 | public void setDataOverStrikeWidth(float dataOverStrikeWidth) { 572 | this.dataOverStrikeWidth = dataOverStrikeWidth; 573 | } 574 | 575 | public float getDataStrikeWidth() { 576 | return dataStrikeWidth; 577 | } 578 | 579 | public void setDataStrikeWidth(float dataStrikeWidth) { 580 | this.dataStrikeWidth = dataStrikeWidth; 581 | } 582 | 583 | public boolean isDrawValueFill() { 584 | return drawValueFill; 585 | } 586 | 587 | public void setDrawValueFill(boolean drawValueFill) { 588 | this.drawValueFill = drawValueFill; 589 | } 590 | 591 | public int getShaderStartColor() { 592 | return shaderStartColor; 593 | } 594 | 595 | public void setShaderStartColor(int shaderStartColor) { 596 | this.shaderStartColor = shaderStartColor; 597 | } 598 | 599 | public int getShaderEndColor() { 600 | return shaderEndColor; 601 | } 602 | 603 | public void setShaderEndColor(int shaderEndColor) { 604 | this.shaderEndColor = shaderEndColor; 605 | } 606 | 607 | public boolean isDrawEndCircle() { 608 | return drawEndCircle; 609 | } 610 | 611 | public void setDrawEndCircle(boolean drawEndCircle) { 612 | this.drawEndCircle = drawEndCircle; 613 | } 614 | 615 | public boolean isDrawEndValue() { 616 | return drawEndValue; 617 | } 618 | 619 | public void setDrawEndValue(boolean drawEndValue) { 620 | this.drawEndValue = drawEndValue; 621 | } 622 | 623 | public int getColorValueCircle() { 624 | return colorValueCircle; 625 | } 626 | 627 | public void setColorValueCircle(int colorValueCircle) { 628 | this.colorValueCircle = colorValueCircle; 629 | } 630 | 631 | public int getColorValueCircleFill() { 632 | return colorValueCircleFill; 633 | } 634 | 635 | public void setColorValueCircleFill(int colorValueCircleFill) { 636 | this.colorValueCircleFill = colorValueCircleFill; 637 | } 638 | 639 | public int getCircleValueTextSize() { 640 | return circleValueTextSize; 641 | } 642 | 643 | public void setCircleValueTextSize(int circleValueTextSize) { 644 | this.circleValueTextSize = circleValueTextSize; 645 | } 646 | 647 | public float getCircleTextSpacing() { 648 | return circleTextSpacing; 649 | } 650 | 651 | public void setCircleTextSpacing(float circleTextSpacing) { 652 | this.circleTextSpacing = circleTextSpacing; 653 | } 654 | 655 | public int getCircleValueTextColor() { 656 | return circleValueTextColor; 657 | } 658 | 659 | public void setCircleValueTextColor(int circleValueTextColor) { 660 | this.circleValueTextColor = circleValueTextColor; 661 | } 662 | 663 | public float getValueCircleStrikeWidth() { 664 | return valueCircleStrikeWidth; 665 | } 666 | 667 | public void setValueCircleStrikeWidth(float valueCircleStrikeWidth) { 668 | this.valueCircleStrikeWidth = valueCircleStrikeWidth; 669 | } 670 | 671 | 672 | private float getCenterY() { 673 | return pointF.y; 674 | } 675 | 676 | private float getCenterX() { 677 | return pointF.x; 678 | } 679 | 680 | public static int dp2px(Context context, int dp) { 681 | return (int) (getDensity(context) * dp + 0.5); 682 | } 683 | 684 | public static int sp2px(Context context, int sp) { 685 | return (int) (getFontDensity(context) * sp + 0.5); 686 | } 687 | 688 | public static int px2dp(Context context, int px) { 689 | return (int) (px / getDensity(context) + 0.5); 690 | } 691 | 692 | public static int px2sp(Context context, int px) { 693 | return (int) (px / getFontDensity(context) + 0.5); 694 | } 695 | 696 | public static float getDensity(Context context) { 697 | return context.getResources().getDisplayMetrics().density; 698 | } 699 | 700 | public static float getFontDensity(Context context) { 701 | return context.getResources().getDisplayMetrics().scaledDensity; 702 | } 703 | } 704 | -------------------------------------------------------------------------------- /radarchartview/src/main/res/values/radar_attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /radarchartview/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RadarChartView 3 | 4 | -------------------------------------------------------------------------------- /radarchartview/src/test/java/com/wangshijia/www/radarchartview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.wangshijia.www.radarchartview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':radarchartview' 2 | --------------------------------------------------------------------------------