Skip to content

Commit c8861f2

Browse files
committed
Added basic error handler to views.
Fixed RadioGroup validation.
1 parent e3fde91 commit c8861f2

File tree

10 files changed

+165
-46
lines changed

10 files changed

+165
-46
lines changed

app/src/main/java/ext/sample/SimpleFormFragment.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import butterknife.ButterKnife;
1818
import butterknife.OnClick;
1919
import ext.extensions.forms.Condition;
20-
import ext.extensions.forms.DateInput;
2120
import ext.extensions.forms.DateMultiInput;
2221
import ext.extensions.forms.Form;
2322
import ext.extensions.forms.Forms.FormBuilder;
@@ -54,16 +53,20 @@ public void onViewCreated(View view, Bundle savedInstanceState) {
5453
@Override
5554
public boolean verify(DateTime value) {
5655
// at least 18
57-
return value.isBefore(DateTime.now().minusYears(18));
56+
return value.isAfter(DateTime.now().minusYears(18));
5857
}
5958
}, R.string.birthday_error));
6059
builder.addViewAndInput((RadioGroup) view.findViewById(R.id.gender),
6160
new StringInput("gender").map(new Mapping<String, Gender>() {
6261
@Override
6362
public Gender bind(String value) throws ValidationException {
6463
try {
65-
return Gender.valueOf(value.toUpperCase());
66-
} catch (IllegalArgumentException e) {
64+
int id = Integer.parseInt(value);
65+
if (id == R.id.male) return Gender.MALE;
66+
if (id == R.id.female) return Gender.FEMALE;
67+
throw new ValidationException("gender", R.string.gender_error);
68+
69+
} catch (NumberFormatException e) {
6770
throw new ValidationException("gender", R.string.gender_error);
6871
}
6972
}
@@ -99,7 +102,7 @@ void submit() {
99102
User user = mForm.bind();
100103
Log.i("FORMS", "Validation passed: " + user);
101104
} catch (ValidationException e) {
102-
Log.i("FORMS", "Validation failed: " + e);
105+
Log.i("FORMS", "Validation failed: " + e.toString(getActivity()));
103106
}
104107
}
105108

lib/src/main/java/ext/drawable/AnimatedStrokeDrawable.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import android.util.TypedValue;
1515
import android.view.View;
1616

17-
import ext.L;
1817
import ext.R;
1918

2019
/**
@@ -121,13 +120,11 @@ private void startAnimation() {
121120
mAnimator.setInterpolator(mInterpolator);
122121
}
123122
mAnimator.start();
124-
L.log("startAnimation {0}", this);
125123
}
126124

127125
private void reverseAnimation() {
128126
if (mAnimator != null) {
129127
mAnimator.reverse();
130-
L.log("reverseAnimation {0}", this);
131128
}
132129
}
133130

@@ -136,7 +133,6 @@ private void stopAnimation() {
136133
mAnimator.removeAllListeners();
137134
mAnimator.removeAllUpdateListeners();
138135
mAnimator.cancel();
139-
L.log("stopAnimation");
140136
}
141137
}
142138

@@ -152,7 +148,6 @@ public void onViewDetachedToWindow(View view) {
152148

153149
@Override
154150
public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
155-
L.log("onFocusChanged {0}", focused);
156151
mFocused = focused;
157152
if (mBounds.isEmpty()) {
158153
mStartAfterBoundsSet = true;

lib/src/main/java/ext/extensions/forms/DateMultiInput.java

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
import org.joda.time.DateTime;
44
import org.joda.time.DateTimeZone;
55

6-
import java.util.Calendar;
7-
import java.util.Date;
8-
import java.util.GregorianCalendar;
9-
import java.util.Locale;
6+
import java.util.ArrayList;
7+
import java.util.List;
108
import java.util.Map;
119
import java.util.TimeZone;
1210

@@ -41,46 +39,52 @@ public DateMultiInput(String key, String dayKey, String monthKey, String yearKey
4139

4240
@Override
4341
public DateTime bind(Map<String, String> data) throws ValidationException {
44-
final int day, month, year;
42+
List<ValidationFailure> failures = new ArrayList<>();
43+
44+
int day = -1, month = -1, year = -1;
4545
try {
4646
day = Integer.parseInt(data.get(dayKey));
47+
if (day < 1) {
48+
failures.add(new ValidationFailure(dayKey, R.string.form_error_min, 1));
49+
}
4750
} catch (NumberFormatException e) {
48-
throw new ValidationException(dayKey, R.string.form_error_int);
51+
failures.add(new ValidationFailure(dayKey, R.string.form_error_int));
4952
}
5053
try {
5154
month = Integer.parseInt(data.get(monthKey));
55+
if (month < 1) {
56+
failures.add(new ValidationFailure(monthKey, R.string.form_error_min, 1));
57+
}
58+
if (month > 12) {
59+
failures.add(new ValidationFailure(monthKey, R.string.form_error_min, 12));
60+
}
5261
} catch (NumberFormatException e) {
53-
throw new ValidationException(monthKey, R.string.form_error_int);
62+
failures.add(new ValidationFailure(monthKey, R.string.form_error_int));
5463
}
5564
try {
5665
year = Integer.parseInt(data.get(yearKey));
5766
} catch (NumberFormatException e) {
58-
throw new ValidationException(yearKey, R.string.form_error_int);
59-
}
60-
if(day < 1){
61-
throw new ValidationException(dayKey, R.string.form_error_min, 1);
67+
failures.add(new ValidationFailure(yearKey, R.string.form_error_int));
6268
}
63-
if(month < 1){
64-
throw new ValidationException(monthKey, R.string.form_error_min, 1);
65-
}
66-
if(month > 12){
67-
throw new ValidationException(monthKey, R.string.form_error_min, 12);
69+
70+
if (failures.size() > 0) {
71+
throw new ValidationException(failures.toArray(new ValidationFailure[failures.size()]));
6872
}
6973

7074
DateTime cal = new DateTime(year, month, 1, 0, 0, DateTimeZone.forTimeZone(TimeZone.getDefault()));
7175
final int maxDay = cal.dayOfMonth().getMaximumValue();
7276

73-
if(day > maxDay){
77+
if (day > maxDay) {
7478
throw new ValidationException(dayKey, R.string.form_error_max, maxDay);
7579
}
7680

7781
final DateTime result = cal.withDayOfMonth(day);
7882

79-
if(result.isBefore(min))
83+
if (min != null && result.isBefore(min))
8084
throw new ValidationException(key, R.string.form_error_min, min.toString("dd/MM/yyyy"));
8185

82-
if(result.isAfter(min))
83-
throw new ValidationException(key, R.string.form_error_max, min.toString("dd/MM/yyyy"));
86+
if (max != null && result.isAfter(max))
87+
throw new ValidationException(key, R.string.form_error_max, max.toString("dd/MM/yyyy"));
8488

8589
return result;
8690
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ext.extensions.forms;
2+
3+
/**
4+
* Created by evelina on 20/09/14.
5+
*/
6+
public interface ErrorHandler {
7+
8+
void onError(ValidationFailure[] failures);
9+
}

lib/src/main/java/ext/extensions/forms/Form.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import ext.extensions.forms.Forms.ViewExtractor;
1313

14+
import static android.text.TextUtils.*;
15+
1416
/**
1517
* Created by evelina on 17/09/14.
1618
*/
@@ -29,7 +31,32 @@ public T bind() throws ValidationException {
2931
for (Entry<String, ViewPair<?>> view : views.entrySet()) {
3032
data.put(view.getKey(), view.getValue().get());
3133
}
32-
return bind(data);
34+
try {
35+
return bind(data);
36+
} catch (ValidationException e) {
37+
onValidationError(e);
38+
throw e;
39+
}
40+
}
41+
42+
private void onValidationError(ValidationException e) {
43+
Map<String, List<ValidationFailure>> failureMap = new HashMap<>();
44+
for (ValidationFailure failure : e.failures) {
45+
if (!failureMap.containsKey(failure.key)) {
46+
failureMap.put(failure.key, new ArrayList<ValidationFailure>());
47+
}
48+
failureMap.get(failure.key).add(failure);
49+
}
50+
51+
for (Entry<String, List<ValidationFailure>> entry : failureMap.entrySet()) {
52+
String key = entry.getKey();
53+
if (!isEmpty(key)) {
54+
ViewPair<?> viewPair = views.get(key);
55+
if (viewPair != null) {
56+
viewPair.onError(entry.getValue());
57+
}
58+
}
59+
}
3360
}
3461

3562
public abstract T bind(Map<String, String> data) throws ValidationException;
@@ -89,14 +116,20 @@ public T bind(Map<String, String> data) throws ValidationException {
89116

90117
class ViewPair<V extends View> {
91118
public final V view;
92-
public final ViewExtractor<V> extractor;
119+
public final ViewExtractor<? super V> extractor;
120+
public final ViewErrorHandler<? super V> errorHandler;
93121

94-
ViewPair(V view, ViewExtractor<V> extractor) {
122+
ViewPair(V view, ViewExtractor<? super V> extractor, ViewErrorHandler<? super V> errorHandler) {
95123
this.view = view;
96124
this.extractor = extractor;
125+
this.errorHandler = errorHandler;
97126
}
98127

99128
public String get() {
100129
return extractor.get(view);
101130
}
131+
132+
public void onError(List<ValidationFailure> failures) {
133+
errorHandler.onError(view, failures.toArray(new ValidationFailure[failures.size()]));
134+
}
102135
}

lib/src/main/java/ext/extensions/forms/Forms.java

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.view.View;
44
import android.widget.CompoundButton;
5+
import android.widget.EditText;
6+
import android.widget.RadioButton;
57
import android.widget.RadioGroup;
68
import android.widget.TextView;
79

@@ -10,6 +12,8 @@
1012
import java.util.Map;
1113
import java.util.Set;
1214

15+
import ext.R;
16+
1317
/**
1418
* Created by evelina on 20/09/14.
1519
*/
@@ -20,15 +24,15 @@ public static <T, V extends View> Form<T> single(final Input<T> input, ViewPair<
2024
}
2125

2226
public static <T> Form<T> single(final Input<T> input, TextView view) {
23-
return new SingleForm<>(input, new ViewPair<>(view, TEXT_VIEW_EXTRACTOR));
27+
return new SingleForm<>(input, new ViewPair<>(view, TEXT_VIEW_EXTRACTOR, TEXT_ERROR_HANDLER));
2428
}
2529

2630
public static <T> Form<T> single(final Input<T> input, CompoundButton view) {
27-
return new SingleForm<>(input, new ViewPair<>(view, COMPOUND_BUTTON_EXTRACTOR));
31+
return new SingleForm<>(input, new ViewPair<>(view, COMPOUND_BUTTON_EXTRACTOR, TEXT_ERROR_HANDLER));
2832
}
2933

3034
public static <T> Form<T> single(final Input<T> input, RadioGroup view) {
31-
return new SingleForm<>(input, new ViewPair<>(view, RADIO_GROUP_EXTRACTOR));
35+
return new SingleForm<>(input, new ViewPair<>(view, RADIO_GROUP_EXTRACTOR, RADIO_GROUP_ERROR_HANDLER));
3236
}
3337

3438
public static Form<Map<String, Object>> multiple(final Input<?>[] inputs, Map<String, ViewPair<?>> views) {
@@ -53,41 +57,50 @@ public synchronized FormBuilder addInput(Input<?> input) {
5357
return this;
5458
}
5559

56-
public synchronized <V extends View> FormBuilder addView(String key, V view, ViewExtractor<V> extractor) {
57-
mViewMap.put(key, new ViewPair<V>(view, extractor));
60+
public synchronized <V extends View> FormBuilder addView(String key, V view, ViewExtractor<? super V> extractor, ViewErrorHandler<? super V> errorHandler) {
61+
mViewMap.put(key, new ViewPair<V>(view, extractor, errorHandler));
5862
return this;
5963
}
6064

6165
public synchronized FormBuilder addView(String key, TextView view) {
62-
return addView(key, view, TEXT_VIEW_EXTRACTOR);
66+
return addView(key, view, TEXT_VIEW_EXTRACTOR, TEXT_ERROR_HANDLER);
67+
}
68+
69+
public synchronized FormBuilder addView(String key, EditText view) {
70+
return addView(key, view, TEXT_VIEW_EXTRACTOR, EDIT_TEXT_ERROR_HANDLER);
6371
}
6472

6573
public synchronized FormBuilder addView(String key, RadioGroup view) {
66-
return addView(key, view, RADIO_GROUP_EXTRACTOR);
74+
return addView(key, view, RADIO_GROUP_EXTRACTOR, RADIO_GROUP_ERROR_HANDLER);
6775
}
6876

6977
public synchronized FormBuilder addView(String key, CompoundButton view) {
70-
return addView(key, view, COMPOUND_BUTTON_EXTRACTOR);
78+
return addView(key, view, COMPOUND_BUTTON_EXTRACTOR, TEXT_ERROR_HANDLER);
7179
}
7280

73-
public synchronized <V extends View> FormBuilder addViewAndInput(V view, ViewExtractor<V> extractor, Input<?> input) {
74-
addView(input.key, view, extractor);
81+
public synchronized <V extends View> FormBuilder addViewAndInput(V view, ViewExtractor<? super V> extractor, ViewErrorHandler<? super V> errorHandler, Input<?> input) {
82+
addView(input.key, view, extractor, errorHandler);
7583
return addInput(input);
7684
}
7785

7886
public synchronized FormBuilder addViewAndInput(TextView view, Input<?> input) {
7987
addInput(input);
80-
return addView(input.key, view, TEXT_VIEW_EXTRACTOR);
88+
return addView(input.key, view);
89+
}
90+
91+
public synchronized FormBuilder addViewAndInput(EditText view, Input<?> input) {
92+
addInput(input);
93+
return addView(input.key, view);
8194
}
8295

8396
public synchronized FormBuilder addViewAndInput(RadioGroup view, Input<?> input) {
8497
addInput(input);
85-
return addView(input.key, view, RADIO_GROUP_EXTRACTOR);
98+
return addView(input.key, view);
8699
}
87100

88101
public synchronized FormBuilder addViewAndInput(CompoundButton view, Input<?> input) {
89102
addInput(input);
90-
return addView(input.key, view, COMPOUND_BUTTON_EXTRACTOR);
103+
return addView(input.key, view);
91104
}
92105

93106
public synchronized Form<Map<String, Object>> build() {
@@ -141,4 +154,27 @@ public void set(RadioGroup view, String value) {
141154
}
142155
};
143156

157+
public static ViewErrorHandler<TextView> TEXT_ERROR_HANDLER = new ViewErrorHandler<TextView>() {
158+
@Override
159+
public void onError(TextView view, ValidationFailure[] failures) {
160+
view.setTextColor(view.getContext().getResources().getColor(R.color.error));
161+
}
162+
};
163+
164+
public static ViewErrorHandler<EditText> EDIT_TEXT_ERROR_HANDLER = new ViewErrorHandler<EditText>() {
165+
@Override
166+
public void onError(EditText view, ValidationFailure[] failures) {
167+
view.setError(failures[0].getMessage(view.getContext()));
168+
}
169+
};
170+
171+
public static ViewErrorHandler<RadioGroup> RADIO_GROUP_ERROR_HANDLER = new ViewErrorHandler<RadioGroup>() {
172+
@Override
173+
public void onError(RadioGroup view, ValidationFailure[] failures) {
174+
RadioButton radio = (RadioButton) view.findViewById(view.getCheckedRadioButtonId());
175+
if (radio != null) {
176+
radio.setTextColor(view.getContext().getResources().getColor(R.color.error));
177+
}
178+
}
179+
};
144180
}

lib/src/main/java/ext/extensions/forms/ValidationException.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package ext.extensions.forms;
22

3+
import android.content.Context;
4+
35
import java.util.Arrays;
46

57
/**
@@ -16,6 +18,21 @@ public ValidationException(String key, int errorMessageId, Object... args) {
1618
this(new ValidationFailure(key, errorMessageId, args));
1719
}
1820

21+
public String toString(Context context) {
22+
StringBuilder b = new StringBuilder("ValidationException {failures=");
23+
boolean firstLine = true;
24+
for (ValidationFailure failure : failures) {
25+
if (firstLine) {
26+
firstLine = false;
27+
} else {
28+
b.append(",");
29+
}
30+
b.append(failure.toString(context));
31+
}
32+
b.append("}");
33+
return b.toString();
34+
}
35+
1936
@Override
2037
public String toString() {
2138
return "ValidationException{" +

0 commit comments

Comments
 (0)