HDL_Widget Android和iOS精简版的库,去掉高德SDK,和iOS裁剪方法
JLChen
2021-01-08 07a4e46efd0d180f881a9761c6f737e5d1c47848
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
package com.hdl.widget;
 
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.SweepGradient;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
 
 
import com.hdl.widgetxm.R;
 
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
 
 
public class HDLArcSeekBar extends View {
    private static final int DEFAULT_EDGE_LENGTH = 260;              // 默认宽高
 
    private static final float CIRCLE_ANGLE = 360;                  // 圆周角
    private static final int DEFAULT_ARC_WIDTH = 15;                // 默认宽度 dp
    private static final float DEFAULT_OPEN_ANGLE = 30;             // 开口角度
    private static final float DEFAULT_ROTATE_ANGLE = 90;           // 旋转角度
    private static final int DEFAULT_BORDER_WIDTH = 0;              // 默认描边宽度
    private static final int DEFAULT_BORDER_COLOR = 0xffffffff;     // 默认描边颜色
 
    private static final int DEFAULT_THUMB_COLOR = 0xffffffff;      // 拖动按钮颜色
    private static final int DEFAULT_THUMB_WIDTH = 5;               // 拖动按钮描边宽度 dp
    private static final int DEFAULT_THUMB_RADIUS = 7;              // 拖动按钮半径 dp
    private static final int DEFAULT_THUMB_SHADOW_RADIUS = 0;       // 拖动按钮阴影半径 dp
    private static final int DEFAULT_THUMB_SHADOW_COLOR = 0xFF000000; // 拖动按钮阴影颜色
 
    private static final int DEFAULT_SHADOW_RADIUS = 0;             // 默认阴影半径 dp
    public static final int THUMB_MODE_STROKE = 0;                 // 拖动按钮模式 - 描边
    public static final int THUMB_MODE_FILL = 1;                   // 拖动按钮模式 - 填充
    public static final int THUMB_MODE_FILL_STROKE = 2;            // 拖动按钮模式 - 填充+描边
    private static final int DEFAULT_MAX_VALUE = 100;               // 默认最大数值
    private static final int DEFAULT_MIN_VALUE = 0;                 // 默认最小数值
    private static final String KEY_PROGRESS_PRESENT = "PRESENT";   // 用于存储和获取当前百分比
 
    // 可配置数据
    private int[] mArcColors;       // Seek 颜色
    private float mArcWidth;        // Seek 宽度
    private float mOpenAngle;       // 开口的角度大小 0 - 360
    private float mRotateAngle;     // 旋转角度
    private int mBorderWidth;       // 描边宽度
    private int mBorderColor;       // 描边颜色
    private int mThumbColor;        // 拖动按钮颜色
    private float mThumbWidth;      // 拖动按钮宽度
    private float mThumbRadius;     // 拖动按钮半径
    private float mThumbShadowRadius;// 拖动按钮阴影半径
    private int mThumbShadowColor;// 拖动按钮阴影颜色
    private int mThumbMode;         // 拖动按钮模式
 
    private int mShadowRadius;      // 阴影半径
 
    private int mMaxValue;          // 最大数值
    private int mMinValue;          // 最小数值
 
    private float mCenterX;         // 圆弧 SeekBar 中心点 X
    private float mCenterY;         // 圆弧 SeekBar 中心点 Y
 
    private float mThumbX;         // 拖动按钮 中心点 X
    private float mThumbY;         // 拖动按钮 中心点 Y
 
    private Path mSeekPath;
    private Path mBorderPath;
    private Paint mArcPaint;
    private Paint mThumbPaint;
    private Paint mBorderPaint;
    private Paint mShadowPaint;
 
    private float[] mTempPos;
    private float[] mTempTan;
    private PathMeasure mSeekPathMeasure;
 
    private float mProgressPresent = 0;         // 当前进度百分比
    private boolean mCanDrag = false;           // 是否允许拖动
    private boolean mAllowTouchSkip = false;    // 是否允许越过边界
//    private GestureDetector mDetector;
    private Matrix mInvertMatrix;               // 逆向 Matrix, 用于计算触摸坐标和绘制坐标的转换
    private Region mArcRegion;                  // ArcPath的实际区域大小,用于判定单击事件
 
