public void init() {
        // get views
        final SeekBar seekBar = binding.preferenceSeekbar;
        valueView = binding.preferenceSeekbarValueView;

        // init seekbar
        seekBar.setMax(maxProgress);
        seekBar.setProgress(startProgress);
        valueView.setText(getValueString(startProgress));

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) {
                if (fromUser && atLeastMin(seekBar, progress)) {
                    valueView.setText(getValueString(progress));
                }
            }

            @Override
            public void onStartTrackingTouch(final SeekBar seekBar) {
                // abstract method needs to be implemented, but is not used here
            }

            @Override
            public void onStopTrackingTouch(final SeekBar seekBar) {
                if (atLeastMin(seekBar, seekBar.getProgress())) {
                    if (saveProgressListener != null) {
                        saveProgressListener.call(seekBar.getProgress());
                    }
                }
            }
        });

        valueView.setOnClickListener(v2 -> {
            final String currentValue = valueToShownValue(progressToValue(seekBar.getProgress()));
            int inputType = InputType.TYPE_CLASS_NUMBER;
            if (getHasDecimals()) {
                inputType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
            }
            SimpleDialog.ofContext(getContext()).setTitle(TextParam.id(R.string.number_input_title, valueToShownValue(minValue), valueToShownValue(maxValue)))
                    .input(new SimpleDialog.InputOptions()
                            .setInputType(inputType)
                            .setInitialValue(currentValue)
                            .setSuffix(getUnitString()), input -> {
                        try {
                            final int newValue = (int) Dialogs.checkInputRange(getContext(), shownValueToValue(Float.parseFloat(input)), minValue, maxValue);
                            final int newProgress = valueToProgress(newValue);
                            seekBar.setProgress(newProgress);
                            if (saveProgressListener != null) {
                                saveProgressListener.call(seekBar.getProgress());
                            }
                            valueView.setText(getValueString(newProgress));
                        } catch (NumberFormatException e) {
                            ViewUtils.showShortToast(getContext(), R.string.number_input_err_format);
                        }
                    });
        });

    }
Naming conventions:
- progress     - the internal value of the android widget (0 to max) - always int
- value        - actual value (may differ from "progress" if non linear or other boundaries) - always int
- shownValue   - displayed value (may differ from "value", if conversions are involved) - in:float out:float or int converted to string
This applies to derived values (like minProgress etc.) as well.
<br />
Parameters for using in XML preferences:
- min          - minimum allowed value
- max          - maximum allowed value
- stepSize     - value will be rounded down to nearest stepSize
- logScaling   - use logarithmic scaling for seekbar display
main/src/main/java/cgeo/geocaching/ui/SeekbarUI.java