6767import androidx .annotation .ColorRes ;
6868import androidx .annotation .DimenRes ;
6969import androidx .annotation .Dimension ;
70+ import androidx .annotation .DrawableRes ;
7071import androidx .annotation .IntDef ;
7172import androidx .annotation .IntRange ;
7273import androidx .annotation .NonNull ;
@@ -293,7 +294,9 @@ private interface TooltipDrawableFactory {
293294 @ NonNull private ColorStateList trackColorActive ;
294295 @ NonNull private ColorStateList trackColorInactive ;
295296
296- @ NonNull private final MaterialShapeDrawable thumbDrawable = new MaterialShapeDrawable ();
297+ @ NonNull private final MaterialShapeDrawable defaultThumbDrawable = new MaterialShapeDrawable ();
298+ @ Nullable private Drawable customThumbDrawable ;
299+ @ NonNull private List <Drawable > customThumbDrawablesForValues = Collections .emptyList ();
297300
298301 private float touchPosition ;
299302 @ SeparationUnit private int separationUnit = UNIT_PX ;
@@ -379,7 +382,8 @@ public TooltipDrawable createTooltipDrawable() {
379382 setClickable (true );
380383
381384 // Set up the thumb drawable to always show the compat shadow.
382- thumbDrawable .setShadowCompatibilityMode (MaterialShapeDrawable .SHADOW_COMPAT_MODE_ALWAYS );
385+ defaultThumbDrawable .setShadowCompatibilityMode (
386+ MaterialShapeDrawable .SHADOW_COMPAT_MODE_ALWAYS );
383387
384388 scaledTouchSlop = ViewConfiguration .get (context ).getScaledTouchSlop ();
385389
@@ -435,7 +439,7 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle
435439 context , R .color .material_slider_active_track_color ));
436440 ColorStateList thumbColor =
437441 MaterialResources .getColorStateList (context , a , R .styleable .Slider_thumbColor );
438- thumbDrawable .setFillColor (thumbColor );
442+ defaultThumbDrawable .setFillColor (thumbColor );
439443
440444 if (a .hasValue (R .styleable .Slider_thumbStrokeColor )) {
441445 setThumbStrokeColor (
@@ -801,6 +805,96 @@ public void setStepSize(float stepSize) {
801805 }
802806 }
803807
808+ /**
809+ * Sets the custom thumb drawable which will be used for all value positions. Note that the custom
810+ * drawable provided will be resized to match the thumb radius set by {@link #setThumbRadius(int)}
811+ * or {@link #setThumbRadiusResource(int)}. Be aware that the image quality may be compromised
812+ * during resizing.
813+ *
814+ * @see #setCustomThumbDrawable(Drawable)
815+ * @see #setCustomThumbDrawablesForValues(int...)
816+ * @see #setCustomThumbDrawablesForValues(Drawable...)
817+ */
818+ void setCustomThumbDrawable (@ DrawableRes int drawableResId ) {
819+ setCustomThumbDrawable (getResources ().getDrawable (drawableResId ));
820+ }
821+
822+ /**
823+ * Sets the custom thumb drawable which will be used for all value positions. Note that the custom
824+ * drawable provided will be resized to match the thumb radius set by {@link #setThumbRadius(int)}
825+ * or {@link #setThumbRadiusResource(int)}. Be aware that the image quality may be compromised
826+ * during resizing.
827+ *
828+ * @see #setCustomThumbDrawable(int)
829+ * @see #setCustomThumbDrawablesForValues(int...)
830+ * @see #setCustomThumbDrawablesForValues(Drawable...)
831+ */
832+ void setCustomThumbDrawable (@ NonNull Drawable drawable ) {
833+ customThumbDrawable = initializeCustomThumbDrawable (drawable );
834+ customThumbDrawablesForValues .clear ();
835+ postInvalidate ();
836+ }
837+
838+ /**
839+ * Sets custom thumb drawables. The drawables provided will be used in its corresponding value
840+ * position - i.e., the first drawable will be used to indicate the first value, and so on. If
841+ * the number of drawables is less than the number of values, the default drawable will be used
842+ * for the remaining values.
843+ *
844+ * <p>Note that the custom drawables provided will be resized to match the thumb radius set by
845+ * {@link #setThumbRadius(int)} or {@link #setThumbRadiusResource(int)}. Be aware that the image
846+ * quality may be compromised during resizing.
847+ *
848+ * @see #setCustomThumbDrawablesForValues(Drawable...)
849+ */
850+ void setCustomThumbDrawablesForValues (@ NonNull @ DrawableRes int ... customThumbDrawableResIds ) {
851+ Drawable [] customThumbDrawables = new Drawable [customThumbDrawableResIds .length ];
852+ for (int i = 0 ; i < customThumbDrawableResIds .length ; i ++) {
853+ customThumbDrawables [i ] = getResources ().getDrawable (customThumbDrawableResIds [i ]);
854+ }
855+ setCustomThumbDrawablesForValues (customThumbDrawables );
856+ }
857+
858+ /**
859+ * Sets custom thumb drawables. The drawables provided will be used in its corresponding value
860+ * position - i.e., the first drawable will be used to indicate the first value, and so on. If
861+ * the number of drawables is less than the number of values, the default drawable will be used
862+ * for the remaining values.
863+ *
864+ * <p>Note that the custom drawables provided will be resized to match the thumb radius set by
865+ * {@link #setThumbRadius(int)} or {@link #setThumbRadiusResource(int)}. Be aware that the image
866+ * quality may be compromised during resizing.
867+ *
868+ * @see #setCustomThumbDrawablesForValues(int...)
869+ */
870+ void setCustomThumbDrawablesForValues (@ NonNull Drawable ... customThumbDrawables ) {
871+ this .customThumbDrawable = null ;
872+ this .customThumbDrawablesForValues = new ArrayList <>();
873+ for (Drawable originalDrawable : customThumbDrawables ) {
874+ this .customThumbDrawablesForValues .add (initializeCustomThumbDrawable (originalDrawable ));
875+ }
876+ postInvalidate ();
877+ }
878+
879+ private Drawable initializeCustomThumbDrawable (Drawable originalDrawable ) {
880+ Drawable drawable = originalDrawable .mutate ().getConstantState ().newDrawable ();
881+ adjustCustomThumbDrawableBounds (drawable );
882+ return drawable ;
883+ }
884+
885+ private void adjustCustomThumbDrawableBounds (Drawable drawable ) {
886+ int thumbDiameter = thumbRadius * 2 ;
887+ int originalWidth = drawable .getIntrinsicWidth ();
888+ int originalHeight = drawable .getIntrinsicHeight ();
889+ if (originalWidth == -1 && originalHeight == -1 ) {
890+ drawable .setBounds (0 , 0 , thumbDiameter , thumbDiameter );
891+ } else {
892+ float scaleRatio = (float ) thumbDiameter / Math .max (originalWidth , originalHeight );
893+ drawable .setBounds (
894+ 0 , 0 , (int ) (originalWidth * scaleRatio ), (int ) (originalHeight * scaleRatio ));
895+ }
896+ }
897+
804898 /** Returns the index of the currently focused thumb */
805899 public int getFocusedThumbIndex () {
806900 return focusedThumbIdx ;
@@ -898,7 +992,7 @@ public void setLabelFormatter(@Nullable LabelFormatter formatter) {
898992 * @attr ref com.google.android.material.R.styleable#Slider_thumbElevation
899993 */
900994 public float getThumbElevation () {
901- return thumbDrawable .getElevation ();
995+ return defaultThumbDrawable .getElevation ();
902996 }
903997
904998 /**
@@ -908,7 +1002,7 @@ public float getThumbElevation() {
9081002 * @attr ref com.google.android.material.R.styleable#Slider_thumbElevation
9091003 */
9101004 public void setThumbElevation (float elevation ) {
911- thumbDrawable .setElevation (elevation );
1005+ defaultThumbDrawable .setElevation (elevation );
9121006 }
9131007
9141008 /**
@@ -947,9 +1041,16 @@ public void setThumbRadius(@IntRange(from = 0) @Dimension int radius) {
9471041 thumbRadius = radius ;
9481042 maybeIncreaseTrackSidePadding ();
9491043
950- thumbDrawable .setShapeAppearanceModel (
1044+ defaultThumbDrawable .setShapeAppearanceModel (
9511045 ShapeAppearanceModel .builder ().setAllCorners (CornerFamily .ROUNDED , thumbRadius ).build ());
952- thumbDrawable .setBounds (0 , 0 , thumbRadius * 2 , thumbRadius * 2 );
1046+ defaultThumbDrawable .setBounds (0 , 0 , thumbRadius * 2 , thumbRadius * 2 );
1047+
1048+ if (customThumbDrawable != null ) {
1049+ adjustCustomThumbDrawableBounds (customThumbDrawable );
1050+ }
1051+ for (Drawable customDrawable : customThumbDrawablesForValues ) {
1052+ adjustCustomThumbDrawableBounds (customDrawable );
1053+ }
9531054
9541055 postInvalidate ();
9551056 }
@@ -974,7 +1075,7 @@ public void setThumbRadiusResource(@DimenRes int radius) {
9741075 * @see #getThumbStrokeColor()
9751076 */
9761077 public void setThumbStrokeColor (@ Nullable ColorStateList thumbStrokeColor ) {
977- thumbDrawable .setStrokeColor (thumbStrokeColor );
1078+ defaultThumbDrawable .setStrokeColor (thumbStrokeColor );
9781079 postInvalidate ();
9791080 }
9801081
@@ -1003,7 +1104,7 @@ public void setThumbStrokeColorResource(@ColorRes int thumbStrokeColorResourceId
10031104 * @see #setThumbStrokeColorResource(int)
10041105 */
10051106 public ColorStateList getThumbStrokeColor () {
1006- return thumbDrawable .getStrokeColor ();
1107+ return defaultThumbDrawable .getStrokeColor ();
10071108 }
10081109
10091110 /**
@@ -1016,7 +1117,7 @@ public ColorStateList getThumbStrokeColor() {
10161117 * @see #getThumbStrokeWidth()
10171118 */
10181119 public void setThumbStrokeWidth (float thumbStrokeWidth ) {
1019- thumbDrawable .setStrokeWidth (thumbStrokeWidth );
1120+ defaultThumbDrawable .setStrokeWidth (thumbStrokeWidth );
10201121 postInvalidate ();
10211122 }
10221123
@@ -1044,7 +1145,7 @@ public void setThumbStrokeWidthResource(@DimenRes int thumbStrokeWidthResourceId
10441145 * @see #setThumbStrokeWidthResource(int)
10451146 */
10461147 public float getThumbStrokeWidth () {
1047- return thumbDrawable .getStrokeWidth ();
1148+ return defaultThumbDrawable .getStrokeWidth ();
10481149 }
10491150
10501151 /**
@@ -1194,7 +1295,7 @@ public void setHaloTintList(@NonNull ColorStateList haloColor) {
11941295 */
11951296 @ NonNull
11961297 public ColorStateList getThumbTintList () {
1197- return thumbDrawable .getFillColor ();
1298+ return defaultThumbDrawable .getFillColor ();
11981299 }
11991300
12001301 /**
@@ -1204,11 +1305,11 @@ public ColorStateList getThumbTintList() {
12041305 * @attr ref com.google.android.material.R.styleable#Slider_thumbColor
12051306 */
12061307 public void setThumbTintList (@ NonNull ColorStateList thumbColor ) {
1207- if (thumbColor .equals (thumbDrawable .getFillColor ())) {
1308+ if (thumbColor .equals (defaultThumbDrawable .getFillColor ())) {
12081309 return ;
12091310 }
12101311
1211- thumbDrawable .setFillColor (thumbColor );
1312+ defaultThumbDrawable .setFillColor (thumbColor );
12121313 invalidate ();
12131314 }
12141315
@@ -1634,23 +1735,35 @@ private void maybeDrawTicks(@NonNull Canvas canvas) {
16341735 }
16351736
16361737 private void drawThumbs (@ NonNull Canvas canvas , int width , int top ) {
1637- // Clear out the track behind the thumb if we're in a disable state since the thumb is
1638- // transparent.
1639- if (!isEnabled ()) {
1640- for (Float value : values ) {
1641- canvas .drawCircle (
1642- trackSidePadding + normalizeValue (value ) * width , top , thumbRadius , thumbPaint );
1738+ for (int i = 0 ; i < values .size (); i ++) {
1739+ float value = values .get (i );
1740+ if (customThumbDrawable != null ) {
1741+ drawThumbDrawable (canvas , width , top , value , customThumbDrawable );
1742+ } else if (i < customThumbDrawablesForValues .size ()) {
1743+ drawThumbDrawable (canvas , width , top , value , customThumbDrawablesForValues .get (i ));
1744+ } else {
1745+ // Clear out the track behind the thumb if we're in a disable state since the thumb is
1746+ // transparent.
1747+ if (!isEnabled ()) {
1748+ canvas .drawCircle (
1749+ trackSidePadding + normalizeValue (value ) * width , top , thumbRadius , thumbPaint );
1750+ }
1751+ drawThumbDrawable (canvas , width , top , value , defaultThumbDrawable );
16431752 }
16441753 }
1754+ }
16451755
1646- for (Float value : values ) {
1647- canvas .save ();
1648- canvas .translate (
1649- trackSidePadding + (int ) (normalizeValue (value ) * width ) - thumbRadius ,
1650- top - thumbRadius );
1651- thumbDrawable .draw (canvas );
1652- canvas .restore ();
1653- }
1756+ private void drawThumbDrawable (
1757+ @ NonNull Canvas canvas , int width , int top , float value , @ NonNull Drawable thumbDrawable ) {
1758+ canvas .save ();
1759+ canvas .translate (
1760+ trackSidePadding
1761+ + (int ) (normalizeValue (value ) * width )
1762+ - (thumbDrawable .getBounds ().width () / 2f ),
1763+ top
1764+ - (thumbDrawable .getBounds ().height () / 2f ));
1765+ thumbDrawable .draw (canvas );
1766+ canvas .restore ();
16541767 }
16551768
16561769 private void maybeDrawHalo (@ NonNull Canvas canvas , int width , int top ) {
@@ -2121,8 +2234,8 @@ protected void drawableStateChanged() {
21212234 label .setState (getDrawableState ());
21222235 }
21232236 }
2124- if (thumbDrawable .isStateful ()) {
2125- thumbDrawable .setState (getDrawableState ());
2237+ if (defaultThumbDrawable .isStateful ()) {
2238+ defaultThumbDrawable .setState (getDrawableState ());
21262239 }
21272240 haloPaint .setColor (getColorForState (haloColor ));
21282241 haloPaint .setAlpha (HALO_ALPHA );
0 commit comments