    //***********************新增***********************************
    private static final int DEFAULT_THUMB_DISTANCE_MULTIPLE = 5;                   //点击的坐标离与mThumb圆心坐标距离,小于5*半径,则可以拖动
    private static final int DEFAULT_ARC_PROGRESS_BAR_COLOR = 0xFFFFFFFF;           // 进度条颜色
    private static final String DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING = "%";         // 默认显示单位
//    private static final int DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC = 70;
    private static final int DEFAULT_PADDING = 80;                                  //圆弧自带PADDING值,为了显示进度test文字
    private Point mTextPoint;                    //旋转90度后的坐标
    private Paint mTextPaint;                   //进度文字显示内容
    private RectF content;                      //显示内容区域
    private float mSweepAngle = 0;              //与开始角度的夹角
    private int mArcProgressBarColor;           //当前进度圆弧颜色
    private Paint mArcProgressBarPaint;         //当前进度圆弧画笔
    private String mProgressBarUnitSring;       //进度单位符号
    private double mArcProgressBarRadius;       //圆弧的半径
    private boolean isClickable = true;
    private int mTextDefaultDistance = 70;                                                // 进度显示文字坐标与进度圆弧的距离 DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC
    private int[] mArcProgressBarColors;       // 当前进度圆弧渐变颜色数组
//    private int mTextColor;        // 拖动按钮颜色
    //***********************新增***********************************
 
 
    public HDLArcSeekBar(Context context) {
        this(context, null);
    }
 
    public HDLArcSeekBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public HDLArcSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setSaveEnabled(true);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        initAttrs(context, attrs);
        initData();
        initPaint();
    }
 
    //--- 初始化 -----------------------------------------------------------------------------------
 
    // 初始化各种属性
    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HDLArcSeekBar);
        mArcColors = getArcColors(context, ta);
        mArcWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_width, dp2px(DEFAULT_ARC_WIDTH));
        mOpenAngle = ta.getFloat(R.styleable.HDLArcSeekBar_arc_open_angle, DEFAULT_OPEN_ANGLE);
        mRotateAngle = ta.getFloat(R.styleable.HDLArcSeekBar_arc_rotate_angle, DEFAULT_ROTATE_ANGLE);
        mMaxValue = ta.getInt(R.styleable.HDLArcSeekBar_arc_max, DEFAULT_MAX_VALUE);
        mMinValue = ta.getInt(R.styleable.HDLArcSeekBar_arc_min, DEFAULT_MIN_VALUE);
        // 如果用户设置的最大值和最小值不合理,则直接按照默认进行处理
        if (mMaxValue <= mMinValue) {
            mMaxValue = DEFAULT_MAX_VALUE;
            mMinValue = DEFAULT_MIN_VALUE;
        }
        int progress = ta.getInt(R.styleable.HDLArcSeekBar_arc_progress, mMinValue);
        setProgress(progress);
        mBorderWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_border_width, dp2px(DEFAULT_BORDER_WIDTH));
        mBorderColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_border_color, DEFAULT_BORDER_COLOR);
 
        mThumbColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_thumb_color, DEFAULT_THUMB_COLOR);
        mThumbRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_radius, dp2px(DEFAULT_THUMB_RADIUS));
        mThumbShadowRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_shadow_radius, dp2px(DEFAULT_THUMB_SHADOW_RADIUS));
        mThumbShadowColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_thumb_shadow_color, DEFAULT_THUMB_SHADOW_COLOR);
        mThumbWidth = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_thumb_width, dp2px(DEFAULT_THUMB_WIDTH));
        mThumbMode = ta.getInt(R.styleable.HDLArcSeekBar_hdl_arc_thumb_mode, THUMB_MODE_STROKE);
 
        mShadowRadius = ta.getDimensionPixelSize(R.styleable.HDLArcSeekBar_arc_shadow_radius, dp2px(DEFAULT_SHADOW_RADIUS));
        mArcProgressBarColor = ta.getColor(R.styleable.HDLArcSeekBar_arc_progress_bar_color, DEFAULT_ARC_PROGRESS_BAR_COLOR);
 
        mArcProgressBarColors = new int[]{mArcProgressBarColor,mArcProgressBarColor};
 
        ta.recycle();
    }
 
    // 获取 Arc 颜色数组
    private int[] getArcColors(Context context, TypedArray ta) {
        int[] ret;
        int resId = ta.getResourceId(R.styleable.HDLArcSeekBar_arc_colors, 0);
        if (0 == resId) {
            resId = R.array.arc_colors_default;
        }
        ret = getColorsByArrayResId(context, resId);
        return ret;
    }
 
    // 根据 resId 获取颜色数组
    private int[] getColorsByArrayResId(Context context, int resId) {
        int[] ret;
        TypedArray colorArray = context.getResources().obtainTypedArray(resId);
        ret = new int[colorArray.length()];
        for (int i = 0; i < colorArray.length(); i++) {
            ret[i] = colorArray.getColor(i, 0);
        }
        return ret;
    }
 
    // 初始化数据
    private void initData() {
        mSeekPath = new Path();
        mBorderPath = new Path();
        mSeekPathMeasure = new PathMeasure();
        mTempPos = new float[2];
        mTempTan = new float[2];
 
//        mDetector = new GestureDetector(getContext(), new OnClickListener());
        mInvertMatrix = new Matrix();
        mArcRegion = new Region();
        mProgressBarUnitSring = DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING;
    }
 
    // 初始化画笔
    private void initPaint() {
        initArcPaint();
        initThumbPaint();
        initTextPanit();
        initProgressBarPaint();
        initBorderPaint();
        initShadowPaint();
    }
 
 
    // 初始化圆弧画笔
    private void initArcPaint() {
        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);
        mArcPaint.setStrokeWidth(mArcWidth);
        mArcPaint.setStyle(Paint.Style.STROKE);
        mArcPaint.setStrokeCap(Paint.Cap.ROUND);
    }
 
    // 初始化拖动按钮画笔
    private void initThumbPaint() {
        mThumbPaint = new Paint();
        mThumbPaint.setAntiAlias(true);
        mThumbPaint.setColor(mThumbColor);
        mThumbPaint.setStrokeWidth(mThumbWidth);
        mThumbPaint.setStrokeCap(Paint.Cap.ROUND);
        if (mThumbMode == THUMB_MODE_FILL) {
            mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        } else if (mThumbMode == THUMB_MODE_FILL_STROKE) {
            mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        } else {
            mThumbPaint.setStyle(Paint.Style.STROKE);
        }
        mThumbPaint.setTextSize(56);
    }
 
    private float getTextSizeDip(float value) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
    }
 
    // 初始化进度圆弧画笔
    private void initProgressBarPaint() {
        mArcProgressBarPaint = new Paint();      //初始化进度圆弧画笔
        mArcProgressBarPaint.setAntiAlias(true);
        mArcProgressBarPaint.setStyle(Paint.Style.STROKE);
        mArcProgressBarPaint.setStrokeCap(Paint.Cap.ROUND);
        mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1);
