├── .gitattributes
├── .gitignore
├── README.md
├── demo
└── CustomViewDeom
│ ├── .idea
│ ├── .name
│ ├── compiler.xml
│ ├── copyright
│ │ └── profiles_settings.xml
│ ├── encodings.xml
│ ├── gradle.xml
│ ├── libraries
│ │ ├── animated_vector_drawable_23_4_0.xml
│ │ ├── appcompat_v7_23_4_0.xml
│ │ ├── hamcrest_core_1_3.xml
│ │ ├── junit_4_12.xml
│ │ ├── support_annotations_23_4_0.xml
│ │ ├── support_v4_23_4_0.xml
│ │ └── support_vector_drawable_23_4_0.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── workspace.xml
│ ├── app
│ ├── .gitignore
│ ├── app.iml
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── administrator
│ │ │ └── customviewdeom
│ │ │ └── ApplicationTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── administrator
│ │ │ │ └── customviewdeom
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── PieChart
│ │ │ │ ├── PieChart.java
│ │ │ │ └── PieData.java
│ │ └── res
│ │ │ ├── layout
│ │ │ └── activity_main.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ │ └── values
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── administrator
│ │ └── customviewdeom
│ │ └── ExampleUnitTest.java
│ └── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gif
└── PieChart.gif
└── source
└── PieChart
├── PieChart.java
├── PieData.java
└── attrs.xml
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CustomView
2 |
3 | # **此项目已合并至[SmallChart](https://github.com/Idtk/SmallChart)**
4 |
5 | ## Introduction
6 | 可定义环形块颜色与名称,显示数据百分比的环形图。用户可自定义动画效果,内外圆半径比,百分比小数位,画笔颜色,字体大小等。
7 |
8 | ## Version
9 | SdkVersion >= 19
10 |
11 | ## Usage
12 | * 新建Pieview
13 | ```java
14 | PieView mPieView = new PieView(this);
15 | ```
16 | or
17 | ```Java
18 |
24 |
25 | PieChart pieChart = (PieChart) findViewById(R.id.pieChart);
26 | ```
27 | * 设置初始角度
28 | ```java
29 | mPieView.setStartAngle(0);
30 | ```
31 | * 设置数据
32 | ```java
33 | mPieView.setPieData(mPieDatas);
34 | //初始化mPieView
35 | private void initData(){
36 | for (int i=0; i<9; i++){
37 | PieData pieData = new PieData();
38 | pieData.setName("区域名);
39 | pieData.setValue(i+1);
40 | pieData.setColor(mColors[i]);
41 | Log.i("colorOld",Integer.toHexString(mColors[i]));
42 | mPieDatas.add(pieData);
43 | }
44 | }
45 | ```
46 |
47 | ## More
48 | * PieView更多设置请查看源码
49 |
50 | ## Demo
51 | 
52 |
53 | ******
54 | ## About
55 |
56 | **博客: www.idtkm.com**
57 | **GitHub: https://github.com/Idtk**
58 | **微博: http://weibo.com/Idtk**
59 | **邮箱: IdtkMa@gmail.com**
60 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/.name:
--------------------------------------------------------------------------------
1 | CustomViewDeom
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
22 |
23 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/animated_vector_drawable_23_4_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/appcompat_v7_23_4_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/junit_4_12.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/support_annotations_23_4_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/support_v4_23_4_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/libraries/support_vector_drawable_23_4_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.example.administrator.customviewdeom"
9 | minSdkVersion 19
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.4.0'
26 | }
27 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/androidTest/java/com/example/administrator/customviewdeom/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customviewdeom;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/java/com/example/administrator/customviewdeom/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customviewdeom;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import com.example.administrator.customviewdeom.PieChart.PieChart;
7 | import com.example.administrator.customviewdeom.PieChart.PieData;
8 |
9 | import java.util.ArrayList;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | private ArrayList mPieDatas = new ArrayList<>();
14 | // 颜色表
15 | private int[] mColors = {0xFFCCFF00, 0xFF6495ED, 0xFFE32636, 0xFF800000, 0xFF808000, 0xFFFF8C69, 0xFF808080,
16 | 0xFFE6B800, 0xFF7CFC00};
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_main);
22 | initData();
23 | PieChart pieChart = (PieChart) findViewById(R.id.pieChart);
24 | pieChart.setPieData(mPieDatas);
25 | }
26 |
27 | private void initData(){
28 | for (int i=0; i<9; i++){
29 | PieData pieData = new PieData();
30 | pieData.setName("区域"+i);
31 | pieData.setValue((float)i+1);
32 | pieData.setColor(mColors[i]);
33 | mPieDatas.add(pieData);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/java/com/example/administrator/customviewdeom/PieChart/PieChart.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customviewdeom.PieChart;
2 |
3 | import android.animation.TimeInterpolator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.content.res.TypedArray;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.Path;
11 | import android.graphics.Point;
12 | import android.graphics.RectF;
13 | import android.support.annotation.Nullable;
14 | import android.util.AttributeSet;
15 | import android.util.Log;
16 | import android.view.MotionEvent;
17 | import android.view.View;
18 | import android.view.animation.AccelerateDecelerateInterpolator;
19 |
20 | import com.example.administrator.customviewdeom.R;
21 |
22 | import java.text.NumberFormat;
23 | import java.util.ArrayList;
24 | import java.util.Arrays;
25 |
26 | /**
27 | * Created by DoBest on 2016/4/15.
28 | * author : Idtk
29 | */
30 | public class PieChart extends View {
31 |
32 | //画笔
33 | private Paint mPaint = new Paint();
34 | //宽高
35 | private int mWidth;
36 | private int mHeight;
37 | //数据
38 | private ArrayList mPieData = new ArrayList<>();
39 | //饼状图初始绘制角度
40 | private float mStartAngle = 0;
41 | private RectF rectF=new RectF(),rectFTra = new RectF(),rectFIn = new RectF();
42 | private float r,rTra,rWhite;
43 | private RectF rectFF = new RectF(),rectFTraF = new RectF(),reatFWhite = new RectF();
44 | private float rF,rTraF,rWhiteF;
45 | //动画
46 | private ValueAnimator animator;
47 | private float animatedValue;
48 | private long animatorDuration = 5000;
49 | private TimeInterpolator timeInterpolator = new AccelerateDecelerateInterpolator();
50 | private boolean animatedFlag = true;
51 | //Touch
52 | private boolean touchFlag = true;
53 | private float[] pieAngles;
54 | private int angleId;
55 | private double offsetScaleRadius = 1.1;
56 | //圆环半径比例
57 | private double widthScaleRadius = 0.8;
58 | private double radiusScaleTransparent = 0.5;
59 | private double radiusScaleInside = 0.43;
60 | //Paint的字体大小
61 | private int percentTextSize = 45;
62 | private int centerTextSize = 60;
63 |
64 | //中间文字颜色
65 | private int centerTextColor = Color.BLACK;
66 | //百分比文字颜色
67 | private int percentTextColor = Color.WHITE;
68 | //百分比的小数位
69 | private int percentDecimal = 0;
70 | //饼图名
71 | private String name = "PieChart";
72 | //居中点
73 | private Point mPoint = new Point();
74 | //小于此角度在未点击状态下不显示百分比
75 | private float minAngle = 30;
76 | //引入Path
77 | private Path outPath = new Path();
78 | private Path midPath = new Path();
79 | private Path inPath = new Path();
80 | private Path outMidPath = new Path();
81 | private Path midInPath = new Path();
82 | //百分比最长字符
83 | private int stringId = 0;
84 | //wrap_content尺寸
85 | // private float wrapSize;
86 |
87 | public PieChart(Context context) {
88 | this(context,null);
89 | }
90 |
91 | public PieChart(Context context, @Nullable AttributeSet attrs) {
92 | this(context,attrs,0);
93 | }
94 |
95 | public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
96 | super(context, attrs, defStyleAttr);
97 | init(context,attrs,defStyleAttr,0);
98 | }
99 |
100 | public PieChart(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
101 | super(context, attrs, defStyleAttr, defStyleRes);
102 | init(context,attrs,defStyleAttr,defStyleRes);
103 | }
104 |
105 | @Override
106 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
107 | // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
108 | int width = measureDimension(widthMeasureSpec);
109 | int height = measureDimension(heightMeasureSpec);
110 | setMeasuredDimension(width,height);
111 | }
112 |
113 | @Override
114 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
115 | super.onSizeChanged(w, h, oldw, oldh);
116 | mWidth = w;
117 | mHeight = h;
118 | //标准圆环
119 | //圆弧
120 | r = (float) (Math.min(mWidth,mHeight)/2*widthScaleRadius);// 饼状图半径
121 | // 饼状图绘制区域
122 | rectF.left = -r;
123 | rectF.top = -r;
124 | rectF.right =r;
125 | rectF.bottom = r;
126 | //白色圆弧
127 | //透明圆弧
128 | rTra = (float) (r*radiusScaleTransparent);
129 | rectFTra.left = -rTra;
130 | rectFTra.top = -rTra;
131 | rectFTra.right = rTra;
132 | rectFTra.bottom = rTra;
133 | //白色圆
134 | rWhite = (float) (r*radiusScaleInside);
135 | rectFIn.left = -rWhite;
136 | rectFIn.top = -rWhite;
137 | rectFIn.right = rWhite;
138 | rectFIn.bottom = rWhite;
139 |
140 | //浮出圆环
141 | //圆弧
142 | rF = (float) (Math.min(mWidth,mHeight)/2*widthScaleRadius*offsetScaleRadius);// 饼状图半径
143 | // 饼状图绘制区域
144 | rectFF.left = -rF;
145 | rectFF.top = -rF;
146 | rectFF.right = rF;
147 | rectFF.bottom = rF;
148 | //白色圆弧
149 | //透明圆弧
150 | rTraF = (float) (rF*radiusScaleTransparent);
151 | rectFTraF.left = -rTraF;
152 | rectFTraF.top = -rTraF;
153 | rectFTraF.right = rTraF;
154 | rectFTraF.bottom = rTraF;
155 | //白色扇形
156 | rWhiteF = (float) (rF*radiusScaleInside);
157 | reatFWhite.left = -rWhiteF;
158 | reatFWhite.top = -rWhiteF;
159 | reatFWhite.right = rWhiteF;
160 | reatFWhite.bottom = rWhiteF;
161 |
162 | //animated
163 | if (animatedFlag){
164 | initAnimator(animatorDuration);
165 | }else {
166 | animatedValue = 360f;
167 | }
168 | }
169 |
170 | @Override
171 | protected void onDraw(Canvas canvas) {
172 | super.onDraw(canvas);
173 | if (mPieData == null)
174 | return;
175 | float currentStartAngle = 0;// 当前起始角度
176 | canvas.translate(mWidth/2,mHeight/2);// 将画布坐标原点移动到中心位置
177 |
178 | canvas.save();
179 | canvas.rotate(mStartAngle);
180 | float drawAngle;
181 | for (int i=0; i=0){
185 | drawAngle = Math.min(pie.getAngle()-1,animatedValue-currentStartAngle);
186 | }else {
187 | drawAngle = 0;
188 | }
189 | if (i==angleId){
190 | drawArc(canvas,currentStartAngle,drawAngle,pie,rF,rTraF,rWhiteF,rectFF,rectFTraF,reatFWhite,mPaint);
191 | }else {
192 | drawArc(canvas,currentStartAngle,drawAngle,pie,r,rTra,rWhite,rectF,rectFTra,rectFIn,mPaint);
193 | }
194 | currentStartAngle += pie.getAngle();
195 | }
196 | canvas.restore();
197 |
198 | currentStartAngle = mStartAngle;
199 | //扇形百分比文字
200 | for (int i=0; ipieAngles[i]-pie.getAngle()/2) {
212 | if (i == angleId) {
213 | textPathX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (rF + rTraF) / 2);
214 | textPathY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (rF + rTraF) / 2);
215 | mPoint.x = textPathX;
216 | mPoint.y = textPathY;
217 | String[] strings = new String[]{pie.getName() + "", numberFormat.format(pie.getPercentage()) + ""};
218 | if (strings.length == 2)
219 | textCenter(strings, mPaint, canvas, mPoint, Paint.Align.CENTER);
220 | } else {
221 | if (pie.getAngle() > minAngle) {
222 | textPathX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rTra) / 2);
223 | textPathY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rTra) / 2);
224 | mPoint.x = textPathX;
225 | mPoint.y = textPathY;
226 | String[] strings = new String[]{numberFormat.format(pie.getPercentage()) + ""};
227 | if (strings.length == 1)
228 | textCenter(strings, mPaint, canvas, mPoint, Paint.Align.CENTER);
229 | }
230 | }
231 | currentStartAngle += pie.getAngle();
232 | }
233 | }
234 | //饼图名
235 | mPaint.setColor(centerTextColor);
236 | mPaint.setTextSize(centerTextSize);
237 | mPaint.setTextAlign(Paint.Align.CENTER);
238 | //根据Paint的TextSize计算Y轴的值
239 | mPoint.x=0;
240 | mPoint.y=0;
241 | String[] strings = new String[]{name+""};
242 | if (strings.length==1)
243 | textCenter(strings,mPaint,canvas,mPoint, Paint.Align.CENTER);
244 |
245 |
246 | }
247 |
248 | @Override
249 | public boolean onTouchEvent(MotionEvent event) {
250 | if (touchFlag&&mPieData.size()>0){
251 | switch (event.getAction()){
252 | case MotionEvent.ACTION_DOWN:
253 | float x = event.getX()-(mWidth/2);
254 | float y = event.getY()-(mHeight/2);
255 | float touchAngle = 0;
256 | if (x<0&&y<0){
257 | touchAngle += 180;
258 | }else if (y<0&&x>0){
259 | touchAngle += 360;
260 | }else if (y>0&&x<0){
261 | touchAngle += 180;
262 | }
263 | touchAngle +=Math.toDegrees(Math.atan(y/x));
264 | touchAngle = touchAngle-mStartAngle;
265 | if (touchAngle<0){
266 | touchAngle = touchAngle+360;
267 | }
268 | float touchRadius = (float) Math.sqrt(y*y+x*x);
269 | if (rTra< touchRadius && touchRadius< r){
270 | angleId = -Arrays.binarySearch(pieAngles,(touchAngle))-1;
271 | invalidate();
272 | }
273 | return true;
274 | case MotionEvent.ACTION_UP:
275 | angleId = -1;
276 | invalidate();
277 | return true;
278 | }
279 | }
280 | return super.onTouchEvent(event);
281 | }
282 |
283 | private void init(Context context,AttributeSet attrs, int defStyleAttr, int defStyleRes){
284 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PieChart, defStyleAttr,defStyleRes);
285 | int n = array.getIndexCount();
286 | for (int i=0; i mPieData){
306 |
307 | float dataMax=0;
308 | if (mPieData ==null||mPieData.size()==0)
309 | return;
310 | pieAngles = new float[mPieData.size()];
311 | float sumValue = 0;
312 | for (int i=0; i1){
399 | NumberFormat numberFormat =NumberFormat.getPercentInstance();
400 | numberFormat.setMinimumFractionDigits(percentDecimal);
401 | paint.setTextSize(percentTextSize);
402 | float percentWidth = paint.measureText(numberFormat.format(mPieData.get(stringId).getPercentage())+"");
403 | paint.setTextSize(centerTextSize);
404 | float nameWidth = paint.measureText(name+"");
405 | wrapSize = (percentWidth*4+nameWidth*2)*(float) offsetScaleRadius;
406 | Log.d("TAG1",percentWidth+":"+nameWidth+":"+wrapSize);
407 | }else {
408 | wrapSize = 0;
409 | }
410 | return (int) wrapSize;
411 | }
412 |
413 | private int measureDimension(int measureSpec){
414 | int size;
415 |
416 | int specMode = MeasureSpec.getMode(measureSpec);
417 | int specSize = MeasureSpec.getSize(measureSpec);
418 |
419 | switch (specMode){
420 | case MeasureSpec.UNSPECIFIED:
421 | size = measureWrap(mPaint);
422 | break;
423 | case MeasureSpec.EXACTLY:
424 | size = specSize;
425 | break;
426 | case MeasureSpec.AT_MOST:
427 | size = Math.min(specSize,measureWrap(mPaint));
428 | break;
429 | default:
430 | size = measureWrap(mPaint);
431 | break;
432 | }
433 | return size;
434 | }
435 |
436 | /**
437 | * 设置起始角度
438 | * @param mStartAngle 起始角度
439 | */
440 | public void setStartAngle(float mStartAngle) {
441 | while (mStartAngle<0){
442 | mStartAngle = mStartAngle+360;
443 | }
444 | while (mStartAngle>360){
445 | mStartAngle = mStartAngle-360;
446 | }
447 | this.mStartAngle = mStartAngle;
448 | }
449 |
450 | /**
451 | * 设置数据
452 | * @param mPieData 数据
453 | */
454 | public void setPieData(ArrayList mPieData) {
455 | this.mPieData = mPieData;
456 | initDate(mPieData);
457 | }
458 |
459 | /**
460 | * 是否显示点触效果
461 | * @param touchFlag 是否显示点触效果
462 | */
463 | public void setTouchFlag(boolean touchFlag) {
464 | this.touchFlag = touchFlag;
465 | }
466 |
467 | /**
468 | * 设置绘制圆环的动画时间
469 | * @param animatorDuration 动画时间
470 | */
471 | public void setAnimatorDuration(long animatorDuration) {
472 | this.animatorDuration = animatorDuration;
473 | }
474 |
475 | /**
476 | * 设置偏移扇形与原扇形的半径比例
477 | * @param offsetScaleRadius 点触扇形的偏移比例
478 | */
479 | public void setOffsetScaleRadius(double offsetScaleRadius) {
480 | this.offsetScaleRadius = offsetScaleRadius;
481 | }
482 |
483 | /**
484 | * 设置圆环外层园的半径与视图的宽度比
485 | * @param widthScaleRadius 外圆环半径与视图宽度比
486 | */
487 | public void setWidthScaleRadius(double widthScaleRadius) {
488 | this.widthScaleRadius = widthScaleRadius;
489 | }
490 |
491 | /**
492 | * 设置透明圆环与外圆环半径比
493 | * @param radiusScaleTransparent 透明圆环与外圆环半径比
494 | */
495 | public void setRadiusScaleTransparent(double radiusScaleTransparent) {
496 | this.radiusScaleTransparent = radiusScaleTransparent;
497 | }
498 |
499 | /**
500 | * 设置内部圆与外部圆环半径比
501 | * @param radiusScaleInside 内部圆与外部圆环半径比
502 | */
503 | public void setRadiusScaleInside(double radiusScaleInside) {
504 | this.radiusScaleInside = radiusScaleInside;
505 | }
506 |
507 | /**
508 | * 设置圆环显示的百分比画笔大小
509 | * @param percentTextSize 百分比画笔大小
510 | */
511 | public void setPercentTextSize(int percentTextSize) {
512 | this.percentTextSize = percentTextSize;
513 | }
514 |
515 | /**
516 | * 设置圆环显示的百分比字体颜色
517 | * @param percentTextColor 百分比字体颜色
518 | */
519 | public void setPercentTextColor(int percentTextColor) {
520 | this.percentTextColor = percentTextColor;
521 | }
522 |
523 | /**
524 | * 设置中心文字画笔大小
525 | * @param centerTextSize 中心文字画笔大小
526 | */
527 | public void setCenterTextSize(int centerTextSize) {
528 | this.centerTextSize = centerTextSize;
529 | }
530 |
531 | /**
532 | * 设置中心文字颜色
533 | * @param centerTextColor 中心文字颜色
534 | */
535 | public void setCenterTextColor(int centerTextColor) {
536 | this.centerTextColor = centerTextColor;
537 | }
538 |
539 | /**
540 | * 设置动画类型
541 | * @param timeInterpolator 动画类型
542 | */
543 | public void setTimeInterpolator(TimeInterpolator timeInterpolator) {
544 | this.timeInterpolator = timeInterpolator;
545 | }
546 |
547 | /**
548 | * 设置百分比的小数位
549 | * @param percentDecimal 百分比的小数位
550 | */
551 | public void setPercentDecimal(int percentDecimal) {
552 | this.percentDecimal = percentDecimal;
553 | }
554 |
555 | public void setName(String name) {
556 | this.name = name;
557 | }
558 |
559 | public void setMinAngle(float minAngle) {
560 | this.minAngle = minAngle;
561 | }
562 |
563 | /**
564 | * 是否开启动画
565 | * @param animatedFlag 默认为true,开启
566 | */
567 | public void setAnimatedFlag(boolean animatedFlag) {
568 | this.animatedFlag = animatedFlag;
569 | }
570 |
571 | /**
572 | * 设置旋转的角度
573 | * @param animatedValue 默认由动画控制
574 | */
575 | public void setAnimatedValue(float animatedValue) {
576 | this.animatedValue = animatedValue;
577 | }
578 |
579 | }
580 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/java/com/example/administrator/customviewdeom/PieChart/PieData.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customviewdeom.PieChart;
2 |
3 | /**
4 | * Created by DoBest on 2016/4/15.
5 | */
6 | public class PieData {
7 |
8 | private String name;
9 | private float value;
10 | private float percentage;
11 | private int color = 0;
12 | private float angle = 0;
13 |
14 | public String getName() {
15 | return name;
16 | }
17 |
18 | public void setName(String name) {
19 | this.name = name;
20 | }
21 |
22 | public float getValue() {
23 | return value;
24 | }
25 |
26 | public void setValue(float value) {
27 | this.value = value;
28 | }
29 |
30 | public float getPercentage() {
31 | return percentage;
32 | }
33 |
34 | public void setPercentage(float percentage) {
35 | this.percentage = percentage;
36 | }
37 |
38 | public int getColor() {
39 | return color;
40 | }
41 |
42 | public void setColor(int color) {
43 | this.color = color;
44 | }
45 |
46 | public float getAngle() {
47 | return angle;
48 | }
49 |
50 | public void setAngle(float angle) {
51 | this.angle = angle;
52 | }
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CustomViewDeom
3 |
4 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/CustomViewDeom/app/src/test/java/com/example/administrator/customviewdeom/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customviewdeom;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/demo/CustomViewDeom/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/demo/CustomViewDeom/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/demo/CustomViewDeom/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
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-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gif/PieChart.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idtk/PieChart/68bf4033a4ac0dddf375a8e9c455d4702019b346/gif/PieChart.gif
--------------------------------------------------------------------------------
/source/PieChart/PieChart.java:
--------------------------------------------------------------------------------
1 | package com.customview.PieChart;
2 |
3 | import android.animation.TimeInterpolator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.content.res.TypedArray;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.Path;
11 | import android.graphics.Point;
12 | import android.graphics.RectF;
13 | import android.support.annotation.Nullable;
14 | import android.util.AttributeSet;
15 | import android.util.Log;
16 | import android.view.MotionEvent;
17 | import android.view.View;
18 | import android.view.animation.AccelerateDecelerateInterpolator;
19 |
20 | import com.customview.R;
21 |
22 | import java.text.NumberFormat;
23 | import java.util.ArrayList;
24 | import java.util.Arrays;
25 |
26 | /**
27 | * Created by DoBest on 2016/4/15.
28 | * author : Idtk
29 | */
30 | public class PieChart extends View {
31 |
32 | //画笔
33 | private Paint mPaint = new Paint();
34 | //宽高
35 | private int mWidth,mViewWidth;
36 | private int mHeight,mViewHeight;
37 | //数据
38 | private ArrayList mPieData = new ArrayList<>();
39 | //饼状图初始绘制角度
40 | private float mStartAngle = 0;
41 | private RectF rectF=new RectF(),rectFTra = new RectF(),rectFIn = new RectF();
42 | private float r,rTra,rWhite;
43 | private RectF rectFF = new RectF(),rectFTraF = new RectF(),reatFWhite = new RectF();
44 | private float rF,rTraF,rWhiteF;
45 | //动画
46 | private ValueAnimator animator;
47 | private float animatedValue;
48 | private long animatorDuration = 5000;
49 | private TimeInterpolator timeInterpolator = new AccelerateDecelerateInterpolator();
50 | private boolean animatedFlag = true;
51 | //Touch
52 | private boolean touchFlag = true;
53 | private float[] pieAngles;
54 | private int angleId;
55 | private double offsetScaleRadius = 1.1;
56 | //圆环半径比例
57 | private double widthScaleRadius = 0.9;
58 | private double radiusScaleTransparent = 0.6;
59 | private double radiusScaleInside = 0.5;
60 | //Paint的字体大小
61 | private int percentTextSize = 45;
62 | private int centerTextSize = 60;
63 |
64 | //中间文字颜色
65 | private int centerTextColor = Color.BLACK;
66 | //百分比文字颜色
67 | private int percentTextColor = Color.WHITE;
68 | //百分比的小数位
69 | private int percentDecimal = 0;
70 | //饼图名
71 | private String name = "PieChart";
72 | //居中点
73 | private Point mPoint = new Point();
74 | //小于此角度在未点击状态下不显示百分比
75 | private float minAngle = 30;
76 | //引入Path
77 | private Path outPath = new Path();
78 | private Path midPath = new Path();
79 | private Path inPath = new Path();
80 | private Path outMidPath = new Path();
81 | private Path midInPath = new Path();
82 | //百分比最长字符
83 | private int stringId = 0;
84 | //百分比文字是否绘制
85 | private boolean percentFlag = true;
86 |
87 |
88 | public PieChart(Context context) {
89 | this(context,null);
90 | }
91 |
92 | public PieChart(Context context, @Nullable AttributeSet attrs) {
93 | this(context,attrs,0);
94 | }
95 |
96 | public PieChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
97 | super(context, attrs, defStyleAttr);
98 | init(context,attrs,defStyleAttr,0);
99 | }
100 |
101 | public PieChart(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
102 | super(context, attrs, defStyleAttr, defStyleRes);
103 | init(context,attrs,defStyleAttr,defStyleRes);
104 | }
105 |
106 | @Override
107 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
108 | // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
109 | int width = measureDimension(widthMeasureSpec);
110 | int height = measureDimension(heightMeasureSpec);
111 | setMeasuredDimension(width,height);
112 | }
113 |
114 | @Override
115 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
116 | super.onSizeChanged(w, h, oldw, oldh);
117 | mWidth = w-getPaddingLeft()-getPaddingRight();//适应padding设置
118 | mHeight = h-getPaddingTop()-getPaddingBottom();//适应padding设置
119 | mViewWidth = w;
120 | mViewHeight = h;
121 | //标准圆环
122 | //圆弧
123 | r = (float) (Math.min(mWidth,mHeight)/2*widthScaleRadius);// 饼状图半径
124 | if (r>Math.min(mWidth,mHeight)){
125 | r =0;
126 | percentFlag = false;
127 | name = "";
128 | }
129 | // 饼状图绘制区域
130 | rectF.left = -r;
131 | rectF.top = -r;
132 | rectF.right =r;
133 | rectF.bottom = r;
134 | //白色圆弧
135 | //透明圆弧
136 | rTra = (float) (r*radiusScaleTransparent);
137 | rectFTra.left = -rTra;
138 | rectFTra.top = -rTra;
139 | rectFTra.right = rTra;
140 | rectFTra.bottom = rTra;
141 | //白色圆
142 | rWhite = (float) (r*radiusScaleInside);
143 | rectFIn.left = -rWhite;
144 | rectFIn.top = -rWhite;
145 | rectFIn.right = rWhite;
146 | rectFIn.bottom = rWhite;
147 |
148 | //浮出圆环
149 | //圆弧
150 | rF = (float) (Math.min(mWidth,mHeight)/2*widthScaleRadius*offsetScaleRadius);// 饼状图半径
151 | // 饼状图绘制区域
152 | rectFF.left = -rF;
153 | rectFF.top = -rF;
154 | rectFF.right = rF;
155 | rectFF.bottom = rF;
156 | //白色圆弧
157 | //透明圆弧
158 | rTraF = (float) (rF*radiusScaleTransparent);
159 | rectFTraF.left = -rTraF;
160 | rectFTraF.top = -rTraF;
161 | rectFTraF.right = rTraF;
162 | rectFTraF.bottom = rTraF;
163 | //白色扇形
164 | rWhiteF = (float) (rF*radiusScaleInside);
165 | reatFWhite.left = -rWhiteF;
166 | reatFWhite.top = -rWhiteF;
167 | reatFWhite.right = rWhiteF;
168 | reatFWhite.bottom = rWhiteF;
169 |
170 | //animated
171 | if (animatedFlag){
172 | initAnimator(animatorDuration);
173 | }else {
174 | animatedValue = 360f;
175 | }
176 | }
177 |
178 | @Override
179 | protected void onDraw(Canvas canvas) {
180 | super.onDraw(canvas);
181 | if (mPieData == null)
182 | return;
183 | float currentStartAngle = 0;// 当前起始角度
184 | canvas.translate(mViewWidth/2,mViewHeight/2);// 将画布坐标原点移动到中心位置
185 |
186 | canvas.save();
187 | canvas.rotate(mStartAngle);
188 | float drawAngle;
189 | for (int i=0; i=0){
193 | drawAngle = Math.min(pie.getAngle()-1,animatedValue-currentStartAngle);
194 | }else {
195 | drawAngle = 0;
196 | }
197 | if (i==angleId){
198 | drawArc(canvas,currentStartAngle,drawAngle,pie,rectFF,rectFTraF,reatFWhite,mPaint);
199 | }else {
200 | drawArc(canvas,currentStartAngle,drawAngle,pie,rectF,rectFTra,rectFIn,mPaint);
201 | }
202 | currentStartAngle += pie.getAngle();
203 | }
204 | canvas.restore();
205 |
206 | currentStartAngle = mStartAngle;
207 | //扇形百分比文字
208 | for (int i=0; ipieAngles[i]-pie.getAngle()/2&&percentFlag) {
220 | if (i == angleId) {
221 | drawText(canvas,pie,currentStartAngle,numberFormat,true);
222 | } else {
223 | if (pie.getAngle() > minAngle) {
224 | drawText(canvas,pie,currentStartAngle,numberFormat,false);
225 | }
226 | }
227 | currentStartAngle += pie.getAngle();
228 | }
229 | }
230 | //饼图名
231 | mPaint.setColor(centerTextColor);
232 | mPaint.setTextSize(centerTextSize);
233 | mPaint.setTextAlign(Paint.Align.CENTER);
234 | //根据Paint的TextSize计算Y轴的值
235 | mPoint.x=0;
236 | mPoint.y=0;
237 | String[] strings = new String[]{name+""};
238 | if (strings.length==1)
239 | textCenter(strings,mPaint,canvas,mPoint, Paint.Align.CENTER);
240 | }
241 |
242 | @Override
243 | public boolean onTouchEvent(MotionEvent event) {
244 | if (touchFlag&&mPieData.size()>0){
245 | switch (event.getAction()){
246 | case MotionEvent.ACTION_DOWN:
247 | float x = event.getX()-(mWidth/2);
248 | float y = event.getY()-(mHeight/2);
249 | float touchAngle = 0;
250 | if (x<0&&y<0){
251 | touchAngle += 180;
252 | }else if (y<0&&x>0){
253 | touchAngle += 360;
254 | }else if (y>0&&x<0){
255 | touchAngle += 180;
256 | }
257 | touchAngle +=Math.toDegrees(Math.atan(y/x));
258 | touchAngle = touchAngle-mStartAngle;
259 | if (touchAngle<0){
260 | touchAngle = touchAngle+360;
261 | }
262 | float touchRadius = (float) Math.sqrt(y*y+x*x);
263 | if (rTra< touchRadius && touchRadius< r){
264 | angleId = -Arrays.binarySearch(pieAngles,(touchAngle))-1;
265 | invalidate();
266 | }
267 | return true;
268 | case MotionEvent.ACTION_UP:
269 | angleId = -1;
270 | invalidate();
271 | return true;
272 | }
273 | }
274 | return super.onTouchEvent(event);
275 | }
276 |
277 | private void init(Context context,AttributeSet attrs, int defStyleAttr, int defStyleRes){
278 | // setLayerType(View.LAYER_TYPE_SOFTWARE, null);
279 | TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PieChart, defStyleAttr,defStyleRes);
280 | int n = array.getIndexCount();
281 | for (int i=0; i mPieData){
302 |
303 | float dataMax=0;
304 | if (mPieData ==null||mPieData.size()==0)
305 | return;
306 | pieAngles = new float[mPieData.size()];
307 | float sumValue = 0;
308 | for (int i=0; i1){
374 | NumberFormat numberFormat =NumberFormat.getPercentInstance();
375 | numberFormat.setMinimumFractionDigits(percentDecimal);
376 | paint.setTextSize(percentTextSize);
377 | float percentWidth = paint.measureText(numberFormat.format(mPieData.get(stringId).getPercentage())+"");
378 | paint.setTextSize(centerTextSize);
379 | float nameWidth = paint.measureText(name+"");
380 | wrapSize = (percentWidth*4+nameWidth*1.0f)*(float) offsetScaleRadius;
381 | }else {
382 | wrapSize = 0;
383 | }
384 | return (int) wrapSize;
385 | }
386 |
387 | private int measureDimension(int measureSpec){
388 | int size = measureWrap(mPaint);
389 |
390 | int specMode = MeasureSpec.getMode(measureSpec);
391 | int specSize = MeasureSpec.getSize(measureSpec);
392 |
393 | switch (specMode){
394 | case MeasureSpec.UNSPECIFIED:
395 | size = measureWrap(mPaint);
396 | break;
397 | case MeasureSpec.EXACTLY:
398 | size = specSize;
399 | break;
400 | case MeasureSpec.AT_MOST:
401 | size = Math.min(specSize,measureWrap(mPaint));
402 | break;
403 | }
404 | return size;
405 | }
406 |
407 | private void drawArc(Canvas canvas, float currentStartAngle, float drawAngle, PieData pie,
408 | RectF outRectF, RectF midRectF, RectF inRectF,Paint paint){
409 | outPath.moveTo(0,0);
410 | outPath.arcTo(outRectF,currentStartAngle,drawAngle);
411 | midPath.moveTo(0,0);
412 | midPath.arcTo(midRectF,currentStartAngle,drawAngle);
413 | inPath.moveTo(0,0);
414 | inPath.arcTo(inRectF,currentStartAngle,drawAngle);
415 | outMidPath.op(outPath,midPath, Path.Op.DIFFERENCE);
416 | midInPath.op(midPath,inPath, Path.Op.DIFFERENCE);
417 | paint.setColor(pie.getColor());
418 | canvas.drawPath(outMidPath,paint);
419 | paint.setAlpha(0x80);//设置透明度
420 | canvas.drawPath(midInPath,paint);
421 | outPath.reset();
422 | midPath.reset();
423 | inPath.reset();
424 | outMidPath.reset();
425 | midInPath.reset();
426 | }
427 |
428 | private void drawText(Canvas canvas, PieData pie ,float currentStartAngle, NumberFormat numberFormat,boolean flag){
429 | int textPathX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rTra) / 2);
430 | int textPathY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rTra) / 2);
431 | mPoint.x = textPathX;
432 | mPoint.y = textPathY;
433 | String[] strings;
434 | if (flag){
435 | strings = new String[]{pie.getName() + "", numberFormat.format(pie.getPercentage()) + ""};
436 | // if (strings.length == 2)
437 | // textCenter(strings, mPaint, canvas, mPoint, Paint.Align.CENTER);
438 | }else {
439 | strings = new String[]{numberFormat.format(pie.getPercentage()) + ""};
440 | // if (strings.length == 1)
441 | // textCenter(strings, mPaint, canvas, mPoint, Paint.Align.CENTER);
442 | }
443 | textCenter(strings, mPaint, canvas, mPoint, Paint.Align.CENTER);
444 | }
445 |
446 | /**
447 | * 设置起始角度
448 | * @param mStartAngle 起始角度
449 | */
450 | public void setStartAngle(float mStartAngle) {
451 | while (mStartAngle<0){
452 | mStartAngle = mStartAngle+360;
453 | }
454 | while (mStartAngle>360){
455 | mStartAngle = mStartAngle-360;
456 | }
457 | this.mStartAngle = mStartAngle;
458 | }
459 |
460 | /**
461 | * 设置数据
462 | * @param mPieData 数据
463 | */
464 | public void setPieData(ArrayList mPieData) {
465 | if (mPieData==null)
466 | return;
467 | this.mPieData = mPieData;
468 | initDate(mPieData);
469 | }
470 |
471 | /**
472 | * 是否显示点触效果
473 | * @param touchFlag 是否显示点触效果
474 | */
475 | public void setTouchFlag(boolean touchFlag) {
476 | this.touchFlag = touchFlag;
477 | }
478 |
479 | /**
480 | * 设置绘制圆环的动画时间
481 | * @param animatorDuration 动画时间
482 | */
483 | public void setAnimatorDuration(long animatorDuration) {
484 | this.animatorDuration = animatorDuration;
485 | }
486 |
487 | /**
488 | * 设置偏移扇形与原扇形的半径比例
489 | * @param offsetScaleRadius 点触扇形的偏移比例
490 | */
491 | public void setOffsetScaleRadius(double offsetScaleRadius) {
492 | this.offsetScaleRadius = offsetScaleRadius;
493 | }
494 |
495 | /**
496 | * 设置圆环外层园的半径与视图的宽度比
497 | * @param widthScaleRadius 外圆环半径与视图宽度比
498 | */
499 | public void setWidthScaleRadius(double widthScaleRadius) {
500 | this.widthScaleRadius = widthScaleRadius;
501 | }
502 |
503 | /**
504 | * 设置透明圆环与外圆环半径比
505 | * @param radiusScaleTransparent 透明圆环与外圆环半径比
506 | */
507 | public void setRadiusScaleTransparent(double radiusScaleTransparent) {
508 | this.radiusScaleTransparent = radiusScaleTransparent;
509 | }
510 |
511 | /**
512 | * 设置内部圆与外部圆环半径比
513 | * @param radiusScaleInside 内部圆与外部圆环半径比
514 | */
515 | public void setRadiusScaleInside(double radiusScaleInside) {
516 | this.radiusScaleInside = radiusScaleInside;
517 | }
518 |
519 | /**
520 | * 设置圆环显示的百分比画笔大小
521 | * @param percentTextSize 百分比画笔大小
522 | */
523 | public void setPercentTextSize(int percentTextSize) {
524 | this.percentTextSize = percentTextSize;
525 | }
526 |
527 | /**
528 | * 设置圆环显示的百分比字体颜色
529 | * @param percentTextColor 百分比字体颜色
530 | */
531 | public void setPercentTextColor(int percentTextColor) {
532 | this.percentTextColor = percentTextColor;
533 | }
534 |
535 | /**
536 | * 设置中心文字画笔大小
537 | * @param centerTextSize 中心文字画笔大小
538 | */
539 | public void setCenterTextSize(int centerTextSize) {
540 | this.centerTextSize = centerTextSize;
541 | }
542 |
543 | /**
544 | * 设置中心文字颜色
545 | * @param centerTextColor 中心文字颜色
546 | */
547 | public void setCenterTextColor(int centerTextColor) {
548 | this.centerTextColor = centerTextColor;
549 | }
550 |
551 | /**
552 | * 设置动画类型
553 | * @param timeInterpolator 动画类型
554 | */
555 | public void setTimeInterpolator(TimeInterpolator timeInterpolator) {
556 | this.timeInterpolator = timeInterpolator;
557 | }
558 |
559 | /**
560 | * 设置百分比的小数位
561 | * @param percentDecimal 百分比的小数位
562 | */
563 | public void setPercentDecimal(int percentDecimal) {
564 | this.percentDecimal = percentDecimal;
565 | }
566 |
567 | public void setName(String name) {
568 | this.name = name;
569 | }
570 |
571 | public void setMinAngle(float minAngle) {
572 | this.minAngle = minAngle;
573 | }
574 |
575 | /**
576 | * 是否开启动画
577 | * @param animatedFlag 默认为true,开启
578 | */
579 | public void setAnimatedFlag(boolean animatedFlag) {
580 | this.animatedFlag = animatedFlag;
581 | }
582 |
583 | /**
584 | * 设置旋转的角度
585 | * @param animatedValue 默认由动画控制
586 | */
587 | public void setAnimatedValue(float animatedValue) {
588 | this.animatedValue = animatedValue;
589 | }
590 |
591 | }
592 |
--------------------------------------------------------------------------------
/source/PieChart/PieData.java:
--------------------------------------------------------------------------------
1 | package com.example.administrator.customview;
2 |
3 | /**
4 | * Created by DoBest on 2016/4/15.
5 | */
6 | public class PieData {
7 |
8 | private String name;
9 | private float value;
10 | private float percentage;
11 | private int color = 0;
12 | private float angle = 0;
13 |
14 | public String getName() {
15 | return name;
16 | }
17 |
18 | public void setName(String name) {
19 | this.name = name;
20 | }
21 |
22 | public float getValue() {
23 | return value;
24 | }
25 |
26 | public void setValue(float value) {
27 | this.value = value;
28 | }
29 |
30 | public float getPercentage() {
31 | return percentage;
32 | }
33 |
34 | public void setPercentage(float percentage) {
35 | this.percentage = percentage;
36 | }
37 |
38 | public int getColor() {
39 | return color;
40 | }
41 |
42 | public void setColor(int color) {
43 | this.color = color;
44 | }
45 |
46 | public float getAngle() {
47 | return angle;
48 | }
49 |
50 | public void setAngle(float angle) {
51 | this.angle = angle;
52 | }
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/source/PieChart/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------