├── .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 | PieChart
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 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 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 | 11 | 12 | -------------------------------------------------------------------------------- /demo/CustomViewDeom/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/CustomViewDeom/app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 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 | --------------------------------------------------------------------------------