Преобразование односторонней привязки данных в двустороннюю с использованием компонентов архитектуры Android - PullRequest
0 голосов
/ 03 февраля 2019

Я реорганизую свое Android-приложение для университетского проекта, чтобы использовать компоненты архитектуры, и мне трудно реализовать двустороннюю привязку данных на SwitchCompat.Приложение имеет простой пользовательский интерфейс с TextView отображением статуса обновлений местоположения и вышеупомянутым SwitchCompat, который включает и выключает обновления местоположения.
На данный момент я использую одностороннюю привязку данных на SwitchCompat атрибут checked, но хотелось бы использовать двустороннюю привязку данных .
Текущая реализация с использованием архитектуры Model-View-ViewModel выглядит следующим образом:
MainViewModel.java :

public class MainViewModel extends ViewModel {

    private LiveData<Resource<Location>> mLocationResource;

    public MainViewModel() {
        mLocationResource = Repository.getInstance().getLocationResource();
    }

    public LiveData<Resource<Location>> getLocationResource() {
        return mLocationResource;
    }

    public void onCheckedChanged (Context context, boolean isChecked) {
        if (isChecked) {
            Repository.getInstance().requestLocationUpdates(context);
        } else {
            Repository.getInstance().removeLocationUpdates(context);
        }
    }
}

Ресурс (увидел идею здесь ) - это класс, содержащий обнуляемые данные (Location) и ненулевое состояние TextViewможет обрабатывать:
State.java

public enum State {
    LOADING,
    UPDATE,
    UNKNOWN,
    STOPPED
}

А теперь реализация android:onCheckedChanged в frag_main.xml :

android:onCheckedChanged="@{(buttonView, isChecked) -> viewModel.onCheckedChanged(context, isChecked)}"
* 1031И, наконец, настраиваемый адаптер привязки для преобразования из состояния в логическое проверенное состояние:
@BindingAdapter({"android:checked"})
public static void setChecked(CompoundButton view, Resource<Location> locationResource) {
    State state = locationResource.getState();
    boolean checked;
    if (state == State.STOPPED) {
        checked = false;
    } else {
        checked = true;
    }
    if (view.isChecked() != checked) {
        view.setChecked(checked);
    }
}

и реализация атрибута android:checked в frag_main.xml :

android:checked="@{viewModel.getLocationResource}"

Как я уже говорил в руководстве для разработчиков Android, как я могу сделать всеl работа внутри android:checked вместо android:checked и android:onCheckedChanged (односторонняя привязка данных к двусторонней привязке данных)?
Также, пожалуйста, дайте мне знать, если вы думаете об архитектуре / логике моего приложенияможет быть улучшено:)

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

В конце я прекратил попытки преобразовать одностороннюю привязку данных в двустороннюю привязку данных, но мне удалось немного упростить атрибут android:checked:
Я заменил значение

"@{viewModel.getLocationResource}"

с

"@{viewModel.locationResource.state != State.STOPPED}"

и полностью удалили android:checked @BindingAdapter.

0 голосов
/ 04 февраля 2019

Вот как я бы это сделал (извините за код Котлина):

Сначала я бы произвел рефакторинг класса Resource<T> и сделал бы переменную state объектом MutableLiveData<State>:

enum class State {
    LOADING,
    UPDATE,
    UNKNOWN,
    STOPPED
}

class Resource<T>() {
    var state = MutableLiveData<State>().apply { 
        value = State.STOPPED //Setting the initial value to State.STOPPED
    }
}

Тогда я бы создал следующий ViewModel:

class MainViewModel: ViewModel() {

     val locationResource = Resource<Location>()

}

В макете привязки данных я бы написал следующее:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="MainViewModel" />

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.SwitchCompat
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:resourceState="@={viewModel.locationResource.state}" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(viewModel.locationResource.state)}" />

    </LinearLayout>

</layout>

Обратите внимание на двустороннее выражение привязки данных @= на SwitchCompat вид.

А теперь на BindingAdapter и InverseBindingAdapter:

@BindingAdapter("resourceState")
fun setResourceState(compoundButton: CompoundButton, resourceState: State) {
    compoundButton.isChecked = when (resourceState) {
        // You can decide yourself how the mapping should look like:
        State.LOADING -> true 
        State.UPDATE -> true
        State.UNKNOWN -> true
        State.STOPPED -> false
    }
}

@InverseBindingAdapter(attribute = "resourceState", event = "resourceStateAttrChanged")
fun getResourceStateAttrChanged(compoundButton: CompoundButton): State =
    // You can decide yourself how the mapping should look like:
    if (compoundButton.isChecked) State.UPDATE else State.STOPPED

@BindingAdapter("resourceStateAttrChanged")
fun setResourceStateAttrChanged(
    compoundButton: CompoundButton,
    attrChange: InverseBindingListener
) {
    compoundButton.setOnCheckedChangeListener { _, isChecked -> 
        attrChange.onChange()

        // Not the best place to put this, but it will work for now:
        if (isChecked) {
            Repository.getInstance().requestLocationUpdates(context);
        } else {
            Repository.getInstance().removeLocationUpdates(context);
        }
    }
}

И это все.Теперь:

  • Всякий раз, когда locationResource.state меняется на State.STOPPED, кнопка SwitchCompat переходит в непроверенное состояние.
  • Всякий раз, когда locationResource.state переходит из State.STOPPED в другое состояние, кнопка SwitchCompat переходит в проверенное состояние.
  • При каждом нажатии кнопки SwitchCompat и переходе в проверенное состояние значение locationResource.state изменяется на State.UPDATE.
  • При каждом нажатии и изменении кнопки SwitchCompatв непроверенное состояние значение locationResource.state изменится на State.STOPPED.

Не стесняйтесь задавать мне любые вопросы, если что-то не понятно.

...