From cde09d4b4e8a2ad8a6d73e449f8fb09e404ea3ea Mon Sep 17 00:00:00 2001 From: ruben Date: Mon, 30 Dec 2019 09:37:39 -0500 Subject: [PATCH] [Slider] Add missing functionality from SeekBar regarding touch events and value changes Resolves https://github.com/material-components/material-components-android/pull/831 GIT_ORIGIN_REV_ID=70dd0a10ef204837b01ed9593cfdd9bcf9137228 Co-authored-by: cketcham PiperOrigin-RevId: 287545665 --- .../slider/SliderContinuousDemoFragment.java | 4 +- .../slider/SliderMainDemoFragment.java | 16 +++ .../catalog/slider/res/values/strings.xml | 3 + .../android/material/slider/Slider.java | 104 ++++++++++++++---- 4 files changed, 101 insertions(+), 26 deletions(-) diff --git a/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java b/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java index e23d1c574..e739cb6e9 100644 --- a/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java +++ b/catalog/java/io/material/catalog/slider/SliderContinuousDemoFragment.java @@ -55,8 +55,8 @@ public class SliderContinuousDemoFragment extends DemoFragment { final String valueFormat) { final TextView sliderValue = view.findViewById(valueId); final Slider slider = view.findViewById(sliderId); - slider.setOnChangeListener( - (slider1, value) -> sliderValue.setText(String.format(valueFormat, value))); + slider.addOnChangeListener( + (slider1, value, fromUser) -> sliderValue.setText(String.format(valueFormat, value))); slider.setValue(slider.getValueFrom()); SwitchCompat switchButton = view.findViewById(switchId); switchButton.setOnCheckedChangeListener( diff --git a/catalog/java/io/material/catalog/slider/SliderMainDemoFragment.java b/catalog/java/io/material/catalog/slider/SliderMainDemoFragment.java index 078e920a8..2557d0200 100644 --- a/catalog/java/io/material/catalog/slider/SliderMainDemoFragment.java +++ b/catalog/java/io/material/catalog/slider/SliderMainDemoFragment.java @@ -25,6 +25,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import com.google.android.material.slider.Slider; +import com.google.android.material.snackbar.Snackbar; import io.material.catalog.feature.DemoFragment; /** A fragment that displays the main Slider demo for the Catalog app. */ @@ -37,6 +38,21 @@ public class SliderMainDemoFragment extends DemoFragment { layoutInflater.inflate(R.layout.cat_slider_fragment, viewGroup, false /* attachToRoot */); Slider slider = view.findViewById(R.id.slider); + slider.addOnSliderTouchListener( + new Slider.OnSliderTouchListener() { + @Override + public void onStartTrackingTouch(Slider slider) { + Snackbar.make( + slider, R.string.cat_slider_start_touch_description, Snackbar.LENGTH_SHORT) + .show(); + } + + @Override + public void onStopTrackingTouch(Slider slider) { + Snackbar.make(slider, R.string.cat_slider_stop_touch_description, Snackbar.LENGTH_SHORT) + .show(); + } + }); Button button = view.findViewById(R.id.button); button.setOnClickListener(v -> slider.setValue(slider.getValueTo())); diff --git a/catalog/java/io/material/catalog/slider/res/values/strings.xml b/catalog/java/io/material/catalog/slider/res/values/strings.xml index d7d76713d..c40591c36 100644 --- a/catalog/java/io/material/catalog/slider/res/values/strings.xml +++ b/catalog/java/io/material/catalog/slider/res/values/strings.xml @@ -34,4 +34,7 @@ Negative numbers! With a label formatter I can has decimal numbers? + Slider started being touched + Slider stopped being touched + diff --git a/lib/java/com/google/android/material/slider/Slider.java b/lib/java/com/google/android/material/slider/Slider.java index e4219faa4..70dd82218 100644 --- a/lib/java/com/google/android/material/slider/Slider.java +++ b/lib/java/com/google/android/material/slider/Slider.java @@ -68,6 +68,8 @@ import com.google.android.material.shape.ShapeAppearanceModel; import com.google.android.material.tooltip.TooltipDrawable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; /** @@ -186,6 +188,8 @@ public class Slider extends View { @NonNull private final Paint activeTicksPaint; @NonNull private TooltipDrawable label; + @NonNull private List changeListeners = new ArrayList<>(); + @NonNull private List touchListeners = new ArrayList<>(); private final int scaledTouchSlop; @@ -198,7 +202,6 @@ public class Slider extends View { private int haloRadius; private int labelPadding; private float touchDownX; - private OnChangeListener listener; private LabelFormatter formatter; private boolean thumbIsPressed = false; private float valueFrom; @@ -241,7 +244,17 @@ public class Slider extends View { /** Interface definition for a callback invoked when a slider's value is changed. */ public interface OnChangeListener { - void onValueChange(Slider slider, float value); + void onValueChange(@NonNull Slider slider, float value, boolean fromUser); + } + + /** + * Interface definition for callbacks invoked when a slider's touch event is being + * started/stopped. + */ + public interface OnSliderTouchListener { + void onStartTrackingTouch(@NonNull Slider slider); + + void onStopTrackingTouch(@NonNull Slider slider); } /** @@ -538,9 +551,7 @@ public class Slider extends View { public void setValue(float value) { if (isValueValid(value)) { thumbPosition = (value - valueFrom) / (valueTo - valueFrom); - if (hasOnChangeListener()) { - listener.onValueChange(this, getValue()); - } + dispatchOnChanged(false); invalidate(); } } @@ -596,21 +607,50 @@ public class Slider extends View { postInvalidate(); } - /** - * Returns {@code true} if the slider has an {@link OnChangeListener} attached, {@code false} - * otherwise. - */ - public boolean hasOnChangeListener() { - return listener != null; - } - /** * Registers a callback to be invoked when the slider changes. * * @param listener The callback to run when the slider changes */ - public void setOnChangeListener(@Nullable OnChangeListener listener) { - this.listener = listener; + public void addOnChangeListener(@Nullable OnChangeListener listener) { + changeListeners.add(listener); + } + + /** + * Removes a callback for value changes from this {@link Slider} + * + * @param listener The callback that'll stop receive slider changes + */ + public void removeOnChangeListener(@NonNull OnChangeListener listener) { + changeListeners.remove(listener); + } + + /** Removes all instances of {@link Slider.OnChangeListener} attached to this slider */ + public void clearOnChangeListeners() { + changeListeners.clear(); + } + + /** + * Registers a callback to be invoked when the slider touch event is being started or stopped + * + * @param listener The callback to run when the slider starts or stops being touched + */ + public void addOnSliderTouchListener(@NonNull OnSliderTouchListener listener) { + touchListeners.add(listener); + } + + /** + * Removes a callback to be invoked when the slider touch event is being started or stopped + * + * @param listener The callback that'll stop be notified when the slider is being touched + */ + public void removeOnSliderTouchListener(@NonNull OnSliderTouchListener listener) { + touchListeners.remove(listener); + } + + /** Removes all instances of {@link Slider.OnSliderTouchListener} attached to this slider */ + public void clearOnSliderTouchListeners() { + touchListeners.clear(); } /** @@ -1242,9 +1282,8 @@ public class Slider extends View { ensureLabel(); updateLabelPosition(); invalidate(); - if (hasOnChangeListener()) { - listener.onValueChange(this, getValue()); - } + onStartTrackingTouch(); + dispatchOnChanged(true); break; case MotionEvent.ACTION_MOVE: if (!thumbIsPressed) { @@ -1253,6 +1292,7 @@ public class Slider extends View { return false; } getParent().requestDisallowInterceptTouchEvent(true); + onStartTrackingTouch(); } thumbIsPressed = true; thumbPosition = position; @@ -1261,15 +1301,14 @@ public class Slider extends View { ensureLabel(); updateLabelPosition(); invalidate(); - if (hasOnChangeListener()) { - listener.onValueChange(this, getValue()); - } + dispatchOnChanged(true); break; case MotionEvent.ACTION_UP: thumbIsPressed = false; thumbPosition = position; snapThumbPosition(); ViewUtils.getContentViewOverlay(this).remove(label); + onStopTrackingTouch(); invalidate(); break; default: @@ -1347,6 +1386,25 @@ public class Slider extends View { return false; } + private void dispatchOnChanged(boolean fromUser) { + final float value = getValue(); + for (OnChangeListener listener : changeListeners) { + listener.onValueChange(this, value, fromUser); + } + } + + private void onStartTrackingTouch() { + for (OnSliderTouchListener listener : touchListeners) { + listener.onStartTrackingTouch(this); + } + } + + private void onStopTrackingTouch() { + for (OnSliderTouchListener listener : touchListeners) { + listener.onStopTrackingTouch(this); + } + } + @Override protected void drawableStateChanged() { super.drawableStateChanged(); @@ -1399,9 +1457,7 @@ public class Slider extends View { if (sliderState.hasFocus) { requestFocus(); } - if (hasOnChangeListener()) { - listener.onValueChange(this, getValue()); - } + dispatchOnChanged(false); } static class SliderState extends BaseSavedState {