从Material Design 1.6.0及以上版本开始 ('com.google.android.material:material:1.6.0'):
有一个官方属性app:labelBehavior="visible"
,建议按照@S.Gissel的答案进行设置,如下所示:
<com.google.android.material.slider.Slider
app:labelBehavior="visible"/>
<com.google.android.material.slider.RangeSlider
app:labelBehavior="visible"/>
从Material Design 1.5.0及以下版本:
使用app:labelBehavior属性没有公共API可以使工具提示始终可见。下面是使用反射的解决方法:
Create a Subclass of a Slider/RangeSlider and override the onDraw(@NonNull Canvas canvas)
method and call the setSliderTooltipAlwaysVisible(Slider slider)
method to keep the Tooltip always visible like below:
For Slider:
public class MyCustomSlider extends Slider {
public MyCustomSlider(@NonNull Context context) {
super(context);
init();
}
public MyCustomSlider(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomSlider(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
invalidate();
}
});
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
setSliderTooltipAlwaysVisible(this);
}
public static void setSliderTooltipAlwaysVisible(Slider slider){
try
{
Class<?> baseSliderCls = Slider.class.getSuperclass();
if (baseSliderCls != null) {
Method ensureLabelsAddedMethod = baseSliderCls.getDeclaredMethod("ensureLabelsAdded");
ensureLabelsAddedMethod.setAccessible(true);
ensureLabelsAddedMethod.invoke(slider);
}
}
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
For RangeSlider:
public class MyCustomRangeSlider extends RangeSlider {
public MyCustomRangeSlider(@NonNull Context context) {
super(context);
init();
}
public MyCustomRangeSlider(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MyCustomRangeSlider(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
invalidate();
}
});
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
setSliderTooltipAlwaysVisible(this);
}
public static void setSliderTooltipAlwaysVisible(RangeSlider slider){
try
{
Class<?> baseSliderCls = RangeSlider.class.getSuperclass();
if (baseSliderCls != null) {
Method ensureLabelsAddedMethod = baseSliderCls.getDeclaredMethod("ensureLabelsAdded");
ensureLabelsAddedMethod.setAccessible(true);
ensureLabelsAddedMethod.invoke(slider);
}
}
catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
The key point here is to call the private method private void ensureLabelsAdded()
of BaseSlider class using a Reflection after the super.onDraw(canvas)
gets called.
Use the above custom Sliders in your xml like below:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<my.package.name.MyCustomSlider
style="@style/Widget.App.Slider"
app:labelBehavior="floating"
android:id="@+id/slider_sound_sensitivity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:valueFrom="0.0"
android:valueTo="100.0"
android:layout_marginTop="@dimen/_8sdp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<my.package.name.MyCustomRangeSlider
style="@style/Widget.App.Slider"
app:labelBehavior="floating"
android:id="@+id/range_humidity_in_percentage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:valueFrom="0.0"
android:valueTo="100.0"
android:layout_marginTop="@dimen/_16sdp"
app:layout_constraintTop_toBottomOf="@+id/slider_sound_sensitivity"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:values="@array/initial_slider_values" />
</androidx.constraintlayout.widget.ConstraintLayout>
注意:如果您仍需要在Material Design 1.6.0中使用此解决方法,则必须将上述xml中的属性app:labelBehavior="floating"
更改为app:labelBehavior="visible"
,并且应用于MyCustomSlider/MyCustomRangeSlider。
同时,style="@style/Widget.App.Slider"
是样式文件styles.xml中定义的自定义样式:
<style name="Widget.App.Slider" parent="Widget.MaterialComponents.Slider">
<item name="materialThemeOverlay">@style/ThemeOverlay.App.Slider</item>
<item name="labelStyle">@style/Widget.App.Tooltip</item>
</style>
<style name="ThemeOverlay.App.Slider" parent="">
<item name="colorPrimary">@android:color/holo_red_light</item>
<item name="colorOnSurface">@android:color/holo_red_light</item>
</style>
<style name="Widget.App.Tooltip" parent="Widget.MaterialComponents.Tooltip">
<item name="android:textAppearance">@style/TextAppearance.App.Tooltip</item>
<item name="backgroundTint">@android:color/holo_orange_light</item>
</style>
<style name="TextAppearance.App.Tooltip" parent="TextAppearance.MaterialComponents.Tooltip">
<item name="android:textColor">@android:color/holo_blue_light</item>
</style>
通过更改backgroundTint颜色,您可以将上述xml中的工具提示背景更改为透明颜色:<item name="backgroundTint">@android:color/transparent</item>
。
使用透明背景始终可见的工具提示结果:
![slider_transparent_tooltip](https://istack.dev59.com/ssVZf.webp)
带有提示框的结果,背景为不透明:
![slider_non_transparent_tooltip](https://istack.dev59.com/8JPuK.webp)