├── README.md
└── RoundMenuView
├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── roundmenuview
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── roundmenuview
│ │ │ ├── MainActivity.java
│ │ │ └── PieChart
│ │ │ ├── MenuChart.java
│ │ │ ├── PieData.java
│ │ │ └── utils
│ │ │ └── PieUtils.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
│ │ ├── btm_chuan.png
│ │ ├── btm_hui.png
│ │ ├── btm_jiang.png
│ │ ├── btm_lu.png
│ │ ├── btm_min.png
│ │ ├── btm_xiang.png
│ │ ├── btm_yue.png
│ │ ├── ic_launcher.png
│ │ └── menu.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── roundmenuview
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/README.md:
--------------------------------------------------------------------------------
1 | ### MenuChart
2 |
3 | ##### 可以设置不同角度,起始角度,不同的模块间比例。
4 | 具体请移步博客地址:[http://blog.csdn.net/qian520ao/article/details/60509040](http://blog.csdn.net/qian520ao/article/details/60509040)
5 |
6 |
7 | ### 二、效果图
8 |
9 | 
10 |
--------------------------------------------------------------------------------
/RoundMenuView/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/RoundMenuView/.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 |
--------------------------------------------------------------------------------
/RoundMenuView/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/RoundMenuView/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/RoundMenuView/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/RoundMenuView/.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 |
--------------------------------------------------------------------------------
/RoundMenuView/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RoundMenuView/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/RoundMenuView/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/RoundMenuView/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 | defaultConfig {
7 | applicationId "com.example.roundmenuview"
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.0.0'
28 | testCompile 'junit:junit:4.12'
29 | }
30 |
--------------------------------------------------------------------------------
/RoundMenuView/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:\AndroidStudio\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 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/androidTest/java/com/example/roundmenuview/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.example.roundmenuview", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/java/com/example/roundmenuview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview;
2 |
3 | import android.graphics.Color;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.view.MotionEvent;
7 | import android.view.View;
8 |
9 | import com.example.roundmenuview.PieChart.MenuChart;
10 | import com.example.roundmenuview.PieChart.PieData;
11 | import com.example.roundmenuview.PieChart.utils.PieUtils;
12 |
13 | import java.util.ArrayList;
14 |
15 | public class MainActivity extends AppCompatActivity {
16 |
17 | private ArrayList mPieDatas = new ArrayList<>();
18 | // 颜色表
19 | private int[] mColors = {0xffff6944, 0xff63ff9e, 0xff38474e, 0xff5effdd, 0xffb579ff, 0xfff4ea2a};
20 | private int[] pieLocation;
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_main);
26 |
27 | initData();
28 | final MenuChart menuChart = (MenuChart) findViewById(R.id.menuChart);
29 | menuChart.setPieData(mPieDatas);
30 | menuChart.setStartAngle(180); //设置起始角度
31 | menuChart.setPieShowAngle(180);//设置总共角度
32 | menuChart.setCenterBitmap(R.mipmap.menu, PieUtils.dp2px(MainActivity.this, 60), PieUtils.dp2px(MainActivity.this, 60));
33 | menuChart.setTouchCenterTextSize(30);
34 | pieLocation = new int[2];
35 | findViewById(R.id.btn_click).setOnTouchListener(new View.OnTouchListener() {
36 | @Override
37 | public boolean onTouch(View v, MotionEvent event) {
38 | switch (event.getAction()) {
39 | case MotionEvent.ACTION_DOWN:
40 | if (menuChart.getVisibility() == View.GONE) {
41 | menuChart.showStartAnim();
42 | } else {
43 | menuChart.showEndAnim();
44 | }
45 | return true;
46 |
47 | case MotionEvent.ACTION_MOVE:
48 | case MotionEvent.ACTION_UP:
49 |
50 | menuChart.getLocationOnScreen(pieLocation);
51 | menuChart.onPieTouchEvent(event, event.getRawX() - pieLocation[0], event.getRawY() - pieLocation[1]);
52 |
53 | return true;
54 | }
55 |
56 |
57 | return false;
58 | }
59 | });
60 |
61 |
62 | }
63 |
64 |
65 | private void initData() {
66 | PieData pieData = new PieData();
67 | pieData.setName("不辣,川菜一点都不辣 T_T");
68 | pieData.setWeight(1);
69 | pieData.setName_label("CHUAN");
70 | pieData.setLabelColor(0xffd81e06);
71 | pieData.setDrawableId(R.mipmap.btm_chuan);
72 | mPieDatas.add(pieData);
73 |
74 |
75 | PieData pieData2 = new PieData();
76 | pieData2.setName("咸,鲜,清淡");
77 | pieData2.setWeight(1);
78 | pieData2.setName_label("YUE");
79 | pieData2.setDrawableId(R.mipmap.btm_yue);
80 | pieData2.setLabelColor(0xff90f895);
81 | mPieDatas.add(pieData2);
82 |
83 |
84 | PieData pieData5 = new PieData();
85 | pieData5.setName("重油盐辣,腊味");
86 | pieData5.setWeight(1);
87 | pieData5.setName_label("XIANG");
88 | pieData5.setDrawableId(R.mipmap.btm_xiang);
89 | pieData5.setLabelColor(0xffe16531);
90 | mPieDatas.add(pieData5);
91 |
92 | PieData pieData4 = new PieData();
93 | pieData4.setName("咸甜");
94 | pieData4.setWeight(1);
95 | pieData4.setName_label("MIN");
96 | pieData4.setDrawableId(R.mipmap.btm_min);
97 | pieData4.setLabelColor(0xfff5c9cb);
98 | mPieDatas.add(pieData4);
99 |
100 |
101 | PieData pieData3 = new PieData();
102 | pieData3.setName("甜,黄酒味");
103 | pieData3.setName_label("SU");
104 | pieData3.setWeight(1);
105 | pieData3.setDrawableId(R.mipmap.btm_jiang);
106 | pieData3.setLabelColor(0xfff4ed61);
107 | mPieDatas.add(pieData3);
108 |
109 | PieData pieData6 = new PieData();
110 | pieData6.setName("鲜,浓油赤酱");
111 | pieData6.setWeight(1);
112 | pieData6.setName_label("LU");
113 | pieData6.setDrawableId(R.mipmap.btm_lu);
114 | pieData6.setLabelColor(0xff944a48);
115 | mPieDatas.add(pieData6);
116 |
117 | }
118 |
119 |
120 | // 闽 f5c9cb 川d81e06 粤90f895 鲁90f895 苏#f4ed61 湘菜#e16531
121 | }
122 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/java/com/example/roundmenuview/PieChart/MenuChart.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview.PieChart;
2 |
3 | import android.animation.TimeInterpolator;
4 | import android.animation.ValueAnimator;
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.BitmapFactory;
8 | import android.graphics.Canvas;
9 | import android.graphics.Color;
10 | import android.graphics.Matrix;
11 | import android.graphics.Paint;
12 | import android.graphics.PorterDuff;
13 | import android.graphics.PorterDuffXfermode;
14 | import android.graphics.RectF;
15 | import android.support.annotation.Nullable;
16 | import android.util.AttributeSet;
17 | import android.util.Log;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 | import android.view.WindowManager;
21 | import android.view.animation.AccelerateDecelerateInterpolator;
22 | import android.widget.Toast;
23 |
24 | import com.example.roundmenuview.PieChart.utils.PieUtils;
25 |
26 | import java.util.ArrayList;
27 | import java.util.Arrays;
28 | import java.util.List;
29 |
30 |
31 | public class MenuChart extends View {
32 | public static final String TAG = "QDX";
33 | //默认的宽高都为屏幕宽度/高度
34 | private int DEFAULT_HEIGHT;
35 | private int DEFAULT_WIDTH;
36 | //宽高
37 | private int mWidth;
38 | private int mHeight;
39 | //数据
40 | private ArrayList mPieData = new ArrayList<>();
41 | // 每块模块的 间隔 以下简称饼状图像为模块
42 | private float PIE_SPACING = 2f;
43 | //整个view展示的角度, 360为圆
44 | private int PIE_VIEW_ANGLE = 360;
45 | /**
46 | * animator 动画时候绘制的角度,最大为 PIE_VIEW_ANGLE
47 | */
48 | private float animatedValue;
49 | //绘制模块 以及 弹出模块上的中心文字
50 | private Paint mPaint = new Paint();
51 | //画在边缘上的 缩写
52 | private Paint mLabelPaint = new Paint();
53 | //XferMode御用的笔
54 | private Paint mXPaint = new Paint();
55 | //饼状图初始绘制角度
56 | private float startAngle = 0;
57 | /**
58 | * rectF 显示bitMap模块
59 | * rectFGold 白金色圈
60 | * rectFIn 白色内圈
61 | * rectFLabel 标签外圈
62 | */
63 | private RectF rectF, rectFGold, rectFIn, rectFLabel;
64 | /**
65 | * rectFF 弹出的最外圈,
66 | * rectFGlodF 弹出的白金层
67 | * reatFInF 弹出的透明层
68 | * rShadowFF 弹出的最外圈的阴影
69 | */
70 | private RectF rectFF, rectFGlodF, rectFInF, rShadowFF;
71 | /**
72 | * rLabel rectFLabel 未弹出时,最外层标签
73 | * r rectF 最外圈的半径
74 | * rGold rectFGold 白金的半径
75 | * rIn reatFInF 透明内圈
76 | * rF rectF 弹出的最外圈的半径
77 | * rGlodF rectFGold 弹出白金的半径
78 | */
79 | private float rLabel, r, rGold, rIn, rF, rGlodF, rInF;
80 |
81 | //展示动画
82 | private ValueAnimator animatorStart;
83 | //end动画
84 | private ValueAnimator animatorEnd;
85 | //动画时间
86 | private long animatorDuration = 500;
87 | private TimeInterpolator timeInterpolator = new AccelerateDecelerateInterpolator();
88 | /**
89 | * 每一个模块占有的总角度 (前几模块角度之和)
90 | */
91 | private float[] pieAngles;
92 | /**
93 | * 选中位置,对应触摸的模块位置
94 | */
95 | private int touchSeleteId;
96 | //点触扇形 偏移比例
97 | private double touchRatioRectFF = 1.4;
98 | //最外层标签
99 | private double labelRadioRectF = 1.2;
100 | //绘制bitmap圆环半径比例
101 | private double widthBmpRadioRectF = 0.8;
102 | //白金层
103 | private double goldRadioRectF = 0.4;
104 | //最里面透明层
105 | private double insideRadiusScale = 0.3;
106 | /**
107 | * 中间bitmap收缩倍数
108 | */
109 | private float bmpScale = 1f;
110 | //文字颜色
111 | private int touchTextColor = Color.BLACK;
112 | //标签文字颜色
113 | private int labelTextColor = Color.WHITE;
114 | //阴影颜色
115 | private int shadowColor = 0x22000000;
116 | //白金区域颜色
117 | private int goldColor = 0x66b8e0e0;
118 |
119 | /**
120 | * 弹出的 模块 中心文字
121 | */
122 | private int touchCenterTextSize = 50;
123 | /**
124 | * 写在边缘的标签文字大小
125 | */
126 | private int labelTextSize = 40;
127 | /**
128 | * 绘制每个模块里的图片
129 | */
130 | private List bmpList = new ArrayList<>();
131 |
132 |
133 | public MenuChart(Context context) {
134 | this(context, null);
135 | }
136 |
137 | public MenuChart(Context context, @Nullable AttributeSet attrs) {
138 | this(context, attrs, 0);
139 | }
140 |
141 | public MenuChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
142 | super(context, attrs, defStyleAttr);
143 | init();
144 | }
145 |
146 | @Override
147 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
148 | if (DEFAULT_WIDTH == 0 || DEFAULT_HEIGHT == 0) {
149 | WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
150 | int displayWidth = wm.getDefaultDisplay().getWidth();
151 | int displayHeight = wm.getDefaultDisplay().getHeight();
152 | DEFAULT_WIDTH = displayWidth;
153 | DEFAULT_HEIGHT = displayHeight;
154 | }
155 |
156 |
157 | int width = measureSize(1, DEFAULT_WIDTH, widthMeasureSpec);
158 | int height = measureSize(1, DEFAULT_HEIGHT, heightMeasureSpec);
159 | int measureSize = Math.min(width, height); //取最小的 宽|高
160 | setMeasuredDimension(measureSize, measureSize);
161 |
162 | }
163 |
164 | @Override
165 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
166 | super.onSizeChanged(w, h, oldw, oldh);
167 | mWidth = w;
168 | mHeight = h;
169 |
170 | rLabel = (float) (Math.max(mWidth, mHeight) / 2 * widthBmpRadioRectF * labelRadioRectF);
171 | rectFLabel = new RectF(-rLabel, -rLabel, rLabel, rLabel);
172 |
173 | //标签层内绘制bmp
174 | r = (float) (Math.max(mWidth, mHeight) / 2 * widthBmpRadioRectF);// 饼状图半径
175 | rectF = new RectF(-r, -r, r, r);
176 |
177 | //白金圆弧
178 | rGold = (float) (r * goldRadioRectF);
179 | rectFGold = new RectF(-rGold, -rGold, rGold, rGold);
180 |
181 | //透明层圆
182 | rIn = (float) (r * insideRadiusScale);
183 | rectFIn = new RectF(-rIn, -rIn, rIn, rIn);
184 |
185 |
186 | /************************以下为弹出模块的RECTF************************************/
187 | //弹出的圆弧
188 | rF = (float) (Math.max(mWidth, mHeight) / 2 * widthBmpRadioRectF * touchRatioRectFF);// 饼状图半径
189 | // 饼状图绘制区域
190 | rectFF = new RectF(-rF, -rF, rF, rF);
191 |
192 | rShadowFF = new RectF(-rF - PIE_SPACING * 10, -rF - PIE_SPACING * 10, rF + PIE_SPACING * 10, rF + PIE_SPACING * 10);
193 |
194 | //弹出的白金层圆弧
195 | rGlodF = (float) (rF * goldRadioRectF);
196 | rectFGlodF = new RectF(-rGlodF, -rGlodF, rGlodF, rGlodF);
197 |
198 | //弹出的透明圆
199 | rInF = (float) (rF * insideRadiusScale);
200 | rectFInF = new RectF(-rInF, -rInF, rInF, rInF);
201 |
202 |
203 | initDate(mPieData);
204 | // showStartAnim();
205 | }
206 |
207 |
208 | private Bitmap bmpCenter;
209 | private Matrix bmpMatrix;
210 |
211 | /**
212 | * 设置居中旋转的bitmap
213 | */
214 | public void setCenterBitmap(int resourceId, int width, int heigth) {
215 | bmpCenter = BitmapFactory.decodeResource(getResources(), resourceId);
216 | bmpCenter = PieUtils.zoomImage(bmpCenter, width, heigth);
217 | bmpMatrix = new Matrix();
218 | }
219 |
220 |
221 | @Override
222 | protected void onDraw(Canvas canvas) {
223 | super.onDraw(canvas);
224 | if (mPieData == null)
225 | return;
226 |
227 | canvas.translate(mWidth / 2, mHeight / 2);// 将画板坐标原点移动到中心位置
228 |
229 | if (bmpCenter != null) { //展示的时候旋转中心图片
230 | bmpMatrix.reset();
231 | bmpMatrix.preTranslate(-bmpCenter.getWidth() / 2, -bmpCenter.getHeight() / 2); //将bitmap移动中心点(原本中心点位于bitmap坐上角(0,0))
232 | bmpMatrix.preRotate(360 / PIE_VIEW_ANGLE * animatedValue * 2, bmpCenter.getWidth() / 2, bmpCenter.getHeight() / 2);//以bitmap中心点为中心 闭着眼 旋转 跳跃
233 | canvas.drawBitmap(bmpCenter, bmpMatrix, mPaint);
234 | }
235 |
236 | // canvas.drawLine(-mWidth / 2, 0, mWidth / 2, 0, mPaint); //绘制一下中心坐标 x,y
237 | // canvas.drawLine(0, mHeight / 2, 0, -mHeight / 2, mPaint);
238 |
239 | /**
240 | * 先画扇形的每一模块
241 | */
242 | drawPieRectF(canvas);
243 |
244 | /**
245 | * 再绘制扇形上的文字/图片
246 | */
247 | drawTextAndBmp(canvas);
248 | }
249 |
250 | /**
251 | * 负责绘制扇形的所有 Rect , Arc
252 | *
253 | * @param canvas 画板
254 | */
255 | private void drawPieRectF(Canvas canvas) {
256 | float currentStartAngle = 0;// 当前已经绘制的角度,我们从0开始,直到animatedValue
257 | canvas.save();
258 | canvas.rotate(startAngle);
259 | float sweepAngle; //当前要绘制的角度
260 |
261 | for (int i = 0; i < mPieData.size(); i++) {
262 | PieData pie = mPieData.get(i);
263 |
264 | sweepAngle = Math.min(pie.getAngle() - PIE_SPACING, animatedValue - currentStartAngle); //-1 是为了显示每个模块之间的空隙
265 |
266 | if (currentStartAngle + sweepAngle == PIE_VIEW_ANGLE - PIE_SPACING) { //防止最后一个模块缺角 ,如果是 360° 且最后一块希望是缺觉,可以注释这段
267 | sweepAngle = pie.getAngle();
268 | }
269 |
270 | if (sweepAngle > 0) {
271 | Log.i(TAG, "绘制角度" + (sweepAngle + currentStartAngle));
272 | Log.i(TAG, "绘制角度 animatedValue" + animatedValue);
273 | if (i == touchSeleteId) { //弹出触摸的板块
274 | drawArc(canvas, currentStartAngle, sweepAngle, pie, rectFF, rectFGlodF, rectFInF, true);
275 | } else {
276 | drawArc(canvas, currentStartAngle, sweepAngle, pie, rectF, rectFGold, rectFIn, false);
277 | }
278 |
279 | }
280 | currentStartAngle += pie.getAngle();
281 | }
282 |
283 | canvas.restore();
284 | }
285 |
286 | /**
287 | * 绘制所有的文字 | bitmap
288 | * 绘制文字和绘制扇形的RectF/Arc 有所不同,绘制文字是当animatedValue达到某一值的时候讲文字绘制
289 | * 而绘制扇形的时候是根据animatedValue一个角度一个角度绘制的
290 | *
291 | * @param canvas 画板
292 | */
293 | private void drawTextAndBmp(Canvas canvas) {
294 | float currentStartAngle = startAngle; //当前绘制的起始角度,用户设定 。绘制文字我们直接加上其实角度的值即可(更便捷计算)
295 |
296 | for (int i = 0; i < mPieData.size(); i++) {
297 | PieData pie = mPieData.get(i);
298 |
299 | int pivotX; //中心点X
300 | int pivotY; //中心点Y
301 |
302 | if (animatedValue > pieAngles[i] - pie.getAngle() / 3) { //当要绘制的角度 >当前模块的 2/3 的时候才绘制。 增强界面动画绘制效果
303 |
304 | if (i == touchSeleteId) { //如果当前模块是触摸的模块,绘制文字
305 | pivotX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (rF + rGlodF) / 2); //获取当前模块中心点x,y轴坐标
306 | pivotY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (rF + rGlodF) / 2);
307 | textCenter(pie.getName(), mPaint, canvas, pivotX, pivotY);
308 | } else {
309 | pivotX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rLabel) / 2);
310 | pivotY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rLabel) / 2);
311 |
312 | canvas.save();
313 | canvas.rotate((currentStartAngle + pie.getAngle() / 2) + 90, pivotX, pivotY); //+90° 原因是 0°的时候文字竖直绘制
314 | textCenter(pie.getName_label(), mLabelPaint, canvas, pivotX, pivotY); //画边缘标签的文字
315 | canvas.restore();
316 |
317 | pivotX = (int) (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rGold) / 2);
318 | pivotY = (int) (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r + rGold) / 2);
319 | if (bmpList != null && bmpList.size() > 0 && bmpList.get(i) != null) { //每次invalidate() 都要重新绘制一遍图片在画板
320 | Log.d(TAG, "绘制图片" + i);
321 | canvas.drawBitmap(bmpList.get(i), (pivotX - (int) (pie.getMax_drawable_size() / 2)), (pivotY - (int) (pie.getMax_drawable_size() / 2)), mPaint);
322 | }
323 |
324 | }
325 | currentStartAngle += pie.getAngle();
326 | }
327 | }
328 | }
329 |
330 | /**
331 | * 画出扇形 ,两种不同情况 展示|触摸弹出
332 | *
333 | * @param canvas 画板
334 | * @param currentStartAngle 当前的扇形总角度
335 | * @param sweepAngle 要绘制扇形(模块)的角度
336 | * @param pie 数据,获取其颜色
337 | * @param outRectF 最外圈的矩形
338 | * @param midRectF 白金的矩形
339 | * @param inRectF 透明区域的矩形
340 | */
341 | private void drawArc(Canvas canvas, float currentStartAngle, float sweepAngle, PieData pie, RectF outRectF, RectF midRectF, RectF inRectF, boolean isTouch) {
342 | int layerID;
343 | if (!isTouch) { //没有touch情况 .显示标签
344 | layerID = canvas.saveLayer(rectFLabel, mPaint, Canvas.ALL_SAVE_FLAG);
345 |
346 | mXPaint.setColor(pie.getLabelColor());
347 | //drawArc里 useCenter的意思是画出的弧形是否连接中心点,否则就连接头尾两点
348 | canvas.drawArc(rectFLabel, currentStartAngle, sweepAngle, true, mXPaint); //标签层写文字
349 |
350 | } else { //touch 情况
351 | layerID = canvas.saveLayer(rShadowFF, mPaint, Canvas.ALL_SAVE_FLAG);
352 |
353 | mXPaint.setColor(shadowColor);
354 | canvas.drawArc(rShadowFF, currentStartAngle - PIE_SPACING, sweepAngle + PIE_SPACING * 2, true, mXPaint); //画个阴影,左右边填充满间隙
355 |
356 |
357 | }
358 | mXPaint.setColor(Color.WHITE);
359 | canvas.drawArc(outRectF, currentStartAngle, sweepAngle, true, mXPaint); //touch情况该层写文字
360 |
361 |
362 | mXPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
363 | canvas.drawArc(midRectF, currentStartAngle - PIE_SPACING, sweepAngle + PIE_SPACING * 2, true, mXPaint); //先把白金区域 擦拭干净。再绘制
364 | mXPaint.setXfermode(null);
365 |
366 |
367 | if (sweepAngle <= (pie.getAngle() - PIE_SPACING) && !isTouch) { //不要间隔
368 | sweepAngle += PIE_SPACING;
369 | }
370 |
371 | mXPaint.setColor(goldColor);
372 | canvas.drawArc(midRectF, currentStartAngle, sweepAngle, true, mXPaint); //白金区域
373 |
374 | mXPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
375 | canvas.drawArc(inRectF, -1f, PIE_VIEW_ANGLE + 2f, true, mXPaint); //透明圈
376 |
377 | Log.d(TAG, "drawArc currentStartAngle==" + currentStartAngle + "+sweepAngle==" + sweepAngle + " ==" + (currentStartAngle + sweepAngle));
378 | mXPaint.setXfermode(null);
379 |
380 | canvas.restoreToCount(layerID);
381 | }
382 |
383 |
384 | @Override
385 | public boolean onTouchEvent(MotionEvent event) {
386 | return onPieTouchEvent(event, event.getX(), event.getY());
387 | }
388 |
389 | /**
390 | * 该方法可提供给外界使用
391 | * 即设置当前屏幕触控点对应 我们控件的坐标,可实现功能: 触摸-->(显示我们控件的btn,我们控件显示)-->手指触摸不放,移动手指弹出 模块
392 | * 使用方法为传入 event, event.getRawX() - pieLocation[0], event.getRawY() - pieLocation[1]
393 | *
394 | * @param eventPivotX 在我们控件内对应的 x 坐标
395 | * @param eventPivotY 在我们控件内对应的 y 坐标
396 | */
397 | public boolean onPieTouchEvent(MotionEvent event, float eventPivotX, float eventPivotY) {
398 | if (mPieData.size() > 0) {
399 | switch (event.getAction()) {
400 | case MotionEvent.ACTION_DOWN:
401 | case MotionEvent.ACTION_MOVE:
402 |
403 | getParent().requestDisallowInterceptTouchEvent(true);
404 | float x = eventPivotX - (mWidth / 2); //因为我们已经将坐标中心点(0,0)移动到 mWidth / 2,mHeight / 2
405 | float y = eventPivotY - (mHeight / 2);
406 |
407 | float touchAngle = 0; //Android 绘图坐标和我们的数学认知不同,安卓由第4 - 3 - 2 -1 象限绘制角度
408 | if (x < 0) { //x<0 的时候 ,求出的角度实为 [0°,-90°) ,此时我们给它掰正
409 | touchAngle += 180;
410 | }
411 | touchAngle += Math.toDegrees(Math.atan(y / x));
412 | touchAngle = touchAngle - startAngle;
413 | if (touchAngle < 0) {
414 | touchAngle = touchAngle + 360;
415 | }
416 | float touchRadius = (float) Math.sqrt(y * y + x * x);//求出触摸的半径
417 |
418 | //我们用touchSeleteId来表示当前选中的模块
419 | if (rIn < touchRadius && touchRadius < rLabel) {//触摸的范围在绘制的区域
420 | if (-Arrays.binarySearch(pieAngles, touchAngle) - 1 == touchSeleteId) { //如果模块有变动(手指挪到其他模块)再绘制
421 | return true; //如果触摸的地方是已经展开的,那么就不再重复绘制
422 | }
423 | touchSeleteId = -Arrays.binarySearch(pieAngles, touchAngle) - 1;
424 | invalidate();
425 | Log.d(TAG, " ACTION_DOWN MOVE invalidate()");
426 |
427 | } else if (0 < touchRadius && touchRadius < rIn) { //触摸的范围在原点bmp范围
428 |
429 | if (animatedValue == PIE_VIEW_ANGLE && event.getAction() == MotionEvent.ACTION_DOWN) { //如果已经全展开
430 | showEndAnim();
431 | } else if (event.getAction() == MotionEvent.ACTION_MOVE) { //有展开模块时候,手指移动到开关,收起模块
432 | touchSeleteId = -1;
433 | invalidate();
434 | }
435 | }
436 |
437 | return true;
438 |
439 |
440 | case MotionEvent.ACTION_UP:
441 |
442 | if (animatedValue == PIE_VIEW_ANGLE) { //如果有触摸且已经显示了 那么隐藏
443 | showEndAnim();
444 | try {
445 | Toast.makeText(getContext(), mPieData.get(touchSeleteId).getName(), Toast.LENGTH_SHORT).show();
446 | } catch (Exception e) {
447 | e.printStackTrace();
448 | }
449 | }
450 | getParent().requestDisallowInterceptTouchEvent(false);
451 |
452 | touchSeleteId = -1;
453 | invalidate();
454 | Log.d(TAG, " ACTION_UP invalidate()");
455 | return true;
456 | }
457 | }
458 | return super.onTouchEvent(event);
459 | }
460 |
461 |
462 | private void init() {
463 | mPaint.setStyle(Paint.Style.FILL);
464 | mPaint.setAntiAlias(true);//抗锯齿
465 | mPaint.setColor(touchTextColor);
466 | mPaint.setTextSize(touchCenterTextSize);
467 | mPaint.setTextAlign(Paint.Align.CENTER);
468 |
469 |
470 | mLabelPaint.setStyle(Paint.Style.FILL);
471 | mLabelPaint.setAntiAlias(true);
472 | mLabelPaint.setColor(labelTextColor);
473 | mLabelPaint.setTextSize(labelTextSize);
474 | mLabelPaint.setTextAlign(Paint.Align.CENTER);
475 |
476 | mXPaint.setStyle(Paint.Style.FILL);
477 | }
478 |
479 |
480 | /**
481 | * 在onSizeChanged 后调用,这样才可以拿到 r 和 rGold 的值。否则无法测量 扇形内最大的bitmap
482 | */
483 | private void initDate(ArrayList mPieData) {
484 |
485 | if (mPieData == null || mPieData.size() == 0)
486 | return;
487 | pieAngles = new float[mPieData.size()];
488 | float sumValue = 0; //总 权重
489 | for (int i = 0; i < mPieData.size(); i++) {
490 | PieData pie = mPieData.get(i);
491 | sumValue += pie.getWeight();
492 | }
493 |
494 | float sumAngle = 0;
495 | for (int i = 0; i < mPieData.size(); i++) {
496 | PieData pie = mPieData.get(i);
497 | float percentage = pie.getWeight() / sumValue;
498 | float angle = percentage * PIE_VIEW_ANGLE;
499 | pie.setAngle(angle);
500 | sumAngle += angle;
501 | pieAngles[i] = sumAngle;
502 |
503 | /****************防止cos90° 取值极其小*********************/
504 |
505 | double centerR = (r + rGold) / 2;
506 | double maxH = r - rGold;
507 | double maxW = Math.sin(Math.toRadians(angle / 2)) * centerR * Math.sqrt(2);
508 | maxW = maxW < 1 ? maxH : maxW;
509 | maxH = maxH < 1 ? maxW : maxH;
510 | /****************防止cos90° 取值极其小*********************/
511 |
512 | pie.setMax_drawable_size(Math.min(maxW, maxH) * bmpScale);
513 | //设置 扇形内最大的bitmap size
514 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), pie.getDrawableId());
515 | bitmap = PieUtils.zoomImage(bitmap, pie.getMax_drawable_size(), pie.getMax_drawable_size());
516 | bmpList.add(bitmap);
517 |
518 | }
519 | touchSeleteId = -1;
520 | }
521 |
522 |
523 | public void showStartAnim() {
524 | setVisibility(View.VISIBLE);
525 | startAnimator();
526 | }
527 |
528 |
529 | public void showEndAnim() {
530 | endAnimator();
531 | animatedValue = 0;
532 |
533 | }
534 |
535 |
536 | private void endAnimator() {
537 | if (animatorEnd != null) {
538 | if (animatorStart.isRunning() || animatorEnd.isRunning()) {
539 | return;
540 | }
541 | animatorEnd.start();
542 | } else {
543 | animatorEnd = ValueAnimator.ofFloat(PIE_VIEW_ANGLE, 0).setDuration(animatorDuration);
544 | animatorEnd.setInterpolator(timeInterpolator);
545 | animatorEnd.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
546 | @Override
547 | public void onAnimationUpdate(ValueAnimator animation) {
548 | animatedValue = (float) animation.getAnimatedValue();
549 | invalidate();
550 | Log.v(TAG, "animatorEnd invalidate()");
551 |
552 | if (animatedValue == 0) {
553 | setVisibility(View.GONE);
554 | }
555 | }
556 | });
557 | animatorEnd.start();
558 | }
559 | }
560 |
561 |
562 | private void startAnimator() {
563 | if (animatorStart != null) {
564 | if (animatorStart.isRunning() || (animatorEnd != null && animatorEnd.isRunning())) {
565 | return;
566 | }
567 | animatorStart.start();
568 | } else {
569 | animatorStart = ValueAnimator.ofFloat(0, PIE_VIEW_ANGLE).setDuration(animatorDuration);
570 | animatorStart.setInterpolator(timeInterpolator);
571 | animatorStart.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
572 | @Override
573 | public void onAnimationUpdate(ValueAnimator animation) {
574 | animatedValue = (float) animation.getAnimatedValue();
575 | invalidate();
576 | Log.v(TAG, "animatorStart invalidate()");
577 |
578 | }
579 | });
580 | animatorStart.start();
581 | }
582 | }
583 |
584 | /**
585 | * 绘制文字
586 | *
587 | * @param pivotX 文字中心点x
588 | * @param pivotY 文字中心点y
589 | */
590 | private void textCenter(String string, Paint paint, Canvas canvas, int pivotX, int pivotY) {
591 | //为了验证文字是否画在中心点,可以画两条基线判断一下
592 | // canvas.drawLine(pivotX - mWidth / 2, pivotY, pivotX + mWidth / 2, pivotY, paint);
593 | // canvas.drawLine(pivotX, pivotY - mHeight / 2, pivotX, pivotY + mHeight / 2, paint);
594 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
595 | float total = -fontMetrics.ascent + fontMetrics.descent;
596 | float yAxis = total / 2 - fontMetrics.descent;
597 |
598 | canvas.drawText(string, pivotX, pivotY + yAxis, paint);
599 | }
600 |
601 |
602 | /**
603 | * 测绘measure
604 | *
605 | * @param specType 1为宽, 其他为高
606 | * @param contentSize 默认值
607 | */
608 | private int measureSize(int specType, int contentSize, int measureSpec) {
609 | int result;
610 | //获取测量的模式和Size
611 | int specMode = MeasureSpec.getMode(measureSpec);
612 | int specSize = MeasureSpec.getSize(measureSpec);
613 |
614 | if (specMode == MeasureSpec.EXACTLY) {
615 | result = Math.max(contentSize, specSize);
616 | } else {
617 | result = contentSize;
618 |
619 | if (specType == 1) {
620 | // 根据传人方式计算宽
621 | result += (getPaddingLeft() + getPaddingRight());
622 | } else {
623 | // 根据传人方式计算高
624 | result += (getPaddingTop() + getPaddingBottom());
625 | }
626 | }
627 |
628 | return result;
629 | }
630 |
631 | /**
632 | * 设置起始角度
633 | *
634 | * @param mStartAngle 起始角度
635 | */
636 | public void setStartAngle(float mStartAngle) {
637 | while (mStartAngle < 0) {
638 | mStartAngle = mStartAngle + 360;
639 | }
640 | while (mStartAngle > 360) {
641 | mStartAngle = mStartAngle - 360;
642 | }
643 | this.startAngle = mStartAngle;
644 | }
645 |
646 | /**
647 | * 设置数据
648 | *
649 | * @param mPieData 数据
650 | */
651 | public void setPieData(ArrayList mPieData) {
652 | this.mPieData = mPieData;
653 | }
654 |
655 | /**
656 | * 设置间距
657 | */
658 | public void setPieSpacing(float spaing) {
659 | this.PIE_SPACING = spaing;
660 | }
661 |
662 | /**
663 | * 整个view的展示角度
664 | */
665 | public void setPieShowAngle(int angle) {
666 | this.PIE_VIEW_ANGLE = angle;
667 | }
668 |
669 | /**
670 | * 写在边缘标签文字的size
671 | */
672 | public void setLabelTextSize(int labelTextSize) {
673 | this.labelTextSize = labelTextSize;
674 | mLabelPaint.setTextSize(labelTextSize);
675 | }
676 |
677 | /**
678 | * 触摸后弹出模块的文字size
679 | */
680 | public void setTouchCenterTextSize(int touchCenterTextSize) {
681 | this.touchCenterTextSize = touchCenterTextSize;
682 | mPaint.setTextSize(touchCenterTextSize);
683 | }
684 |
685 | /**
686 | * 标签文字颜色
687 | */
688 | public void setLabelTextColor(int labelTextColor) {
689 | this.labelTextColor = labelTextColor;
690 | mLabelPaint.setColor(labelTextColor);
691 | }
692 |
693 | /**
694 | * 触摸后弹出模块的文字颜色
695 | */
696 | public void setTouchTextColor(int touchTextColor) {
697 | this.touchTextColor = touchTextColor;
698 | mPaint.setColor(touchTextColor);
699 | }
700 |
701 | /**
702 | * 白金模块颜色
703 | */
704 | public void setGoldColor(int goldColor) {
705 | this.goldColor = goldColor;
706 | }
707 |
708 | /**
709 | * 触摸后弹出模块的阴影颜色
710 | */
711 | public void setShadowColor(int shadowColor) {
712 | this.shadowColor = shadowColor;
713 | }
714 |
715 | /**
716 | * 中间bmp比例
717 | */
718 | public void setBmpScale(float bmpScale) {
719 | this.bmpScale = bmpScale;
720 | }
721 |
722 | /**
723 | * 最里面透明层比例
724 | */
725 | public void setRatioInsideRect(double insideRadiusScale) {
726 | this.insideRadiusScale = insideRadiusScale;
727 | }
728 |
729 | /**
730 | * 白金层比例
731 | */
732 | public void setRatioGoldRect(double goldRatio) {
733 | this.goldRadioRectF = goldRatio;
734 | }
735 |
736 | /**
737 | * 绘制bitmap层比例
738 | */
739 | public void setRatioBmpRect(double widthBmpScaleRadius) {
740 | this.widthBmpRadioRectF = widthBmpScaleRadius;
741 | }
742 |
743 | /**
744 | * 绘制标签层比例
745 | */
746 | public void setRatioLabelRect(double labelRadiusScale) {
747 | this.labelRadioRectF = labelRadiusScale;
748 | }
749 |
750 | /**
751 | * 绘制触摸层(弹出层)比例
752 | */
753 | public void setTouchRatioRect(double touchScaleRadius) {
754 | this.touchRatioRectFF = touchScaleRadius;
755 | }
756 |
757 | /**
758 | * 中间的bitmap
759 | */
760 | public void setBmpCenter(Bitmap bmpCenter) {
761 | this.bmpCenter = bmpCenter;
762 | }
763 |
764 | /**
765 | * 动画持续时间
766 | */
767 | public void setAnimatorDuration(long animatorDuration) {
768 | this.animatorDuration = animatorDuration;
769 | }
770 | }
771 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/java/com/example/roundmenuview/PieChart/PieData.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview.PieChart;
2 |
3 |
4 | public class PieData {
5 | private String name;
6 | //缩写
7 | private String name_label;
8 | //设置权重,默认为1
9 | private float weight = 1;
10 | //标签层颜色
11 | private int labelColor = 0;
12 | private float angle = 0;
13 | //内切bitmap的最大宽/高
14 | private double max_drawable_size;
15 |
16 | public String getName_label() {
17 | return name_label;
18 | }
19 |
20 | public void setName_label(String name_label) {
21 | this.name_label = name_label;
22 | }
23 |
24 | public double getMax_drawable_size() {
25 | return max_drawable_size;
26 | }
27 |
28 | public void setMax_drawable_size(double max_drawable_size) {
29 | this.max_drawable_size = max_drawable_size;
30 | }
31 |
32 | private int drawableId;
33 |
34 | public int getDrawableId() {
35 | return drawableId;
36 | }
37 |
38 | public void setDrawableId(int drawableId) {
39 | this.drawableId = drawableId;
40 | }
41 |
42 | public String getName() {
43 | return name;
44 | }
45 |
46 | public void setName(String name) {
47 | this.name = name;
48 | }
49 |
50 | public float getWeight() {
51 | return weight;
52 | }
53 |
54 | public void setWeight(float weight) {
55 | this.weight = weight;
56 | }
57 |
58 |
59 | public int getLabelColor() {
60 | return labelColor;
61 | }
62 |
63 | public void setLabelColor(int labelColor) {
64 | this.labelColor = labelColor;
65 | }
66 |
67 | public float getAngle() {
68 | return angle;
69 | }
70 |
71 | public void setAngle(float angle) {
72 | this.angle = angle;
73 | }
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/java/com/example/roundmenuview/PieChart/utils/PieUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview.PieChart.utils;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Matrix;
6 |
7 | /**
8 | * Created by QDX on 2017/3/2.
9 | */
10 |
11 | public class PieUtils {
12 |
13 | /***
14 | * 图片的缩放方法
15 | *
16 | * @param bgimage :源图片资源
17 | * @param newWidth :缩放后宽度
18 | * @param newHeight :缩放后高度
19 | */
20 | public static Bitmap zoomImage(Bitmap bgimage, double newWidth, double newHeight) {
21 | // 获取这个图片的宽和高
22 | float width = bgimage.getWidth();
23 | float height = bgimage.getHeight();
24 | // 创建操作图片用的matrix对象
25 | Matrix matrix = new Matrix();
26 | // 计算宽高缩放率
27 | float scaleWidth = ((float) newWidth) / width;
28 | float scaleHeight = ((float) newHeight) / height;
29 | // 缩放图片动作
30 | matrix.postScale(scaleWidth, scaleHeight);
31 |
32 | return Bitmap.createBitmap(bgimage, 0, 0, (int) width, (int) height, matrix, true);
33 | }
34 |
35 | /**
36 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
37 | */
38 | public static int dp2px(Context context, float dpValue) {
39 | final float scale = context.getResources().getDisplayMetrics().density;
40 | return (int) (dpValue * scale + 0.5f);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
19 |
20 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_chuan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_chuan.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_hui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_hui.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_jiang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_jiang.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_lu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_lu.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_min.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_xiang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_xiang.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_yue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/btm_yue.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/app/src/main/res/mipmap-xxxhdpi/menu.png
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RoundMenuView
3 |
4 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RoundMenuView/app/src/test/java/com/example/roundmenuview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.roundmenuview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/RoundMenuView/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/RoundMenuView/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/RoundMenuView/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qdxxxx/MenuChart/c7edfe12e46865550db7b620bfa826aecd180e0f/RoundMenuView/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/RoundMenuView/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.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/RoundMenuView/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/RoundMenuView/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/RoundMenuView/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------