//        mArcProgressBarPaint.setColor(mArcProgressBarColor);
 
 
    }
 
    // 初始化拖动按钮顶部文字画笔和进度圆弧画笔
    private void initTextPanit() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStrokeWidth(1);
        mTextPaint.setTextSize(getTextSizeDip(12));
        mTextPaint.setColor(mThumbColor);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }
 
    // 初始化拖动按钮画笔
    private void initBorderPaint() {
        mBorderPaint = new Paint();
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBorderPaint.setStyle(Paint.Style.STROKE);
    }
 
    // 初始化阴影画笔
    private void initShadowPaint() {
        mShadowPaint = new Paint();
        mShadowPaint.setAntiAlias(true);
        mShadowPaint.setStrokeWidth(mBorderWidth);
        mShadowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }
 
    //--- 初始化结束 -------------------------------------------------------------------------------
 
    //--- 状态存储 ---------------------------------------------------------------------------------
 
    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable("superState", super.onSaveInstanceState());
        bundle.putFloat(KEY_PROGRESS_PRESENT, mProgressPresent);
        return bundle;
    }
 
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            this.mProgressPresent = bundle.getFloat(KEY_PROGRESS_PRESENT);
            state = bundle.getParcelable("superState");
        }
        if (null != mOnProgressChangeListener) {
            mOnProgressChangeListener.onProgressChanged(this, getProgress(), false);
        }
        super.onRestoreInstanceState(state);
    }
 
    //--- 状态存储结束 -----------------------------------------------------------------------------
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int ws = MeasureSpec.getSize(widthMeasureSpec);     //取出宽度的确切数值
        int wm = MeasureSpec.getMode(widthMeasureSpec);     //取出宽度的测量模式
        int hs = MeasureSpec.getSize(heightMeasureSpec);    //取出高度的确切数值
        int hm = MeasureSpec.getMode(heightMeasureSpec);    //取出高度的测量模
 
        if (wm == MeasureSpec.UNSPECIFIED) {
            wm = MeasureSpec.EXACTLY;
            ws = dp2px(DEFAULT_EDGE_LENGTH);
        } else if (wm == MeasureSpec.AT_MOST) {
            wm = MeasureSpec.EXACTLY;
            ws = Math.min(dp2px(DEFAULT_EDGE_LENGTH), ws);
        }
        if (hm == MeasureSpec.UNSPECIFIED) {
            hm = MeasureSpec.EXACTLY;
            hs = dp2px(DEFAULT_EDGE_LENGTH);
        } else if (hm == MeasureSpec.AT_MOST) {
            hm = MeasureSpec.EXACTLY;
            hs = Math.min(dp2px(DEFAULT_EDGE_LENGTH), hs);
        }
        setMeasuredDimension(MeasureSpec.makeMeasureSpec(ws, wm), MeasureSpec.makeMeasureSpec(hs, hm));
    }
 
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 计算在当前大小下,内容应该显示的大小和起始位置
        int safeW = w - getPaddingLeft() - getPaddingRight();
        int safeH = h - getPaddingTop() - getPaddingBottom();
        float edgeLength, startX, startY;
        float fix = mArcWidth / 2 + mBorderWidth + mShadowRadius * 2;  // 修正距离,画笔宽度的修正
        if (safeW < safeH) {
            // 宽度小于高度,以宽度为准
            edgeLength = safeW - fix;
            startX = getPaddingLeft();
            startY = (safeH - safeW) / 2.0f + getPaddingTop();
        } else {
            // 宽度大于高度,以高度为准
            edgeLength = safeH - fix;
            startX = (safeW - safeH) / 2.0f + getPaddingLeft();
            startY = getPaddingTop();
        }
 
        // 得到显示区域和中心的
        content = new RectF(startX + fix + DEFAULT_PADDING, startY + fix + DEFAULT_PADDING, startX + edgeLength - DEFAULT_PADDING, startY + edgeLength - DEFAULT_PADDING);
        mCenterX = content.centerX();
        mCenterY = content.centerY();
 
        // 得到路径
        mSeekPath.reset();
        mSeekPath.addArc(content, mOpenAngle / 2, CIRCLE_ANGLE - mOpenAngle);
        mSeekPathMeasure.setPath(mSeekPath, false);
        computeThumbPos(mProgressPresent);
        mArcProgressBarRadius = getDistanceWithCenter(mThumbX, mThumbY);//计算出圆弧的半径
        resetShaderColor();
        mInvertMatrix.reset();
        mInvertMatrix.preRotate(-mRotateAngle, mCenterX, mCenterY);
 
        mArcPaint.getFillPath(mSeekPath, mBorderPath);
        mBorderPath.close();
        mArcRegion.setPath(mBorderPath, new Region(0, 0, w, h));
    }
 
 
 
    /**
     * 重置 mArcProgressBarColors 颜色
     * 2019-8-14
     */
    private void resetmArcProgressBarColor() {
//        // 计算渐变数组
//        float startPos = (mOpenAngle / 2) / CIRCLE_ANGLE;
//        float stopPos = (CIRCLE_ANGLE - (mOpenAngle / 2)) / CIRCLE_ANGLE;
//        int len = mArcProgressBarColors.length - 1;
//        float distance = (stopPos - startPos) / len;
//        float pos[] = new float[mArcProgressBarColors.length];
//        for (int i = 0; i < mArcProgressBarColors.length; i++) {
//            pos[i] = startPos + (distance * i);
//        }
//        SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcProgressBarColors, pos);
 
        SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcProgressBarColors, null);
 
 
        mArcProgressBarPaint.setShader(gradient);
    }
 
    // 重置 shader 颜色
    private void resetShaderColor() {
        // 计算渐变数组
        float startPos = (mOpenAngle / 2) / CIRCLE_ANGLE;
        float stopPos = (CIRCLE_ANGLE - (mOpenAngle / 2)) / CIRCLE_ANGLE;
        int len = mArcColors.length - 1;
        float distance = (stopPos - startPos) / len;
        float pos[] = new float[mArcColors.length];
        for (int i = 0; i < mArcColors.length; i++) {
            pos[i] = startPos + (distance * i);
        }
        SweepGradient gradient = new SweepGradient(mCenterX, mCenterY, mArcColors, pos);
        mArcPaint.setShader(gradient);
    }
 
    // 具体绘制
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mTextPoint.x, mTextPoint.y, mTextPaint);
        canvas.rotate(mRotateAngle, mCenterX, mCenterY);
