Как создать Binding Adapter для материала. Slider view? - PullRequest
4 голосов
/ 26 мая 2020

Моя цель - двусторонняя привязка материала. Просмотр слайдера в MutableLiveData из моей модели просмотра:

   <com.google.android.material.slider.Slider
        ...
        android:value="@={viewmodel.fps}"
        ...
    />

Конечно, это не работает, потому что в библиотеке привязки androidx.databinding нет адаптера привязки данных для Slider

[databinding] Cannot find a getter for <com.google.android.material.slider.Slider android:value> that accepts parameter type <java.lang.Integer>. If a binding adapter provides the getter, check that the adapter is annotated correctly and that the parameter type matches.

Но у них есть один для SeekBar: / androidx / databinding / adapters / SeekBarBindingAdapter. java

Как я понимаю, двусторонняя привязка данных должна работать только с атрибутом "progress", а для односторонней привязки данных требуются два атрибута: "onChanged" и "progress".

Я попытался адаптировать SeekBarBindingAdapter для Slider:

    @InverseBindingMethods({
            @InverseBindingMethod(type = Slider.class, attribute = "android:value"),
    })
    public class SliderBindingAdapter {
        @BindingAdapter("android:value")
        public static void setValue(Slider view, int value) {
            if (value != view.getValue()) {
                view.setValue(value);
            }
        }

@BindingAdapter(value = {"android:valueAttrChanged", "android:onValueChange"}, requireAll = false)
    public static void setOnSliderChangeListener(Slider view, final Slider.OnChangeListener valChanged, final InverseBindingListener attrChanged) {
        if (valChanged == null)
            view.addOnChangeListener(null);
        else
            view.addOnChangeListener((slider, value, fromUser) -> {
                if (valChanged != null)
                    valChanged.onValueChange(slider, value, fromUser);
            });


        if (attrChanged != null) {
            attrChanged.onChange();
        }
    }

    @Override
    public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {

    }

Это не building:

Could not find event android:valueAttrChanged on View type Slider

, но почему он ищет valueAttrChanged, если я использую только

android:value="@={viewmodel.fps}"

?

Как мне найти правильный атрибут для добавления в BindingAdapter, если Я не вижу valueAttrChanged в классе Slider?

1 Ответ

2 голосов
/ 31 мая 2020

Давайте посмотрим на метод SeekBarBindingAdapter setOnSeekBarChangeListener(). Он добавляет четыре разных атрибута: {"android:onStartTrackingTouch", "android:onStopTrackingTouch", "android:onProgressChanged", "android:progressAttrChanged"}, но только последний используется для двусторонней привязки данных.

Но почему здесь четыре атрибута? Если вы посмотрите на класс SeekBar , у него есть метод setOnSeekBarChangeListener(), который позволяет вам устанавливать и удалять слушателя. Проблема в том, что у SeekBar может быть только один слушатель, и что слушатель предоставляет разные обратные вызовы: onProgressChanged, onStartTrackingTouch и onStopTrackingTouch.

SeekBarBindingAdapter регистрирует свой собственный слушатель, что означает, что никто не может зарегистрировать другого слушателя, не удаляя существующий. Вот почему SeekBarBindingAdapter предоставляет атрибуты onStartTrackingTouch, onStopTrackingTouch и onProgressChanged, поэтому вы можете прослушивать эти события, не регистрируя свои собственные OnSeekBarChangeListener.

На самом деле адаптер Slider может быть намного проще чем SeekBarBindingAdapter, потому что Slider позволяет добавлять и удалять слушателей, используя addOnChangeListener() и removeOnChangeListener(). Таким образом, адаптер двусторонней привязки данных может зарегистрировать свой собственный слушатель, а любой другой может зарегистрировать других слушателей, не удаляя предыдущие.

Это позволяет нам определить довольно краткий адаптер. Я создал пример kotlin, надеюсь, вы сможете перевести его на java:

@InverseBindingAdapter(attribute = "android:value")
fun getSliderValue(slider: Slider) = slider.value

@BindingAdapter("android:valueAttrChanged")
fun setSliderListeners(slider: Slider, attrChange: InverseBindingListener) {
    slider.addOnChangeListener { _, _, _ ->
        attrChange.onChange()
    }
}

И макет:

...
<com.google.android.material.slider.Slider
    ...
    android:value="@={model.count}" />
...

Вы можете найти полные исходники здесь .

...