@@ -124,6 +124,9 @@ public enum DividerType { // 分隔线类型
124
124
125
125
private boolean isAlphaGradient = false ; //透明度渐变
126
126
127
+ //是否开启3D
128
+ private boolean isOpen3D = true ;
129
+
127
130
public WheelView (Context context ) {
128
131
this (context , null );
129
132
}
@@ -215,8 +218,19 @@ private void reMeasure() {//重新测量
215
218
216
219
//半圆的周长 = item高度乘以item数目-1
217
220
int halfCircumference = (int ) (itemHeight * (itemsVisible - 1 ));
218
- //整个圆的周长除以PI得到直径,这个直径用作控件的总高度
219
- measuredHeight = (int ) ((halfCircumference * 2 ) / Math .PI );
221
+ if (isOpen3D ) {
222
+ //整个圆的周长除以PI得到直径,这个直径用作控件的总高度
223
+ measuredHeight = (int ) ((halfCircumference * 2 ) / Math .PI );
224
+ if (paintCenterText .getTextScaleX () == paintOuterText .getTextScaleX ()) {
225
+ paintCenterText .setTextScaleX (1.1F );
226
+ }
227
+ } else {
228
+ // itemsVisible = 5;//写死了不过可拓展
229
+ if (paintCenterText .getTextScaleX () != paintOuterText .getTextScaleX ()) {
230
+ paintCenterText .setTextScaleX (paintOuterText .getTextScaleX ());
231
+ }
232
+ measuredHeight = (int ) (itemHeight * (itemsVisible - 2 ));
233
+ }
220
234
//求出半径
221
235
radius = (int ) (halfCircumference / Math .PI );
222
236
//控件宽度,这里支持weight
@@ -443,39 +457,135 @@ protected void onDraw(Canvas canvas) {
443
457
if (!TextUtils .isEmpty (label ) && isCenterLabel ) {
444
458
//绘制文字,靠右并留出空隙
445
459
int drawRightContentStart = measuredWidth - getTextWidth (paintCenterText , label );
446
- canvas .drawText (label , drawRightContentStart - CENTER_CONTENT_OFFSET , centerY , paintCenterText );
460
+ if (isOpen3D ) {
461
+ canvas .drawText (label , drawRightContentStart - CENTER_CONTENT_OFFSET , centerY , paintCenterText );
462
+ } else {
463
+ canvas .drawText (label , drawRightContentStart , centerY , paintCenterText );
464
+ }
447
465
}
466
+ if (isOpen3D ) {
467
+ // 设置数组中每个元素的值
468
+ int counter = 0 ;
469
+ while (counter < itemsVisible ) {
470
+ Object showText ;
471
+ int index = preCurrentIndex - (itemsVisible / 2 - counter );//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值
472
+
473
+ //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项
474
+ if (isLoop ) {
475
+ index = getLoopMappingIndex (index );
476
+ showText = adapter .getItem (index );
477
+ } else if (index < 0 ) {
478
+ showText = "" ;
479
+ } else if (index > adapter .getItemsCount () - 1 ) {
480
+ showText = "" ;
481
+ } else {
482
+ showText = adapter .getItem (index );
483
+ }
448
484
449
- // 设置数组中每个元素的值
450
- int counter = 0 ;
451
- while (counter < itemsVisible ) {
452
- Object showText ;
453
- int index = preCurrentIndex - (itemsVisible / 2 - counter );//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值
485
+ canvas .save ();
486
+ // 弧长 L = itemHeight * counter - itemHeightOffset
487
+ // 求弧度 α = L / r (弧长/半径) [0,π]
488
+ double radian = ((itemHeight * counter - itemHeightOffset )) / radius ;
489
+ // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限
490
+ // angle [-90°,90°]
491
+ float angle = (float ) (90D - (radian / Math .PI ) * 180D );//item第一项,从90度开始,逐渐递减到 -90度
454
492
455
- //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项
456
- if (isLoop ) {
457
- index = getLoopMappingIndex (index );
458
- showText = adapter .getItem (index );
459
- } else if (index < 0 ) {
460
- showText = "" ;
461
- } else if (index > adapter .getItemsCount () - 1 ) {
462
- showText = "" ;
463
- } else {
464
- showText = adapter .getItem (index );
465
- }
493
+ // 计算取值可能有细微偏差,保证负90°到90°以外的不绘制
494
+ if (angle > 90F || angle < -90F ) {
495
+ canvas .restore ();
496
+ } else {
497
+ //获取内容文字
498
+ String contentText ;
466
499
467
- canvas .save ();
468
- // 弧长 L = itemHeight * counter - itemHeightOffset
469
- // 求弧度 α = L / r (弧长/半径) [0,π]
470
- double radian = ((itemHeight * counter - itemHeightOffset )) / radius ;
471
- // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限
472
- // angle [-90°,90°]
473
- float angle = (float ) (90D - (radian / Math .PI ) * 180D );//item第一项,从90度开始,逐渐递减到 -90度
500
+ //如果是label每项都显示的模式,并且item内容不为空、label 也不为空
501
+ if (!isCenterLabel && !TextUtils .isEmpty (label ) && !TextUtils .isEmpty (getContentText (showText ))) {
502
+ contentText = getContentText (showText ) + label ;
503
+ } else {
504
+ contentText = getContentText (showText );
505
+ }
506
+ // 根据当前角度计算出偏差系数,用以在绘制时控制文字的 水平移动 透明度 倾斜程度.
507
+ float offsetCoefficient = (float ) Math .pow (Math .abs (angle ) / 90f , 2.2 );
508
+
509
+ reMeasureTextSize (contentText );
510
+ //计算开始绘制的位置
511
+ measuredCenterContentStart (contentText );
512
+ measuredOutContentStart (contentText );
513
+ float translateY = (float ) (radius - Math .cos (radian ) * radius - (Math .sin (radian ) * maxTextHeight ) / 2D );
514
+ //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差
515
+ canvas .translate (0.0F , translateY );
516
+ if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY ) {
517
+ // 条目经过第一条线
518
+ canvas .save ();
519
+ canvas .clipRect (0 , 0 , measuredWidth , firstLineY - translateY );
520
+ canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
521
+ setOutPaintStyle (offsetCoefficient , angle );
522
+ canvas .drawText (contentText , drawOutContentStart , maxTextHeight , paintOuterText );
523
+ canvas .restore ();
524
+ canvas .save ();
525
+ canvas .clipRect (0 , firstLineY - translateY , measuredWidth , (int ) (itemHeight ));
526
+ canvas .scale (1.0F , (float ) Math .sin (radian ) * 1.0F );
527
+ canvas .drawText (contentText , drawCenterContentStart , maxTextHeight - CENTER_CONTENT_OFFSET , paintCenterText );
528
+ canvas .restore ();
529
+ } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY ) {
530
+ // 条目经过第二条线
531
+ canvas .save ();
532
+ canvas .clipRect (0 , 0 , measuredWidth , secondLineY - translateY );
533
+ canvas .scale (1.0F , (float ) Math .sin (radian ) * 1.0F );
534
+ canvas .drawText (contentText , drawCenterContentStart , maxTextHeight - CENTER_CONTENT_OFFSET , paintCenterText );
535
+ canvas .restore ();
536
+ canvas .save ();
537
+ canvas .clipRect (0 , secondLineY - translateY , measuredWidth , (int ) (itemHeight ));
538
+ canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
539
+ setOutPaintStyle (offsetCoefficient , angle );
540
+ canvas .drawText (contentText , drawOutContentStart , maxTextHeight , paintOuterText );
541
+ canvas .restore ();
542
+ } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY ) {
543
+ // 中间条目
544
+ // canvas.clipRect(0, 0, measuredWidth, maxTextHeight);
545
+ //让文字居中
546
+ float Y = maxTextHeight - CENTER_CONTENT_OFFSET ;//因为圆弧角换算的向下取值,导致角度稍微有点偏差,加上画笔的基线会偏上,因此需要偏移量修正一下
547
+ canvas .drawText (contentText , drawCenterContentStart , Y , paintCenterText );
548
+ //设置选中项
549
+ selectedItem = preCurrentIndex - (itemsVisible / 2 - counter );
550
+ } else {
551
+ // 其他条目
552
+ canvas .save ();
553
+ canvas .clipRect (0 , 0 , measuredWidth , (int ) (itemHeight ));
554
+ canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
555
+ setOutPaintStyle (offsetCoefficient , angle );
556
+ // 控制文字水平偏移距离
557
+ canvas .drawText (contentText , drawOutContentStart + textXOffset * offsetCoefficient , maxTextHeight , paintOuterText );
558
+ canvas .restore ();
559
+ }
560
+ canvas .restore ();
561
+ paintCenterText .setTextSize (textSize );
562
+ }
563
+ counter ++;
564
+ }
565
+ } else {
566
+ // TODO 2020/6/12 此处未做超出范围后不再绘制的优化...
567
+ int counter = 0 ;
568
+ while (counter < itemsVisible ) {
569
+ Object showText ;
570
+ int index = preCurrentIndex - (itemsVisible / 2 - counter );//索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值
571
+
572
+ //判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项
573
+ if (isLoop ) {
574
+ index = getLoopMappingIndex (index );
575
+ showText = adapter .getItem (index );
576
+ } else if (index < 0 ) {
577
+ showText = "" ;
578
+ } else if (index > adapter .getItemsCount () - 1 ) {
579
+ showText = "" ;
580
+ } else {
581
+ showText = adapter .getItem (index );
582
+ }
474
583
475
- // 计算取值可能有细微偏差,保证负90°到90°以外的不绘制
476
- if (angle > 90F || angle < -90F ) {
477
- canvas .restore ();
478
- } else {
584
+ canvas .save ();
585
+ //itemHeightOffset 默认为偏移原来位置的点的距离
586
+ float translateY = itemHeight * (counter - 1 ) - itemHeightOffset ;
587
+ translateY = (float ) (translateY + ((itemHeight - maxTextHeight ) / 2D ));
588
+ canvas .translate (0.0F , translateY );//右下正坐标系 translateY>0 向下移动坐标系
479
589
//获取内容文字
480
590
String contentText ;
481
591
@@ -485,64 +595,47 @@ protected void onDraw(Canvas canvas) {
485
595
} else {
486
596
contentText = getContentText (showText );
487
597
}
488
- // 根据当前角度计算出偏差系数,用以在绘制时控制文字的 水平移动 透明度 倾斜程度.
489
- float offsetCoefficient = (float ) Math .pow (Math .abs (angle ) / 90f , 2.2 );
490
598
491
599
reMeasureTextSize (contentText );
492
600
//计算开始绘制的位置
493
601
measuredCenterContentStart (contentText );
494
602
measuredOutContentStart (contentText );
495
- float translateY = (float ) (radius - Math .cos (radian ) * radius - (Math .sin (radian ) * maxTextHeight ) / 2D );
496
- //根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差
497
- canvas .translate (0.0F , translateY );
498
603
if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY ) {
499
604
// 条目经过第一条线
500
605
canvas .save ();
501
606
canvas .clipRect (0 , 0 , measuredWidth , firstLineY - translateY );
502
- canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
503
- setOutPaintStyle (offsetCoefficient , angle );
504
607
canvas .drawText (contentText , drawOutContentStart , maxTextHeight , paintOuterText );
505
608
canvas .restore ();
506
609
canvas .save ();
507
610
canvas .clipRect (0 , firstLineY - translateY , measuredWidth , (int ) (itemHeight ));
508
- canvas .scale (1.0F , (float ) Math .sin (radian ) * 1.0F );
509
- canvas .drawText (contentText , drawCenterContentStart , maxTextHeight - CENTER_CONTENT_OFFSET , paintCenterText );
611
+ canvas .drawText (contentText , drawCenterContentStart , maxTextHeight , paintCenterText );
510
612
canvas .restore ();
511
613
} else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY ) {
512
614
// 条目经过第二条线
513
615
canvas .save ();
514
616
canvas .clipRect (0 , 0 , measuredWidth , secondLineY - translateY );
515
- canvas .scale (1.0F , (float ) Math .sin (radian ) * 1.0F );
516
- canvas .drawText (contentText , drawCenterContentStart , maxTextHeight - CENTER_CONTENT_OFFSET , paintCenterText );
617
+ canvas .drawText (contentText , drawCenterContentStart , maxTextHeight , paintCenterText );
517
618
canvas .restore ();
518
619
canvas .save ();
519
620
canvas .clipRect (0 , secondLineY - translateY , measuredWidth , (int ) (itemHeight ));
520
- canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
521
- setOutPaintStyle (offsetCoefficient , angle );
522
621
canvas .drawText (contentText , drawOutContentStart , maxTextHeight , paintOuterText );
523
622
canvas .restore ();
524
623
} else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY ) {
525
624
// 中间条目
526
625
// canvas.clipRect(0, 0, measuredWidth, maxTextHeight);
527
626
//让文字居中
528
- float Y = maxTextHeight - CENTER_CONTENT_OFFSET ; //因为圆弧角换算的向下取值,导致角度稍微有点偏差,加上画笔的基线会偏上,因此需要偏移量修正一下
627
+ float Y = maxTextHeight ;
529
628
canvas .drawText (contentText , drawCenterContentStart , Y , paintCenterText );
629
+
530
630
//设置选中项
531
631
selectedItem = preCurrentIndex - (itemsVisible / 2 - counter );
532
632
} else {
533
- // 其他条目
534
- canvas .save ();
535
- canvas .clipRect (0 , 0 , measuredWidth , (int ) (itemHeight ));
536
- canvas .scale (1.0F , (float ) Math .sin (radian ) * SCALE_CONTENT );
537
- setOutPaintStyle (offsetCoefficient , angle );
538
- // 控制文字水平偏移距离
539
- canvas .drawText (contentText , drawOutContentStart + textXOffset * offsetCoefficient , maxTextHeight , paintOuterText );
540
- canvas .restore ();
633
+ canvas .drawText (contentText , drawOutContentStart , maxTextHeight , paintOuterText );
541
634
}
542
635
canvas .restore ();
543
636
paintCenterText .setTextSize (textSize );
637
+ counter ++;
544
638
}
545
- counter ++;
546
639
}
547
640
}
548
641
@@ -813,6 +906,20 @@ public void setLineSpacingMultiplier(float lineSpacingMultiplier) {
813
906
}
814
907
}
815
908
909
+ public void setOpen3D (boolean isOpen3D ) {
910
+ this .isOpen3D = isOpen3D ;
911
+ if (!isOpen3D ) {
912
+ // itemsVisible = 5;//此处未做拓展
913
+ if (paintCenterText .getTextScaleX () != paintOuterText .getTextScaleX ()) {
914
+ paintCenterText .setTextScaleX (paintOuterText .getTextScaleX ());
915
+ }
916
+ } else {
917
+ if (paintCenterText .getTextScaleX () == paintOuterText .getTextScaleX ()) {
918
+ paintCenterText .setTextScaleX (1.1F );
919
+ }
920
+ }
921
+ }
922
+
816
923
public boolean isLoop () {
817
924
return isLoop ;
818
925
}
0 commit comments