├── 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 | 
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 |
8 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------