//        mShadowPaint.setShadowLayer(mShadowRadius * 2, 0, 0, getColor());
        canvas.drawPath(mBorderPath, mShadowPaint);
        canvas.drawPath(mSeekPath, mArcPaint);
        //当前进度
        resetmArcProgressBarColor();//渐变颜色
        canvas.drawArc(content, mOpenAngle / 2, mSweepAngle, false, mArcProgressBarPaint);
        if (mBorderWidth > 0) {
            canvas.drawPath(mBorderPath, mBorderPaint);
        }
        if (mThumbShadowRadius > 0) {
            mThumbPaint.setShadowLayer(mThumbShadowRadius, 0, 0, mThumbShadowColor);
            canvas.drawCircle(mThumbX, mThumbY, mThumbRadius, mThumbPaint);
            mThumbPaint.clearShadowLayer();
        }
        canvas.drawCircle(mThumbX, mThumbY, mThumbRadius, mThumbPaint);
        canvas.restore();
    }
 
    private boolean moved = false;
    private int lastProgress = -1;
 
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        if(!isClickable) return false;
        int action = event.getActionMasked();
        switch (action) {
            case ACTION_DOWN:
                moved = false;
                judgeCanDrag(event);
                if (null != mOnProgressChangeListener) {
                    mOnProgressChangeListener.onStartTrackingTouch(this);
                }
                break;
            case ACTION_MOVE:
                if (!mCanDrag) {
                    break;
                }
                float tempProgressPresent = getCurrentProgress(event.getX(), event.getY());
                if (!mAllowTouchSkip) {
                    // 不允许突变
                    if (Math.abs(tempProgressPresent - mProgressPresent) > 0.5f) {
                        break;
                    }
                }
                // 允许突变 或者非突变
                mProgressPresent = tempProgressPresent;
                computeThumbPos(mProgressPresent);
                // 事件回调
                if (null != mOnProgressChangeListener && getProgress() != lastProgress) {
                    mOnProgressChangeListener.onProgressChanged(this, getProgress(), true);
                    lastProgress = getProgress();
                }
                moved = true;
                break;
            case ACTION_UP:
            case ACTION_CANCEL:
                if(!moved){
                    if (isInArcProgress(event.getX(), event.getY())) {
                        // 点击允许突变
                        mProgressPresent = getCurrentProgress(event.getX(), event.getY());
                        computeThumbPos(mProgressPresent);
                    }
                }
                if (null != mOnProgressChangeListener) {
                    mOnProgressChangeListener.onStopTrackingTouch(this);
                }
 
                break;
        }
