Skip to content

Commit 28c9fb7

Browse files
author
Kaushik Gopal
committed
feat: add new example that explains a good use case of .combineLatest (form validation
1 parent d443f68 commit 28c9fb7

File tree

7 files changed

+235
-1
lines changed

7 files changed

+235
-1
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.morihacky.android.rxjava;
2+
3+
import android.os.Bundle;
4+
import android.support.annotation.Nullable;
5+
import android.support.v4.app.Fragment;
6+
import android.view.LayoutInflater;
7+
import android.view.View;
8+
import android.view.ViewGroup;
9+
import android.widget.EditText;
10+
import android.widget.TextView;
11+
import butterknife.ButterKnife;
12+
import butterknife.InjectView;
13+
import com.morihacky.android.rxjava.app.R;
14+
import rx.Observable;
15+
import rx.Observer;
16+
import rx.Subscription;
17+
import rx.android.widget.OnTextChangeEvent;
18+
import rx.android.widget.WidgetObservable;
19+
import rx.functions.Func3;
20+
import timber.log.Timber;
21+
22+
import static android.util.Patterns.EMAIL_ADDRESS;
23+
import static com.google.common.base.Strings.isNullOrEmpty;
24+
25+
public class FormValidationCombineLatestFragment
26+
extends Fragment {
27+
28+
@InjectView(R.id.btn_demo_form_valid) TextView _btnValidIndicator;
29+
@InjectView(R.id.demo_combl_email) EditText _email;
30+
@InjectView(R.id.demo_combl_password) EditText _password;
31+
@InjectView(R.id.demo_combl_num) EditText _number;
32+
33+
private Observable<OnTextChangeEvent> _emailChangeObservable;
34+
private Observable<OnTextChangeEvent> _passwordChangeObservable;
35+
private Observable<OnTextChangeEvent> _numberChangeObservable;
36+
37+
private Subscription _subscription = null;
38+
39+
@Override
40+
public View onCreateView(LayoutInflater inflater,
41+
@Nullable ViewGroup container,
42+
@Nullable Bundle savedInstanceState) {
43+
View layout = inflater.inflate(R.layout.fragment_form_validation_comb_latest, container, false);
44+
ButterKnife.inject(this, layout);
45+
46+
_emailChangeObservable = WidgetObservable.text(_email);
47+
_passwordChangeObservable = WidgetObservable.text(_password);
48+
_numberChangeObservable = WidgetObservable.text(_number);
49+
50+
_combineLatestEvents();
51+
52+
return layout;
53+
}
54+
55+
@Override
56+
public void onPause() {
57+
super.onPause();
58+
if (_subscription != null) {
59+
_subscription.unsubscribe();
60+
}
61+
}
62+
63+
private void _combineLatestEvents() {
64+
_subscription = Observable.combineLatest(_emailChangeObservable,
65+
_passwordChangeObservable,
66+
_numberChangeObservable,
67+
new Func3<OnTextChangeEvent, OnTextChangeEvent, OnTextChangeEvent, Boolean>() {
68+
@Override
69+
public Boolean call(OnTextChangeEvent onEmailChangeEvent,
70+
OnTextChangeEvent onPasswordChangeEvent,
71+
OnTextChangeEvent onNumberChangeEvent) {
72+
73+
boolean emailValid = !isNullOrEmpty(onEmailChangeEvent.text().toString()) &&
74+
EMAIL_ADDRESS.matcher(onEmailChangeEvent.text()).matches();
75+
if (!emailValid) {
76+
_email.setError("Invalid Email!");
77+
}
78+
79+
boolean passValid = !isNullOrEmpty(onPasswordChangeEvent.text().toString()) &&
80+
onPasswordChangeEvent.text().length() > 8;
81+
if (!passValid) {
82+
_password.setError("Invalid Password!");
83+
}
84+
85+
boolean numValid = !isNullOrEmpty(onNumberChangeEvent.text().toString());
86+
if (numValid) {
87+
int num = Integer.parseInt(onNumberChangeEvent.text().toString());
88+
numValid = num > 0 && num <= 100;
89+
}
90+
if (!numValid) {
91+
_number.setError("Invalid Number!");
92+
}
93+
94+
return emailValid && passValid && numValid;
95+
96+
}
97+
})//
98+
.subscribe(new Observer<Boolean>() {
99+
@Override
100+
public void onCompleted() {
101+
Timber.d("completed");
102+
}
103+
104+
@Override
105+
public void onError(Throwable e) {
106+
Timber.e(e, "there was an eroor");
107+
}
108+
109+
@Override
110+
public void onNext(Boolean formValid) {
111+
if (formValid) {
112+
_btnValidIndicator.setBackgroundColor(getResources().getColor(R.color.blue));
113+
} else {
114+
_btnValidIndicator.setBackgroundColor(getResources().getColor(R.color.gray));
115+
}
116+
}
117+
});
118+
}
119+
}

app/src/main/java/com/morihacky/android/rxjava/MainActivity.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ protected void onCreate(Bundle savedInstanceState) {
2121
getSupportFragmentManager().beginTransaction()
2222
.addToBackStack(this.toString())
2323
.replace(R.id.activity_main, new MainFragment(), this.toString())
24-
//.replace(R.id.activity_main, new TimingDemoFragment(), this.toString())
2524
.commit();
2625
}
2726

app/src/main/java/com/morihacky/android/rxjava/MainFragment.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,13 @@ public void demoTimeout() {
9494
.replace(R.id.activity_main, new DemoTimeoutFragment(), this.toString())
9595
.commit();
9696
}
97+
98+
@OnClick(R.id.btn_demo_form_validation_combinel)
99+
public void formValidation() {
100+
getActivity().getSupportFragmentManager()
101+
.beginTransaction()
102+
.addToBackStack(this.toString())
103+
.replace(R.id.activity_main, new FormValidationCombineLatestFragment(), this.toString())
104+
.commit();
105+
}
97106
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<LinearLayout
2+
android:orientation="vertical"
3+
android:layout_height="match_parent"
4+
android:layout_width="match_parent"
5+
xmlns:android="http://schemas.android.com/apk/res/android"
6+
>
7+
8+
<TextView
9+
android:layout_height="wrap_content"
10+
android:layout_width="match_parent"
11+
android:padding="10dp"
12+
android:gravity="center"
13+
android:text="@string/msg_demo_form_comb_latest"
14+
/>
15+
16+
17+
<LinearLayout
18+
android:orientation="horizontal"
19+
android:layout_height="wrap_content"
20+
android:layout_width="match_parent"
21+
>
22+
23+
<TextView
24+
android:layout_weight="1"
25+
android:layout_height="wrap_content"
26+
android:layout_width="0dp"
27+
android:padding="10dp"
28+
android:text="Enter a valid email below:"
29+
/>
30+
31+
<EditText
32+
android:id="@+id/demo_combl_email"
33+
android:layout_weight="1"
34+
android:layout_height="wrap_content"
35+
android:layout_width="0dp"
36+
android:inputType="textEmailAddress"
37+
/>
38+
</LinearLayout>
39+
40+
<LinearLayout
41+
android:orientation="horizontal"
42+
android:layout_height="wrap_content"
43+
android:layout_width="match_parent"
44+
>
45+
46+
<TextView
47+
android:layout_weight="1"
48+
android:layout_height="wrap_content"
49+
android:layout_width="0dp"
50+
android:padding="10dp"
51+
android:text="password (> than 8 chrs):"
52+
/>
53+
54+
<EditText
55+
android:id="@+id/demo_combl_password"
56+
android:layout_weight="1"
57+
android:layout_height="wrap_content"
58+
android:layout_width="0dp"
59+
/>
60+
</LinearLayout>
61+
62+
63+
<LinearLayout
64+
android:orientation="horizontal"
65+
android:layout_height="wrap_content"
66+
android:layout_width="match_parent"
67+
>
68+
69+
<TextView
70+
android:layout_weight="1"
71+
android:layout_height="wrap_content"
72+
android:layout_width="0dp"
73+
android:padding="10dp"
74+
android:text="number (between 1 &amp; 100):"
75+
/>
76+
77+
<EditText
78+
android:id="@+id/demo_combl_num"
79+
android:layout_weight="1"
80+
android:layout_height="wrap_content"
81+
android:layout_width="0dp"
82+
android:inputType="number"
83+
/>
84+
</LinearLayout>
85+
86+
<TextView
87+
android:id="@+id/btn_demo_form_valid"
88+
android:background="@color/gray"
89+
android:layout_gravity="center"
90+
android:layout_height="wrap_content"
91+
android:layout_width="wrap_content"
92+
android:layout_marginTop="10dp"
93+
android:padding="10dp"
94+
android:text="Submit"
95+
/>
96+
97+
</LinearLayout>

app/src/main/res/layout/fragment_main.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,11 @@
5656
android:layout_height="wrap_content"
5757
android:layout_width="match_parent"
5858
android:text="@string/btn_demo_rxbus"/>
59+
60+
<Button
61+
android:id="@+id/btn_demo_form_validation_combinel"
62+
android:layout_height="wrap_content"
63+
android:layout_width="match_parent"
64+
android:text="@string/btn_demo_form_validation_combinel"/>
5965
</LinearLayout>
6066
</ScrollView>

app/src/main/res/values/colors.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
<color name="red">#eb676b</color>
44
<color name="blue">#488cef</color>
55
<color name="green">#3bc4a2</color>
6+
<color name="gray">#cfcfcf</color>
67
</resources>

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<string name="btn_demo_double_binding_textview">Double binding (PublishSubject)</string>
1414
<string name="btn_demo_polling">Polling with RxJava</string>
1515
<string name="btn_demo_rxbus">Event Bus with RxJava</string>
16+
<string name="btn_demo_form_validation_combinel">Form Validation with CombineLatest</string>
1617

1718
<string name="msg_demo_concurrency_schedulers">This is a demo of how long running operations can be offloaded to a background thread. After the operation is done, we resume back on the main thread. All using RxJava! \n\n To really see this shine. Hit the button multiple times and see how the button click which is a ui operation is never blocked because the long operation only runs in the background</string>
1819
<string name="msg_demo_buffer">This is a demo of how events can be accumulated using the "buffer" operation. Tap the button below repetitively and you will notice in the logs that button taps are collected over a span of 2s and printed below.</string>
@@ -22,4 +23,6 @@
2223
<string name="msg_demo_doublebinding">Watch how the result gloriously auto-updates based on your changing inputs. Using a technique like this, you could achieve the two-way binding in Angular Js, or more efficiently use a pattern like the Presentation View Model.</string>
2324
<string name="msg_demo_polling">This is demo of polling or making a call repeatedly with RxJava. \n\nSimple polling: Notice in the logs how a network call (simulated) is repeatedly made in the background.</string>
2425
<string name="msg_demo_rxbus_1">Tap on the below button and RxBus will listen to the events</string>
26+
<string name="msg_demo_form_comb_latest">Monitor the state of multiple observables with the combineLatest operator. The submit button uses combineLatest to monitor validity of each of the 3 inputs. Only after the 3 inputs contain valid inputs will the submit button be enabled</string>
27+
2528
</resources>

0 commit comments

Comments
 (0)