Страницы

Поиск по вопросам

четверг, 18 октября 2018 г.

Действие на потерю фокуса в `EditText`

У меня есть несколько EditText,и мне нужно проверять на валидность когда фокус теряется.
Вот сам код:
public class ExpandableListAdapter extends BaseExpandableListAdapter {
String[] countries; private Context _context; private List _listDataHeader; private HashMap> _listDataChild; String[] data; public static EditText etBookingName; public static EditText etBookingLastname; public static EditText etBookingBirthday; public static AutoCompleteTextView etBookingNationality; public static Spinner spinner; public static EditText etBookingPassport; public static EditText etExpiryDate;
public ExpandableListAdapter(Context context, List listDataHeader, HashMap> listChildData) { this._context = context; this._listDataHeader = listDataHeader; this._listDataChild = listChildData; }
@Override public Object getChild(int groupPosition, int childPosititon) { return this._listDataChild.get(this._listDataHeader.get(groupPosition)) .get(childPosititon); }
@Override public long getChildId(int groupPosition, int childPosition) { return childPosition; }
@Override public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) { LayoutInflater infalInflater = (LayoutInflater) this._context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = infalInflater.inflate(R.layout.booking_list_items, null); convertView.findViewById(R.id.etBookingNationality); etBookingName = (EditText) convertView.findViewById(R.id.etBookingName); etBookingLastname = (EditText) convertView.findViewById(R.id.etBookingLastname); etBookingBirthday = (EditText) convertView.findViewById(R.id.etBookingBirthday); etBookingPassport = (EditText) convertView.findViewById(R.id.etBookingPassport); rgBookingGender = (RadioGroup) convertView.findViewById(R.id.rgBookingGender); etExpiryDate = (EditText) convertView.findViewById(R.id.etDocumentExpiryDate);
etBookingNationality.setAdapter(nationAdapter); etBookingBirthday.addTextChangedListener(Mask.insert("##.##.####", etBookingBirthday)); etExpiryDate.addTextChangedListener(Mask.insert("##.##.####", etExpiryDate));
} });
@Override public void onNothingSelected(AdapterView parent) {
} });
etBookingName.setOnFocusChangeListener(validate); etBookingLastname.setOnFocusChangeListener(validate); etBookingBirthday.setOnFocusChangeListener(validate); etBookingPassport.setOnFocusChangeListener(validate); etExpiryDate.setOnFocusChangeListener(validate); etBookingNationality.setOnFocusChangeListener(validate); }
return convertView; }

