2017-10-21 7 views
0

안녕하세요. 내 안드로이드 앱에서 데이터 바인딩 및 mvvm 아키텍처를 사용하려고합니다. 레이아웃에서 데이터 바인딩을 사용하여 클릭 리스너를 추가하고 사용자 이름 및 비밀번호 edittext 값을 뷰 모델에 보내면 웹 서비스가 실행되고 LoginActivity이라는 적절한 메소드가 startHomeActivity()과 같이 호출됩니다.데이터 바인딩을 사용하여 모델을 보려면 클릭 수신기를 설정하고 edittext 필드 값을 전달하는 방법

누구든지이 작업을 수행하는 방법을 알고 있습니까? 아니면 잘못된 접근 방법을 취하고 있습니까? 내 활동, 레이아웃 및 뷰 모델의 코드 조각

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> 

답변

2

우선 모두 ViewModel의 이름을 바꿉니다. 그 뷰는, LoginViewModel와 같은 이름이 아니면 안되는 것을 의미합니다. 이 시도 (안드로이드에서 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>이 필요합니다.

이제 클릭 이벤트를 액티비티에 전달하여 클릭이 발생했는지 확인하고 싶습니다. 이 경우, ViewModel에 리스너를 만들고 Observable에 데이터를 전달합니다.

class LoginViewModel { 

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

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

그런 다음 활동 또는 단편을 사용하여 LiveData (라이프 사이클 인식 기능)를 사용하여 UIEvent를 관찰 할 수 있습니다. 당신은 MutableLiveData 인 클래스 SingleLiveData 필요

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

그러나 방출의 onec 데이터를 무효화 :

이제 같은 UI 이벤트에 대한 관찰하기 위해 뷰 모델에 바인딩 어떤 조각/활동을 사용할 수 있습니다.

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

Context 누출을 피하기 위해 WeakReferences를 사용하여 여러 시도를하고 있지만 그렇게하지 않는 것이 좋습니다. 이유는 당신이 당신의 견해와 논리를 나누기를 원한다는 것입니다. 게으르거나 약한 경우에도 참조가 있으면 아키텍처가 손상됩니다.

+0

고맙습니다. +1 –