Конструктор класса для различных типов параметров

Всех приветствую.

Пытаюсю сделать реализацию получения значений от элементов UI не зависящей от конкретного элемента (Кнопка, seekbar, imageview и т.д)

Значение от этих элемнтов должен принимать уже другой класс

Но тут проблема - как понять, что конкретно было передано в класс - кнопка или seekbar? А может вообще imageView?

Прикол в том, что для каждого элемента разные способы получения значений.

Да и не каждый элемент может вернуть значение от нужного элемента.

Допустим, чтобы поворачивать колеса у автомобиля - больше подойдет SeekBar, нежели кнопка / картинка с onClickListener'ом.

Пример кода, где я застрял:

public interface IUIElementValueListener {
    void onStateChanged(float value);
}

public abstract class UIElement {
    protected float value;
    protected boolean old_button_state;

    Button button;
    SeekBar seekBar;
    ImageView imageView;

    IUIElementValueListener listener;

    public UIElement(Button button, Seekbar seekbar, ImageView imageView, IUIElementValueListener listener){
        this.listener = listener;
        
        // Бред же, ну?

        if (button != null){
            initButton(button);
        }

        if (seekBar != null){
            initSeekbar(seekBar);
        }

        if (imageView != null){
            initImageView(imageView);
        }
    }

    public abstract void initButton(Button button);
    public abstract void initSeekbar(SeekBar seekBar);
    public abstract void initImageView(ImageView imageView);


    public float getValue(){
        return this.value;
    }
}
public class Steering extends UIElement {
    public Steering(Button button, SeekBar seekBar, ImageView imageView, IUIElementValueListener listener) {
        super(button, seekBar, imageView, listener);
    }
    @Override
    public void initSeekbar(SeekBar seekBar) {
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                value = i;
                listener.onStateChanged(value);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }

    // Да, кнопкой повернуть колеса можно. Но лучше использовать seekbar, а метод initButton отсюда лучше убрать вовсе. 

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public void initButton(Button button) {
        button.setOnTouchListener((View.OnTouchListener) (v, event) -> {
            if(event.getAction() == MotionEvent.ACTION_UP){
                value = 0.0f;
                listener.onStateChanged(value);

                return true;
            }
            if (event.getAction() == MotionEvent.ACTION_DOWN){
                value = 1.0f;
                listener.onStateChanged(value);

                return true;
            }
            return false;
        });
    }



    @Override
    public void initImageView(ImageView imageView) {

    }
}

public class GearBox extends UIElement{
    public GearBox(Button button, SeekBar seekBar, ImageView imageView, IUIElementValueListener listener) {
        super(button, seekBar, imageView, listener);
    }

    @Override
    public void initSeekbar(SeekBar seekBar) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            seekBar.setMin(0);
        }
        seekBar.setMax(2);

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                value = i;
                listener.onStateChanged(value);

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }

    @Override
    public void initButton(Button button) {
        button.setOnClickListener(view -> {
            old_button_state = !old_button_state;
            value = old_button_state ? 1.0f : 0.0f;
            listener.onStateChanged(value);

        });
    }

    @Override
    public void initImageView(ImageView imageView) {
        imageView.setOnClickListener(view -> {
            old_button_state = !old_button_state;
            value = old_button_state ? 1.0f : 0.0f;
            listener.onStateChanged(value);
        });
    }
}

Ответы (1 шт):

Автор решения: woesss

Можно в абстрактном классе использовать общий предок (View), в имплементациях приводить к нужному конкретному типу (каждый наследник UIElement у вас ведь будет работать с одним, заранее известным типом виджета, как я понял).
Ещё лучше использовать дженерики - это позволит избавиться от кастов и связанных с этим ошибок в рантайме:

public abstract class UIElement<T extends View> {
    protected float value;
    protected boolean old_button_state;

    T view;

    IUIElementValueListener listener;
    public UIElement(T view, IUIElementValueListener listener){
        this.listener = listener;
        init(view);
    }

    public abstract void init(T view);


    public float getValue(){
        return this.value;
    }
}
public class Steering extends UIElement<SeekBar> {
    public Steering(SeekBar seekBar, IUIElementValueListener listener) {
        super(seekBar, listener);
    }
    @Override
    public void init(SeekBar seekBar) {
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                value = i;
                listener.onStateChanged(value);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }
}
→ Ссылка