例如下面图片这种情况
- 有EditText、有TextView、有按钮点选
- EditText本身有输入限制包括错误提示和错误数据不支持输入、TextView或其它控件错误提示
- 以上所有条件 错误或全部正确时自动修改底部按钮状态
- 底部按钮点击时 如果有错误时提示
通常做法:
//fragment或activity添加EditText监听,并对各种条件进行判断
TextWatcher conditionInputWatcher = new NearTextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (TextUtils.isEmpty(s.toString())) {
return;
}
if (s.toString().contains(".")) {
showToast(getString(R.string.member_card_creat_only_integer));
String result = s.toString().trim().replace(".", "");
etMemberCardCreateGetPointPrice.setText(result);
etMemberCardCreateGetPointPrice.setSelection(result.length());
return;
}
presenter.checkConditionMoneyInput(s.toString());
}
};
etGetPointPrice.addTextChangedListener(conditionInputWatcher);
//presenter实现
public boolean checkConditionMoneyInput(String getPointsMoney) {
if (!isConditionMoneyValid(getPointsMoney)) {
view.showToast(formErrorMsg);
return false;
}
return true;
}
private boolean isConditionMoneyValid(String getPointsMoney) {
if (TextUtils.isEmpty(getPointsMoney) || !ValidateUtil.isInteger(getPointsMoney)) {
formErrorMsg = mContext.getString(R.string.member_card_create_condition_money_format);
return false;
}
int i = Integer.parseInt(getPointsMoney);
if (i <= 0) {
formErrorMsg = mContext.getString(R.string.member_card_create_presente_price_can_not_be_zero);
return false;
}
if (i >= 1000) {
formErrorMsg = mContext.getString(R.string.member_card_create_present_price_error_msg);
return false;
}
return true;
}
//点击完成按钮也同样需要一系列的判断
/**
* 设置完成按钮是否可以点击
*/
public boolean checkEnableBtn(String getPointsMoney, String presentName,
String presentPrice, String expireTime) {
this.getPointsMoney = getPointsMoney;
this.presentPrice = presentPrice;
this.presentName = presentName;
this.expireTime = expireTime;
if (!isConditionMoneyValid(getPointsMoney)) {
view.renderUnableBtn();
return false;
}
...
view.renderEnableBtn();
return true;
}
弊端:
- EditText各种输入判断逻辑糅合在一起,写起来相当复杂且容易出错,其它页面对相同的逻辑处理无法复用
- 底部按钮点击、其它控件点击 都需要判断所有的输入条件
- 对于错误输出 有些toast提示 有些只是底部按钮置灰或者会有其它形式
解决:
- 各种输入条件拆分并且可复用,通用逻辑处理抽离
方案一:
//各种条件判断抽离 方便逻辑复用
public class ConditionEmpty extends BaseCondition<EditText> {
public ConditionEmpty(String errorMsg) {
super(errorMsg);
}
@Override
public boolean check(EditText editText) {
return !TextUtils.isEmpty(editText.getText().toString());
}
@Override
public boolean isShowMsgTextWatcher() {
return false;
}
}
//通过给EditText添加条件方式设置EditText逻辑及错误输出文案
etMemberCardCreateGetPointPrice.addCondition(new ConditionEmpty(getString(R.string.member_card_create_condition_money_empty)))
.addCondition(new ConditionNotPoint(getString(R.string.member_card_creat_only_integer)))
.addCondition(new ConditionInteger(getString(R.string.member_card_create_condition_money_format)))
.addCondition(notZeroCondition)
.addCondition(numberCondition);
etMemberCardCreateGetPointPrice.addConditionListener(this);
//自定义EditText封装统一的判断逻辑
public class NearEditText extends EditText {
private Context context;
private ArrayList<ViewCondition> conditions;
private TextWatcher textWatcher;
private ConditionListener conditionListener;
...
private void init(Context context) {
this.context = context;
conditions = new ArrayList<>();
textWatcher = new NearTextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if (s == null) {
return;
}
String input = s.toString().trim();
checkCondition(input, TextInputSource.INPUT_SOURCE_TEXTWATCHER);
if(conditionListener != null) {
conditionListener.textChange();
}
}
};
addTextChangedListener(textWatcher);
}
/**
* 添加一个EditText判断条件
*
* @param condition 判断条件
*/
public NearEditText addCondition(ViewCondition condition) {
conditions.add(condition);
return this;
}
public void addConditionListener(ConditionListener conditionListener) {
this.conditionListener = conditionListener;
}
/**
* 判断是否满足条件
*
* @param input 当前EditText输入的内容
* @param inputSource 是否显示输入内容错误提示
* 1、正在输入 且输入不合法时 提示
* 2、点击完成按钮 且本输入内容不合法 提示
* 3、其它文本输入 且本输入内容不合法时 不提示
* @return 当前输入内容是否满足条件 true满足 false 不满足
*/
public boolean checkCondition(String input, @TextInputSource.TextInputSourceTemplate int inputSource) {
for (ViewCondition condition : conditions) {
//不满足条件
if (!condition.check(input, NearEditText.this)) {
//错误提示
if (!TextUtils.isEmpty(condition.getErrorMsg())) {
boolean showToast = false;
switch (inputSource) {
case TextInputSource.INPUT_SOURCE_TEXTWATCHER:
if (condition.isShowMsgTextWatcher()) {
showToast = true;
}
break;
case TextInputSource.INPUT_SOURCE_BUTTON:
if (condition.isShowMsgButton()) {
showToast = true;
}
break;
case TextInputSource.INPUT_SOURCE_OTHER_TEXT:
if (condition.isShowMsgOtherText()) {
showToast = true;
}
break;
}
if (showToast) {
ToastUtil.showShort(context, condition.getErrorMsg());
}
}
//反馈给前端进行其他处理
if(conditionListener != null) {
//反馈给前端进行其他处理
conditionListener.conditionError(condition);
}
return false;
}
}
return true;
}
public void removeConditionListener() {
if (textWatcher != null) {
removeTextChangedListener(textWatcher);
}
}
public interface ConditionListener {
void conditionError(ViewCondition condition);
void textChange();
}
}
//点击完成按钮的点击 改为
public boolean checkEnableBtn(NearEditText etGetPointPrice, NearEditText etPresentName,
NearEditText etPresentPrice, TextView tvExpireTime, int inputSource) {
this.getPointsMoney = getTextViewStr(etGetPointPrice);
this.presentName = getTextViewStr(etPresentName);
this.presentPrice = getTextViewStr(etPresentPrice);
this.expireTime = getTextViewStr(tvExpireTime);
if (!etGetPointPrice.checkCondition(getPointsMoney, inputSource)) {
return false;
}
...
return true;
}