├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── themes.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 │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ ├── layout │ │ │ │ ├── popup_dialog.xml │ │ │ │ └── activity_main.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── progressdemo │ │ │ │ ├── DialogXpopu.java │ │ │ │ ├── BarChartEntity.java │ │ │ │ ├── CoordinateView.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── CalculateUtil.java │ │ │ │ ├── ProgressCircle.java │ │ │ │ └── BarChartView.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── progressdemo │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── progressdemo │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .idea ├── .gitignore ├── compiler.xml ├── vcs.xml ├── runConfigurations.xml ├── misc.xml ├── gradle.xml └── jarRepositories.xml ├── Video_20220728_032901_434.gif ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── b17884b8a1c628879511e7865e9ce44.jpg ├── README.md ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "ProgressDemo" 2 | include ':app' 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /Video_20220728_032901_434.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/Video_20220728_032901_434.gif -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ProgressDemo 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /b17884b8a1c628879511e7865e9ce44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/b17884b8a1c628879511e7865e9ce44.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Levipeng/CutormView/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CutormView 2 | 效果图如下,有加载动画,可以动态修改渐变色和总分数,当前分数 3 | progressView.setMaxNumber(500,150,"#FFC0CB","#FFD700"); 4 | ![image](https://github.com/Levipeng/CutormView/blob/main/b17884b8a1c628879511e7865e9ce44.jpg) 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jul 27 16:03:55 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/progressdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 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 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/DialogXpopu.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.lxj.xpopup.core.AttachPopupView; 8 | import com.lxj.xpopup.core.PositionPopupView; 9 | 10 | /** 11 | * @Description TODO 12 | * @Author pt 13 | * @Date 2022/8/17 9:10 14 | */ 15 | public class DialogXpopu extends AttachPopupView { 16 | public DialogXpopu(@NonNull Context context) { 17 | super(context); 18 | } 19 | 20 | @Override 21 | protected int getImplLayoutId() { 22 | return R.layout.popup_dialog; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/progressdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.example.progressdemo", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /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=-Xmx2048m -Dfile.encoding=UTF-8 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 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion 32 7 | 8 | 9 | defaultConfig { 10 | applicationId "com.example.progressdemo" 11 | minSdkVersion 21 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | } 30 | 31 | dependencies { 32 | 33 | implementation 'androidx.appcompat:appcompat:1.2.0' 34 | implementation 'com.google.android.material:material:1.2.1' 35 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 36 | testImplementation 'junit:junit:4.+' 37 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 39 | implementation 'com.lxj:xpopup:2.1.4' 40 | 41 | 42 | 43 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/popup_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 17 | 18 | 22 | 27 | 32 | 37 | 38 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/BarChartEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | /** 4 | * 柱状图实体类 5 | */ 6 | public class BarChartEntity { 7 | private String xLabel; 8 | private Float[] yValue; 9 | private float sum; 10 | 11 | private int selectColor; 12 | private int imgResId; 13 | 14 | public BarChartEntity(String xLabel, Float[] yValue) { 15 | this.xLabel = xLabel; 16 | this.yValue = yValue; 17 | for (float y : yValue) { 18 | sum+=y; 19 | } 20 | } 21 | 22 | public void setSelectColor(int selectColor) { 23 | this.selectColor = selectColor; 24 | } 25 | 26 | public int getSelectColor() { 27 | return selectColor; 28 | } 29 | 30 | public int getImgResId() { 31 | return imgResId; 32 | } 33 | 34 | public void setImgResId(int imgResId) { 35 | this.imgResId = imgResId; 36 | } 37 | 38 | public String getxLabel() { 39 | return xLabel; 40 | } 41 | 42 | public void setxLabel(String xLabel) { 43 | this.xLabel = xLabel; 44 | } 45 | 46 | public BarChartEntity(Float[] yValue) { 47 | this.yValue = yValue; 48 | } 49 | 50 | public Float[] getyValue() { 51 | return yValue; 52 | } 53 | 54 | public void setyValue(Float[] yValue) { 55 | this.yValue = yValue; 56 | for (float y : yValue) { 57 | sum+=y; 58 | } 59 | } 60 | 61 | public float getSum() { 62 | return sum; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/CoordinateView.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.util.TypedValue; 9 | import android.view.View; 10 | 11 | import androidx.annotation.Nullable; 12 | 13 | /** 14 | * @Description 坐标轴柱状图 15 | * @Author pt 16 | * @Date 2022/8/5 14:24 17 | */ 18 | public class CoordinateView extends View { 19 | //坐标轴画笔 20 | private Paint mXYPaint; 21 | //坐标轴距离左边的间距 22 | private float mLeftWidth; 23 | //坐标轴Y的高度 24 | private float mYHeight; 25 | 26 | public CoordinateView(Context context) { 27 | this(context,null); 28 | } 29 | 30 | public CoordinateView(Context context, @Nullable AttributeSet attrs) { 31 | this(context, attrs,0); 32 | } 33 | 34 | public CoordinateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 35 | super(context, attrs, defStyleAttr); 36 | init(); 37 | } 38 | 39 | private void init() { 40 | initPaint(); 41 | mLeftWidth=dp2px(getContext(),50); 42 | mYHeight=dp2px(getContext(),200); 43 | } 44 | 45 | /** 46 | * 初始化画笔 47 | */ 48 | private void initPaint() { 49 | //坐标轴画笔 50 | mXYPaint= new Paint(); 51 | mXYPaint.setAntiAlias(true); 52 | mXYPaint.setStyle(Paint.Style.FILL); 53 | mXYPaint.setColor(Color.BLACK); 54 | } 55 | 56 | @Override 57 | protected void onDraw(Canvas canvas) { 58 | super.onDraw(canvas); 59 | //绘制坐标系 60 | canvas.drawLine(mLeftWidth,0,mLeftWidth,mYHeight,mXYPaint); 61 | canvas.drawLine(mLeftWidth,mYHeight,getWidth(),mYHeight,mXYPaint); 62 | 63 | } 64 | 65 | public int dp2px(Context context, float dpVal) { 66 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, 67 | context.getResources().getDisplayMetrics()); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.graphics.Color; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Toast; 10 | 11 | import com.lxj.xpopup.XPopup; 12 | import com.lxj.xpopup.enums.PopupAnimation; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private ProgressCircle progressView; 21 | private ArrayList datas; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | BarChartView barChartViews = (BarChartView) findViewById(R.id.bar_view); 28 | // List str=new ArrayList<>(); 29 | // str.add("abcde"); 30 | // str.add("abcde"); 31 | // str.add("abcde"); 32 | // str.add("abcde"); 33 | // str.add("abcde"); 34 | // str.add("abcde"); 35 | // str.add("abcde"); 36 | // str.add("abcde"); 37 | // List> inList=new ArrayList<>(); 38 | // List intList=new ArrayList(); 39 | // intList.add(10); 40 | // intList.add(50); 41 | // intList.add(40); 42 | // inList.add(intList); 43 | // inList.add(intList); 44 | // inList.add(intList); 45 | // inList.add(intList); 46 | // inList.add(intList); 47 | // inList.add(intList); 48 | // inList.add(intList); 49 | // inList.add(intList); 50 | // ArrayList str1=new ArrayList<>(); 51 | // str1.add("a"); 52 | // str1.add("a"); 53 | // str1.add("a"); 54 | // str1.add("a"); 55 | // 56 | // barChartViews.setChartData(str,inList,true,str1); 57 | barChartViews.initChart(new int[]{Color.parseColor("#F0836C") 58 | , Color.parseColor("#F4A359") 59 | , Color.parseColor("#F6D35A") 60 | , Color.parseColor("#9DDA53") 61 | , Color.parseColor("#3AD7A0")}, 62 | new String[]{"E", "D", "C", "B", "A"}, null, "人数"); 63 | 64 | 65 | List datas = new ArrayList<>(); 66 | Float[] valueArr; 67 | float count = 0; 68 | for (int i = 0; i < 10; i++) { 69 | count = 0; 70 | valueArr = new Float[5]; 71 | valueArr[0] = (float) (Math.random() * 50); 72 | count += valueArr[0]; 73 | valueArr[1] = (float) (Math.random() * 50); 74 | count += valueArr[1]; 75 | if ((100 - count) < 50) { 76 | valueArr[2] = (float) (Math.random() * (100 - count)); 77 | count += valueArr[2]; 78 | } else { 79 | valueArr[2] = (float) (Math.random() * 50); 80 | count += valueArr[2]; 81 | } 82 | if ((100 - count) < 50) { 83 | valueArr[3] = (float) (Math.random() * (100 - count)); 84 | count += valueArr[3]; 85 | } else { 86 | valueArr[3] = (float) (Math.random() * 50); 87 | count += valueArr[3]; 88 | } 89 | valueArr[4] = (100 - count); 90 | // datas.add(new BarChartEntity("三年" + String.valueOf(i) + "班", new Float[]{6f,6f,9f,3f,0f})); 91 | datas.add(new BarChartEntity("三年" + String.valueOf(i) + "班", valueArr)); 92 | } 93 | List finalDatas1 = datas; 94 | final XPopup.Builder builder = new XPopup.Builder(MainActivity.this) 95 | // .isCenterHorizontal(true) 96 | .watchView(barChartViews); 97 | 98 | barChartViews.setOnItemBarClickListener(new BarChartView.OnItemBarClickListener() { 99 | @SuppressLint("DefaultLocale") 100 | @Override 101 | public void onClick(int position, float x, float y) { 102 | // Toast.makeText(getContext(), String.format("及格人数:%f\n不及格人数:%f", finalDatas1.get(position).getyValue()[0], finalDatas1.get(position).getyValue()[1]), Toast.LENGTH_SHORT).show(); 103 | // Toast.makeText(MainActivity.this,"点击了"+position,Toast.LENGTH_SHORT).show(); 104 | builder.hasShadowBg(false) 105 | .asCustom(new DialogXpopu(MainActivity.this)) 106 | .show(); 107 | // new XPopup.Builder(MainActivity.this) 108 | // .popupAnimation(PopupAnimation.NoAnimation) 109 | // .offsetX((int)x) 110 | // .offsetY((int)y) 111 | // .asCustom() 112 | // .show(); 113 | } 114 | }); 115 | 116 | 117 | barChartViews.setData(datas, 0, 40); 118 | barChartViews.startAnimation(); 119 | // 120 | // progressView = (ProgressCircle)findViewById(R.id.progress_circular); 121 | // progressView.setMaxNumber(500,250,"#DCF5EC","#3BD298","#E6F1EF"); 122 | } 123 | 124 | public void onClick(View view) { 125 | progressView.setMaxNumber(500, 250, "#FFffff", "#FFFACD", "#E6F1EF"); 126 | } 127 | 128 | public void onClick1(View view) { 129 | progressView.setMaxNumber(500, 323, "#FFffff", "#FFE4E1", "#E6F1EF"); 130 | } 131 | 132 | public void onClick2(View view) { 133 | progressView.setMaxNumber(500, 340, "#FFffff", "#228B22", "#E6F1EF"); 134 | } 135 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/CalculateUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import android.content.Context; 4 | import android.graphics.Paint; 5 | import java.math.BigDecimal; 6 | 7 | /** 8 | * 图表工具类 9 | */ 10 | public class CalculateUtil { 11 | 12 | /** 13 | * 获取这个最大数 数总共有几位 14 | * 15 | * @param value 16 | * @return 17 | */ 18 | public static int getScale(float value) { 19 | if (value >= 1 && value < 10) { 20 | return 0; 21 | } 22 | if (value == 0) { 23 | return 0; 24 | } 25 | if (value >= 10) { 26 | return 1 + getScale(value / 10); 27 | } else { 28 | return getScale(value * 10) - 1; 29 | } 30 | } 31 | 32 | public static float getRangeTop(float value) { 33 | //value: [1,10) 34 | 35 | if (value ==1.0) { 36 | return 1.0f; 37 | } 38 | 39 | if (value < 1.2) { 40 | return 1.2f; 41 | } 42 | 43 | if (value < 1.5) { 44 | return 1.5f; 45 | } 46 | 47 | if (value < 2.0) { 48 | return 2.0f; 49 | } 50 | 51 | if (value < 3.0) { 52 | return 3.0f; 53 | } 54 | 55 | if (value < 4.0) { 56 | return 4.0f; 57 | } 58 | 59 | if (value < 5.0) { 60 | return 5.0f; 61 | } 62 | 63 | if (value < 6.0) { 64 | return 6.0f; 65 | } 66 | 67 | if (value < 8.0) { 68 | return 8.0f; 69 | } 70 | 71 | return 10.0f; 72 | } 73 | 74 | public static float getRangeMin(float value) { 75 | //value: [1,10) 76 | if (value < 1.0) { 77 | return 0f; 78 | } 79 | 80 | if (value < 1.5) { 81 | return 1.0f; 82 | } 83 | 84 | if (value < 2.0) { 85 | return 1.0f; 86 | } 87 | 88 | if (value < 3.0) { 89 | return 2.0f; 90 | } 91 | 92 | if (value < 4.0) { 93 | return 3.0f; 94 | } 95 | 96 | if (value < 5.0) { 97 | return 4.0f; 98 | } 99 | 100 | if (value < 6.0) { 101 | return 5.0f; 102 | } 103 | if (value < 7.0) { 104 | return 6.0f; 105 | } 106 | 107 | if (value < 8.0) { 108 | return 7.0f; 109 | } 110 | if (value < 9.0) { 111 | return 8.0f; 112 | } 113 | 114 | return 9.0f; 115 | } 116 | 117 | /** 118 | * 数字的乘法精度计算 119 | */ 120 | public static float numMathMul(float d1, float d2) { 121 | BigDecimal b1 = new BigDecimal(d1); 122 | BigDecimal b2 = new BigDecimal(d2); 123 | float res = b1.multiply(b2).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue(); 124 | return res; 125 | } 126 | public static BigDecimal add(double v1,double v2){ 127 | BigDecimal b1 = new BigDecimal(Double.toString(v1)); 128 | BigDecimal b2 = new BigDecimal(Double.toString(v2)); 129 | return b1.add(b2); 130 | } 131 | 132 | public static BigDecimal sub(double v1,double v2){ 133 | BigDecimal b1 = new BigDecimal(Double.toString(v1)); 134 | BigDecimal b2 = new BigDecimal(Double.toString(v2)); 135 | return b1.subtract(b2); 136 | } 137 | 138 | 139 | public static BigDecimal mul(double v1,double v2){ 140 | BigDecimal b1 = new BigDecimal(Double.toString(v1)); 141 | BigDecimal b2 = new BigDecimal(Double.toString(v2)); 142 | return b1.multiply(b2); 143 | } 144 | 145 | public static BigDecimal div(double v1,double v2){ 146 | BigDecimal b1 = new BigDecimal(Double.toString(v1)); 147 | BigDecimal b2 = new BigDecimal(Double.toString(v2)); 148 | return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP);//四舍五入,保留2位小数 149 | //除不尽的情况 150 | } 151 | /** 152 | * 得到最大宽度值得文本 153 | * 154 | * @param maxDivisionValue 155 | * @return 156 | */ 157 | public static float getDivisionTextMaxWidth(float maxDivisionValue, String unitName) { 158 | Paint textPaint = new Paint(); 159 | textPaint.setTextSize(20); 160 | BigDecimal bigDecimal = new BigDecimal(maxDivisionValue); 161 | float max = textPaint.measureText(String.valueOf(bigDecimal.intValue())); 162 | for (int i = 2; i <= 10; i++) { 163 | if (maxDivisionValue * 0.1 >= 1) { 164 | //当数字非常大的时候会出现精度丢失的情况 所以候使用BigDecimal做运算 165 | BigDecimal bd = new BigDecimal(maxDivisionValue); 166 | BigDecimal fen = new BigDecimal(0.1 * i); 167 | String text = String.valueOf(bd.multiply(fen).longValue())+unitName; 168 | float w = textPaint.measureText(text); 169 | if (w > max) { 170 | max = w; 171 | } 172 | } else { 173 | max = textPaint.measureText(String.valueOf(maxDivisionValue * 10)+unitName); 174 | } 175 | } 176 | return max; 177 | } 178 | 179 | /** 180 | * 提供精确的小数位四舍五入处理。 181 | * @param v 需要四舍五入的数字 182 | * @param scale 小数点后保留几位 183 | * @return 四舍五入后的结果 184 | */ 185 | public static double round(double v, int scale) { 186 | if (scale < 0) { 187 | throw new IllegalArgumentException( 188 | "The scale must be a positive integer or zero"); 189 | } 190 | BigDecimal b = new BigDecimal(Double.toString(v)); 191 | BigDecimal ne = new BigDecimal("1"); 192 | return b.divide(ne, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/progressdemo/ProgressCircle.java: -------------------------------------------------------------------------------- 1 | package com.example.progressdemo; 2 | 3 | import android.animation.ValueAnimator; 4 | import android.content.Context; 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.RectF; 11 | import android.graphics.Shader; 12 | import android.graphics.Typeface; 13 | import android.util.AttributeSet; 14 | import android.util.Log; 15 | import android.util.TypedValue; 16 | import android.view.View; 17 | import android.view.animation.LinearInterpolator; 18 | 19 | /** 20 | * 扇形图 21 | */ 22 | public class ProgressCircle extends View { 23 | private float mRingBias = 0.15f; 24 | protected float mRadius; 25 | //格子数 总进度 26 | protected int mMaxProgress; 27 | //当前进度 28 | protected int mProgress; 29 | 30 | protected float mCenterX; 31 | protected float mCenterY; 32 | 33 | private Paint mPaint; 34 | private int mColor1; 35 | private int mColor2; 36 | private int mInactiveColor; 37 | private int mBackgroundColor; 38 | private float angleDu; // 39 | 40 | 41 | private Paint mPaint1; 42 | 43 | private Paint mPaint2; 44 | 45 | private Paint textPaint; 46 | 47 | private Paint textAllPaint; 48 | 49 | private Paint textCountPaint; 50 | private int maxNumber = 500;//设置最大分数,默认500 51 | private int currentNumber = 400;//设置当前数字 52 | private float scale; 53 | private float realDU; 54 | 55 | 56 | public ProgressCircle(Context context, AttributeSet attrs, int defStyle) { 57 | super(context, attrs, defStyle); 58 | 59 | initAttributes(context, attrs); 60 | } 61 | 62 | public ProgressCircle(Context context, AttributeSet attrs) { 63 | super(context, attrs); 64 | 65 | initAttributes(context, attrs); 66 | } 67 | 68 | public ProgressCircle(Context context) { 69 | super(context); 70 | } 71 | 72 | private void initAttributes(Context context, AttributeSet attrs) { 73 | mMaxProgress = 17; 74 | mProgress = 14; 75 | } 76 | 77 | private void updateDimensions(int width, int height) { 78 | 79 | mCenterX = width / 2.0f; 80 | mCenterY = height / 2.0f; 81 | 82 | int diameter = Math.min(width, height); 83 | 84 | float outerRadius = diameter / 2; 85 | float sectionHeight = (float) (2.5 * outerRadius * mRingBias); 86 | mRadius = outerRadius - sectionHeight / 2; 87 | angleDu = (float) (Math.PI / 180f); 88 | scale = width / 1080f; 89 | translation = translation * scale; 90 | 91 | mBackgroundColor=Color.parseColor("#E6F1EF"); 92 | initPaint(); 93 | } 94 | private String endColor="#3BD298"; 95 | private String startColor="#DCF5EC"; 96 | private void initPaint() { 97 | mMaxProgress = 17; 98 | mProgress = 0; 99 | 100 | mPaint = new Paint(); 101 | mPaint.setAntiAlias(true); 102 | mPaint.setStyle(Paint.Style.FILL); 103 | //渐变开始颜色 104 | mColor1 = Color.parseColor(startColor); 105 | //渐变结束颜色 106 | mColor2 = Color.parseColor(endColor); 107 | mInactiveColor = Color.parseColor("#E2E6EA"); 108 | mPaint.setColor(mColor1); 109 | //格子画笔 110 | mPaint1 = new Paint(); 111 | mPaint1.setAntiAlias(true); 112 | mPaint1.setStyle(Paint.Style.FILL); 113 | mPaint1.setColor(mColor1); 114 | 115 | //外圈画笔 116 | mPaint2 = new Paint(); 117 | mPaint2.setAntiAlias(true); 118 | mPaint2.setStrokeWidth(10); 119 | mPaint2.setStyle(Paint.Style.STROKE); 120 | mPaint2.setColor(mInactiveColor); 121 | 122 | //左下脚右下角文本画笔 123 | textPaint = new Paint(); 124 | textPaint.setTextSize(dp2px(getContext(), 20 * scale)); // 文字大小 125 | textPaint.setAntiAlias(true); 126 | textPaint.setStyle(Paint.Style.FILL_AND_STROKE); 127 | textPaint.setColor(mInactiveColor); 128 | 129 | //中间总分画笔 130 | textAllPaint = new Paint(); 131 | textAllPaint.setTextSize(dp2px(getContext(), 30 * scale)); // 文字大小 132 | textAllPaint.setAntiAlias(true); 133 | textAllPaint.setStyle(Paint.Style.FILL_AND_STROKE); 134 | textAllPaint.setColor(Color.parseColor("#666666")); 135 | 136 | //中间分数画笔 137 | textCountPaint = new Paint(); 138 | textCountPaint.setTextSize(dp2px(getContext(), 60 * scale)); // 文字大小 139 | textCountPaint.setAntiAlias(true); 140 | textCountPaint.setStyle(Paint.Style.FILL_AND_STROKE); 141 | textCountPaint.setTypeface(Typeface.DEFAULT_BOLD); 142 | textCountPaint.setColor(mColor2); 143 | 144 | } 145 | 146 | 147 | @Override 148 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 149 | super.onSizeChanged(w, h, oldw, oldh); 150 | updateDimensions(w, h); 151 | } 152 | 153 | private float translation = 110; 154 | 155 | @Override 156 | protected void onDraw(Canvas canvas) { 157 | 158 | 159 | float arcLeft = mCenterX - mRadius; 160 | float arcTop = mCenterY - mRadius; 161 | float arcRight = mCenterX + mRadius; 162 | float arcBottom = mCenterY + mRadius; 163 | RectF arcRF0 = new RectF(arcLeft, arcTop, arcRight, arcBottom); 164 | //外圈 165 | RectF arcRF1 = new RectF(arcLeft - 30, arcTop - 30, arcRight + 30, arcBottom + 30); 166 | Paint PaintArc = new Paint(); 167 | PaintArc.setAntiAlias(true); 168 | 169 | float Percentage = 0.0f; 170 | float CurrPer = -225.0f; 171 | float wPer = 3.0f;//间隔度数 172 | //绘制内部白色扇形 173 | canvas.drawArc(arcRF1, CurrPer, 270, false, mPaint2); 174 | for (int i = 0; i < mMaxProgress; i++) { 175 | //每个格子所占的度数 176 | Percentage = (270 - (mMaxProgress - 1) * wPer) / mMaxProgress; 177 | 178 | if (i < mProgress) { 179 | float bias = (float) i / (float) (mMaxProgress); 180 | int color = interpolateColor(mColor1, mColor2, bias); 181 | mPaint.setColor(color); 182 | canvas.drawArc(arcRF0, CurrPer, Percentage, true, mPaint); 183 | } else { 184 | canvas.scale(1.0f, 1.0f); 185 | float v =Math.abs(realDU - (CurrPer + 225.0f)) ; 186 | if (v mData = new ArrayList<>(); 93 | /** 94 | * item中的Y轴最大值 95 | */ 96 | private float mMaxYValue; 97 | /** 98 | * Y轴最大的刻度值 99 | */ 100 | private float mMaxYDivisionValue; 101 | /** 102 | * 柱子的矩形 103 | */ 104 | private RectF mBarRect, mBarRectClick; 105 | /** 106 | * 绘制的区域 107 | */ 108 | private RectF mDrawArea; 109 | /** 110 | * 每一个bar的宽度 111 | */ 112 | private int mBarWidth; 113 | /** 114 | * 每个bar之间的距离 115 | */ 116 | private int mBarSpace; 117 | /** 118 | * 向右边滑动的距离 119 | */ 120 | private float mLeftMoving; 121 | /** 122 | * 左后一次的x坐标 123 | */ 124 | private float mLastPointX; 125 | /** 126 | * 当前移动的距离 127 | */ 128 | private float mMovingThisTime = 0.0f; 129 | /** 130 | * 右边的最大和最小值 131 | */ 132 | private int mMaxRight, mMinRight; 133 | /** 134 | * 下面两个相当于图表的原点 135 | */ 136 | private float mStartX; 137 | private int mStartY; 138 | /** 139 | * 柱形图左边的x轴坐标 和右边的x轴坐标 140 | */ 141 | private final List mBarLeftXPoints = new ArrayList<>(); 142 | private final List mBarRightXPoints = new ArrayList<>(); 143 | 144 | /** 145 | * 是否隐藏Y坐标 146 | */ 147 | private boolean isHideAxisY; 148 | /** 149 | * 是否隐藏X坐标 150 | */ 151 | private boolean isHideAxisX; 152 | 153 | /** 154 | * 用户点击到了无效位置 155 | */ 156 | public static final int INVALID_POSITION = -1; 157 | private OnItemBarClickListener mOnItemBarClickListener; 158 | private GestureDetector mGestureListener; 159 | /** 160 | * 是否绘制点击效果 161 | */ 162 | private boolean isDrawBorder; 163 | /** 164 | * 点击的地方 165 | */ 166 | private int mClickPosition; 167 | 168 | //滑动速度相关 169 | private VelocityTracker mVelocityTracker; 170 | private Scroller mScroller; 171 | /** 172 | * fling最大速度 173 | */ 174 | private int mMaxVelocity; 175 | /** 176 | * 单位标题X坐标 177 | */ 178 | private String mUnitTiltleX; 179 | private String mUnitNameX; 180 | /** 181 | * 单位标题Y坐标 182 | */ 183 | private String mUnitTitleY; 184 | /** 185 | * 单位名称 186 | */ 187 | private String mUnitNameY; 188 | 189 | /** 190 | * 柱状顶部显示个数 191 | */ 192 | private boolean isShowBarValue; 193 | 194 | public void setOnItemBarClickListener(OnItemBarClickListener onRangeBarClickListener) { 195 | this.mOnItemBarClickListener = onRangeBarClickListener; 196 | } 197 | 198 | public interface OnItemBarClickListener { 199 | void onClick(int position,float x,float y); 200 | } 201 | 202 | public BarChartView(Context context) { 203 | super(context); 204 | init(context); 205 | } 206 | 207 | public BarChartView(Context context, @Nullable AttributeSet attrs) { 208 | super(context, attrs); 209 | init(context); 210 | } 211 | 212 | public BarChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 213 | super(context, attrs, defStyleAttr); 214 | init(context); 215 | } 216 | public int dp2px(Context context, float dpVal) { 217 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, 218 | context.getResources().getDisplayMetrics()); 219 | } 220 | 221 | private void init(Context context) { 222 | mContext = context; 223 | mBarWidth = dp2px(mContext,20); 224 | mBarSpace =dp2px(mContext,20); 225 | mTopMargin =dp2px(mContext,20); 226 | mBottomMargin = dp2px(mContext,10); 227 | mRightMargin = 0; 228 | mLeftMargin = 0; 229 | mBarType = TYPE_VERTICAL; 230 | mUnitNameY = ""; 231 | mUnitNameX = ""; 232 | mMaxYValue = 100; 233 | 234 | mScroller = new Scroller(context); 235 | mMaxVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity(); 236 | mGestureListener = new GestureDetector(context, new RangeBarOnGestureListener()); 237 | 238 | mAxisPaint = new Paint(); 239 | mAxisPaint.setColor(Color.parseColor("#999999")); 240 | mAxisPaint.setStrokeWidth(dp2px(mContext,0.5f)); 241 | 242 | mTextPaint = new Paint(); 243 | mTextPaint.setStrokeWidth(1); 244 | mTextPaint.setAntiAlias(true); 245 | mTextPaint.setTextSize(dp2px(mContext,10)); 246 | 247 | mBarPaint = new Paint(); 248 | mBarPaint.setAntiAlias(true); 249 | mBarPaint.setColor(mBarColors != null && mBarColors.length > 0 ? mBarColors[0] : Color.parseColor("#6FC5F4")); 250 | 251 | mBorderPaint = new Paint(); 252 | mBorderPaint.setAntiAlias(true); 253 | mBorderPaint.setStyle(Paint.Style.FILL); 254 | mBorderPaint.setColor(Color.rgb(0, 0, 0)); 255 | mBorderPaint.setAlpha(120); 256 | 257 | mBarRect = new RectF(0, 0, 0, 0); 258 | mBarRectClick = new RectF(0, 0, 0, 0); 259 | mDrawArea = new RectF(0, 0, 0, 0); 260 | 261 | setData(mData, 100, 0); 262 | } 263 | 264 | 265 | public void initChart(int[] colors, String[] barLevelLabel, String mUnitX, String mUnitY) { 266 | this.mBarColors = colors; 267 | this.mBarLevelLabel = barLevelLabel; 268 | this.mUnitTiltleX = mUnitX; 269 | this.mUnitTitleY = mUnitY; 270 | 271 | mBarEndColors = new int[mBarColors.length]; 272 | for (int i = 0; i < mBarColors.length; i++) { 273 | mBarEndColors[i] = mBarColors[i] & 0x00FFFFFF; 274 | mBarEndColors[i] = mBarEndColors[i] | 0x66000000; 275 | } 276 | } 277 | 278 | public void initChart(int[] colors, int[] endColors, String[] barLevelLabel, String mUnitX, String mUnitY) { 279 | this.mBarColors = colors; 280 | this.mBarEndColors = endColors; 281 | this.mBarLevelLabel = barLevelLabel; 282 | this.mUnitTiltleX = mUnitX; 283 | this.mUnitTitleY = mUnitY; 284 | } 285 | 286 | public void setBarWidth(int barWidth) { 287 | mBarWidth = barWidth; 288 | } 289 | 290 | public void setHideAxisY(boolean hideAxisY) { 291 | isHideAxisY = hideAxisY; 292 | } 293 | 294 | public void setBarSpace(int barSpace) { 295 | mBarSpace = barSpace; 296 | } 297 | 298 | public void setShowBarValue(boolean showBarValue) { 299 | isShowBarValue = showBarValue; 300 | } 301 | 302 | public void setUnitNameX(String unitNameX) { 303 | mUnitNameX = unitNameX; 304 | } 305 | 306 | public void setUnitNameY(String unitNameY) { 307 | mUnitNameY = unitNameY; 308 | } 309 | 310 | public void setBarType(int barType) { 311 | mBarType = barType; 312 | } 313 | 314 | /** 315 | * @param list 316 | * @param maxValue Y坐最大值,0的话不限制 317 | * @param minValue Y坐标最小值 318 | */ 319 | public void setData(List list, int maxValue, int minValue) { 320 | this.mData = list; 321 | if (list != null && list.size() > 0) { 322 | mMaxYValue = calculateMaxValueY(list); 323 | } 324 | if (maxValue > 0 && mMaxYValue > maxValue) { 325 | mMaxYValue = maxValue; 326 | } 327 | if (minValue > 0 && mMaxYValue < minValue) { 328 | mMaxYValue = minValue; 329 | } 330 | getRange(mMaxYValue); 331 | calculateOffsetY(); 332 | 333 | if (!TextUtils.isEmpty(mUnitTiltleX)) { 334 | mRightMargin = (int) (mTextPaint.measureText(mUnitTiltleX) +dp2px(mContext,5)); 335 | } else { 336 | mRightMargin = dp2px(mContext,5); 337 | } 338 | 339 | } 340 | 341 | /** 342 | * 对颜色进行透明值0x66 343 | * 344 | * @param color 345 | * @return 346 | */ 347 | private int calculateAlphaColor(int color) { 348 | int newColor = color & 0x00FFFFFF; 349 | 350 | newColor = newColor | 0x66000000; 351 | return newColor; 352 | } 353 | 354 | /** 355 | * 计算出Y轴最大值 356 | * 357 | * @return 358 | */ 359 | private float calculateMaxValueY(List list) { 360 | float start = list.get(0).getSum(); 361 | for (BarChartEntity entity : list) { 362 | if (entity.getSum() > start) { 363 | start = entity.getSum(); 364 | } 365 | } 366 | return start; 367 | } 368 | 369 | /** 370 | * 得到柱状图的最大和最小的分度值 371 | */ 372 | private void getRange(float maxYValue) { 373 | int scale = CalculateUtil.getScale(maxYValue);//获取这个最大数 数总共有几位 374 | float value = (float) Math.pow(10, scale); 375 | float unScaleValue = (float) (maxYValue / value);//最大值除以位数之后剩下的值 比如1200/1000 后剩下1.2 376 | mMaxYDivisionValue = (float) (CalculateUtil.getRangeTop(unScaleValue) * value);//获取Y轴的最大的分度值 377 | BigDecimal b = new BigDecimal(mMaxYDivisionValue); 378 | mMaxYDivisionValue = b.setScale(1, BigDecimal.ROUND_HALF_UP).floatValue(); 379 | if (mMaxYDivisionValue > maxYValue) { 380 | mMaxYDivisionValue = maxYValue; 381 | } 382 | mStartX = CalculateUtil.getDivisionTextMaxWidth(mMaxYDivisionValue, mUnitNameY); 383 | 384 | if (!TextUtils.isEmpty(mUnitTitleY) && mStartX < mTextPaint.measureText(mUnitTitleY)) { 385 | mStartX = mTextPaint.measureText(mUnitTitleY) + dp2px(mContext,10);; 386 | } else { 387 | mStartX = mStartX +dp2px(mContext,10);; 388 | } 389 | } 390 | 391 | @Override 392 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 393 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 394 | } 395 | 396 | @Override 397 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 398 | super.onSizeChanged(w, h, oldw, oldh); 399 | mTotalWidth = w; 400 | mTotalHeight = h; 401 | mMaxHeight = h - getPaddingTop() - getPaddingBottom() - mBottomMargin - mTopMargin - (int) mYOffset; 402 | 403 | if (mBarLevelLabel != null) { 404 | mMaxHeight = (int) (mMaxHeight * 0.8); 405 | } else { 406 | mMaxHeight = (int) (mMaxHeight * 0.8); 407 | } 408 | mPaddingBottom = getPaddingBottom(); 409 | mPaddingTop = getPaddingTop(); 410 | int paddingLeft = getPaddingLeft(); 411 | mPaddingRight = getPaddingRight(); 412 | 413 | } 414 | 415 | //获取滑动范围和指定区域 416 | private void getArea() { 417 | if (mBarType == TYPE_VERTICAL) { 418 | mMaxRight = (int) (mStartX + (mBarSpace + mBarWidth) * mData.size()) + mBarSpace; 419 | } else { 420 | if (mBarColors.length == 2) { 421 | mMaxRight = (int) (mStartX + (mBarSpace + mBarWidth + mBarWidth) * mData.size()) + mBarSpace; 422 | } else { 423 | mMaxRight = (int) (mStartX + (mBarSpace + mBarWidth) * mData.size()) + mBarSpace; 424 | } 425 | } 426 | mMinRight = mTotalWidth - mLeftMargin - mRightMargin; 427 | mStartY = mTotalHeight - mBottomMargin - mPaddingBottom - (int) mYOffset; 428 | mDrawArea = new RectF(mStartX, mPaddingTop, mTotalWidth - mPaddingRight - mRightMargin, mTotalHeight - mPaddingBottom); 429 | } 430 | 431 | @Override 432 | protected void onDraw(Canvas canvas) { 433 | super.onDraw(canvas); 434 | // if (mData == null || mData.isEmpty()) return; 435 | if (mData == null) return; 436 | getArea(); 437 | checkTheLeftMoving(); 438 | //绘制刻度线 和 刻度 439 | drawYAxisAndText(canvas); 440 | //绘制单位 441 | drawUnit(canvas); 442 | //颜色类型标签 443 | drawBarTypeLabel(canvas); 444 | //调用clipRect()方法后,只会显示被裁剪的区域 445 | canvas.clipRect(mDrawArea.left, mDrawArea.top, mDrawArea.right, mDrawArea.bottom + mDrawArea.height()); 446 | //绘制柱子 447 | drawBar(canvas); 448 | //绘制X轴的text 449 | drawXAxisAndText(canvas); 450 | } 451 | 452 | private void drawBarTypeLabel(Canvas canvas) { 453 | if (mBarLevelLabel == null) { 454 | return; 455 | } 456 | mTextPaint.setAntiAlias(true); 457 | // Typeface typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD); 458 | mTextPaint.setTypeface(Typeface.DEFAULT); 459 | mTextPaint.setTextSize(dp2px(mContext,10)); 460 | int circleWidth =dp2px(mContext,4);; 461 | float startX = mDrawArea.right; 462 | for (int i = 0; i < mBarLevelLabel.length; i++) { 463 | mTextPaint.setColor(Color.parseColor("#333333")); 464 | startX = startX - mTextPaint.measureText(mBarLevelLabel[i]); 465 | canvas.drawText(mBarLevelLabel[i], startX, mTopMargin + mPaddingTop, mTextPaint); 466 | startX = (float) (startX - circleWidth * 1.2); 467 | if (mBarType == TYPE_HORIZONTAL) { 468 | mTextPaint.setColor(mBarColors[mBarLevelLabel.length - i - 1]); 469 | } else { 470 | mTextPaint.setColor(mBarColors[i]); 471 | 472 | } 473 | canvas.drawCircle(startX, mTopMargin + mPaddingTop - circleWidth, circleWidth, mTextPaint); 474 | startX -=dp2px(mContext,20);; 475 | } 476 | 477 | } 478 | 479 | protected Rect mRect = new Rect(); 480 | 481 | private void drawUnit(Canvas canvas) { 482 | mTextPaint.setAntiAlias(true); 483 | mTextPaint.setColor(Color.parseColor("#999999")); 484 | mTextPaint.setTypeface(Typeface.DEFAULT); 485 | mTextPaint.setTextSize(dp2px(mContext,8)); 486 | 487 | if (!TextUtils.isEmpty(mUnitTitleY)) { 488 | canvas.drawText(mUnitTitleY, dp2px(mContext,8), mStartY - mMaxHeight - dp2px(mContext,215), 489 | mTextPaint); 490 | } 491 | 492 | if (!TextUtils.isEmpty(mUnitTiltleX)) { 493 | mTextPaint.getTextBounds(mUnitTiltleX, 0, mUnitTiltleX.length(), mRect); 494 | //画X坐标单位标签 495 | canvas.drawText(mUnitTiltleX, mMinRight + 5, mStartY + (mRect.bottom - mRect.top) / 2, mTextPaint); 496 | } 497 | } 498 | 499 | /** 500 | * 检查向左滑动的距离 确保没有画出屏幕 501 | */ 502 | private void checkTheLeftMoving() { 503 | if (mLeftMoving > (mMaxRight - mMinRight)) { 504 | mLeftMoving = mMaxRight - mMinRight; 505 | } 506 | if (mLeftMoving < 0) { 507 | mLeftMoving = 0; 508 | } 509 | } 510 | 511 | // private void drawXAxisText(Canvas canvas) { 512 | // //这里设置 x 轴的字一条最多显示3个,大于三个就换行 513 | // for (int i = 0; i < mData.size(); i++) { 514 | // String text = mData.get(i).getxLabel(); 515 | // if (text.length() <= 3) { 516 | // canvas.drawText(text, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text) - mBarWidth) / 2, mTotalHeight - mBottomMargin * 2 / 3 517 | // , mTextPaint); 518 | // } else { 519 | // String text1 = text.substring(0, 3); 520 | // String text2 = text.substring(3, text.length()); 521 | // canvas.drawText(text1, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text1) - mBarWidth) / 2, 522 | // mTotalHeight - mBottomMargin * 2 / 3, mTextPaint); 523 | // canvas.drawText(text2, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text2) - mBarWidth) / 2, mTotalHeight - mBottomMargin / 3, 524 | // mTextPaint); 525 | // } 526 | // } 527 | // } 528 | 529 | /** 530 | * 计算Y坐偏移 531 | */ 532 | private void calculateOffsetY() { 533 | mTextPaint.setTypeface(Typeface.DEFAULT); 534 | mTextPaint.setTextSize(dp2px(mContext,10)); 535 | if (mData != null && !mData.isEmpty()) { 536 | float yOffset = mTextPaint.measureText(mData.get(0).getxLabel()); 537 | for (int i = 1; i < mData.size(); i++) { 538 | float curLength = mTextPaint.measureText(mData.get(i).getxLabel()); 539 | if (curLength > mYOffset) { 540 | yOffset = curLength; 541 | } 542 | } 543 | mYOffset = (float) (yOffset * Math.cos(Math.PI * mTextAngle / 180)); 544 | float xOffset = (float) (yOffset * Math.sin(Math.PI * mTextAngle / 180)); 545 | if (xOffset > mBarSpace) { 546 | mXOffset = xOffset - mBarSpace; 547 | } 548 | } 549 | } 550 | 551 | private final Path mTextPath = new Path(); 552 | private float mYOffset; 553 | private float mXOffset; 554 | /** 555 | * X左边标签旋转度数 556 | */ 557 | private float mTextAngle = 35; 558 | 559 | private void drawXAxisAndText(Canvas canvas) { 560 | mTextPaint.setAntiAlias(true); 561 | mTextPaint.setColor(Color.parseColor("#999999")); 562 | mTextPaint.setTypeface(Typeface.DEFAULT); 563 | mTextPaint.setTextSize(dp2px(mContext,8)); 564 | // mTextPaint.setTextSize(Dimens.dpToPx(10)); 565 | // mTextPaint.setColor(Color.parseColor("#333333")); 566 | // if (mTextType == TEXT_TYPE_HORIZONTAL) { 567 | // //这里设置 x 轴的字一条最多显示3个,大于三个就换行 568 | // for (int i = 0; i < mData.size(); i++) { 569 | // String text = mData.get(i).getxLabel(); 570 | // if (text.length() <= 3) { 571 | // canvas.drawText(text, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text) - mBarWidth) / 2, mTotalHeight - mBottomMargin * 572 | // 2 / 3, mTextPaint); 573 | // } else { 574 | // String text1 = text.substring(0, 3); 575 | // String text2 = text.substring(3, text.length()); 576 | // canvas.drawText(text1, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text1) - mBarWidth) / 2, mTotalHeight - mBottomMargin 577 | // * 2 / 3, mTextPaint); 578 | // canvas.drawText(text2, mBarLeftXPoints.get(i) - (mTextPaint.measureText(text2) - mBarWidth) / 2, mTotalHeight - mBottomMargin 579 | // / 3, mTextPaint); 580 | // } 581 | // } 582 | // } else { 583 | 584 | for (int i = 0; i < mData.size(); i++) { 585 | mTextPath.reset(); 586 | String text = mData.get(i).getxLabel(); 587 | float textLength = mTextPaint.measureText(text); 588 | float xOffset = (float) (textLength * Math.sin(Math.PI * mTextAngle / 180)); 589 | float yOffset = (float) (textLength * Math.cos(Math.PI * mTextAngle / 180)); 590 | if (TYPE_HORIZONTAL == mBarType && mBarColors.length == 2) { 591 | 592 | mTextPath.moveTo((float) (mBarLeftXPoints.get(i) + mBarWidth * 1.5 - xOffset), mTotalHeight - mBottomMargin / 2 - mYOffset + yOffset); 593 | mTextPath.lineTo((float) (mBarLeftXPoints.get(i) + mBarWidth * 1.5), mTotalHeight - mBottomMargin / 2 - mYOffset); 594 | } else { 595 | mTextPath.moveTo(mBarLeftXPoints.get(i) + mBarWidth / 2 - xOffset, mTotalHeight - mBottomMargin / 2 - mYOffset + yOffset); 596 | mTextPath.lineTo(mBarLeftXPoints.get(i) + mBarWidth / 2, mTotalHeight - mBottomMargin / 2 - mYOffset); 597 | } 598 | canvas.drawTextOnPath(text, mTextPath, 0, 0, mTextPaint); 599 | } 600 | // } 601 | 602 | } 603 | 604 | private float percent = 1f; 605 | private final TimeInterpolator pointInterpolator = new DecelerateInterpolator(); 606 | 607 | public void startAnimation() { 608 | ValueAnimator mAnimator = ValueAnimator.ofFloat(0, 1); 609 | mAnimator.setDuration(600); 610 | mAnimator.setInterpolator(pointInterpolator); 611 | mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 612 | @Override 613 | public void onAnimationUpdate(ValueAnimator animation) { 614 | percent = (float) animation.getAnimatedValue(); 615 | invalidate(); 616 | } 617 | }); 618 | mAnimator.start(); 619 | } 620 | 621 | private void drawBar(Canvas canvas) { 622 | mBarLeftXPoints.clear(); 623 | mBarRightXPoints.clear(); 624 | mBarRect.bottom = mStartY; 625 | int radiusWidth = dp2px(mContext,4); 626 | BarChartEntity barChartEntity; 627 | 628 | mTextPaint.setTextSize(dp2px(mContext,8)); 629 | float barLabelWidth; 630 | String barLabel; 631 | for (int index = 0; index < mData.size(); index++) { 632 | barChartEntity = mData.get(index); 633 | if (mBarType == TYPE_VERTICAL) { 634 | if (mBarColors.length == 1) { 635 | 636 | mBarRect.left = (int) (mStartX + mBarWidth * index + mBarSpace * (index + 1) - mLeftMoving) + (int) mXOffset; 637 | mBarRect.top = mStartY - (int) ((mMaxHeight * (barChartEntity.getyValue()[0] / mMaxYDivisionValue)) * percent); 638 | mBarRect.right = mBarRect.left + mBarWidth; 639 | 640 | if (barChartEntity.getSelectColor() != 0) { 641 | LinearGradient linearGradient = new LinearGradient(mBarRect.left, mBarRect.top, mBarRect.left, mBarRect.bottom, 642 | barChartEntity.getSelectColor(), calculateAlphaColor(barChartEntity.getSelectColor()), 643 | Shader.TileMode.MIRROR); 644 | //设置垂直向下渐变 645 | mBarPaint.setShader(linearGradient); 646 | canvas.drawRoundRect(mBarRect, mBarWidth >> 1, mBarWidth >> 1, mBarPaint); 647 | } else { 648 | LinearGradient linearGradient = new LinearGradient(mBarRect.left, mBarRect.top, mBarRect.left, mBarRect.bottom, 649 | mBarColors[0], mBarEndColors[0], 650 | Shader.TileMode.MIRROR); 651 | //设置垂直向下渐变 652 | mBarPaint.setShader(linearGradient); 653 | canvas.drawRoundRect(mBarRect, mBarWidth >> 1, mBarWidth >> 1, mBarPaint); 654 | } 655 | 656 | 657 | if (isShowBarValue && percent == 1) { 658 | barLabel = String.format("%d%s", barChartEntity.getyValue()[0].intValue(), mUnitNameY); 659 | barLabelWidth = mTextPaint.measureText(barLabel); 660 | float startX = mBarRect.left; 661 | float startY = mBarRect.top; 662 | if (barChartEntity.getImgResId() > 0) { 663 | Bitmap bitmap =createBitmap(mContext,(int) (mBarWidth * 1.2), (int) (mBarWidth * 1.5), barChartEntity.getImgResId()); 664 | canvas.drawBitmap(bitmap, startX + mBarWidth / 2 - 5, startY - bitmap.getHeight(), mTextPaint); 665 | startY = startY - bitmap.getHeight(); 666 | } 667 | 668 | canvas.drawText(barLabel, startX + (mBarWidth - barLabelWidth) / 2, startY - 5, mTextPaint); 669 | } 670 | } else { 671 | int eachHeight = 0;//每一块的高度 672 | mBarRect.left = (int) (mStartX + mBarWidth * index + mBarSpace * (index + 1) - mLeftMoving) + (int) mXOffset; 673 | mBarRect.right = mBarRect.left + mBarWidth; 674 | for (int j = 0; j < mBarColors.length; j++) { 675 | mBarPaint.setColor(mBarColors[j]); 676 | mBarRect.bottom = (int) (mStartY - eachHeight * percent); 677 | 678 | mBarRect.top = (int) (mBarRect.bottom - ((mMaxHeight * (barChartEntity.getyValue()[j] / mMaxYDivisionValue))) * percent); 679 | // if (j == 0) { 680 | //// canvas.drawRoundRect(mBarRect, Dimens.dpToPx( 2), Dimens.dpToPx( 2), mBarPaint); 681 | //// mBarRect.bottom = mBarRect.top + (((int) (mBarRect.bottom - mBarRect.top)) >> 1); 682 | // canvas.drawRect(mBarRect, mBarPaint); 683 | // } else if (j == (mBarColors.length - 1)) { 684 | // //画圆角 685 | // canvas.drawRoundRect(mBarRect, radiusWidth, radiusWidth, mBarPaint); 686 | // mBarRect.top = mBarRect.bottom - (((int) (mBarRect.bottom - mBarRect.top)) >> 1); 687 | // canvas.drawRect(mBarRect, mBarPaint); 688 | // } else { 689 | canvas.drawRect(mBarRect, mBarPaint); 690 | // } 691 | 692 | eachHeight += (int) ((mMaxHeight * (barChartEntity.getyValue()[j] / mMaxYDivisionValue))); 693 | } 694 | } 695 | mBarLeftXPoints.add((int) mBarRect.left); 696 | mBarRightXPoints.add((int) mBarRect.right); 697 | } else { 698 | 699 | float width = 0; 700 | if (isDrawBorder && mClickPosition == index) { 701 | mBarPaint.setAlpha(255); 702 | width = 20; 703 | } else { 704 | mBarPaint.setAlpha(200); 705 | width = 0; 706 | } 707 | if (mBarColors.length == 2) { 708 | mBarRect.left = (int) (mStartX + mBarWidth * index * 2 + mBarSpace * (index + 1) - mLeftMoving); 709 | mBarRect.top = mStartY - (int) ((mMaxHeight * (barChartEntity.getyValue()[0] / mMaxYDivisionValue))) * percent; 710 | mBarRect.right = mBarRect.left + mBarWidth; 711 | 712 | mBarLeftXPoints.add((int) mBarRect.left); 713 | LinearGradient linearGradient = 714 | new LinearGradient(mBarRect.left, mBarRect.top, mBarRect.left, mBarRect.bottom, mBarColors[0], mBarEndColors[0], 715 | Shader.TileMode.MIRROR); 716 | 717 | mBarPaint.setShader(linearGradient); 718 | canvas.drawRoundRect(mBarRect, radiusWidth, radiusWidth, mBarPaint); 719 | mBarRect.top = mBarRect.bottom - (((int) (mBarRect.bottom - mBarRect.top)) >> 1); 720 | // canvas.drawRect(mBarRect, mBarPaint); 721 | 722 | mBarRect.left = mBarRect.right; 723 | mBarRect.top = mStartY - (int) ((mMaxHeight * (barChartEntity.getyValue()[1] / mMaxYDivisionValue))) * percent; 724 | mBarRect.right = mBarRect.left + mBarWidth; 725 | 726 | 727 | linearGradient = 728 | new LinearGradient(mBarRect.left, mBarRect.top, mBarRect.left, mBarRect.bottom, mBarColors[1], mBarEndColors[1], 729 | Shader.TileMode.MIRROR); 730 | mBarPaint.setShader(linearGradient); 731 | canvas.drawRoundRect(mBarRect, radiusWidth, radiusWidth, mBarPaint); 732 | // mBarRect.top = mBarRect.bottom - (((int) (mBarRect.bottom - mBarRect.top)) >> 1); 733 | // canvas.drawRect(mBarRect, mBarPaint); 734 | 735 | mBarRightXPoints.add((int) mBarRect.right); 736 | } 737 | } 738 | 739 | 740 | } 741 | //画点击效果 742 | // if (isDrawBorder) { 743 | // drawBorder(canvas, mClickPosition); 744 | // } 745 | } 746 | 747 | private void drawBorder(Canvas canvas, int position) { 748 | mBarRectClick.left = (int) (mStartX + mBarWidth * position + mBarSpace * (position + 1) - mLeftMoving); 749 | mBarRectClick.right = mBarRectClick.left + mBarWidth; 750 | mBarRectClick.bottom = mStartY; 751 | mBarRectClick.top = mStartY - (int) (mMaxHeight * (mData.get(position).getSum() / mMaxYDivisionValue)); 752 | canvas.drawRect(mBarRectClick, mBorderPaint); 753 | } 754 | 755 | /** 756 | * Y轴上的text (1)当最大值大于1 的时候 将其分成5份 计算每个部分的高度 分成几份可以自己定 757 | * (2)当最大值大于0小于1的时候 也是将最大值分成5份 758 | * (3)当为0的时候使用默认的值 759 | */ 760 | private void drawYAxisAndText(Canvas canvas) { 761 | mTextPaint.setAntiAlias(true); 762 | mTextPaint.setColor(Color.parseColor("#999999")); 763 | mTextPaint.setTypeface(Typeface.DEFAULT); 764 | mTextPaint.setTextSize(dp2px(mContext,8)); 765 | 766 | float eachHeight = (mMaxHeight / 5f); 767 | float textValue = 0; 768 | String text = null; 769 | if (mMaxYValue > 1) { 770 | for (int i = 0; i <= 5; i++) { 771 | float startY = mStartY - eachHeight * i; 772 | BigDecimal maxValue = new BigDecimal(mMaxYDivisionValue); 773 | BigDecimal fen = new BigDecimal(0.2 * i); 774 | //因为图表分了5条线,如果能除不进,需要显示小数点不然数据不准确 775 | if (mMaxYDivisionValue % 5 != 0) { 776 | text = maxValue.multiply(fen).floatValue() + mUnitNameY; 777 | } else { 778 | if (maxValue.multiply(fen).longValue() == 0) { 779 | text = String.valueOf(maxValue.multiply(fen).longValue()); 780 | } else { 781 | text = maxValue.multiply(fen).longValue() + mUnitNameY; 782 | } 783 | } 784 | if (!isHideAxisY) { 785 | //画Y坐标刻度 786 | canvas.drawText(text, mStartX - mTextPaint.measureText(text) - 10, startY + mTextPaint.measureText("0") / 2, mTextPaint); 787 | } 788 | if (i == 0) { 789 | if (!isHideAxisX) { 790 | //画X坐标横线 791 | canvas.drawLine(mStartX, startY, mTotalWidth - mPaddingRight - mRightMargin, startY, mAxisPaint); 792 | } 793 | if (!isHideAxisY) { 794 | //画Y坐标横线 795 | canvas.drawLine(mStartX, startY, mStartX, startY - mMaxHeight - 10, mAxisPaint); 796 | } 797 | } 798 | } 799 | } else if (mMaxYValue > 0 && mMaxYValue <= 1) { 800 | for (int i = 0; i <= 5; i++) { 801 | float startY = mStartY - eachHeight * i; 802 | textValue = CalculateUtil.numMathMul(mMaxYDivisionValue, (float) (0.2 * i)); 803 | text = String.valueOf(textValue); 804 | if (!isHideAxisY) { 805 | 806 | canvas.drawText(text, mStartX - mTextPaint.measureText(text) - 5, startY + mTextPaint.measureText("0") / 2, mTextPaint); 807 | canvas.drawLine(mStartX, startY, mTotalWidth - mPaddingRight - mRightMargin, startY, mAxisPaint); 808 | } 809 | } 810 | } else { 811 | for (int i = 0; i <= 5; i++) { 812 | float startY = mStartY - eachHeight * i; 813 | text = String.valueOf(10 * i); 814 | if (!isHideAxisY) { 815 | canvas.drawText(text, mStartX - mTextPaint.measureText(text) - 5, startY + mTextPaint.measureText("0") / 2, mTextPaint); 816 | canvas.drawLine(mStartX, startY, mTotalWidth - mPaddingRight - mRightMargin, startY, mAxisPaint); 817 | } 818 | } 819 | } 820 | } 821 | 822 | private void initOrResetVelocityTracker() { 823 | if (mVelocityTracker == null) { 824 | mVelocityTracker = VelocityTracker.obtain(); 825 | } else { 826 | mVelocityTracker.clear(); 827 | } 828 | } 829 | 830 | private void recycleVelocityTracker() { 831 | if (mVelocityTracker != null) { 832 | mVelocityTracker.recycle(); 833 | mVelocityTracker = null; 834 | } 835 | } 836 | 837 | @Override 838 | public void computeScroll() { 839 | if (mScroller.computeScrollOffset()) { 840 | mMovingThisTime = (mScroller.getCurrX() - mLastPointX); 841 | mLeftMoving = mLeftMoving + mMovingThisTime; 842 | mLastPointX = mScroller.getCurrX(); 843 | postInvalidate(); 844 | } 845 | } 846 | 847 | @Override 848 | public boolean onTouchEvent(MotionEvent event) { 849 | Log.d("onSingleTapUp:","onTouchEvent--x:"+event.getX()+" y:"+event.getY()); 850 | switch (event.getAction()) { 851 | case MotionEvent.ACTION_DOWN: 852 | mLastPointX = event.getX(); 853 | mScroller.abortAnimation();//终止动画 854 | initOrResetVelocityTracker(); 855 | mVelocityTracker.addMovement(event);//将用户的移动添加到跟踪器中。 856 | break; 857 | case MotionEvent.ACTION_MOVE: 858 | float movex = event.getX(); 859 | mMovingThisTime = mLastPointX - movex; 860 | mLeftMoving = mLeftMoving + mMovingThisTime; 861 | mLastPointX = movex; 862 | invalidate(); 863 | mVelocityTracker.addMovement(event); 864 | break; 865 | case MotionEvent.ACTION_UP: 866 | mVelocityTracker.addMovement(event); 867 | mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); 868 | int initialVelocity = (int) mVelocityTracker.getXVelocity(); 869 | mVelocityTracker.clear(); 870 | mScroller.fling((int) event.getX(), (int) event.getY(), -initialVelocity / 2, 871 | 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0); 872 | invalidate(); 873 | mLastPointX = event.getX(); 874 | break; 875 | case MotionEvent.ACTION_CANCEL: 876 | recycleVelocityTracker(); 877 | break; 878 | default: 879 | return super.onTouchEvent(event); 880 | } 881 | if (mGestureListener != null) { 882 | mGestureListener.onTouchEvent(event); 883 | } 884 | return true; 885 | } 886 | 887 | /** 888 | * 点击 889 | */ 890 | private class RangeBarOnGestureListener implements GestureDetector.OnGestureListener { 891 | @Override 892 | public boolean onDown(MotionEvent e) { 893 | return true; 894 | } 895 | 896 | @Override 897 | public void onShowPress(MotionEvent e) { 898 | } 899 | 900 | @Override 901 | public boolean onSingleTapUp(MotionEvent e) { 902 | int position = identifyWhichItemClick(e.getX(), e.getY()); 903 | Log.d("onSingleTapUp","x:"+e.getX()+" y:"+e.getY()); 904 | if (position != INVALID_POSITION && mOnItemBarClickListener != null) { 905 | mOnItemBarClickListener.onClick(position,e.getX(),e.getY()); 906 | setClicked(position); 907 | invalidate(); 908 | 909 | 910 | } 911 | return true; 912 | } 913 | 914 | @Override 915 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 916 | return false; 917 | } 918 | 919 | @Override 920 | public void onLongPress(MotionEvent e) { 921 | } 922 | 923 | @Override 924 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 925 | return false; 926 | } 927 | } 928 | 929 | /** 930 | * 设置选中的位置 931 | * 932 | * @param position 933 | */ 934 | public void setClicked(int position) { 935 | isDrawBorder = true; 936 | mClickPosition = position; 937 | } 938 | 939 | private int mLastXIntercept; 940 | private int mLastYIntercept; 941 | 942 | /** 943 | * 事件分发,在这里将子view不需要的事件交给父容器处理 944 | * 945 | * @param ev 946 | * @return 947 | */ 948 | @Override 949 | public boolean dispatchTouchEvent(MotionEvent ev) { 950 | int x = (int) ev.getX(); 951 | int y = (int) ev.getY(); 952 | switch (ev.getAction() & MotionEvent.ACTION_MASK) { 953 | case MotionEvent.ACTION_DOWN: 954 | //禁用父布局拦截事件,从而失去后续Action(即失去Move,UP等) 955 | getParent().requestDisallowInterceptTouchEvent(true); 956 | break; 957 | case MotionEvent.ACTION_MOVE: 958 | int a1 = x - mLastXIntercept; 959 | int a2 = y - mLastYIntercept; 960 | //上下滑动时候不拦截事件滑动事件,允许父View拦截事件 961 | if ((Math.abs(a1) < Math.abs(a2)) || (a2 > 0 && (mLeftMoving == (mMaxRight - mMinRight))) || (a2 < 0 && mLeftMoving == 0)) { 962 | getParent().requestDisallowInterceptTouchEvent(false); 963 | } 964 | break; 965 | case MotionEvent.ACTION_UP: 966 | break; 967 | } 968 | mLastXIntercept = x; 969 | mLastYIntercept = y; 970 | return super.dispatchTouchEvent(ev); 971 | } 972 | 973 | /** 974 | * 根据点击的手势位置识别是第几个柱图被点击 975 | * 976 | * @param x 977 | * @param y 978 | * @return -1时表示点击的是无效位置 979 | */ 980 | private int identifyWhichItemClick(float x, float y) { 981 | float leftx = 0; 982 | float rightx = 0; 983 | if (mData != null) { 984 | for (int i = 0; i < mData.size(); i++) { 985 | leftx = mBarLeftXPoints.get(i); 986 | rightx = mBarRightXPoints.get(i); 987 | if (x < leftx) { 988 | break; 989 | } 990 | if (leftx <= x && x <= rightx) { 991 | return i; 992 | } 993 | } 994 | } 995 | return INVALID_POSITION; 996 | } 997 | 998 | public Bitmap createBitmap(Context context,int width, int height, int resId) { 999 | Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId); 1000 | return zoomImg(bitmap,width,height); 1001 | 1002 | } 1003 | 1004 | public Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) { 1005 | Bitmap newbm = null; 1006 | if (bm != null) { 1007 | // 获得图片的宽高 1008 | int width = bm.getWidth(); 1009 | int height = bm.getHeight(); 1010 | // 计算缩放比例 1011 | float scaleWidth = ((float) newWidth) / width; 1012 | float scaleHeight = ((float) newHeight) / height; 1013 | // 取得想要缩放的matrix参数 1014 | Matrix matrix = new Matrix(); 1015 | matrix.postScale(scaleWidth, scaleHeight); 1016 | // 得到新的图片 1017 | newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); 1018 | } 1019 | return newbm; 1020 | } 1021 | 1022 | public void setTextAngle(float textAngle) { 1023 | mTextAngle = textAngle; 1024 | } 1025 | 1026 | public void setBottomMargin(int bottomMargin) { 1027 | mBottomMargin = bottomMargin; 1028 | } 1029 | 1030 | public void setTopMargin(int topMargin) { 1031 | mTopMargin = topMargin; 1032 | } 1033 | 1034 | public void setRightMargin(int rightMargin) { 1035 | mRightMargin = rightMargin; 1036 | } 1037 | 1038 | public void setLeftMargin(int leftMargin) { 1039 | mLeftMargin = leftMargin; 1040 | } 1041 | } 1042 | --------------------------------------------------------------------------------