Как установить функцию прослушивания кликов и передать значение поля edittext для просмотра модели с использованием привязки данных

Привет, Я пытаюсь использовать привязку данных и mvvm-архитектуру в своем приложении для Android. Я хочу добавить прослушиватель кликов, используя привязку данных в макете, и отправить значения имени пользователя и пароля edittext в модель представления и выполнить веб-службу и вызвать соответствующий метод LoginActivity такой как startHomeActivity() .

Кто-нибудь знает, как это сделать или я принимаю неправильный подход? У меня ниже фрагмента кода моей деятельности, макета и модели представления

LoginActivity.kt

 class LoginActivity : BaseActivity(), LoginNavigator { @Inject lateinit var loginViewModel: LoginActivityViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val activityLoginBinding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login) } override fun startHomeActivity() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun startRegistrationActivity() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun startForgotPasswordActivity() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun handleError(throwable: Throwable) { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } } 

LoginActivityViewModel.kt

 class LoginActivityViewModel { fun login(email: String, password: String) { } /** * Validate email and password. It checks email and password is empty or not * and validate email address is correct or not * @param email email address for login * @param password password for login * @return true if email and password pass all conditions else false */ fun isEmailAndPasswordValid(email: String, password: String): Boolean { if (email.isEmpty()) return false if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false if (password.isEmpty()) return false return true } } 

activity_login.xml

 <layout> <ScrollView 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" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" tools:context="com.app.android.login.LoginActivity" tools:ignore="missingPrefix"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="@dimen/default_view_margin_bottom_8dp"> <android.support.design.widget.TextInputLayout android:id="@+id/til_login_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_view_margin_right_8dp" android:layout_marginStart="@dimen/default_view_margin_left_8dp" android:textColorHint="@color/colorSecondaryText" app:hintTextAppearance="@style/AppTheme.InputLayoutStyle" app:layout_constraintBottom_toTopOf="@+id/til_login_password" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed"> <android.support.design.widget.TextInputEditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/login_email" android:imeOptions="actionNext" android:singleLine="true" android:textColor="@color/colorPrimaryText" /> </android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout android:id="@+id/til_login_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_view_margin_right_8dp" android:layout_marginStart="@dimen/default_view_margin_left_8dp" android:textColorHint="@color/colorSecondaryText" app:hintTextAppearance="@style/AppTheme.InputLayoutStyle" app:layout_constraintBottom_toTopOf="@+id/btn_login_login" app:layout_constraintTop_toBottomOf="@+id/til_login_email" app:layout_constraintVertical_chainStyle="packed"> <android.support.design.widget.TextInputEditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/login_password" android:imeOptions="actionDone" android:singleLine="true" android:textColor="@color/colorPrimaryText" /> </android.support.design.widget.TextInputLayout> <Button android:id="@+id/btn_login_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_view_margin_right_8dp" android:layout_marginStart="@dimen/default_view_margin_left_8dp" android:layout_marginTop="48dp" android:text="@string/login_btn_text" android:textColor="@color/colorWhite" app:layout_constraintBottom_toTopOf="@+id/textview_login_forgot_password" app:layout_constraintTop_toBottomOf="@+id/til_login_password" app:layout_constraintVertical_chainStyle="packed" /> <TextView android:id="@+id/textview_login_forgot_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_view_margin_right_8dp" android:layout_marginStart="@dimen/default_view_margin_left_8dp" android:layout_marginTop="36dp" android:gravity="center" android:text="@string/login_forgot_password" app:layout_constraintBottom_toTopOf="@+id/btn_login_register" app:layout_constraintTop_toBottomOf="@+id/btn_login_login" app:layout_constraintVertical_chainStyle="packed" /> <Button android:id="@+id/btn_login_register" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/default_view_margin_right_8dp" android:layout_marginStart="@dimen/default_view_margin_left_8dp" android:text="@string/login_sign_up" android:textColor="@color/colorWhite" app:layout_constraintBottom_toBottomOf="parent" /> </android.support.constraint.ConstraintLayout> </ScrollView> </layout> 

Прежде всего переименуйте ViewModel. Его разделяет вид, который означает, что имя должно быть чем-то вроде LoginViewModel. Для этой попытки (которая наилучшим образом доступна с использованием шаблона mvvm в android) вам нужен AAC / LiveData .

Во-вторых, вы должны сделать двустороннюю привязку данных и назначить ViewModel вашему макету.

 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewModel" type="...YourVm" /> </data> <android.support.design.widget.TextInputEditText ... android:text="@={viewModel.yourField}" /> <Button ... onClick="@{viewModel.onClick}" /> </layout> 

Это требует ObservableField<String> в вашей ViewModel.

Теперь вы хотите проверить, произошел ли щелчок, передав событие click в вашей активности. В этом случае вы создаете Listener в своей ViewModel и передаете данные в наблюдаемый.

 class LoginViewModel { val yourField = ObservableField<String>() val uiEventLiveData = SingleLiveData<Int>() fun onClick(view:View) { uiObservable.data = 1 // or any other event } } 

После этого вы можете использовать свою активность или фрагмент, чтобы наблюдать за UIEvents, используя LiveData (который является жизненным циклом!).

Теперь вы можете использовать ЛЮБЫЕ Фрагменты / Действия, которые привязаны к ViewModel для наблюдения за событиями пользовательского интерфейса, например:

 class YourActivity { private val yourvm by lazy { ViewModelProviders.of(this, viewModelFactory).get(Yourvm::class.java) } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { // .... binding.viewModel = yourVm } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) yourVm.uiEventLiveData.observe(this, Observer { when(it) { 1-> { doSomeLoginStuff(yourVm.yourField, ...) } //click happened, do something else -> .... // unknown ui event } }) } 

Вам нужен класс SingleLiveData, который является MutableLiveData, но сводит на нет ваши данные.

 class SingleLiveData<T> : MutableLiveData<T>() { private val mPending = AtomicBoolean(false) @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<T>) { if (hasActiveObservers()) { Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") } // Observe the internal MutableLiveData super.observe(owner, Observer { t -> if (mPending.compareAndSet(true, false)) { observer.onChanged(t) } }) } @MainThread override fun setValue(t: T?) { mPending.set(true) super.setValue(t) } /** * Used for cases where T is Void, to make calls cleaner. */ @MainThread fun call() { value = null } companion object { private val TAG = "SingleLiveData" } } 

Есть несколько попыток сделать это с помощью WeakReferences, чтобы избежать утечки Контекста, но я настоятельно рекомендую не делать этого. Причина в том, что вы хотите разделить логику с вашим представлением. Имея ссылки, даже если они ленивы или слабы, ломает архитектуру.