[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 <cketcham@gmail.com>
PiperOrigin-RevId: 287545665
This commit is contained in:
ruben 2019-12-30 09:37:39 -05:00 committed by Avigail
parent a011ace28f
commit cde09d4b4e
4 changed files with 101 additions and 26 deletions

View File

@ -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(

View File

@ -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()));

View File

@ -34,4 +34,7 @@
<string name="cat_slider_title_3" description="The title for a demonstration slider">Negative numbers!</string>
<string name="cat_slider_title_4" description="The title for a demonstration slider">With a label formatter</string>
<string name="cat_slider_title_5" description="The title for a demonstration slider">I can has decimal numbers?</string>
<string name="cat_slider_start_touch_description" description="Indicates the slider is now being touched">Slider started being touched</string>
<string name="cat_slider_stop_touch_description" description="Indicates the slider stopped being touched">Slider stopped being touched</string>
</resources>

View File

@ -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<OnChangeListener> changeListeners = new ArrayList<>();
@NonNull private List<OnSliderTouchListener> 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 {