Skip to content

Commit c520d86

Browse files
committed
[Slider] Add new tick visibility modes
1 parent bdf73bf commit c520d86

File tree

7 files changed

+167
-23
lines changed

7 files changed

+167
-23
lines changed

catalog/java/io/material/catalog/slider/res/layout/cat_slider_demo_discrete.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163
android:valueFrom="0"
164164
android:valueTo="10"
165165
android:stepSize="1"
166-
app:tickVisible="false"/>
166+
app:tickVisibilityMode="hidden" />
167167
</LinearLayout>
168168

169169
<com.google.android.material.materialswitch.MaterialSwitch

docs/components/Slider.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,15 @@ Element | Attribute | Related method(
345345
**Color for tick's inactive part** | `app:tickColorInactive` | `setTickInactiveTintList`<br/>`getTickInactiveTintList` | `?attr/colorPrimary`
346346
**Radius for tick's active part** | `app:tickRadiusActive` | `setTickActiveRadius`<br/>`getTickActiveRadius` | `1dp`
347347
**Radius for tick's inactive part** | `app:tickRadiusInactive` | `setTickInactiveRadius`<br/>`getTickInactiveRadius` | `1dp`
348-
**Tick visible** | `app:tickVisible` | `setTickVisible`<br/>`isTickVisible()` | `true`
348+
**Tick visible** (deprecated) | `app:tickVisible` | `setTickVisible`<br/>`isTickVisible()` | `true`
349+
**Tick visibility mode** | `app:tickVisibilityMode` | `setTickVisibilityMode`<br/>`getTickVisibilityMode()` | `autoLimit`
349350

350351
**Note:** `app:tickColor` takes precedence over `app:tickColorActive` and
351-
`app:tickColorInative`. It's a shorthand for setting both values to the same
352+
`app:tickColorInactive`. It's a shorthand for setting both values to the same
352353
thing.
353354

355+
**Note:** `app:tickVisible` is deprecated in favor of `app:tickVisibilityMode`.
356+
354357
#### Styles
355358

356359
Element | Style

lib/java/com/google/android/material/slider/BaseSlider.java

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
import static com.google.android.material.slider.LabelFormatter.LABEL_GONE;
2525
import static com.google.android.material.slider.LabelFormatter.LABEL_VISIBLE;
2626
import static com.google.android.material.slider.LabelFormatter.LABEL_WITHIN_BOUNDS;
27+
import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_AUTO_HIDE;
28+
import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_AUTO_LIMIT;
29+
import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_HIDDEN;
30+
import static com.google.android.material.slider.TickVisibilityMode.TICK_VISIBILITY_VISIBLE_ALL;
2731
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
2832
import static java.lang.Float.compare;
2933
import static java.lang.Math.abs;
@@ -299,7 +303,7 @@ abstract class BaseSlider<
299303
private int focusedThumbIdx = -1;
300304
private float stepSize = 0.0f;
301305
private float[] ticksCoordinates;
302-
private boolean tickVisible = true;
306+
private int tickVisibilityMode;
303307
private int tickActiveRadius;
304308
private int tickInactiveRadius;
305309
private int trackWidth;
@@ -471,7 +475,10 @@ private void processAttributes(Context context, AttributeSet attrs, int defStyle
471475
? haloColor
472476
: AppCompatResources.getColorStateList(context, R.color.material_slider_halo_color));
473477

474-
tickVisible = a.getBoolean(R.styleable.Slider_tickVisible, true);
478+
tickVisibilityMode = a.hasValue(R.styleable.Slider_tickVisibilityMode)
479+
? a.getInt(R.styleable.Slider_tickVisibilityMode, -1)
480+
: convertToTickVisibilityMode(a.getBoolean(R.styleable.Slider_tickVisible, true));
481+
475482
boolean hasTickColor = a.hasValue(R.styleable.Slider_tickColor);
476483
int tickColorInactiveRes =
477484
hasTickColor ? R.styleable.Slider_tickColor : R.styleable.Slider_tickColorInactive;
@@ -1526,22 +1533,61 @@ public void setTickInactiveTintList(@NonNull ColorStateList tickColor) {
15261533
/**
15271534
* Returns whether the tick marks are visible. Only used when the slider is in discrete mode.
15281535
*
1529-
* @see #setTickVisible(boolean)
15301536
* @attr ref com.google.android.material.R.styleable#Slider_tickVisible
15311537
*/
15321538
public boolean isTickVisible() {
1533-
return tickVisible;
1539+
switch (tickVisibilityMode) {
1540+
case TICK_VISIBILITY_VISIBLE_ALL:
1541+
case TICK_VISIBILITY_AUTO_LIMIT:
1542+
return true;
1543+
case TICK_VISIBILITY_AUTO_HIDE:
1544+
return getDesiredTickCount() <= getMaxTickCount();
1545+
case TICK_VISIBILITY_HIDDEN:
1546+
return false;
1547+
default:
1548+
throw new RuntimeException("isTickVisible() is not implemented for tickVisibilityMode=" + tickVisibilityMode);
1549+
}
15341550
}
15351551

15361552
/**
15371553
* Sets whether the tick marks are visible. Only used when the slider is in discrete mode.
15381554
*
15391555
* @param tickVisible The visibility of tick marks.
15401556
* @attr ref com.google.android.material.R.styleable#Slider_tickVisible
1557+
* @deprecated Use {@link #setTickVisibilityMode(int)} instead.
15411558
*/
1559+
@Deprecated
15421560
public void setTickVisible(boolean tickVisible) {
1543-
if (this.tickVisible != tickVisible) {
1544-
this.tickVisible = tickVisible;
1561+
setTickVisibilityMode(convertToTickVisibilityMode(tickVisible));
1562+
}
1563+
1564+
@TickVisibilityMode
1565+
private int convertToTickVisibilityMode(boolean tickVisible) {
1566+
return tickVisible
1567+
? TICK_VISIBILITY_AUTO_LIMIT
1568+
: TICK_VISIBILITY_HIDDEN;
1569+
}
1570+
1571+
/**
1572+
* Returns the current tick visibility mode.
1573+
*
1574+
* @see #setTickVisibilityMode(int)
1575+
* @attr ref com.google.android.material.R.styleable#Slider_tickVisibilityMode
1576+
*/
1577+
@TickVisibilityMode
1578+
public int getTickVisibilityMode() {
1579+
return tickVisibilityMode;
1580+
}
1581+
1582+
/**
1583+
* Sets the tick visibility mode. Only used when the slider is in discrete mode.
1584+
*
1585+
* @see #getTickVisibilityMode()
1586+
* @attr ref com.google.android.material.R.styleable#Slider_tickVisibilityMode
1587+
*/
1588+
public void setTickVisibilityMode(@TickVisibilityMode int tickVisibilityMode) {
1589+
if (this.tickVisibilityMode != tickVisibilityMode) {
1590+
this.tickVisibilityMode = tickVisibilityMode;
15451591
postInvalidate();
15461592
}
15471593
}
@@ -1716,24 +1762,60 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
17161762
updateHaloHotspot();
17171763
}
17181764

1719-
private void maybeCalculateTicksCoordinates() {
1765+
private void updateTicksCoordinates() {
1766+
validateConfigurationIfDirty();
1767+
17201768
if (stepSize <= 0.0f) {
1769+
updateTicksCoordinates(/* tickCount= */ 0);
17211770
return;
17221771
}
17231772

1724-
validateConfigurationIfDirty();
1773+
final int tickCount;
1774+
switch (tickVisibilityMode) {
1775+
case TICK_VISIBILITY_VISIBLE_ALL:
1776+
tickCount = getDesiredTickCount();
1777+
break;
1778+
case TICK_VISIBILITY_AUTO_LIMIT:
1779+
tickCount = min(getDesiredTickCount(), getMaxTickCount());
1780+
break;
1781+
case TICK_VISIBILITY_AUTO_HIDE:
1782+
int desiredTickCount = getDesiredTickCount();
1783+
tickCount = desiredTickCount <= getMaxTickCount() ? desiredTickCount : 0;
1784+
break;
1785+
case TICK_VISIBILITY_HIDDEN:
1786+
tickCount = 0;
1787+
break;
1788+
default:
1789+
throw new RuntimeException("Calculation of tick coordinates is not implemented for tickVisibilityMode=" + tickVisibilityMode);
1790+
}
1791+
1792+
updateTicksCoordinates(tickCount);
1793+
}
1794+
1795+
private int getDesiredTickCount() {
1796+
return (int) ((valueTo - valueFrom) / stepSize + 1);
1797+
}
1798+
1799+
private int getMaxTickCount() {
1800+
return trackWidth / (trackHeight * 2) + 1;
1801+
}
1802+
1803+
private void updateTicksCoordinates(int tickCount) {
1804+
if (tickCount == 0) {
1805+
ticksCoordinates = null;
1806+
return;
1807+
}
17251808

1726-
int tickCount = (int) ((valueTo - valueFrom) / stepSize + 1);
1727-
// Limit the tickCount if they will be too dense.
1728-
tickCount = min(tickCount, trackWidth / (trackHeight * 2) + 1);
17291809
if (ticksCoordinates == null || ticksCoordinates.length != tickCount * 2) {
17301810
ticksCoordinates = new float[tickCount * 2];
17311811
}
17321812

17331813
float interval = trackWidth / (float) (tickCount - 1);
1814+
float trackCenterY = calculateTrackCenter();
1815+
17341816
for (int i = 0; i < tickCount * 2; i += 2) {
17351817
ticksCoordinates[i] = trackSidePadding + i / 2f * interval;
1736-
ticksCoordinates[i + 1] = calculateTrackCenter();
1818+
ticksCoordinates[i + 1] = trackCenterY;
17371819
}
17381820
}
17391821

@@ -1742,7 +1824,7 @@ private void updateTrackWidth(int width) {
17421824
trackWidth = max(width - trackSidePadding * 2, 0);
17431825

17441826
// Update the visible tick coordinates.
1745-
maybeCalculateTicksCoordinates();
1827+
updateTicksCoordinates();
17461828
}
17471829

17481830
private void updateHaloHotspot() {
@@ -1771,7 +1853,7 @@ protected void onDraw(@NonNull Canvas canvas) {
17711853
validateConfigurationIfDirty();
17721854

17731855
// Update the visible tick coordinates.
1774-
maybeCalculateTicksCoordinates();
1856+
updateTicksCoordinates();
17751857
}
17761858

17771859
super.onDraw(canvas);
@@ -1847,7 +1929,7 @@ private void drawActiveTrack(@NonNull Canvas canvas, int width, int yCenter) {
18471929
}
18481930

18491931
private void maybeDrawTicks(@NonNull Canvas canvas) {
1850-
if (!tickVisible || stepSize <= 0.0f) {
1932+
if (ticksCoordinates == null || ticksCoordinates.length == 0) {
18511933
return;
18521934
}
18531935

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.google.android.material.slider;
2+
3+
import androidx.annotation.IntDef;
4+
5+
import java.lang.annotation.Retention;
6+
import java.lang.annotation.RetentionPolicy;
7+
8+
/**
9+
* Mode to specify the visibility of tick marks.
10+
*/
11+
@IntDef({
12+
TickVisibilityMode.TICK_VISIBILITY_VISIBLE_ALL,
13+
TickVisibilityMode.TICK_VISIBILITY_AUTO_LIMIT,
14+
TickVisibilityMode.TICK_VISIBILITY_AUTO_HIDE,
15+
TickVisibilityMode.TICK_VISIBILITY_HIDDEN
16+
})
17+
@Retention(RetentionPolicy.SOURCE)
18+
public @interface TickVisibilityMode {
19+
20+
/**
21+
* All tick marks will be drawn, even if they are spaced too densely.
22+
*/
23+
int TICK_VISIBILITY_VISIBLE_ALL = 0;
24+
25+
/**
26+
* All tick marks will be drawn if they are not spaced too densely. Otherwise, the maximum
27+
* allowed number of tick marks will be drawn.
28+
* Note that in this case, the drawn ticks may not match the actual snap values.
29+
*/
30+
int TICK_VISIBILITY_AUTO_LIMIT = 1;
31+
32+
/**
33+
* All tick marks will be drawn if they are not spaced too densely. Otherwise, the tick marks
34+
* will not be drawn.
35+
*/
36+
int TICK_VISIBILITY_AUTO_HIDE = 2;
37+
38+
/**
39+
* Tick marks will not be drawn.
40+
*/
41+
int TICK_VISIBILITY_HIDDEN = 3;
42+
}

lib/java/com/google/android/material/slider/res-public/values/public.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<public name="tickRadiusActive" type="attr" />
3333
<public name="tickRadiusInactive" type="attr" />
3434
<public name="tickVisible" type="attr" />
35+
<public name="tickVisibilityMode" type="attr" />
3536
<public name="trackColorActive" type="attr" />
3637
<public name="trackColorInactive" type="attr" />
3738
<public name="trackHeight" type="attr" />

lib/java/com/google/android/material/slider/res/values/attrs.xml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,24 @@
6363
<attr name="tickRadiusActive" format="dimension" />
6464
<!-- The radius of the inactive tick. Only used when the slider is in discrete mode.-->
6565
<attr name="tickRadiusInactive" format="dimension" />
66-
<!-- Whether to show the tick marks. Only used when the slider is in discrete mode. -->
66+
<!-- Whether to show the tick marks. Only used when the slider is in discrete mode.
67+
{@deprecated Use tickVisibilityMode instead.} -->
6768
<attr name="tickVisible" format="boolean" />
69+
<!-- Mode to specify the visibility of tick marks. Only used when the slider is in
70+
discrete mode. -->
71+
<attr name="tickVisibilityMode" format="enum">
72+
<!-- All tick marks will be drawn, even if they are spaced too densely. -->
73+
<enum name="visibleAll" value="0" />
74+
<!-- All tick marks will be drawn if they are not spaced too densely. Otherwise,
75+
the maximum allowed number of tick marks will be drawn.
76+
Note that in this case, the drawn ticks may not match the actual snap values. -->
77+
<enum name="autoLimit" value="1" />
78+
<!-- All tick marks will be drawn if they are not spaced too densely. Otherwise,
79+
the tick marks will not be drawn. -->
80+
<enum name="autoHide" value="2" />
81+
<!-- Tick marks will not be drawn. -->
82+
<enum name="hidden" value="3" />
83+
</attr>
6884
<!-- The color of the track. -->
6985
<attr name="trackColor" />
7086
<!-- The color of active portion of the track. -->

lib/javatests/com/google/android/material/slider/SliderConfigTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,20 @@ public static Iterable<Object[]> data() {
8989
new Object[] {
9090
/* valueFrom = */ 10f,
9191
/* valueTo = */ 3f,
92-
/* stepValue = */ 2f,
92+
/* stepValue = */ 0f,
9393
/* value = */ 10f,
9494
IllegalStateException.class,
9595
},
96-
new Object[] {
97-
/* valueFrom = */ 0f, /* valueTo = */ 1f, /* stepValue = */ 0, /* value = */ 0f, null,
98-
},
9996
new Object[] {
10097
/* valueFrom = */ 10f,
10198
/* valueTo = */ 3f,
10299
/* stepValue = */ 2f,
103100
/* value = */ 10f,
104101
IllegalStateException.class,
105102
},
103+
new Object[] {
104+
/* valueFrom = */ 0f, /* valueTo = */ 1f, /* stepValue = */ 0, /* value = */ 0f, null,
105+
},
106106
new Object[] {
107107
/* valueFrom = */ 0f,
108108
/* valueTo = */ 5f,

0 commit comments

Comments
 (0)