Путаница из-за MVVM и двухстороннего связывания данных - PullRequest
0 голосов
/ 19 октября 2018

Я пытаюсь создать простой экран «Вход в систему» ​​с использованием шаблона MVVM.У меня есть двусторонняя привязка данных между моим View и Model классом, но что же остается для ViewModel?

Первоначально я думал, что у меня даже не будет Model class и my ViewModel class будут иметь свойства для двустороннего связывания данных с View, но класс ViewModel уже расширяет класс, необходимый для его раздувания во Fragment, и, следовательно, не может расширятьсяBaseObservable, чтобы разрешить двустороннюю привязку данных.

Я думаю, что в целом я запутался в том, как эти компоненты должны взаимодействовать друг с другом или что мне нужно / не нужно.

Мой фрагмент (просмотр)

public class LoginFragment extends Fragment {

    private LoginViewModel mViewModel;
    public static LoginFragment newInstance() {
        return new LoginFragment();
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
        LoginFragmentBinding binding = LoginFragmentBinding.inflate(inflater, container, false);
        binding.setViewModel(mViewModel); // mViewModel is null here...
        binding.setLoginInfo(new LoginInfo());
        return binding.getRoot();
    }
}

И некоторые из моих login_fragment.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
    <variable name="viewModel" type="login.ui.LoginViewModel"/>
    <variable name="loginInfo" type="login.ui.model.LoginInfo" />
</data>
<android.support.constraint.ConstraintLayout
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".login.ui.LoginFragment">

    <EditText
        android:id="@+id/input_password"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:autofillHints="password"
        android:ems="10"
        android:hint="@string/hint_password"
        android:inputType="textPassword"
        android:text="@={loginInfo.password}"
        app:layout_constraintBottom_toTopOf="@id/button_sign_in"
        app:layout_constraintEnd_toEndOf="@id/input_username"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/input_username"
        app:layout_constraintTop_toBottomOf="@id/input_username" />

    <EditText
        android:id="@+id/input_username"
        android:layout_width="350dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="285dp"
        android:autofillHints="username"
        android:ems="10"
        android:hint="@string/hint_username"
        android:inputType="textEmailAddress"
        android:text="@={loginInfo.username}"
        app:layout_constraintBottom_toTopOf="@id/input_password"
        app:layout_constraintEnd_toStartOf="@id/guideline"
        app:layout_constraintStart_toStartOf="@id/guideline"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button_sign_in"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/hint_sign_in"
        android:onClick="@{()-> viewModel.onSignInClicked(loginInfo)}"
        app:layout_constraintBottom_toTopOf="@id/button_create_account"
        app:layout_constraintEnd_toEndOf="@id/input_password"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@id/input_password"
        app:layout_constraintTop_toBottomOf="@id/input_password" />

Мой класс ViewModel

public class LoginViewModel extends ViewModel {
    // Want to bind this to a button in the XML, but
    // the mViewModel instance in the LoginFragment isnt assigned
    public void onSignInClicked(LoginInfo info) {
        Log.i("Username", info.getUsername());
        Log.i("Password", info.getPassword());
        // TODO: Actual log in attempt
    }
}

Мой класс LoginInfo (модель)

public class LoginInfo extends BaseObservable {
    private String username = "";
    private String password = "";

    @Bindable
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        /*Listener will repeatedly call setPassword() every time it is notified,
         avoid infinite loops*/
        if (!this.username.equals(username)) {
            Log.i("Username", username);
            this.username = username;
            notifyPropertyChanged(BR.username);
        }
    }

    @Bindable
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
         /*Listener will repeatedly call setPassword() every time it is notified,
         avoid infinite loops*/
        if (!this.password.equals(password)) {
            Log.i("Password", password);
            this.password = password;
            notifyPropertyChanged(BR.password);
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 19 октября 2018

Все шаблоны MV * предназначены для облегчения слабой связи между уровнем взаимодействия и бизнес-логикой.

В MVVM ваш View должен знать о вашей модели, но никогда не ретранслировать ее.

Работа VM заключается в том, чтобы взять данные из модели и проанализировать их так, чтобы View мог показать их.Пользователь.

0 голосов
/ 19 октября 2018

Разделение между различными слоями в MVVM является чем-то вроде серой области.

Лично я разделяю его на

  • Модель - только данные приложения, ни больше, ни меньше - обычно как простые классы POCO.

  • View - пользовательский интерфейс, здесь нет бизнес-логики.

  • ViewModel - все остальное.Основная роль ViewModel - это главный контроллер и поставщик данных для View.Это может принимать форму прямого доступа к объекту модели для привязки данных, (коллекций) других объектов sub-ViewModel, команд пользовательского интерфейса для запуска обработки, (внедренных) сервисов, таких как внешнее хранение и извлечение данных.

Подробнее о моих последних публикациях в блоге - Модель / ViewModel .Да, я знаю, что этот вопрос помечен как Android, и мой блог в основном посвящен WPF, но общие принципы все еще применяются.

0 голосов
/ 19 октября 2018

Я думаю, вы немного запутались в MVVM.

В MVVM (Model-View-ViewModel) нет прямой связи между классом модели и классом представления.Я помню это, написав это как M-VM-V .Это означает, что ваше представление (фрагмент, действие, xml) будет взаимодействовать с ВМ и наоборот.И ваша модель (классы данных, pojo) будет взаимодействовать с классом viewModel и наоборот.

Итак, вам не следует использовать и модель представления, и модель в xml.Сохраните ссылку на модель в классе view-model, чтобы установить данные и получить данные.Используйте view-модель, имеющую наблюдаемые, для связывания данных с xml.

Кроме того, вы никогда не должны писать viewModel.getModel().getSomething() где-либо в любом классе представления.Вместо этого создайте метод в модели представления, который возвращает такое значение.Это все о том, как проще было бы заменить класс модели, не меняя ни одной строки в любом из ваших классов представлений.

...