├── .DS_Store
├── .gitignore
├── .idea
├── checkstyle-idea.xml
├── compiler.xml
├── encodings.xml
├── jarRepositories.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── git-hook.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sign
│ │ └── signchart
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── sign
│ │ │ └── signchart
│ │ │ ├── MainActivity.java
│ │ │ └── nestscroll
│ │ │ ├── NestScrollActivity.java
│ │ │ └── RecyclerFragment.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_nest_scroll.xml
│ │ └── fragment_recycler.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── sign
│ └── signchart
│ └── ExampleUnitTest.java
├── build.gradle
├── gif
└── example.gif
├── gitHooks
└── commit-msg
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── wheelchart
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
└── main
├── AndroidManifest.xml
├── java
└── com
│ └── sign
│ └── wheelchart
│ ├── Entry.java
│ ├── HorizontalWheelChartView.java
│ ├── Utils.java
│ └── WheelChartLayout.java
└── res
└── values
├── attr.xml
├── color.xml
└── strings.xml
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # IntelliJ
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/assetWizardSettings.xml
41 | .idea/dictionaries
42 | .idea/libraries
43 | .idea/caches
44 |
45 | # Keystore files
46 | # Uncomment the following line if you do not want to check your keystore files in.
47 | #*.jks
48 |
49 | # External native build folder generated in Android Studio 2.2 and later
50 | .externalNativeBuild
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
--------------------------------------------------------------------------------
/.idea/checkstyle-idea.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WheelChart
2 | **Usage**
3 |
4 | ```implementation 'com.sign.wheelchart:wheelchart:1.1.1'```
5 |
6 | ```
7 |
14 | ```
15 |
16 | **Features**
17 | - 绘制横向图表
18 | - 可拖动并处理惯性滚动事件
19 | - 拖动或惯性滚动结束后,自动选中离中心最近的下标
20 | - 点击图表,根据点击区域选中离点击区域最近的下标
21 | - 处理嵌套滚动
22 |
23 | **blog**
24 |
25 | https://blog.csdn.net/SS_S1gn/article/details/89599605
26 |
27 | **Screens**
28 |
29 | 
30 |
31 | **Contributing**
32 |
33 | Yes:) If you found a bug, have an idea how to improve library or have a question, please create new issue or comment existing one. If you would like to contribute code fork the repository and send a pull request.
34 |
35 | **Email**
36 |
37 | yongshengdev@163.com
38 |
39 | **Special Thanks**
40 |
41 | [hencoder](https://hencoder.com/)
42 | https://github.com/totond/BooheeRuler
43 |
44 | **bug fix**
45 |
46 | 1.1.1 第一次打开直接跳转到指定位置;
47 | 将计算最大值、最小值逻辑放在setData中处理 不必特殊计算
48 | y轴单位可修改
49 |
50 | 1.1.0 修复sumsung s6滚动完毕不能回滚到中心区域的bug
51 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply from: 'git-hook.gradle'
3 |
4 | android {
5 | compileSdkVersion 28
6 | defaultConfig {
7 | applicationId "com.sign.signchart"
8 | minSdkVersion 16
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
26 | testImplementation 'junit:junit:4.12'
27 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
29 | implementation 'com.android.support:design:28.0.0'
30 | implementation 'com.sign.wheelchart:wheelchart:1.1.1'
31 | }
32 |
--------------------------------------------------------------------------------
/app/git-hook.gradle:
--------------------------------------------------------------------------------
1 | /**
2 | * 用法:
3 | * apply from: 'git-hook.gradle'
4 | */
5 |
6 | /**
7 | * 紧急开关
8 | */
9 | def forbid = false
10 |
11 | project.afterEvaluate {
12 | if (forbid) {
13 | preBuild.dependsOn 'resetGitHookConfig'
14 | } else {
15 | preBuild.dependsOn 'prepareGitHookConfig'
16 | }
17 | }
18 |
19 | task resetGitHookConfig {
20 | doFirst {
21 | File file = getGitHookFile('commit-msg')
22 | if (file != null) {
23 | file.delete()
24 | }
25 | }
26 | }
27 |
28 | def getGitHookFile(fileName) {
29 | def dirPath = getGitHookDir()
30 | if (dirPath != null && dirPath.length() > 0) {
31 | def file = new File(dirPath, fileName)
32 | if (file.exists()) {
33 | return file
34 | }
35 | }
36 | return null
37 | }
38 |
39 | task prepareGitHookConfig(type: Copy) {
40 | from getConfigFile()
41 | into getGitHookDir()
42 | // 需要添加权限
43 | fileMode 0755
44 | }
45 |
46 | def getConfigFile() {
47 | File configFile = new File(project.rootDir, "gitHooks/commit-msg")
48 | if (configFile.exists()) {
49 | return configFile.absolutePath
50 | }
51 | return null
52 | }
53 |
54 | def getGitHookDir() {
55 | File gitHookDir = new File(project.rootDir, ".git/hooks/")
56 | if (!gitHookDir.exists()) {
57 | println("can't find .git directory in the ${project.rootDir.absolutePath}," +
58 | " please ensure it have been tracked by git!")
59 | return null
60 | }
61 | return gitHookDir.absolutePath
62 | }
63 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/sign/signchart/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.sign.signchart;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.sign.signchart", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sign/signchart/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.sign.signchart;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.util.Log;
7 | import android.view.View;
8 |
9 | import com.sign.signchart.nestscroll.NestScrollActivity;
10 | import com.sign.wheelchart.Entry;
11 | import com.sign.wheelchart.HorizontalWheelChartView;
12 | import com.sign.wheelchart.Utils;
13 | import com.sign.wheelchart.WheelChartLayout;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | public class MainActivity extends AppCompatActivity {
19 | private static final String TAG = "MainActivity";
20 | private int mSelectIndex = -1;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 | WheelChartLayout wclBillStraight = findViewById(R.id.wcl_bill_straight);
27 | WheelChartLayout wclBillBezier = findViewById(R.id.wcl_bill_bezier);
28 | List list = new ArrayList<>();
29 | initDemoList(list);
30 | //折线图
31 | float xLabelInterval1 = (Utils.getScreenWidth(this) - Utils.dp2px(this, 40)) / 4f;
32 | wclBillStraight.setXLabelInterval(xLabelInterval1);
33 | wclBillStraight.setInterceptTouchEvent(false);
34 | wclBillStraight.setSelectIndex(0);
35 | wclBillStraight.setData(list);
36 | wclBillStraight.setScrollBackListener(new HorizontalWheelChartView.ScrollBackListener() {
37 | @Override
38 | public void onScrollBack(int selectIndex) {
39 | Log.d(TAG, "onScrollBack selectIndex === " + selectIndex);
40 | if (mSelectIndex != selectIndex) {
41 | mSelectIndex = selectIndex;
42 | Log.d(TAG, "selectIndex has change, can refresh. selectIndex === " + mSelectIndex);
43 | }
44 | }
45 | });
46 | //贝塞尔连接
47 | float xLabelInterval2 = Utils.getScreenWidth(this) / 4f;
48 | wclBillBezier.setXLabelInterval(xLabelInterval2);
49 | wclBillBezier.setInterceptTouchEvent(false);
50 | wclBillBezier.setSelectIndex(0);
51 | wclBillBezier.setData(list);
52 | }
53 |
54 | public void nestScroll(View view) {
55 | startActivity(new Intent(this, NestScrollActivity.class));
56 | }
57 |
58 | private void initDemoList(List list) {
59 | list.add(new Entry("Jan", 200));
60 | list.add(new Entry("Feb", 200));
61 | list.add(new Entry("Mar", 5000));
62 | list.add(new Entry("Apr", 5000));
63 | list.add(new Entry("May", 300));
64 | list.add(new Entry("Jun", 3000));
65 | list.add(new Entry("Jul", 5000));
66 | list.add(new Entry("Aug", 3000));
67 | list.add(new Entry("Sept", 4500));
68 | list.add(new Entry("Oct", 2500));
69 | list.add(new Entry("Nov", 3000));
70 | list.add(new Entry("Dec", 200));
71 | list.add(new Entry("Jan", 200));
72 | list.add(new Entry("Feb", 200));
73 | list.add(new Entry("Mar", 5000));
74 | list.add(new Entry("Apr", 5000));
75 | list.add(new Entry("May", 300));
76 | list.add(new Entry("Jun", 3000));
77 | list.add(new Entry("Jul", 5000));
78 | list.add(new Entry("Aug", 3000));
79 | list.add(new Entry("Sept", 4500));
80 | list.add(new Entry("Oct", 2500));
81 | list.add(new Entry("Nov", 3000));
82 | list.add(new Entry("Dec", 200));
83 | list.add(new Entry("Jan", 200));
84 | list.add(new Entry("Feb", 200));
85 | list.add(new Entry("Mar", 5000));
86 | list.add(new Entry("Apr", 5000));
87 | list.add(new Entry("May", 300));
88 | list.add(new Entry("Jun", 3000));
89 | list.add(new Entry("Jul", 5000));
90 | list.add(new Entry("Aug", 3000));
91 | list.add(new Entry("Sept", 4500));
92 | list.add(new Entry("Oct", 2500));
93 | list.add(new Entry("Nov", 3000));
94 | list.add(new Entry("Dec", 200));
95 | list.add(new Entry("Jan", 200));
96 | list.add(new Entry("Feb", 200));
97 | list.add(new Entry("Mar", 5000));
98 | list.add(new Entry("Apr", 5000));
99 | list.add(new Entry("May", 300));
100 | list.add(new Entry("Jun", 3000));
101 | list.add(new Entry("Jul", 5000));
102 | list.add(new Entry("Aug", 3000));
103 | list.add(new Entry("Sept", 4500));
104 | list.add(new Entry("Oct", 2500));
105 | list.add(new Entry("Nov", 3000));
106 | list.add(new Entry("Dec", 200));
107 | list.add(new Entry("Jan", 200));
108 | list.add(new Entry("Feb", 200));
109 | list.add(new Entry("Mar", 5000));
110 | list.add(new Entry("Apr", 5000));
111 | list.add(new Entry("May", 300));
112 | list.add(new Entry("Jun", 3000));
113 | list.add(new Entry("Jul", 5000));
114 | list.add(new Entry("Aug", 3000));
115 | list.add(new Entry("Sept", 4500));
116 | list.add(new Entry("Oct", 2500));
117 | list.add(new Entry("Nov", 3000));
118 | list.add(new Entry("Dec", 200));
119 | list.add(new Entry("Jan", 200));
120 | list.add(new Entry("Feb", 200));
121 | list.add(new Entry("Mar", 5000));
122 | list.add(new Entry("Apr", 5000));
123 | list.add(new Entry("May", 300));
124 | list.add(new Entry("Jun", 3000));
125 | list.add(new Entry("Jul", 5000));
126 | list.add(new Entry("Aug", 3000));
127 | list.add(new Entry("Sept", 4500));
128 | list.add(new Entry("Oct", 2500));
129 | list.add(new Entry("Nov", 3000));
130 | list.add(new Entry("Dec", 200));
131 | list.add(new Entry("Jan", 200));
132 | list.add(new Entry("Feb", 200));
133 | list.add(new Entry("Mar", 5000));
134 | list.add(new Entry("Apr", 5000));
135 | list.add(new Entry("May", 300));
136 | list.add(new Entry("Jun", 3000));
137 | list.add(new Entry("Jul", 5000));
138 | list.add(new Entry("Aug", 3000));
139 | list.add(new Entry("Sept", 4500));
140 | list.add(new Entry("Oct", 2500));
141 | list.add(new Entry("Nov", 3000));
142 | list.add(new Entry("Dec", 200));
143 | list.add(new Entry("Jan", 200));
144 | list.add(new Entry("Feb", 200));
145 | list.add(new Entry("Mar", 5000));
146 | list.add(new Entry("Apr", 5000));
147 | list.add(new Entry("May", 300));
148 | list.add(new Entry("Jun", 3000));
149 | list.add(new Entry("Jul", 5000));
150 | list.add(new Entry("Aug", 3000));
151 | list.add(new Entry("Sept", 4500));
152 | list.add(new Entry("Oct", 2500));
153 | list.add(new Entry("Nov", 3000));
154 | list.add(new Entry("Dec", 200));
155 | list.add(new Entry("Jan", 200));
156 | list.add(new Entry("Feb", 200));
157 | list.add(new Entry("Mar", 5000));
158 | list.add(new Entry("Apr", 5000));
159 | list.add(new Entry("May", 300));
160 | list.add(new Entry("Jun", 3000));
161 | list.add(new Entry("Jul", 5000));
162 | list.add(new Entry("Aug", 3000));
163 | list.add(new Entry("Sept", 4500));
164 | list.add(new Entry("Oct", 2500));
165 | list.add(new Entry("Nov", 3000));
166 | list.add(new Entry("Dec", 200));
167 | list.add(new Entry("Jan", 200));
168 | list.add(new Entry("Feb", 200));
169 | list.add(new Entry("Mar", 5000));
170 | list.add(new Entry("Apr", 5000));
171 | list.add(new Entry("May", 300));
172 | list.add(new Entry("Jun", 3000));
173 | list.add(new Entry("Jul", 5000));
174 | list.add(new Entry("Aug", 3000));
175 | list.add(new Entry("Sept", 4500));
176 | list.add(new Entry("Oct", 2500));
177 | list.add(new Entry("Nov", 3000));
178 | list.add(new Entry("Dec", 200));
179 | list.add(new Entry("Jan", 200));
180 | list.add(new Entry("Feb", 200));
181 | list.add(new Entry("Mar", 5000));
182 | list.add(new Entry("Apr", 5000));
183 | list.add(new Entry("May", 300));
184 | list.add(new Entry("Jun", 3000));
185 | list.add(new Entry("Jul", 5000));
186 | list.add(new Entry("Aug", 3000));
187 | list.add(new Entry("Sept", 4500));
188 | list.add(new Entry("Oct", 2500));
189 | list.add(new Entry("Nov", 3000));
190 | list.add(new Entry("Dec", 200));
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sign/signchart/nestscroll/NestScrollActivity.java:
--------------------------------------------------------------------------------
1 | package com.sign.signchart.nestscroll;
2 |
3 | import android.support.annotation.Nullable;
4 | import android.support.design.widget.TabLayout;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.app.FragmentPagerAdapter;
7 | import android.support.v4.view.ViewPager;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.os.Bundle;
10 |
11 | import com.sign.signchart.R;
12 | import com.sign.wheelchart.Entry;
13 | import com.sign.wheelchart.Utils;
14 | import com.sign.wheelchart.WheelChartLayout;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | public class NestScrollActivity extends AppCompatActivity {
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(R.layout.activity_nest_scroll);
25 |
26 | WheelChartLayout chartBillLayout = findViewById(R.id.cbl_bill);
27 | List list = new ArrayList<>();
28 | list.add(new Entry("Jan", 200));
29 | list.add(new Entry("Feb", 200));
30 | list.add(new Entry("Mar", 5000));
31 | list.add(new Entry("Apr", 5000));
32 | list.add(new Entry("May", 300));
33 | list.add(new Entry("Jun", 3000));
34 | list.add(new Entry("Jul", 5000));
35 | list.add(new Entry("Aug", 3000));
36 | list.add(new Entry("Sept", 4500));
37 | list.add(new Entry("Oct", 2500));
38 | list.add(new Entry("Nov", 3000));
39 | list.add(new Entry("Dec", 200));
40 | float xLabelInterval = Utils.getScreenWidth(this) / 4f;
41 | chartBillLayout.setXLabelInterval(xLabelInterval);
42 | chartBillLayout.setInterceptTouchEvent(false);
43 | chartBillLayout.setSelectIndex(0);
44 | chartBillLayout.setData(list);
45 | ViewPager viewPager = findViewById(R.id.view_pager);
46 | viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
47 | @Override
48 | public Fragment getItem(int position) {
49 | return new RecyclerFragment();
50 | }
51 |
52 | @Override
53 | public int getCount() {
54 | return 6;
55 | }
56 |
57 | @Nullable
58 | @Override
59 | public CharSequence getPageTitle(int position) {
60 | return "分类" + position;
61 | }
62 | });
63 | TabLayout tabLayout = findViewById(R.id.tab_layout);
64 | tabLayout.setupWithViewPager(viewPager);
65 | tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sign/signchart/nestscroll/RecyclerFragment.java:
--------------------------------------------------------------------------------
1 | package com.sign.signchart.nestscroll;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.Gravity;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.TextView;
14 | import com.sign.signchart.R;
15 |
16 | /**
17 | * Created by cys on 2018/8/17 0017.
18 | */
19 | public class RecyclerFragment extends Fragment {
20 | @Nullable
21 | @Override
22 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
23 | return inflater.inflate(R.layout.fragment_recycler, container, false);
24 | }
25 |
26 | @Override
27 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
28 | super.onViewCreated(view, savedInstanceState);
29 | RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
30 | recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
31 | recyclerView.setAdapter(new RecyclerView.Adapter() {
32 | @NonNull
33 | @Override
34 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
35 | TextView textView = new TextView(getActivity());
36 | textView.setText("条目 in RecyclerView");
37 | textView.setHeight(100);
38 | textView.setGravity(Gravity.CENTER);
39 |
40 | return new RecyclerView.ViewHolder(textView) {
41 | };
42 | }
43 |
44 | @Override
45 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
46 |
47 | }
48 |
49 | @Override
50 | public int getItemCount() {
51 | return 30;
52 | }
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
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/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
28 |
29 |
35 |
36 |
43 |
44 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_nest_scroll.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
19 |
20 |
26 |
27 |
28 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_recycler.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WheelChart
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/sign/signchart/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sign.signchart;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.0'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.2'
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 |
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gif/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/gif/example.gif
--------------------------------------------------------------------------------
/gitHooks/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # 获取当前提交的 commit msg
4 | commit_msg=`cat $1`
5 |
6 | msg_re="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{2,}"
7 |
8 | if [[ ! $commit_msg =~ $msg_re ]]
9 | then
10 | echo "commit消息提交不合法,请使用IDEA约束插件 Git Commit Template 格式化,详情请查看Commit Message格式:https://www.yuque.com/docs/share/9a4bf0a0-902a-4347-9b9f-48e162af691d?# 《代码review流程及工具整理》\n"
11 |
12 | # 异常退出
13 | exit 1
14 | fi
15 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
15 |
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yongshengdev/WheelChart/0388bd4a43956bb88b8377e170c21df46bce474e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 22 17:37:19 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':wheelchart'
2 |
--------------------------------------------------------------------------------
/wheelchart/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
--------------------------------------------------------------------------------
/wheelchart/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 |
5 |
6 | android {
7 | compileSdkVersion 28
8 |
9 |
10 | defaultConfig {
11 | minSdkVersion 16
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
17 |
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 |
32 | implementation 'com.android.support:appcompat-v7:28.0.0'
33 | testImplementation 'junit:junit:4.12'
34 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
35 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
36 | }
37 |
38 | version = "1.1.1"//version
39 | group = "com.sign.wheelchart"//groupId
40 |
41 | def siteUrl = 'https://github.com/SilenceBurst/WheelChart' //项目的主页
42 | def gitUrl = 'https://github.com/SilenceBurst/WheelChart.git' //github仓库的地址
43 | Properties properties = new Properties()
44 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
45 |
46 | bintray {
47 | user = properties.getProperty("bintray.user")
48 | key = properties.getProperty("bintray.apikey")
49 | pkg {
50 | repo = 'WheelChart'//创建的仓库名字
51 | name = 'com.sign.wheelchart'//packageName
52 | websiteUrl = siteUrl
53 | vcsUrl = gitUrl
54 | licenses = ['Apache-2.0']//创建仓库时选择的license type
55 | userOrg = 'silenceburst' //自己bintray创建的organization名称
56 | publish = true // 是否是公开项目。
57 |
58 | version {
59 | name = '1.1.1'//版本号
60 | desc = 'Android WheelChart'//版本描述
61 | released = new Date()
62 | vcsTag = 'v1.0'
63 | attributes = ['gradle-plugin': 'com.use.less:com.use.less.gradle:gradle-useless-plugin']
64 | }
65 | }
66 | configurations = ['archives']
67 | }
68 | install {
69 | repositories.mavenInstaller {
70 | pom {
71 | project {
72 | packaging 'aar'
73 | //项目描述
74 | description 'Android WheelChart'
75 | url siteUrl
76 | // 填写开源协议
77 | licenses {
78 | license {
79 | name 'Apache-2.0'
80 | url ' http://www.apache.org/licenses/LICENSE-2.0'
81 | }
82 | }
83 | developers {
84 | developer {
85 | id 'SilenceBurst'
86 | //填写邮件地址
87 | email 'yongshengdeveloper@gmail.com'
88 | }
89 | }
90 | scm {
91 | connection gitUrl
92 | developerConnection gitUrl
93 | url siteUrl
94 | }
95 | }
96 | }
97 | }
98 | }
99 | //打包源码
100 | task sourcesJar(type: Jar) {
101 | from android.sourceSets.main.java.srcDirs
102 | classifier = 'sources'
103 | }
104 | //打包源码doc文档
105 | task javadoc(type: Javadoc) {
106 | options.encoding = "UTF-8"
107 | source = android.sourceSets.main.java.srcDirs
108 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
109 | }
110 | task javadocJar(type: Jar, dependsOn: javadoc) {
111 | classifier = 'javadoc'
112 | from javadoc.destinationDir
113 | }
114 | artifacts {
115 | archives javadocJar
116 | archives sourcesJar
117 | }
--------------------------------------------------------------------------------
/wheelchart/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/wheelchart/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/wheelchart/src/main/java/com/sign/wheelchart/Entry.java:
--------------------------------------------------------------------------------
1 | package com.sign.wheelchart;
2 |
3 | /**
4 | * Created by CaoYongSheng
5 | * on 2019-04-23
6 | *
7 | * @author admin
8 | */
9 | public class Entry {
10 | private String xLabel;
11 | private double yValue;
12 |
13 | public Entry(String xLabel, double yValue) {
14 | this.xLabel = xLabel;
15 | this.yValue = yValue;
16 | }
17 |
18 | public String getXLabel() {
19 | return xLabel;
20 | }
21 |
22 | public void setXLabel(String xLabel) {
23 | this.xLabel = xLabel;
24 | }
25 |
26 | public double getYValue() {
27 | return yValue;
28 | }
29 |
30 | public void setYValue(double yValue) {
31 | this.yValue = yValue;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/wheelchart/src/main/java/com/sign/wheelchart/HorizontalWheelChartView.java:
--------------------------------------------------------------------------------
1 | package com.sign.wheelchart;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.DashPathEffect;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.PointF;
10 | import android.os.Build;
11 | import android.view.MotionEvent;
12 | import android.view.VelocityTracker;
13 | import android.view.View;
14 | import android.view.ViewConfiguration;
15 | import android.view.ViewGroup;
16 | import android.view.ViewTreeObserver;
17 | import android.widget.OverScroller;
18 |
19 | import java.util.ArrayList;
20 | import java.util.List;
21 |
22 | /**
23 | * Created by CaoYongSheng
24 | * on 2019-04-22
25 | * 点击事件的判定规则 若down事件后紧接up事件(move距离小于IGNORE_MOVE_OFFSET忽略) 则判断为点击事件 跳转到指定坐标
26 | *
27 | * @author admin
28 | */
29 | public class HorizontalWheelChartView extends View {
30 | private static final String TAG = "HorizontalWheelChart";
31 | private final static float BEZIER_RADIAN = 0.16f;//贝塞尔连接线弧度 值越小越尖锐
32 | private final static float BLACK_X_LABEL_LINE = 20;//px轴线两端的空白
33 | private final static float BLANK_VALUE_TEXT = 12;//px坐标点和文字之间的间距
34 | private final static int INVALID_ID = -1;//非法触控id
35 | //惯性回滚最小偏移值,小于这个值就应该直接滑动到目的点
36 | private static final int MIN_SCROLLER_DP = 1;
37 | private float IGNORE_MOVE_OFFSET = 2.5f;//可忽略的move事件偏移量
38 | private float IGNORE_OFFSET = 0.0001f;//可忽略的偏移量
39 | private boolean mDownAndUp = false;//down事件后紧接up事件
40 | private int mActivePointerId = INVALID_ID;//记录首个触控点的id 避免多点触控引起的滚动
41 | private WheelChartLayout mParent;
42 | private Context mContext;
43 | //提前刻画量
44 | private float mDrawOffset;
45 | private Paint mXLabelLinePaint, mXLabelTextNormalPaint, mXLabelTextSelectPaint, mXValuePointColorPaint, mXValuePointWhitePaint, mNormalValueTextPaint, mSelectValueTextPaint, mLinkLinePaint;
46 | //x轴轴线的路径
47 | private Path mXLabelLinePath;
48 | //x轴label未选中文字属性
49 | private Paint.FontMetrics mXLabelTextNormalMetrics;
50 | //x轴label选中文字属性
51 | private Paint.FontMetrics mXLabelTextSelectMetrics;
52 | //x轴label正常文字的高度
53 | private float mXLabelTextNormalHeight;
54 | //value选中文字的高度
55 | private float mValueSelectHeight;
56 | //选中坐标文字属性
57 | private Paint.FontMetrics mSelectValueMetrics;
58 | //未选中坐标文字属性
59 | private Paint.FontMetrics mNormalValueMetrics;
60 | //值的坐标
61 | private List mPointList;
62 | //选中值的路径
63 | private Path mPointPath;
64 | //上个touch事件的x坐标
65 | private float mLastX = 0;
66 | //上个touch事件的y坐标
67 | private float mLastY = 0;
68 | //一半宽度
69 | private int mHalfWidth = 0;
70 | //图表的总长度、最小可滑动值、最大可滑动值
71 | private int mLength, mMinPosition = 0, mMaxPosition = 0;
72 | //速度获取
73 | private VelocityTracker mVelocityTracker;
74 | //惯性最大最小速度
75 | private int mMaximumVelocity, mMinimumVelocity;
76 | //控制滑动
77 | private OverScroller mOverScroller;
78 | //当前选中的下标
79 | private int mSelectIndex = 0;
80 | //回滚的监听
81 | private ScrollBackListener mScrollBackListener;
82 |
83 | /**
84 | * 回滚的监听 回滚发生的条件:1.首次进入的选中 2.up时的回滚或惯性滚动结束的回滚 3.cancel时的回滚
85 | * 可以在使用该view页面保存上次的选中下标 回滚结束,下标不一致,刷新下标并做相关的操作
86 | */
87 | public interface ScrollBackListener {
88 | void onScrollBack(int selectIndex);
89 | }
90 |
91 | public HorizontalWheelChartView(Context context, WheelChartLayout wheelChartLayout) {
92 | super(context);
93 | mParent = wheelChartLayout;
94 | refreshSize();
95 | init(context);
96 | }
97 |
98 | public void init(Context context) {
99 | mContext = context;
100 | mXLabelLinePath = new Path();
101 | mPointPath = new Path();
102 | mPointList = new ArrayList<>();
103 | mSelectIndex = mParent.getSelectIndex();
104 | mDrawOffset = Utils.dp2px(context, mParent.getXLabelInterval());
105 | mOverScroller = new OverScroller(mContext);
106 | mVelocityTracker = VelocityTracker.obtain();
107 | mMaximumVelocity = ViewConfiguration.get(context)
108 | .getScaledMaximumFlingVelocity();
109 | mMinimumVelocity = ViewConfiguration.get(context)
110 | .getScaledMinimumFlingVelocity();
111 | initPaint();
112 | checkAPILevel();
113 | //第一次进入,滚动到指定位置
114 | getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
115 | @Override
116 | public void onGlobalLayout() {
117 | getViewTreeObserver().removeOnGlobalLayoutListener(this);
118 | scrollImmediate();
119 | }
120 | });
121 | }
122 |
123 | //处理滑动 计算现在的event坐标和上一个触摸事件的坐标来计算偏移量 决定scrollBy的多少
124 | @Override
125 | public boolean onTouchEvent(MotionEvent event) {
126 | //开始速度检测
127 | if (mVelocityTracker == null) {
128 | mVelocityTracker = VelocityTracker.obtain();
129 | }
130 | mVelocityTracker.addMovement(event);
131 | ViewGroup parent = (ViewGroup) getParent();//为了解决刻度尺在scrollview这种布局里面滑动冲突问题
132 | switch (event.getAction()) {
133 | case MotionEvent.ACTION_DOWN:
134 | mDownAndUp = true;
135 | //记录首个触控点的id
136 | mActivePointerId = event.findPointerIndex(event.getActionIndex());
137 | if (!mOverScroller.isFinished()) {
138 | mOverScroller.abortAnimation();
139 | }
140 | mLastX = event.getX();
141 | mLastY = event.getY();
142 | parent.requestDisallowInterceptTouchEvent(false);//按下时开始让父控件不要处理任何touch事件
143 | break;
144 | case MotionEvent.ACTION_MOVE:
145 | if (mActivePointerId == INVALID_ID || event.findPointerIndex(mActivePointerId) == INVALID_ID) {
146 | break;
147 | }
148 | //计算首个触控点x方向移动距离
149 | float moveX = mLastX - event.getX(mActivePointerId);
150 | //计算首个触控点y方向移动距离
151 | float moveY = mLastY - event.getY(mActivePointerId);
152 | //判断x方向移动距离大于等于y方向距离 则判断为x轴滚动即滑动图表 反之判断为y轴滚动将事件交由父布局处理
153 | if (Math.abs(moveX) >= Math.abs(moveY)) {
154 | if (Math.abs(moveX) > IGNORE_MOVE_OFFSET) {
155 | parent.requestDisallowInterceptTouchEvent(true);
156 | mDownAndUp = false;
157 | mLastX = event.getX(mActivePointerId);
158 | scrollBy((int) moveX, 0);
159 | }
160 | } else {
161 | if (Math.abs(moveY) > IGNORE_MOVE_OFFSET) {
162 | parent.requestDisallowInterceptTouchEvent(false);
163 | mDownAndUp = false;
164 | mLastY = event.getY(mActivePointerId);
165 | }
166 | }
167 | break;
168 | case MotionEvent.ACTION_UP:
169 | //判断是单点事件 跳转到指定位置
170 | if (mDownAndUp) {
171 | int index = (int) ((event.getX() + getScrollX()) / mParent.getXLabelInterval());
172 | if ((event.getX() + getScrollX()) % mParent.getXLabelInterval() > mParent.getXLabelInterval() * 0.5) {
173 | if (index <= mParent.getData().size() - 2) {
174 | index++;
175 | }
176 | }
177 | if (mSelectIndex != index) {
178 | mSelectIndex = index;
179 | scrollBackToExactPosition();
180 | }
181 | mDownAndUp = false;
182 | } else {
183 | //处理松手后的Fling
184 | mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
185 | int velocityX = (int) mVelocityTracker.getXVelocity();
186 | if (Math.abs(velocityX) > mMinimumVelocity) {
187 | fling(-velocityX);
188 | } else {
189 | scrollBackToExactPosition();
190 | }
191 | }
192 | //VelocityTracker回收
193 | if (mVelocityTracker != null) {
194 | mVelocityTracker.recycle();
195 | mVelocityTracker = null;
196 | }
197 | mActivePointerId = INVALID_ID;
198 | mLastX = 0;
199 | mLastY = 0;
200 | parent.requestDisallowInterceptTouchEvent(false);//up或者cancel的时候恢复
201 | break;
202 | case MotionEvent.ACTION_CANCEL:
203 | mDownAndUp = false;
204 | mActivePointerId = INVALID_ID;
205 | mLastX = 0;
206 | mLastY = 0;
207 | if (!mOverScroller.isFinished()) {
208 | mOverScroller.abortAnimation();
209 | }
210 | //回滚到整点刻度
211 | scrollBackToExactPosition();
212 | //VelocityTracker回收
213 | if (mVelocityTracker != null) {
214 | mVelocityTracker.recycle();
215 | mVelocityTracker = null;
216 | }
217 | parent.requestDisallowInterceptTouchEvent(false);//up或者cancel的时候恢复
218 | break;
219 | default:
220 | mDownAndUp = false;
221 | break;
222 | }
223 | return true;
224 | }
225 |
226 | //惯性滚动
227 | private void fling(int vX) {
228 | mOverScroller.fling(getScrollX(), 0, vX, 0, mMinPosition, mMaxPosition, 0, 0);
229 | invalidate();
230 | }
231 |
232 | /**
233 | * 由fling方法发起的invalidate,会最终调用view的computeScroll方法
234 | * 而在computeScroll方法中又发起了postInvalidate,最终又会调用view的computeScroll,如此循环绘制形成了惯性滚动
235 | */
236 | @Override
237 | public void computeScroll() {
238 | if (mOverScroller.computeScrollOffset()) {
239 | scrollTo(mOverScroller.getCurrX(), mOverScroller.getCurrY());
240 | //这是最后OverScroller的最后一次滑动,如果这次滑动完了mCurrentScale不是整数,则把图表移动到最近的整数位置
241 | if (mOverScroller.isFinished()) {
242 | //fling完毕,检测是否需要回滚
243 | scrollBackToExactPosition();
244 | }
245 | postInvalidate();
246 | }
247 | }
248 |
249 | //首次进入或view尺寸变化 直接滚动到指定位置
250 | private void scrollImmediate() {
251 | float rightPosition = mSelectIndex * mParent.getXLabelInterval() - (float) getWidth() / 2;
252 | if (mScrollBackListener != null) {
253 | mScrollBackListener.onScrollBack(mSelectIndex);
254 | }
255 | if (Math.abs(getScrollX() - rightPosition) > IGNORE_OFFSET) {
256 | scrollBy(Math.round(rightPosition - getScrollX()), 0);
257 | }
258 | }
259 |
260 | //触摸事件或惯性滚动结束后 应滚动到中心位置
261 | private void scrollBackToExactPosition() {
262 | float rightPosition = mSelectIndex * mParent.getXLabelInterval() - (float) getWidth() / 2;
263 | if (mScrollBackListener != null) {
264 | mScrollBackListener.onScrollBack(mSelectIndex);
265 | }
266 | if (Math.abs(getScrollX() - rightPosition) > IGNORE_OFFSET) {
267 | int dx = Math.round(rightPosition - getScrollX());
268 | if (Math.abs(dx) > MIN_SCROLLER_DP) {
269 | //渐变回弹
270 | mOverScroller.startScroll(getScrollX(), getScrollY(), dx, 0, 500);
271 | invalidate();
272 | } else {
273 | //立刻回弹
274 | scrollBy(dx, 0);
275 | }
276 | }
277 | }
278 |
279 | @Override
280 | public void scrollTo(int x, int y) {
281 | //默认左边缘为x最小值-半个控件的宽度
282 | if (x < mMinPosition) {
283 | x = mMinPosition;
284 | }
285 | //默认右边缘为x最大值+半个控件的宽度
286 | if (x > mMaxPosition) {
287 | x = mMaxPosition;
288 | }
289 | if (x != getScrollX()) {
290 | super.scrollTo(x, y);
291 | }
292 | mSelectIndex = scrollX2Index(x);
293 | }
294 |
295 | private int scrollX2Index(int x) {
296 | int index = (int) ((x + getWidth() * 0.5) / mParent.getXLabelInterval());
297 | if ((x + getWidth() * 0.5) % mParent.getXLabelInterval() > mParent.getXLabelInterval() * 0.5) {
298 | if (index <= mParent.getData().size() - 2) {
299 | index++;
300 | }
301 | }
302 | return index;
303 | }
304 |
305 | public void refreshSize() {
306 | mLength = (int) ((mParent.getData().size() - 1) * mParent.getXLabelInterval());
307 | mHalfWidth = getWidth() / 2;
308 | //左右空白间距为控件宽度一半
309 | mMinPosition = -mHalfWidth;
310 | mMaxPosition = mLength - mHalfWidth;
311 | }
312 |
313 | //获取控件宽高,设置相应信息
314 | @Override
315 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
316 | super.onSizeChanged(w, h, oldw, oldh);
317 | refreshSize();
318 | scrollImmediate();
319 | }
320 |
321 | @Override
322 | protected void onDraw(Canvas canvas) {
323 | super.onDraw(canvas);
324 | //起始、终止绘制值的下标
325 | int startIndex = (int) ((getScrollX() - mDrawOffset) / mParent.getXLabelInterval());
326 | int endIndex = (int) ((getScrollX() + getWidth() + mDrawOffset) / mParent.getXLabelInterval());
327 | int height = getHeight();
328 | //先画path路径
329 | mPointList.clear();
330 | for (int i = startIndex; i <= endIndex; i++) {
331 | if (i >= 0 && i < mParent.getData().size()) {
332 | float locationX = i * mParent.getXLabelInterval();
333 | if (mParent.getXLabelGravity() == WheelChartLayout.X_TOP) {
334 | //上方及下方空白间距+label文字高度+label文字和轴线的间距+选中值文字高度+坐标点和文字间距(*2 多了一份是坐标点的半径)
335 | float distance = (float) ((height - BLACK_X_LABEL_LINE - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mValueSelectHeight - BLANK_VALUE_TEXT * 2) /
336 | (mParent.getYMaxValue() - mParent.getYMinValue()) * (mParent.getData().get(i).getYValue() - mParent.getYMinValue()));
337 | //TODO onDraw方法中不能循环创建对象
338 | mPointList.add(new PointF(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT)));
339 | } else {
340 | float distance = (float) ((height - BLACK_X_LABEL_LINE - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mValueSelectHeight - BLANK_VALUE_TEXT * 2) /
341 | (mParent.getYMaxValue() - mParent.getYMinValue()) * (mParent.getData().get(i).getYValue() - mParent.getYMinValue()));
342 | mPointList.add(new PointF(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval())));
343 | }
344 | }
345 | }
346 | drawValuePath(canvas);
347 | for (int i = startIndex; i <= endIndex; i++) {
348 | if (i >= 0 && i < mParent.getData().size()) {
349 | float locationX = i * mParent.getXLabelInterval();
350 | if (mParent.getXLabelGravity() == WheelChartLayout.X_TOP) {
351 | //x轴label文字在上面
352 | if (mParent.getData().get(i).getXLabel().length() > 0) {
353 | //是否是中心线
354 | if (mSelectIndex == i) {
355 | canvas.drawText(mParent.getData().get(i).getXLabel(), locationX, -mXLabelTextSelectMetrics.top, mXLabelTextSelectPaint);
356 | } else {
357 | canvas.drawText(mParent.getData().get(i).getXLabel(), locationX, -mXLabelTextNormalMetrics.top, mXLabelTextNormalPaint);
358 | }
359 | }
360 | //x轴轴线
361 | mXLabelLinePath.reset();
362 | mXLabelLinePath.moveTo(locationX, mXLabelTextNormalHeight + mParent.getXLabelTextLineInterval());
363 | mXLabelLinePath.lineTo(locationX, height);
364 | canvas.drawPath(mXLabelLinePath, mXLabelLinePaint);
365 | float distance = (float) ((height - BLACK_X_LABEL_LINE - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mValueSelectHeight - BLANK_VALUE_TEXT * 2) /
366 | (mParent.getYMaxValue() - mParent.getYMinValue()) * (mParent.getData().get(i).getYValue() - mParent.getYMinValue()));
367 | //是否是中心值
368 | if (mSelectIndex == i) {
369 | mXValuePointColorPaint.setStrokeWidth(8);
370 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT), 4, mXValuePointColorPaint);
371 | mXValuePointWhitePaint.setStrokeWidth(4);
372 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT), 8, mXValuePointWhitePaint);
373 | mXValuePointColorPaint.setStrokeWidth(3);
374 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT), 12, mXValuePointColorPaint);
375 | canvas.drawText(mParent.getYUnit() + mParent.getData().get(i).getYValue(), locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mSelectValueMetrics.bottom - BLANK_VALUE_TEXT * 2), mSelectValueTextPaint);
376 | } else {
377 | mXValuePointWhitePaint.setStrokeWidth(8);
378 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT), 4, mXValuePointWhitePaint);
379 | mXValuePointColorPaint.setStrokeWidth(3);
380 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - BLANK_VALUE_TEXT), 8, mXValuePointColorPaint);
381 | canvas.drawText(mParent.getYUnit() + mParent.getData().get(i).getYValue(), locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mNormalValueMetrics.bottom - BLANK_VALUE_TEXT * 2), mNormalValueTextPaint);
382 | }
383 | } else {
384 | //x轴label文字在下面
385 | if (mParent.getData().get(i).getXLabel().length() > 0) {
386 | //是否是中心线
387 | if (mSelectIndex == i) {
388 | canvas.drawText(mParent.getData().get(i).getXLabel(), locationX, getHeight() - mXLabelTextSelectMetrics.bottom, mXLabelTextSelectPaint);
389 | } else {
390 | canvas.drawText(mParent.getData().get(i).getXLabel(), locationX, getHeight() - mXLabelTextNormalMetrics.bottom, mXLabelTextNormalPaint);
391 | }
392 | }
393 | //x轴轴线
394 | mXLabelLinePath.reset();
395 | mXLabelLinePath.moveTo(locationX, 0);
396 | mXLabelLinePath.lineTo(locationX, getHeight() - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval());
397 | canvas.drawPath(mXLabelLinePath, mXLabelLinePaint);
398 | float distance = (float) ((height - BLACK_X_LABEL_LINE - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mValueSelectHeight - BLANK_VALUE_TEXT * 2) /
399 | (mParent.getYMaxValue() - mParent.getYMinValue()) * (mParent.getData().get(i).getYValue() - mParent.getYMinValue()));
400 | //是否是中心值
401 | if (mSelectIndex == i) {
402 | mXValuePointColorPaint.setStrokeWidth(8);
403 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - BLANK_VALUE_TEXT), 4, mXValuePointColorPaint);
404 | mXValuePointWhitePaint.setStrokeWidth(4);
405 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - BLANK_VALUE_TEXT), 8, mXValuePointWhitePaint);
406 | mXValuePointColorPaint.setStrokeWidth(3);
407 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - BLANK_VALUE_TEXT), 12, mXValuePointColorPaint);
408 | canvas.drawText(mParent.getYUnit() + mParent.getData().get(i).getYValue(), locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mSelectValueMetrics.bottom - BLANK_VALUE_TEXT * 2), mSelectValueTextPaint);
409 | } else {
410 | mXValuePointWhitePaint.setStrokeWidth(8);
411 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - BLANK_VALUE_TEXT), 4, mXValuePointWhitePaint);
412 | mXValuePointColorPaint.setStrokeWidth(3);
413 | canvas.drawCircle(locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - BLANK_VALUE_TEXT), 8, mXValuePointColorPaint);
414 | canvas.drawText(mParent.getYUnit() + mParent.getData().get(i).getYValue(), locationX, (height - distance - BLACK_X_LABEL_LINE / 2 - mXLabelTextNormalHeight - mParent.getXLabelTextLineInterval() - mNormalValueMetrics.bottom - BLANK_VALUE_TEXT * 2), mNormalValueTextPaint);
415 | }
416 | }
417 | }
418 | }
419 | }
420 |
421 | //对需要绘制的坐标点用贝塞尔曲线连接 代码来自:https://www.jianshu.com/p/98088ff77607
422 | private void drawValuePath(Canvas canvas) {
423 | if (mPointList.size() <= 0) {
424 | return;
425 | }
426 | //保存曲线路径
427 | mPointPath.reset();
428 | //折线绘制
429 | if (mParent.getLinkLineType() == WheelChartLayout.STRAIGHT) {
430 | for (int i = 0; i < mPointList.size(); i++) {
431 | if (i == 0) {
432 | mPointPath.moveTo(mPointList.get(i).x, mPointList.get(i).y);
433 | } else {
434 | mPointPath.lineTo(mPointList.get(i).x, mPointList.get(i).y);
435 | }
436 | }
437 | } else if (mParent.getLinkLineType() == WheelChartLayout.BEZIER) {
438 | float prePreviousPointX = Float.NaN;
439 | float prePreviousPointY = Float.NaN;
440 | float previousPointX = Float.NaN;
441 | float previousPointY = Float.NaN;
442 | float currentPointX = Float.NaN;
443 | float currentPointY = Float.NaN;
444 | float nextPointX;
445 | float nextPointY;
446 | int lineSize = mPointList.size();
447 | for (int valueIndex = 0; valueIndex < lineSize; ++valueIndex) {
448 | if (Float.isNaN(currentPointX)) {
449 | PointF point = mPointList.get(valueIndex);
450 | currentPointX = point.x;
451 | currentPointY = point.y;
452 | }
453 | if (Float.isNaN(previousPointX)) {
454 | //是否是第一个点
455 | if (valueIndex > 0) {
456 | PointF point = mPointList.get(valueIndex - 1);
457 | previousPointX = point.x;
458 | previousPointY = point.y;
459 | } else {
460 | //是的话就用当前点表示上一个点
461 | previousPointX = currentPointX;
462 | previousPointY = currentPointY;
463 | }
464 | }
465 | if (Float.isNaN(prePreviousPointX)) {
466 | //是否是前两个点
467 | if (valueIndex > 1) {
468 | PointF point = mPointList.get(valueIndex - 2);
469 | prePreviousPointX = point.x;
470 | prePreviousPointY = point.y;
471 | } else {
472 | //是的话就用当前点表示上上个点
473 | prePreviousPointX = previousPointX;
474 | prePreviousPointY = previousPointY;
475 | }
476 | }
477 | // 判断是不是最后一个点了
478 | if (valueIndex < lineSize - 1) {
479 | PointF point = mPointList.get(valueIndex + 1);
480 | nextPointX = point.x;
481 | nextPointY = point.y;
482 | } else {
483 | //是的话就用当前点表示下一个点
484 | nextPointX = currentPointX;
485 | nextPointY = currentPointY;
486 | }
487 | if (valueIndex == 0) {
488 | // 将Path移动到开始点
489 | mPointPath.moveTo(currentPointX, currentPointY);
490 | } else {
491 | // 求出控制点坐标
492 | float firstDiffX = (currentPointX - prePreviousPointX);
493 | float firstDiffY = (currentPointY - prePreviousPointY);
494 | float secondDiffX = (nextPointX - previousPointX);
495 | float secondDiffY = (nextPointY - previousPointY);
496 | float firstControlPointX = previousPointX + (BEZIER_RADIAN * firstDiffX);
497 | float firstControlPointY = previousPointY + (BEZIER_RADIAN * firstDiffY);
498 | float secondControlPointX = currentPointX - (BEZIER_RADIAN * secondDiffX);
499 | float secondControlPointY = currentPointY - (BEZIER_RADIAN * secondDiffY);
500 | //画出曲线
501 | mPointPath.cubicTo(firstControlPointX, firstControlPointY, secondControlPointX, secondControlPointY,
502 | currentPointX, currentPointY);
503 | }
504 | // 更新值,
505 | prePreviousPointX = previousPointX;
506 | prePreviousPointY = previousPointY;
507 | previousPointX = currentPointX;
508 | previousPointY = currentPointY;
509 | currentPointX = nextPointX;
510 | currentPointY = nextPointY;
511 | }
512 | }
513 | canvas.drawPath(mPointPath, mLinkLinePaint);
514 | }
515 |
516 | //初始化画笔
517 | private void initPaint() {
518 | //x轴轴线
519 | mXLabelLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
520 | mXLabelLinePaint.setStrokeWidth(mParent.getXLabelLineWidth());
521 | mXLabelLinePaint.setColor(mParent.getXLabelLineColor());
522 | mXLabelLinePaint.setStyle(Paint.Style.STROKE);
523 | //是否是虚线
524 | if (mParent.getXLabelLineLength() > 0 && mParent.getXLabelLineDashLength() > 0) {
525 | mXLabelLinePaint.setPathEffect(new DashPathEffect(new float[]{mParent.getXLabelLineLength(), mParent.getXLabelLineDashLength()}, 0));
526 | }
527 | //x轴label文字
528 | mXLabelTextNormalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
529 | mXLabelTextNormalPaint.setTextSize(mParent.getXLabelTextNormalSize());
530 | mXLabelTextNormalPaint.setColor(mParent.getXLabelTextNormalColor());
531 | mXLabelTextNormalPaint.setTextAlign(Paint.Align.CENTER);
532 | mXLabelTextNormalMetrics = mXLabelTextNormalPaint.getFontMetrics();
533 | mXLabelTextNormalHeight = mXLabelTextNormalMetrics.bottom - mXLabelTextNormalMetrics.top;
534 | //x轴label文字
535 | mXLabelTextSelectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
536 | mXLabelTextSelectPaint.setTextSize(mParent.getXLabelTextSelectSize());
537 | mXLabelTextSelectPaint.setColor(mParent.getXLabelTextSelectColor());
538 | mXLabelTextSelectPaint.setTextAlign(Paint.Align.CENTER);
539 | mXLabelTextSelectMetrics = mXLabelTextNormalPaint.getFontMetrics();
540 | //坐标点的绘制
541 | mXValuePointColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
542 | mXValuePointColorPaint.setStrokeWidth(3);
543 | mXValuePointColorPaint.setColor(mParent.getValuePointColor());
544 | mXValuePointColorPaint.setStyle(Paint.Style.STROKE);
545 | mXValuePointColorPaint.setStrokeCap(Paint.Cap.ROUND);
546 | //坐标点白色填充的绘制
547 | mXValuePointWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
548 | mXValuePointWhitePaint.setStrokeWidth(5);
549 | mXValuePointWhitePaint.setColor(Color.WHITE);
550 | mXValuePointWhitePaint.setStyle(Paint.Style.STROKE);
551 | mXValuePointWhitePaint.setStrokeCap(Paint.Cap.ROUND);
552 | //未选中坐标值
553 | mNormalValueTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
554 | mNormalValueTextPaint.setTextSize(mParent.getNormalValueTextSize());
555 | mNormalValueTextPaint.setColor(mParent.getNormalValueTextColor());
556 | mNormalValueTextPaint.setTextAlign(Paint.Align.CENTER);
557 | mNormalValueMetrics = mNormalValueTextPaint.getFontMetrics();
558 | //选中坐标值
559 | mSelectValueTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
560 | mSelectValueTextPaint.setTextSize(mParent.getSelectValueTextSize());
561 | mSelectValueTextPaint.setColor(mParent.getSelectValueTextColor());
562 | mSelectValueTextPaint.setTextAlign(Paint.Align.CENTER);
563 | mSelectValueMetrics = mSelectValueTextPaint.getFontMetrics();
564 | mValueSelectHeight = mSelectValueMetrics.bottom - mSelectValueMetrics.top;
565 | //连接线
566 | mLinkLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
567 | mLinkLinePaint.setColor(mParent.getLinkLineColor());
568 | mLinkLinePaint.setStrokeWidth(mParent.getLinkLineWidth());
569 | mLinkLinePaint.setStyle(Paint.Style.STROKE);
570 | }
571 |
572 | //API小于18则关闭硬件加速,否则setAntiAlias()方法不生效
573 | private void checkAPILevel() {
574 | if (Build.VERSION.SDK_INT < 18) {
575 | setLayerType(LAYER_TYPE_NONE, null);
576 | }
577 | }
578 |
579 | @Override
580 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
581 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
582 | }
583 |
584 | public void setScrollBackListener(ScrollBackListener scrollBackListener) {
585 | this.mScrollBackListener = scrollBackListener;
586 | }
587 | }
588 |
--------------------------------------------------------------------------------
/wheelchart/src/main/java/com/sign/wheelchart/Utils.java:
--------------------------------------------------------------------------------
1 | package com.sign.wheelchart;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.util.DisplayMetrics;
6 | import android.util.TypedValue;
7 |
8 | /**
9 | * Created by CaoYongSheng
10 | * on 2019-04-22
11 | *
12 | * @author admin
13 | */
14 | public class Utils {
15 | public static float dp2px(Context context, float dpValue) {
16 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
17 | }
18 | public static float sp2px(Context context, float dpValue) {
19 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dpValue, context.getResources().getDisplayMetrics());
20 | }
21 | public static int getScreenWidth(Context context) {
22 | DisplayMetrics localDisplayMetrics = new DisplayMetrics();
23 | ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
24 | return localDisplayMetrics.widthPixels;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/wheelchart/src/main/java/com/sign/wheelchart/WheelChartLayout.java:
--------------------------------------------------------------------------------
1 | package com.sign.wheelchart;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.support.annotation.ColorInt;
7 | import android.support.annotation.IntDef;
8 | import android.util.AttributeSet;
9 | import android.view.MotionEvent;
10 | import android.view.ViewGroup;
11 |
12 | import java.lang.annotation.Retention;
13 | import java.lang.annotation.RetentionPolicy;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | * Created by CaoYongSheng
19 | * on 2019-04-22
20 | *
21 | * @author admin
22 | */
23 | public class WheelChartLayout extends ViewGroup {
24 | private Context mContext;
25 | private HorizontalWheelChartView mWheelChartView;
26 |
27 | //x轴标签位置定义
28 | public static final int X_TOP = 1, X_BOTTOM = 2;
29 |
30 | @IntDef({X_TOP, X_BOTTOM})
31 | @Retention(RetentionPolicy.SOURCE)
32 | private @interface XLabelGravity {
33 | }
34 |
35 | private @WheelChartLayout.XLabelGravity
36 | int mXLabelGravity = X_TOP;
37 | //是否允许子view处理滑动、点击等事件
38 | private boolean mInterceptTouchEvent = false;
39 | //x轴标签之间间距 单位px
40 | private float mXLabelInterval = 50;
41 | //x轴标签未选中文字大小
42 | private float mXLabelTextNormalSize = 14;
43 | //x轴标签未选中文字颜色
44 | @ColorInt
45 | private int mXLabelTextNormalColor = getResources().getColor(R.color.colorGray);
46 | //x轴标签选中文字大小
47 | private float mXLabelTextSelectSize = 14;
48 | //x轴标签选中文字颜色
49 | @ColorInt
50 | private int mXLabelTextSelectColor = getResources().getColor(R.color.blue);
51 | //x轴标签和轴线之间的间距 单位dp
52 | private float mXLabelTextLineInterval = 5;
53 | //x轴轴线的宽度
54 | private float mXLabelLineWidth = 1;
55 | //x轴轴线的颜色
56 | @ColorInt
57 | private int mXLabelLineColor = getResources().getColor(R.color.blue);
58 | //x轴轴线 若为虚线 线长 单位px
59 | private float mXLabelLineLength = 5;
60 | //x轴轴线 若为虚线 线间距 单位px
61 | private float mXLabelLineDashLength = 2;
62 | //坐标之间连线的宽度
63 | private float mLinkLineWidth = 2;
64 | //坐标之间连线的颜色
65 | @ColorInt
66 | private int mLinkLineColor = getResources().getColor(R.color.blue);
67 |
68 | //坐标之间的连线类型定义
69 | public static final int STRAIGHT = 1, BEZIER = 2;
70 |
71 | @IntDef({STRAIGHT, BEZIER})
72 | @Retention(RetentionPolicy.SOURCE)
73 | private @interface LinkLineType {
74 | }
75 |
76 | private @WheelChartLayout.LinkLineType
77 | int mLinkLineType = STRAIGHT;
78 | //坐标值未选中时的字体大小
79 | private float mNormalValueTextSize = 12;
80 | //坐标值未选中时的字体颜色
81 | @ColorInt
82 | private int mNormalValueTextColor = getResources().getColor(R.color.colorGray);
83 | //坐标值选中时的字体大小
84 | private float mSelectValueTextSize = 14;
85 | //坐标值选中时的字体颜色
86 | @ColorInt
87 | private int mSelectValueTextColor = getResources().getColor(R.color.colorBlack);
88 | //坐标点的颜色
89 | @ColorInt
90 | private int mValuePointColor = getResources().getColor(R.color.blue);
91 | //y轴的单位
92 | private String mYUnit = "R$ ";
93 | //Y轴的最大值
94 | private double mYMaxValue = 0;
95 | //Y轴的最小值
96 | private double mYMinValue = 0;
97 | //默认选中的下标
98 | private int mSelectIndex = 0;
99 | //数据源
100 | private List mData = new ArrayList<>();
101 |
102 | public WheelChartLayout(Context context) {
103 | super(context);
104 | initChartView(context);
105 | }
106 |
107 | public WheelChartLayout(Context context, AttributeSet attrs) {
108 | super(context, attrs);
109 | initChartView(context);
110 | initAttrs(context, attrs);
111 | }
112 |
113 | public WheelChartLayout(Context context, AttributeSet attrs, int defStyleAttr) {
114 | super(context, attrs, defStyleAttr);
115 | initChartView(context);
116 | initAttrs(context, attrs);
117 | }
118 |
119 | private void initAttrs(Context context, AttributeSet attrs) {
120 | TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.WheelChartLayout, 0, 0);
121 | mXLabelGravity = typedArray.getInt(R.styleable.WheelChartLayout_xLabelGravity, mXLabelGravity);
122 | mXLabelInterval = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelInterval, mXLabelInterval);
123 | mXLabelTextNormalSize = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelTextNormalSize, Utils.sp2px(context, mXLabelTextNormalSize));
124 | mXLabelTextNormalColor = typedArray.getColor(R.styleable.WheelChartLayout_xLabelTextNormalColor, mXLabelTextNormalColor);
125 | mXLabelTextSelectSize = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelTextNormalSize, Utils.sp2px(context, mXLabelTextSelectSize));
126 | mXLabelTextSelectColor = typedArray.getColor(R.styleable.WheelChartLayout_xLabelTextSelectColor, mXLabelTextSelectColor);
127 | mXLabelTextLineInterval = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelTextLineInterval, Utils.dp2px(context, mXLabelTextLineInterval));
128 | mXLabelLineWidth = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelLineWidth, Utils.dp2px(context, mXLabelLineWidth));
129 | mXLabelLineColor = typedArray.getColor(R.styleable.WheelChartLayout_xLabelLineColor, mXLabelLineColor);
130 | mXLabelLineLength = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelLineLength, mXLabelLineLength);
131 | mXLabelLineDashLength = typedArray.getDimension(R.styleable.WheelChartLayout_xLabelLineDashLength, mXLabelLineDashLength);
132 | mLinkLineWidth = typedArray.getDimension(R.styleable.WheelChartLayout_linkLineWidth, Utils.dp2px(context, mLinkLineWidth));
133 | mLinkLineColor = typedArray.getColor(R.styleable.WheelChartLayout_linkLineColor, mLinkLineColor);
134 | mLinkLineType = typedArray.getInt(R.styleable.WheelChartLayout_linkLineType, mLinkLineType);
135 | mNormalValueTextSize = typedArray.getDimension(R.styleable.WheelChartLayout_normalValueTextSize, Utils.sp2px(context, mNormalValueTextSize));
136 | mNormalValueTextColor = typedArray.getColor(R.styleable.WheelChartLayout_normalValueTextColor, mNormalValueTextColor);
137 | mSelectValueTextSize = typedArray.getDimension(R.styleable.WheelChartLayout_selectValueTextSize, Utils.sp2px(context, mSelectValueTextSize));
138 | mSelectValueTextColor = typedArray.getColor(R.styleable.WheelChartLayout_selectValueTextColor, mSelectValueTextColor);
139 | mValuePointColor = typedArray.getColor(R.styleable.WheelChartLayout_valuePointColor, mValuePointColor);
140 | typedArray.recycle();
141 | }
142 |
143 | private void initChartView(Context context) {
144 | mContext = context;
145 | mWheelChartView = new HorizontalWheelChartView(context, this);
146 | LayoutParams layoutParams = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
147 | mWheelChartView.setLayoutParams(layoutParams);
148 | addView(mWheelChartView);
149 | }
150 |
151 | @Override
152 | public boolean onInterceptTouchEvent(MotionEvent ev) {
153 | if (mInterceptTouchEvent) {
154 | return true;
155 | }
156 | return super.onInterceptTouchEvent(ev);
157 | }
158 |
159 | @Override
160 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
161 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
162 | measureChild(mWheelChartView, widthMeasureSpec, heightMeasureSpec);
163 | }
164 |
165 | @Override
166 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
167 | if (changed) {
168 | int parentLeft = getPaddingLeft();
169 | int parentTop = getPaddingTop();
170 | mWheelChartView.layout(parentLeft, parentTop, mWheelChartView.getMeasuredWidth() + parentLeft, parentTop + mWheelChartView.getMeasuredHeight());
171 | }
172 | }
173 |
174 | public void setInterceptTouchEvent(boolean interceptTouchEvent) {
175 | this.mInterceptTouchEvent = interceptTouchEvent;
176 | }
177 |
178 | public void setData(List data) {
179 | mData = data;
180 | if (data != null && data.size() > 0) {
181 | mYMinValue = data.get(0).getYValue();
182 | mYMaxValue = data.get(0).getYValue();
183 | for (int i = 1; i < data.size(); i++) {
184 | if (data.get(i).getYValue() < mYMinValue) {
185 | mYMinValue = data.get(i).getYValue();
186 | }
187 | if (data.get(i).getYValue() > mYMaxValue) {
188 | mYMaxValue = data.get(i).getYValue();
189 | }
190 | }
191 | }
192 | //若数据最大值最小值相等 构造最大、最小值
193 | if (mYMaxValue == mYMinValue) {
194 | if (mYMinValue == 0) {
195 | mYMaxValue = 100;
196 | } else {
197 | mYMaxValue = mYMaxValue + 100;
198 | mYMinValue = mYMinValue - 100;
199 | }
200 | }
201 | mWheelChartView.refreshSize();
202 | mWheelChartView.init(mContext);
203 | }
204 |
205 | public List getData() {
206 | return mData;
207 | }
208 |
209 | @Override
210 | protected void onDraw(Canvas canvas) {
211 | super.onDraw(canvas);
212 | }
213 |
214 | public int getXLabelGravity() {
215 | return mXLabelGravity;
216 | }
217 |
218 | public void setXLabelGravity(int mXLabelGravity) {
219 | this.mXLabelGravity = mXLabelGravity;
220 | }
221 |
222 | public float getXLabelInterval() {
223 | return mXLabelInterval;
224 | }
225 |
226 | public void setXLabelInterval(float mXLabelInterval) {
227 | this.mXLabelInterval = mXLabelInterval;
228 | }
229 |
230 | public float getXLabelTextNormalSize() {
231 | return mXLabelTextNormalSize;
232 | }
233 |
234 | public void setXLabelTextNormalSize(float mXLabelTextNormalSize) {
235 | this.mXLabelTextNormalSize = mXLabelTextNormalSize;
236 | }
237 |
238 | public int getXLabelTextNormalColor() {
239 | return mXLabelTextNormalColor;
240 | }
241 |
242 | public void setXLabelTextNormalColor(int mXLabelTextNormalColor) {
243 | this.mXLabelTextNormalColor = mXLabelTextNormalColor;
244 | }
245 |
246 | public float getXLabelTextSelectSize() {
247 | return mXLabelTextSelectSize;
248 | }
249 |
250 | public void setXLabelTextSelectSize(float mXLabelTextSelectSize) {
251 | this.mXLabelTextSelectSize = mXLabelTextSelectSize;
252 | }
253 |
254 | public int getXLabelTextSelectColor() {
255 | return mXLabelTextSelectColor;
256 | }
257 |
258 | public void setXLabelTextSelectColor(int mXLabelTextSelectColor) {
259 | this.mXLabelTextSelectColor = mXLabelTextSelectColor;
260 | }
261 |
262 | public float getXLabelTextLineInterval() {
263 | return mXLabelTextLineInterval;
264 | }
265 |
266 | public void setXLabelTextLineInterval(float mXLabelTextLineInterval) {
267 | this.mXLabelTextLineInterval = mXLabelTextLineInterval;
268 | }
269 |
270 | public float getXLabelLineWidth() {
271 | return mXLabelLineWidth;
272 | }
273 |
274 | public void setXLabelLineWidth(float mXLabelLineWidth) {
275 | this.mXLabelLineWidth = mXLabelLineWidth;
276 | }
277 |
278 | public int getXLabelLineColor() {
279 | return mXLabelLineColor;
280 | }
281 |
282 | public void setXLabelLineColor(int mXLabelLineColor) {
283 | this.mXLabelLineColor = mXLabelLineColor;
284 | }
285 |
286 | public float getXLabelLineLength() {
287 | return mXLabelLineLength;
288 | }
289 |
290 | public void setXLabelLineLength(float mXLabelLineLength) {
291 | this.mXLabelLineLength = mXLabelLineLength;
292 | }
293 |
294 | public float getXLabelLineDashLength() {
295 | return mXLabelLineDashLength;
296 | }
297 |
298 | public void setXLabelLineDashLength(float mXLabelLineDashLength) {
299 | this.mXLabelLineDashLength = mXLabelLineDashLength;
300 | }
301 |
302 | public float getLinkLineWidth() {
303 | return mLinkLineWidth;
304 | }
305 |
306 | public void setLinkLineWidth(float mLinkLineWidth) {
307 | this.mLinkLineWidth = mLinkLineWidth;
308 | }
309 |
310 | public int getLinkLineColor() {
311 | return mLinkLineColor;
312 | }
313 |
314 | public void setLinkLineColor(int mLinkLineColor) {
315 | this.mLinkLineColor = mLinkLineColor;
316 | }
317 |
318 | public int getLinkLineType() {
319 | return mLinkLineType;
320 | }
321 |
322 | public void setLinkLineType(int mLinkLineType) {
323 | this.mLinkLineType = mLinkLineType;
324 | }
325 |
326 | public float getNormalValueTextSize() {
327 | return mNormalValueTextSize;
328 | }
329 |
330 | public void setNormalValueTextSize(float mNormalValueTextSize) {
331 | this.mNormalValueTextSize = mNormalValueTextSize;
332 | }
333 |
334 | public int getNormalValueTextColor() {
335 | return mNormalValueTextColor;
336 | }
337 |
338 | public void setNormalValueTextColor(int mNormalValueTextColor) {
339 | this.mNormalValueTextColor = mNormalValueTextColor;
340 | }
341 |
342 | public float getSelectValueTextSize() {
343 | return mSelectValueTextSize;
344 | }
345 |
346 | public void setSelectValueTextSize(float mSelectValueTextSize) {
347 | this.mSelectValueTextSize = mSelectValueTextSize;
348 | }
349 |
350 | public int getSelectValueTextColor() {
351 | return mSelectValueTextColor;
352 | }
353 |
354 | public void setSelectValueTextColor(int mSelectValueTextColor) {
355 | this.mSelectValueTextColor = mSelectValueTextColor;
356 | }
357 |
358 | public double getYMaxValue() {
359 | return mYMaxValue;
360 | }
361 |
362 | public void setYMaxValue(double mYMaxValue) {
363 | this.mYMaxValue = mYMaxValue;
364 | }
365 |
366 | public double getYMinValue() {
367 | return mYMinValue;
368 | }
369 |
370 | public void setYMinValue(double mYMinValue) {
371 | this.mYMinValue = mYMinValue;
372 | }
373 |
374 | public int getValuePointColor() {
375 | return mValuePointColor;
376 | }
377 |
378 | public void setValuePointColor(int mValuePointColor) {
379 | this.mValuePointColor = mValuePointColor;
380 | }
381 |
382 | public int getSelectIndex() {
383 | return mSelectIndex;
384 | }
385 |
386 | public void setSelectIndex(int mSelectIndex) {
387 | this.mSelectIndex = mSelectIndex;
388 | }
389 |
390 | public String getYUnit() {
391 | return mYUnit;
392 | }
393 |
394 | public void setYUnit(String yUnit) {
395 | this.mYUnit = yUnit;
396 | }
397 |
398 | //设置回调
399 | public void setScrollBackListener(HorizontalWheelChartView.ScrollBackListener scrollBackListener) {
400 | mWheelChartView.setScrollBackListener(scrollBackListener);
401 | }
402 | }
403 |
--------------------------------------------------------------------------------
/wheelchart/src/main/res/values/attr.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 |
--------------------------------------------------------------------------------
/wheelchart/src/main/res/values/color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #666666
4 | #000000
5 | #06C0D8
6 |
--------------------------------------------------------------------------------
/wheelchart/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WheelChart
3 |
4 |
--------------------------------------------------------------------------------