如何设置单击侦听器并使用数据绑定将edittext字段值传递给视图模型 [英] How to set click listener and pass edittext fields value to view model using data binding

查看:86
本文介绍了如何设置单击侦听器并使用数据绑定将edittext字段值传递给视图模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Android应用中使用数据绑定和mvvm体系结构.我想使用布局中的数据绑定添加单击侦听器,并将用户名和密码edittext的值发送到视图模型,它将执行Web服务并调用LoginActivity的适当方法,如startHomeActivity().

Hi I am trying to use data binding and mvvm architecture in my android app. I want to add click listener using data binding in the layout and send the values of username and password edittext to the view model and it will execute the web service and call appropriate method of LoginActivity like startHomeActivity().

有人知道该怎么做吗?还是我采取了错误的方法?我的活动,布局和视图模型的代码段如下所示:

Does anyone know how to do this or Am I taking wrong approach ? I have below snippet of code of my activity, layout and view model

LoginActivity.kt

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

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

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.为此尝试(这是在Android中使用mvvm模式的最佳选择),您需要AAC/ LiveData .

First of all rename your ViewModel. Its seperated by the View which means the name should be something like LoginViewModel. For this attempt (which is the best available using mvvm pattern in android) you need AAC/LiveData.

第二,您应该进行双向数据绑定,然后将ViewModel分配给您的布局.

Second you should do two-way databinding and assign the ViewModel to your Layout.

<?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>

这需要在ViewModel中使用ObservableField<String>.

That requires a ObservableField<String> in your ViewModel.

现在,您要通过在活动中传递click事件来验证是否发生了点击.对于这种情况,您可以在ViewModel中创建侦听器,然后将数据传递给Observable.

Now you want to validate if a click happened by passing the click Event in your activity. For that case you create the Listener in your ViewModel and pass the Data to an Observable.

class LoginViewModel {

    val yourField = ObservableField<String>()
    val uiEventLiveData = SingleLiveData<Int>()

    fun onClick(view:View) {
       uiObservable.data = 1 // or any other event
    }
}

此后,您可以使用LiveData(具有生命周期意识!)使用Activity或Fragment观察UIEvent.

After this you can use your Activity or Fragment to observe for UIEvents using LiveData (which is lifecycle-aware!).

现在,您可以使用绑定到ViewModel的ANY片段/活动来观察UI事件,例如:

Now you can use ANY Fragment / Activity which is bound to the ViewModel to observe for UI Events like:

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,但是将其发出的数据无效.

You need the Class SingleLiveData which is a MutableLiveData but nullify your data onec its emitted.

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来避免上下文泄漏,但是我强烈建议不要这样做.原因是您想用视图拆分逻辑.即使是懒惰或脆弱的引用,也会破坏体系结构.

There are several attempts doing that with WeakReferences to avoid Context leak but i highly recommend not doing that. The Reason is that you want to split logic with your view. Having references even if they are lazy or weak breaks the architecture.

这篇关于如何设置单击侦听器并使用数据绑定将edittext字段值传递给视图模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