2017-10-20 65 views
0

我試圖在單擊按鈕時使用RxJava對EditText執行驗證,但是我很難將驗證移動到ViewModel,這會使測試變得更容易。我使用Jake Wharton的RxBindings來獲取UI輸入,並使用PublishSubject的RxJava2的Flowable.combineLatest在單擊AlertDialog上的按鈕時觸發Flowable。這是我走到這一步:使用RxJava2在ViewModel上進行EditText驗證

private Flowable<CharSequence> projectTitleObservable; 
private final PublishSubject<CharSequence> createProjectClicked = PublishSubject.create(); 

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) { 
    final Context context = getActivity(); 

    View dialogView = LayoutInflater.from(context).inflate(R.layout.new_project, null); 

    ButterKnife.bind(this, dialogView); 

    projectTitleObservable = RxTextView.textChanges(projectNameEditText).toFlowable(BackpressureStrategy.LATEST); 

    ConnectableFlowable <CharSequence> connectableFlowable = Flowable.combineLatest(createProjectClicked.toFlowable(BackpressureStrategy.LATEST), projectTitleObservable, (ignored, title) -> { 
     boolean validTitle = !TextUtils.isEmpty(title); 
     if (!validTitle) { 
      projectNameEditText.setError("Project must have a name"); 
     } 

     Timber.d("Hey, lambdas work! Look -> " + title); 
     return title; 
    }) 
      .publish(); 

    connectableFlowable.subscribe(test -> Timber.d(test.toString())); 

    return new AlertDialog.Builder(context) 
      .setTitle("Add new Project") 
      .setView(dialogView) 
      .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { 
       Timber.d("Ok clicked!"); 
       createProjectClicked.onNext("It works!"); 
      }) 
      .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> Timber.d("Cancel clicked!")) 
      .create(); 
} 

我只發佈和訂閱在同一個地方,以確保它移動到ConnectableFlowable在ViewModel前實際工作。我得到的唯一日誌是「確定點擊!」這對我來說意味着它從來沒有訂閱。任何想法爲什麼它不訂閱?

+0

如果返回標題前加上一個Log.d它會被執行嗎? –

+0

我想你應該在你的combineLastst closure裏面調用onNext –

+0

在返回標題之前有一個Timber.d(和Log.d相同),它永遠不會被調用。這個想法是,我希望能夠在我調用createProjectClicked.onNext時執行ConnectableFlowable,如果日誌未被調用,那麼onNext將不會在combineLatest中調用。 – DavisJP

回答

0

我可以使用flatMap來實現這種行爲。下面是如何顯示敬酒如果文本是有效的一個例子:

override fun onCreate(savedInstanceState: Bundle?) { 
    super.onCreate(savedInstanceState) 
    setContentView(R.layout.activity_main) 

    val btnSubject = PublishSubject.create<Boolean>() 
    val textObservable = RxTextView.textChangeEvents(editText) 
      .filter { validateText(it.text().toString()) } 

    btnSubject.flatMap { textObservable } 
      .subscribe({ 
       Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show() 
      }) 


    btn.setOnClickListener { 
     btnSubject.onNext(true) 
    } 
} 

private fun validateText(text: String) = text.length >= 5 

如果你需要表現出一定的反饋,如果文本是不正確,你可以爲一個地圖更改過濾器並使用它像這樣:

override fun onCreate(savedInstanceState: Bundle?) { 
    super.onCreate(savedInstanceState) 
    setContentView(R.layout.activity_main) 

    val btnSubject = PublishSubject.create<Boolean>() 
    val textObservable = RxTextView.textChangeEvents(editText) 
      .map { validateText(it.text().toString()) } 

    btnSubject.flatMap { textObservable } 
      .subscribe({ isTextCorrect -> 
       if (isTextCorrect) { 
        Toast.makeText(this, "Text is correct", Toast.LENGTH_SHORT).show() 
       } else { 
        Toast.makeText(this, "Text NOT is correct", Toast.LENGTH_SHORT).show() 
       } 
      }) 


    btn.setOnClickListener { 
     btnSubject.onNext(true) 
    } 
} 

private fun validateText(text: String) = text.length >= 5 

我剛測試過這段代碼,工作得很好。

快樂的代碼!

+0

這非常酷,不僅解決了ConnectableFlowables的混亂問題,而且允許在課堂外調用方法。謝謝! – DavisJP

+0

我會爲不熟悉Kotlin的人添加上面代碼的java「翻譯」。 – DavisJP

0

Java版本:

顯示舉杯如果代碼是有效的:

private Observable<TextViewTextChangeEvent> taskTitleObservable; 
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create(); 

@Override 
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 
    super.onViewCreated(view, savedInstanceState); 
    ButterKnife.bind(this, view); 

    taskTitleObservable = RxTextView.textChangeEvents(taskTitle) 
    .filter(event -> validateText(event.text().toString())); 

    createTaskClicked.flatMap(test -> taskTitleObservable).subscribe(aVoid -> { 
     Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show(); 
    }); 

    RxView.clicks(fab).subscribe(aVoid -> { 
     createTaskClicked.onNext(true); 
    }); 
} 

private Boolean validateText (String text) { 
    return text.length() >= 5; 
} 

反饋,如果文本是不正確:

private Observable<Boolean> taskTitleObservable; 
private final PublishSubject<Boolean> createTaskClicked = PublishSubject.create(); 

@Override 
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 
    super.onViewCreated(view, savedInstanceState); 
    ButterKnife.bind(this, view); 

    taskTitleObservable = RxTextView.textChangeEvents(taskTitle) 
      .map(event -> validateText(event.text().toString())); 

    createTaskClicked.flatMap(test -> taskTitleObservable).subscribe(isTextCorrect -> { 
     if (isTextCorrect) { 
      Toast.makeText(getActivity(), "Text is correct", Toast.LENGTH_SHORT).show(); 
     } else { 
      Toast.makeText(getActivity(), "Text is incorrect", Toast.LENGTH_SHORT).show(); 
     } 
    }); 

    RxView.clicks(fab).subscribe(aVoid -> { 
     createTaskClicked.onNext(true); 
    }); 
} 

private Boolean validateText (String text) { 
    return text.length() >= 5; 
}