View.OnFocusChangeListener validate = new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) {
if(v == etBookingName && !hasFocus) { validation_name(etBookingName); } else if(v == etBookingLastname && !hasFocus) { validation_last_name(etBookingLastname); } else if (v == etBookingBirthday && !hasFocus) { validation_birthday(etBookingBirthday); } else if (v == etBookingPassport && !hasFocus) { validation_passport(etBookingPassport); } else if (v == etExpiryDate && !hasFocus) { validation_expiry(etExpiryDate); } else if(v == etBookingNationality && !hasFocus) { validation_nation(etBookingNationality); } } };
А Валидации типа этого:
static void validation_name(EditText edit) throws NumberFormatException { String name = edit.getText().toString(); if (name.length() == 0) { edit.setError("Введите имя"); } else if (!name.matches("[a-zA-Z ]+")) { edit.setError("Введите имя латинскими буквами"); } } static void validation_last_name(EditText edit) throws NumberFormatException { String lastName = edit.getText().toString(); if (lastName.length() == 0) ........
Не работает, точнее валидация срабатывает когда просто кликаешь на EditText, а мне нужно чтобы валидация срабатывала когда фокус теряется!
Вопрос: Что я пропустил?


Ответ

UPD_1 (дабы не засорять ответ лишней информацией удалил все не относящееся к решению проблемы)
После некоторых экспериментов выяснилось, что при клике на EditText находящийся внутри группы в ExpandableListView фокус скачет несколько раз подряд, то появляется, то пропадает. Внутри других компонентов такого поведения замечено не было.
У меня не получилось решить эту проблему стандартными средствами андроид, поэтому я написал небольшой костыль, работающий как часы и решающий данную проблему.
На словах решение выглядит так:
Проинициализировать все части компонента в конструкторе адаптера, в переопределенных методах из BaseExpandableListAdapter (таких как getGroupView, getChildView и т.п.) возвращать проинициализированные ранее части компонента При изменении фокуса на 200 миллисекунд отписываться от события смены фокуса, потом опять подписываться
Вот полностью работоспособный пример с двумя группами:
MainActivity:
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
ExpandableListView listView = (ExpandableListView)findViewById(R.id.listView); listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); ArrayList groups = new ArrayList<>(); groups.add("Group 1"); groups.add("Group 2"); Adapter adapter = new Adapter(this, groups); listView.setAdapter(adapter); } }
Adapter:
public class Adapter extends BaseExpandableListAdapter {
private final Context mContext;
private ArrayList groups = new ArrayList<>(); private ArrayList views = new ArrayList<>(); private ArrayList nameEditTexts = new ArrayList<>(); private ArrayList lastNameEditTexts = new ArrayList<>(); private ArrayList birthdayEditTexts = new ArrayList<>(); private ArrayList passportEditTexts = new ArrayList<>();
public Adapter(Context context, ArrayList groups) { mContext = context; for (int i = 0; i < groups.size(); i++) { initGroup(groups.get(i)); } addOnFocusChangeListeners(onFocusChangeListener); }
private void initGroup(String groupTitle) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup group = (ViewGroup) inflater.inflate(R.layout.group, null); TextView textGroup = (TextView) group.findViewById(R.id.textGroup); textGroup.setText(groupTitle); groups.add(group); initGroupContent(); }
private void initGroupContent() { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View groupContent = inflater.inflate(R.layout.item, null); views.add(groupContent);
nameEditTexts.add((EditText) groupContent.findViewById(R.id.nameEditText)); lastNameEditTexts.add((EditText) groupContent.findViewById(R.id.lastNameEditText)); birthdayEditTexts.add((EditText) groupContent.findViewById(R.id.birthdayEditText)); passportEditTexts.add((EditText) groupContent.findViewById(R.id.passportEditText)); }
private void addOnFocusChangeListeners(View.OnFocusChangeListener listener) { for (int i = 0; i < groups.size(); i++) { nameEditTexts.get(i).setOnFocusChangeListener(listener); lastNameEditTexts.get(i).setOnFocusChangeListener(listener); birthdayEditTexts.get(i).setOnFocusChangeListener(listener); passportEditTexts.get(i).setOnFocusChangeListener(listener); } }
View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { addOnFocusChangeListeners(null); startTimer(); return; } EditText edit = (EditText) v; if (hasEditText(nameEditTexts, v)) validateName(edit); else if (hasEditText(lastNameEditTexts, v)) validateLastName(edit); else if (hasEditText(birthdayEditTexts, v)) validateBirthday(edit); else if (hasEditText(passportEditTexts, v)) validatePassport(edit); } };
private boolean hasEditText(ArrayList list, View v) { for (int i = 0; i < list.size(); i++) { if (list.get(i) == v) return true; } return false; }
private void startTimer() { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { addOnFocusChangeListeners(onFocusChangeListener); } }, 200); }
static void validateName(EditText edit) { String text = edit.getText().toString(); if (text.length() == 0) edit.setError("Name error text"); else edit.setError(null); }
static void validateLastName(EditText edit) { String text = edit.getText().toString(); if (text.length() == 0) edit.setError("LastName error text"); else edit.setError(null); }
static void validateBirthday(EditText edit) { String text = edit.getText().toString(); if (text.length() == 0) edit.setError("Birthday error text"); else edit.setError(null); }
static void validatePassport(EditText edit) { String text = edit.getText().toString(); if (text.length() == 0) edit.setError("Passport error text"); else edit.setError(null); }
@Override public void onGroupCollapsed(int groupPosition) { nameEditTexts.get(groupPosition).setError(null); lastNameEditTexts.get(groupPosition).setError(null); birthdayEditTexts.get(groupPosition).setError(null); passportEditTexts.get(groupPosition).setError(null); nameEditTexts.get(groupPosition).setText(""); lastNameEditTexts.get(groupPosition).setText(""); birthdayEditTexts.get(groupPosition).setText(""); passportEditTexts.get(groupPosition).setText(""); }
@Override public void onGroupExpanded(int groupPosition) { nameEditTexts.get(groupPosition).setError(null); lastNameEditTexts.get(groupPosition).setError(null); birthdayEditTexts.get(groupPosition).setError(null); passportEditTexts.get(groupPosition).setError(null); }
@Override public int getGroupCount() { return groups.size(); }
@Override public int getChildrenCount(int groupPosition) { return 1; }
@Override public Object getGroup(int groupPosition) { return groups.get(groupPosition); }
@Override public Object getChild(int groupPosition, int childPosition) { return views.get(groupPosition); }
@Override public long getGroupId(int groupPosition) { return 1; }
@Override public long getChildId(int groupPosition, int childPosition) { return 1; }
@Override public boolean hasStableIds() { return true; }
@Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { return groups.get(groupPosition); }
@Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { return views.get(groupPosition); }
@Override public boolean isChildSelectable(int groupPosition, int childPosition) { return false; } }
group.xml:


item.xml:







Примечание: в классе Adapter в функциях onGroupCollapsed и onGroupExpanded я добавил очистку полей, чтобы при следующем открытии они были пустыми и не провалидированными, этого можно и не делать, в зависимости от необходимого поведения.
Остальное должно быть понятно из названий функций. Если возникнут вопросы, отвечу на них в комментариях к ответу.

Комментариев нет:

Отправить комментарий