UIをAndroidイベント処理結果に変更


1.緒論


ログイン認証やアイデンティティ&ニックネームの繰り返し確認に失敗したことを視覚的に伝える機能を作成したいです.
これは、ViewModelでログインまたは再検証の失敗処理を行うと、UIがユーザーに失敗を促すことを意味します.

2.本題


1.考えておく



1.LiveDataが必要


MVVM構造では、ViewすなわちActivityはView Modelを知っているが、View ModelはViewを知らない.
ViewModelでは、LiveDataでActivityにUIを変更させることで、結果と検証結果を検証できます.

2.MutableLiveData値の更新方法


LiveDataを見てUI更新を行うので、メインスレッドを書くのですが、MutableLiveDataの値更新はどのようにすればよいのでしょうか?余計な細部かもしれないけど、考えてみました.
MutipleLiveDataの値を変更する場合はsetValue()とpostValue()を使用します.この2つはスレッド部分で違いがあります.

setValue()


Sets the value. If there are active observers, the value will be dispatched to them.T his method must be called from the main thread. If you need set a value from a background thread, you can use postValue
setValue()を使用して値を更新すると、プライマリ・スレッドで呼び出されるため、このメソッドはすぐに反映されます.

postValue()


liveData.postValue("a");
liveData.setValue("b");
The value "b"would be set at first and later the main thread would override it with the value "a".
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
しかしpostValue()はI/Oスケジューラを利用し,最後にメインスレッドに値を渡すため,値の反映がやや遅い.
Android Developerの例では、liveDataの値はまずbであり、次にaに更新される.(つまり、両方の方法でメインスレッドが使用されます.)
ログイン認証とEメールとニックネームの重複認証は、サーバapiを使用してネットワークアクセスする必要があります.このため、メインスレッド上で処理するとANDR(Android Not Responding)エラーが発生する可能性があるため、PostValue()を使用することにした.(バッファアイコンを掛けるまで時間はかかりませんよね…?)

3.Event Wrapperの使用


簡単に言えば、ViewはView ModelのLivedataをエラーと見なしています.
サーバは、Eメールのチェックを繰り返すことができないため、アプリケーションを離れて戻ってくるという警告UIを受信しました.
上図を参照すると、ビューモデルのライフサイクルは変わらないが、アクティブ状態はonStop()である.アクティブライフサイクルに接続されたコンポーネントがON STOPイベントを受信することを識別します.中には傍観者もいるので、ユーザーがアプリケーションを再開すると、LiveDataをオフにして警告UIを表示する必要がある場合があります.(Observerオブジェクトは、非アクティブからアクティブになったときに最近値を受信する特性があるためかもしれません.)
結論:ライフサイクル中に無停止のワンタイムイベント転送処理プロセス、イベントWrapperが必要
内部にイベント処理を表示するhasbeenHandledは、使い捨てイベントの核心機能です.アクティビティが「アクティビティ」->「非アクティビティ」->「アクティビティ」->「非アクティビティ」になるたびに、LiveDataが最近のデータを受信しようとしても、LieveDataの値はEvent Wrapperにあるため、イベント処理が完了するとnullが返されます.そのため、データを以前のデータ(?)に偽装することができます.また、不要なUI更新を繰り返さない.
Event.kt
class Event<out T>(private val content:T) {
    var hasBeenHandled = false

    fun getContentIfNotHandled():T?{
        return if(hasBeenHandled){ //이벤트가 이미 처리된 상태
            null
        } else {
            hasBeenHandled = true //이벤트 처리 표시하기
            content //값 반환
        }
    }
    /**
     * 이벤트의 처리 여부에 상관 없이 값을 반환
     */
    fun peekContent():T = content
}

3.結論


実現したい機能は3種類あるが、結果は似ている.
ログイン検証成功&失敗->UIの更新
Eメール、ニックネームの再検証成功&失敗->UIの更新

これらのモードはすべて同じで、重複電子メール確認機能だけを示したいです.次のコードは、repositoryのデータコールバックインタフェースを介して、Eメールの繰り返しチェックが失敗したかどうか、成功したかどうか(成功した場合、繰り返したかどうか)を実現します.繰り返しチェックに成功した場合は、MutableLiveDataのPostValue()メソッドを使用し、Event Wrapperを使用して繰り返しの有無を示し、ビュー内の観察者が観察できるようにします.
SignupViewModel.kt
fun checkUserEmail(emailText:String){
        repo.checkUserEmail(emailText,object :SgRepository.GetDataCallback<Boolean>{
            override fun onSuccess(data: Boolean?) {
                if (data != null) {
                    //LiveData로 액티비티에 성공신호 제공
                        if(data == true) _emailOk.postValue(Event("emailOk"))
                    else if(data == false) _emailOk.postValue(Event("emailFail"))
                }
            }

            override fun onFailure(throwable: Throwable) {
                //실패
                Log.d("SignupVM.checkUserEmail","onFailure")
            }
        })
    }
ViewModelはMutableLiveDataの値を変更していますが、ViewはLiveDataで読み込み前に観察できます.イベントWrapperによって伝達される最新データの値が「emailOk」か「emailFail」かによって異なるUI更新が実行されることが実現される.
SignupActivity.kt
//이메일 중복확인 결과
        viewmodel.emailOk.observe(this@SignupActivity, androidx.lifecycle.Observer {
            it.getContentIfNotHandled()?.let {
                if(it == "emailOk"){
                    //성공
                    binding.apply {
                        emailCheckbtn.setBackgroundResource(R.drawable.signup_confirmbtn2)
                        emailCheckbtn.isEnabled = false
                        emailOk.visibility = View.VISIBLE
                        emailFail.visibility = View.INVISIBLE
                    }
                }
                else if(it == "emailFail"){
                    //실패
                    binding.apply {
                        emailCheckbtn.setBackgroundResource(R.drawable.signup_confirmbtn)
                        emailCheckbtn.isEnabled = true
                        emailOk.visibility = View.INVISIBLE
                        emailFail.visibility = View.VISIBLE
                    }
                }
            }
        })
他のコードは次のハブで動作しています.学部生なので足りない点が多いのでコメントをお願いします!
https://github.com/LeeYongIn0517/Bangu_android
図と資料の出所
LiveData関連
https://thdev.tech/android/2021/02/01/LiveData-Intro/
イベントWrapper関連
https://seunghyun.in/android/6/
https://leveloper.tistory.com/200
ビューモデル、アクティブライフサイクルに関連
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko