private void init() {
        setOrientation(VERTICAL);
        final ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), R.style.cgeo);
        inflate(ctw, R.layout.continuousrangeslider_view, this);
        binding = ContinuousrangesliderViewBinding.bind(this);
        setScale(0f, 100f, null, 11, factor);
        binding.sliderInternal.addOnSliderTouchListener(new RangeSlider.OnSliderTouchListener() {
            int lastThumb = -1;
            Float lastValue = -1.0f;
            Float grace = null;

            @Override
            public void onStartTrackingTouch(final @NonNull RangeSlider slider) {
                if (grace == null) {
                    grace = (slider.getValueTo() - slider.getValueFrom()) / 20.0f;
                }
                lastThumb = slider.getActiveThumbIndex(); // on touch start it's the active thumb
                final List<Float> values = slider.getValues();
                if (lastThumb >= 0 && lastThumb < values.size()) {
                    lastValue = values.get(lastThumb);
                } else {
                    lastValue = null;
                }
            }

            @Override
            public void onStopTrackingTouch(final @NonNull RangeSlider slider) {
                final List<Float> values = slider.getValues();
                // on touch end it's the focussed thumb, though!
                // still on the same thumb, and not having moved it out of grace area?
                if (slider.getFocusedThumbIndex() == lastThumb && lastThumb >= 0 && lastThumb < values.size() && Math.abs(lastValue - values.get(lastThumb)) < grace) {
                    final String defaultValue = String.format(Locale.getDefault(), "%d", Math.round(lastValue * factor));
                    int inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL;
                    if (slider.getValueFrom() < 0) {
                        inputType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
                    }
                    final Consumer<String> listener = input -> {
                        try {
                            final float newValue = Dialogs.checkInputRange(getContext(), Float.parseFloat(input), slider.getValueFrom() * factor, slider.getValueTo() * factor) / factor;
                            if (lastThumb == 0) {
                                // we want to set lower bound, but new value is larger than current upper bound, so swap
                                if (newValue > values.get(1)) {
                                    slider.setValues(values.get(1), newValue);
                                } else {
                                    slider.setValues(newValue, values.get(1));
                                }
                            } else {
                                // we want to set upper bound, but new value is smaller than current lower bound, so swap
                                if (newValue < values.get(0)) {
                                    slider.setValues(newValue, values.get(0));
                                } else {
                                    slider.setValues(values.get(0), newValue);
                                }
                            }
                        } catch (NumberFormatException e) {
                            ViewUtils.showShortToast(getContext(), R.string.number_input_err_format);
                        }
                    };
                    SimpleDialog.ofContext(getContext()).setTitle(TextParam.id(R.string.number_input_title, Math.round(slider.getValueFrom() * factor), Math.round(slider.getValueTo() * factor)))
                            .input(new SimpleDialog.InputOptions().setInputType(inputType).setInitialValue(defaultValue).setSuffix(""), listener);
                }
            }
        });
    }
View displays and maintains a slider where user can select single or range of items from a sorted item list.
Tapping on a thumb allows manual value input
main/src/main/java/cgeo/geocaching/ui/ContinuousRangeSlider.java