//        mDetector.onTouchEvent(event);
        invalidate();
        return true;
    }
 
    // 判断是否允许拖动
    private void judgeCanDrag(MotionEvent event) {
        float[] pos = {event.getX(), event.getY()};
        mInvertMatrix.mapPoints(pos);
        if (getDistance(pos[0], pos[1]) <= mThumbRadius * DEFAULT_THUMB_DISTANCE_MULTIPLE) {
            mCanDrag = true;
        } else {
            mCanDrag = false;
        }
    }
 
 
    private class OnClickListener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // 判断是否点击在了进度区域
            if (!isInArcProgress(e.getX(), e.getY())) {
                return false;
            }
            // 点击允许突变
            mProgressPresent = getCurrentProgress(e.getX(), e.getY());
            computeThumbPos(mProgressPresent);
 
            Log.i("onSingleTapUp","onSingleTapUp");
//            // 事件回调
//            if (null != mOnProgressChangeListener) {
//                mOnProgressChangeListener.onProgressChanged(HDLArcSeekBar.this, getProgress(), true);
////                mOnProgressChangeListener.onStopTrackingTouch(HDLArcSeekBar.this);
//            }
            return true;
        }
    }
 
//    // 判断该点是否在进度条上面
//    private boolean isInArcProgress(float px, float py) {
//        float[] pos = {px, py};
//        mInvertMatrix.mapPoints(pos);
//        return mArcRegion.contains((int) pos[0], (int) pos[1]);
//    }
 
    /**
     * 判断该点是否在进度条附近 2019-07-17
     * 对比判断该点是否在进度条上面,增大可点击区域
     *
     * @param px 当前坐标x
     * @param py 当前坐标y
     * @return
     */
    private boolean isInArcProgress(float px, float py) {
        float[] pos = {px, py};
        mInvertMatrix.mapPoints(pos);
        double mDistance = getDistanceWithCenter(pos[0], pos[1]);
//        Log.i("kkk" ,"KmArcProgressBarRadiusmDistance"+mDistance);
        if ((mDistance >= (mArcProgressBarRadius * 2 / 3))
                && (mDistance <= (mArcProgressBarRadius * 4 / 3))) {
            return true;
        } else {
            return false;
        }
    }
 
    // 获取当前进度理论进度数值
    private float getCurrentProgress(float px, float py) {
        float diffAngle = getDiffAngle(px, py);
        float progress = diffAngle / (CIRCLE_ANGLE - mOpenAngle);
        if (progress < 0) progress = 0;
        if (progress > 1) progress = 1;
        return progress;
    }
 
    // 获得当前点击位置所成角度与开始角度之间的数值差
    private float getDiffAngle(float px, float py) {
        float angle = getAngle(px, py);
        float diffAngle;
        diffAngle = angle - mRotateAngle;
        if (diffAngle < 0) {
            diffAngle = (diffAngle + CIRCLE_ANGLE) % CIRCLE_ANGLE;
        }
        diffAngle = diffAngle - mOpenAngle / 2;
        return diffAngle;
    }
 
    // 计算指定位置与内容区域中心点的夹角
    private float getAngle(float px, float py) {
        float angle = (float) ((Math.atan2(py - mCenterY, px - mCenterX)) * 180 / 3.14f);
        if (angle < 0) {
            angle += 360;
        }
        return angle;
    }
 
    // 计算指定位置与上次位置的距离
    private float getDistance(float px, float py) {
        return (float) Math.sqrt((px - mThumbX) * (px - mThumbX) + (py - mThumbY) * (py - mThumbY));
    }
 
    // 计算坐标圆点的距离
    private double getDistanceWithCenter(float px, float py) {
        return Math.sqrt((px - mCenterX) * (px - mCenterX) + (py - mCenterY) * (py - mCenterY));
    }
 
    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
    }
 
    /**
     * 获取相同距离下,圆弧对应的第三点坐标
     *
     * @param mPoint 圆弧坐标
     *               第三点距离第二点的距离
     *               DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC 距离
     */
    private Point calcOutsidePoint(Point mPoint) {
        double newX = (mPoint.x * (mArcProgressBarRadius + mTextDefaultDistance) - mCenterX * mTextDefaultDistance) / mArcProgressBarRadius;
        double newY = (mPoint.y * (mArcProgressBarRadius + mTextDefaultDistance) - mCenterY * mTextDefaultDistance) / mArcProgressBarRadius;
        return new Point((int) newX, (int) newY);
    }
 
 
    // 计算拖动块应该显示的位置
    private void computeThumbPos(float present) {
        if (present < 0) present = 0;
        if (present > 1) present = 1;
        if (null == mSeekPathMeasure) return;
        float distance = mSeekPathMeasure.getLength() * present;
        mSeekPathMeasure.getPosTan(distance, mTempPos, mTempTan);
        mThumbX = mTempPos[0];
        mThumbY = mTempPos[1];
        Point mmTextPoint = calcNewPoint(new Point((int) mThumbX, (int) mThumbY), new Point((int) mCenterX, (int) mCenterY), mRotateAngle);
        mSweepAngle = getDiffAngle(mmTextPoint.x, mmTextPoint.y);
        if (mSweepAngle < 0) mSweepAngle = 0;
//        Log.i("mSweepAngle", "mSweepAngle:" + mSweepAngle);
 
        mTextPoint = calcOutsidePoint(mmTextPoint);
    }
 
    //--- 线性取色 ---------------------------------------------------------------------------------
 
    /**
     * 获取当前进度的具体颜色
     *
     * @return 当前进度在渐变中的颜色
     */
    public int getColor() {
        return getColor(mProgressPresent);
    }
 
    /**
     * 获取某个百分比位置的颜色
     *
     * @param radio 取值[0,1]
     * @return 最终颜色
     */
    private int getColor(float radio) {
        float diatance = 1.0f / (mArcColors.length - 1);
        int startColor;
        int endColor;
        if (radio >= 1) {
            return mArcColors[mArcColors.length - 1];
        }
        for (int i = 0; i < mArcColors.length; i++) {
            if (radio <= i * diatance) {
                if (i == 0) {
                    return mArcColors[0];
                }
                startColor = mArcColors[i - 1];
                endColor = mArcColors[i];
                float areaRadio = getAreaRadio(radio, diatance * (i - 1), diatance * i);
                return getColorFrom(startColor, endColor, areaRadio);
            }
        }
        return -1;
    }
 
    /**
     * 计算当前比例在子区间的比例
     *
     * @param radio         总比例
     * @param startPosition 子区间开始位置
     * @param endPosition   子区间结束位置
     * @return 自区间比例[0, 1]
     */
    private float getAreaRadio(float radio, float startPosition, float endPosition) {
        return (radio - startPosition) / (endPosition - startPosition);
    }
 
    /**
     * 取两个颜色间的渐变区间 中的某一点的颜色
     *
     * @param startColor 开始的颜色
     * @param endColor   结束的颜色
     * @param radio      比例 [0, 1]
     * @return 选中点的颜色
     */
    private int getColorFrom(int startColor, int endColor, float radio) {
        int redStart = Color.red(startColor);
        int blueStart = Color.blue(startColor);
        int greenStart = Color.green(startColor);
        int redEnd = Color.red(endColor);
        int blueEnd = Color.blue(endColor);
        int greenEnd = Color.green(endColor);
 
        int red = (int) (redStart + ((redEnd - redStart) * radio + 0.5));
        int greed = (int) (greenStart + ((greenEnd - greenStart) * radio + 0.5));
        int blue = (int) (blueStart + ((blueEnd - blueStart) * radio + 0.5));
        return Color.argb(255, red, greed, blue);
    }
 
 
    /**
     * 计算某点绕中心点旋转一个角度后的坐标
     *
     * @param pCenter 中心坐标
     */
    private static Point calcNewPoint(Point p, Point pCenter, float angle) {
        // calc arc
        float l = (float) ((angle * Math.PI) / 180);
 
        //sin/cos value
        float cosv = (float) Math.cos(l);
        float sinv = (float) Math.sin(l);
 
        // calc new point
        float newX = (float) ((p.x - pCenter.x) * cosv - (p.y - pCenter.y) * sinv + pCenter.x);
        float newY = (float) ((p.x - pCenter.x) * sinv + (p.y - pCenter.y) * cosv + pCenter.y);
        return new Point((int) newX, (int) newY);
    }
 
