如何设置单击侦听器并将edittext字段值传递给使用数据绑定查看模型

时间:2017-10-21 10:08:56

标签: android mvvm kotlin android-databinding android-mvvm

您好我正在尝试在我的Android应用中使用数据绑定和mvvm架构。我想在布局中使用数据绑定添加单击侦听器,并将用户名和密码edittext的值发送到视图模型,它将执行Web服务并调用LoginActivity的适当方法{{1 }}

有谁知道如何做到这一点还是我采取了错误的做法?我有以下代码片段的活动,布局和视图模型

LoginActivity.kt

startHomeActivity()

LoginActivityViewModel.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.
    }

}

activity_login.xml

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
    }

}

1 个答案:

答案 0 :(得分:6)

首先重命名您的ViewModel。它由View分隔,这意味着名称应该像LoginViewModel。对于此尝试(这是在android中使用mvvm模式最好的),您需要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>

这需要ViewModel中的ObservableField<String>

现在,您希望通过在活动中传递click事件来验证是否发生了点击。在这种情况下,您可以在ViewModel中创建监听器,并将数据传递给Observable。

class LoginViewModel {

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

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

在此之后,您可以使用Activity或Fragment来观察使用LiveData的UIEvents(这是生命周期感知的!)。

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

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
          }
    })
}

您需要Class 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来避免Context泄漏,但我强烈建议不要这样做。原因是您希望将逻辑与您的视图分开。即使它们是懒惰或弱的参考也会破坏架构。