//    /**
//     * 求第三点坐标
//     *
//     * @param pCenter  中心坐标
//     * @param distance 第三点距离第二点的距离
//     */
//    private static Point calcOutsidePoint(Point p, Point pCenter, double distance) {
//        //计算2点之间的距离
//        double ll = Math.sqrt((p.x - pCenter.x) * (p.x - pCenter.x) + (p.y - pCenter.y) * (p.y - pCenter.y));
//
//        // calc new point
//        double newX = (p.x * (ll + distance) - pCenter.x * distance) / ll;
//        double newY = (p.y * (ll + distance) - pCenter.y * distance) / ll;
//        return new Point((int) newX, (int) newY);
//    }
//
 
 
    //****************************对外接口****************************************************
    /**
     *  设置控件是否可用点击
     * @param isClickable
     */
    public void setIsClickable(boolean isClickable) {
        this.isClickable = isClickable;
    }
 
    /**
     * 设置进度
     *
     * @param progress 进度值
     */
    public void setProgress(int progress) {
        System.out.println("setProgress = " + progress);
        if (progress > mMaxValue) progress = mMaxValue;
        if (progress < mMinValue) progress = mMinValue;
        mProgressPresent = (progress - mMinValue) * 1.0f / (mMaxValue - mMinValue);
//        System.out.println("setProgress present = " + mProgressPresent);
//        if (null != mOnProgressChangeListener) {
//            mOnProgressChangeListener.onProgressChanged(this, progress, false);
//        }
        computeThumbPos(mProgressPresent);
        postInvalidate();
    }
 
    /**
     * 获取当前进度数值
     *
     * @return 当前进度数值
     */
    public int getProgress() {
        return (int) (mProgressPresent * (mMaxValue - mMinValue)) + mMinValue;
    }
 
    /**
     * 设置颜色
     * 3个颜色 渐变效果
     * @param colors 颜色
     */
    public void setProgressBarColors(int[] colors) {
        if(colors.length < 2) return;
        mArcProgressBarColors = colors;
        resetmArcProgressBarColor();
        postInvalidate();
    }
 
    /**
     * 设置颜色 单种颜色
     * 2019-8-15
     * @param colors 颜色
     */
    public void setProgressBarColor(int colors) {
        int[] colorsArray = new int[]{colors, colors};
        mArcProgressBarColors = colorsArray;
        resetmArcProgressBarColor();
        postInvalidate();
    }
 
    /**
     * 设置颜色 2个颜色以上
     * 2019-8-15
     * @param colors 颜色
     */
    public void setArcColors(int[] colors) {
        if(colors.length < 2) return;
        mArcColors = colors;
        resetShaderColor();
        postInvalidate();
    }
 
    /**
     * 设置颜色
     * 单种颜色
     * @param colors 颜色
     */
    public void setArcColor(int colors) {
        int[] colorsArray = new int[]{colors, colors, colors};
        mArcColors = colorsArray;
        resetShaderColor();
        postInvalidate();
    }
 
    /**
     * 设置最大数值
     *
     * @param max 最大数值
     */
    public void setMaxValue(int max) {
        mMaxValue = max;
    }
 
    /**
     * 设置最小数值
     *
     * @param min 最小数值
     */
    public void setMinValue(int min) {
        mMinValue = min;
    }
 
//    /**
//     * 设置进度条颜色
//     *
//     * @param arcProgressPainColor 进度条颜色
//     */
//    public void setProgressBarColor(int arcProgressPainColor) {
//        mArcProgressBarColor = arcProgressPainColor;
//        mArcProgressBarPaint.setColor(mArcProgressBarColor);
//    }
 
    /**
     * 设置进度显示值单位
     *
     * @param progressBarUnitSring 进度显示值单位
     */
    public void setProgressBarUnitSring(String progressBarUnitSring) {
        mProgressBarUnitSring = progressBarUnitSring;
    }
 
//    /**
//     * 设置颜色
//     *
//     * @param colorArrayRes 颜色资源 R.array.arc_color
//     */
//    public void setArcColors(int colorArrayRes) {
//        setArcColors(getColorsByArrayResId(getContext(), colorArrayRes));
//    }
 
    /**
     * 设置圆弧宽度,同时同步拖到按钮宽度
     * @param arcWidth
     */
    public void setArcWidthDefaultStyle(int arcWidth) {
        mArcWidth = dp2px(arcWidth);
        mThumbRadius = mArcWidth / 3;
        mThumbWidth = mArcWidth * 2 / 9;
 
        mArcPaint.setStrokeWidth(mArcWidth);
        mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1);
        mThumbPaint.setStrokeWidth(mThumbWidth);
    }
 
    /**
     * 开口角度
     * @param openAngle
     */
    public void setOpenAngle(float openAngle) {
        mOpenAngle = openAngle;
    }
 
    /**
     *
     * @param thumbMode
     */
    public void setThumbMode(int thumbMode) {
        mThumbMode = thumbMode;
        if (mThumbMode == THUMB_MODE_FILL) {
            mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        } else if (mThumbMode == THUMB_MODE_FILL_STROKE) {
            mThumbPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        } else {
            mThumbMode = THUMB_MODE_STROKE;
            mThumbPaint.setStyle(Paint.Style.STROKE);
        }
    }
 
    /**
     *
     * @param arcWidth
     */
    public void setArcWidth(int arcWidth) {
        mArcWidth = dp2px(arcWidth);
        mArcPaint.setStrokeWidth(mArcWidth);
        mArcProgressBarPaint.setStrokeWidth(mArcWidth - 1);
    }
 
    /**
     * 拖动滑块半径
     * @param thumbRadius
     */
    public void setThumbRadius(int thumbRadius) {
        mThumbRadius =  dp2px(thumbRadius);
        mThumbWidth = mThumbRadius * 2 / 3;
        mThumbPaint.setStrokeWidth(mThumbWidth);
//        initThumbPaint();
    }
 
    /**
     * 拖动滑块颜色
     * @param thumbColor
     */
    public void setThumbColor(int thumbColor) {
        mThumbColor = thumbColor;
        mThumbPaint.setColor(mThumbColor);
    }
 
 
    /**
     * 进度文字大小
     * @param textSize
     */
    public void setProgressTextSize(int textSize){
        mTextPaint.setTextSize(getTextSizeDip(textSize));
    }
 
    /**
     * 进度文字颜色
     * @param textPaintColor
     */
    public void setProgressTextColor(int textPaintColor){
        mTextPaint.setColor(textPaintColor);
    }
 
    /**
     * 进度文字与圆弧距离
     * @param textDefaultDistance
     */
    public void setTextDefaultDistance(int textDefaultDistance) {
        mTextDefaultDistance = dp2px(textDefaultDistance);
    }
 
// endregion -----------------------------------------------------------------------------------
    // region 状态回调 ------------------------------------------------------------------------------
 
    private OnProgressChangeListener mOnProgressChangeListener;
 
    public void setOnProgressChangeListener(OnProgressChangeListener onProgressChangeListener) {
        mOnProgressChangeListener = onProgressChangeListener;
    }
 
    public interface OnProgressChangeListener {
        /**
         * 进度发生变化
         *
         * @param seekBar  拖动条
         * @param progress 当前进度数值
         * @param isUser   是否是用户操作, true 表示用户拖动, false 表示通过代码设置
         */
        void onProgressChanged(HDLArcSeekBar seekBar, int progress, boolean isUser);
 
        /**
         * 用户开始拖动
         *
         * @param seekBar 拖动条
         */
        void onStartTrackingTouch(HDLArcSeekBar seekBar);
 
        /**
         * 用户结束拖动
         *
         * @param seekBar 拖动条
         */
        void onStopTrackingTouch(HDLArcSeekBar seekBar);
    }
    // endregion -----------------------------------------------------------------------------------
}