--- ОБНОВЛЕННЫЙ ВОПРОС С БОЛЕЕ ДЕТАЛЯМИ ---
Я обновляю этот вопрос, чтобы добавить больше деталей. Мне жаль сообщать о множественных ошибках в этом вопросе, но я столкнулся с несколькими проблемами, связанными со следующим кодом.
Прежде всего, это мой (новый) код для тестирования TextModeLayout, который легко копировать и тестировать (я изменил код из предыдущей версии этого вопроса) :
package ...;
import com.codename1.io.Log;
import static com.codename1.ui.CN.addNetworkErrorListener;
import static com.codename1.ui.CN.getCurrentForm;
import static com.codename1.ui.CN.updateNetworkThreadCount;
import com.codename1.ui.Container;
import com.codename1.ui.Dialog;
import com.codename1.ui.Form;
import com.codename1.ui.PickerComponent;
import com.codename1.ui.TextComponent;
import com.codename1.ui.Toolbar;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.TextModeLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.ui.validation.Constraint;
import com.codename1.ui.validation.LengthConstraint;
import com.codename1.ui.validation.Validator;
import java.util.Date;
import java.util.Calendar;
public class MyApplication {
private Form current;
private Resources theme;
public void init(Object context) {
// use two network threads instead of one
updateNetworkThreadCount(2);
theme = UIManager.initFirstTheme("/theme");
// Enable Toolbar on all Forms by default
Toolbar.setGlobalToolbar(true);
// Pro only feature
Log.bindCrashProtection(true);
addNetworkErrorListener(err -> {
// prevent the event from propagating
err.consume();
if (err.getError() != null) {
Log.e(err.getError());
}
Log.sendLogAsync();
Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
});
}
public void start() {
if (current != null) {
current.show();
return;
}
MyForm test = new MyForm();
test.show();
}
public void stop() {
current = getCurrentForm();
if (current instanceof Dialog) {
((Dialog) current).dispose();
current = getCurrentForm();
}
}
public void destroy() {
}
}
class MyForm extends Form {
public MyForm() {
super("Anagrafica", BoxLayout.y());
}
/**
* Creates and shows the Registry Form
*/
@Override
public void show() {
// More info: https://www.codenameone.com/blog/pixel-perfect-text-input-part-2.html
TextModeLayout textModeLayout = new TextModeLayout(4, 1);
Container inputPersonData = new Container(textModeLayout);
TextComponent name = new TextComponent().labelAndHint("Name").errorMessage("Insert your name");
TextComponent surname = new TextComponent().labelAndHint("Surname").errorMessage("Insert your surname");
PickerComponent gender = PickerComponent.createStrings("Male", "Female", "Other", "Not specified").label("Gender").errorMessage("Please choose an option");
PickerComponent date = PickerComponent.createDate(null).label("Birthday").errorMessage("Are you at least 13 years old?");
Validator validator = new Validator();
validator.setShowErrorMessageForFocusedComponent(false);
validator.addConstraint(name, new LengthConstraint(2));
validator.addConstraint(surname, new LengthConstraint(2));
validator.addConstraint(gender, new Constraint() {
@Override
public boolean isValid(Object value) {
boolean res = false;
if (value != null && value instanceof String) {
res = true;
}
return res;
}
@Override
public String getDefaultFailMessage() {
return "Please choose an option";
}
});
validator.addConstraint(date, new Constraint() {
@Override
public boolean isValid(Object value) {
boolean res = false;
if (value != null && value instanceof Date) {
Calendar birthday = Calendar.getInstance();
birthday.setTime((Date) value);
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.YEAR, -13);
if (birthday.before(calendar)) {
res = true;
}
}
return res;
}
@Override
public String getDefaultFailMessage() {
return "You must be at least 13 years old";
}
});
inputPersonData.add(name);
inputPersonData.add(surname);
inputPersonData.add(gender);
inputPersonData.add(date);
add(inputPersonData);
//setEditOnShow(name.getField());
super.show();
Log.p("Registry Form shown correctly");
}
}
Я сгенерировал apk и ipa и протестировал их на реальных устройствах в моей руке (не в ферме устройств). Чтобы лучше понять поведение, я записал два видео.
Android 7: как вы можете видеть на видео Android.mp4 , сообщение об ошибке только имени (но не фамилии) всегда отображается, несмотря на то, что я предоставил правильный ввод. Кроме того, приложение вылетает, когда я нажимаю на кнопку выбора дня рождения с java.lang.NullPointerException. Полный журнал находится здесь: android_date_picker_crash.txt
iPhoneX (iOS 11.2.2): как вы можете видеть на видео iPhone.mp4 , красные крестики справа от полей корректно отображаются только в том случае, если ввод недействителен (как я ожидается), но тексты errorMessages всегда отображаются также с допустимыми данными. Существует также проблема с выбором пола, потому что, если я коснусь пола, а затем нажму OK (без прокрутки параметров), первый вариант («мужской») не будет выбран (в видео, которое я пробовал два раза).
Таким образом, тот же код вызывает сбой на Android, но не на iOS Более того, тексты errorTexts отображаются некорректно в обеих ОС, но с разным поведением.
В iOS также есть странная ошибка: если вы заблокируете экран, а затем разблокируете его (попытавшись использовать правую боковую кнопку iPhone X, а затем разблокируйте, используя FaceID), поля ввода дублируются, как на этом скриншоте: error.jpg . Эта ошибка может быть легко воспроизведена.
Спасибо за поддержку.
--- СТАРЫЙ ВОПРОС ---
Обратите внимание на следующие снимки экрана (относящиеся к тому же коду): на Android errorMessage
из TextComponent name
отображается с допустимым вводом (в то время как errorMessage
из TextComponent surname
не отображается с допустимый ввод, как и ожидалось) ; на iPhoneX сообщения об ошибках TextComponent name
и TextComponent surname
отображаются с правильным вводом. Зачем? В чем дело?
Конечно, ожидаемое поведение таково, что errorMessage
не отображается с допустимым вводом.
Под скриншотами я копирую полный код формы.
Скриншот настоящего Android 7 (в моей руке):
Снимок экрана реального iPhoneX iOS11 (в ферме устройств):
Полный код:
import com.codename1.io.Log;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.PickerComponent;
import com.codename1.ui.TextComponent;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.TextModeLayout;
import com.codename1.ui.validation.Constraint;
import com.codename1.ui.validation.LengthConstraint;
import com.codename1.ui.validation.Validator;
import static com.myproject.registration.Registrazione.getArrowBack;
import java.util.Date;
public class Registry extends Form {
public Registry() {
super("Anagrafica", BoxLayout.y());
}
/**
* Creates and shows the Registry Form
*
* @param backForm the previous Form to show tapping the back arrow
*/
public void show(Form backForm) {
// Back command (linked to the left arrow in the toolbar and to back hardware button)
setBackCommand(getToolbar().addCommandToLeftBar("", getArrowBack(), (e) -> {
Log.p("Invoked setBackCommand to go to the previous form");
Display.getInstance().callSerially(() -> {
backForm.showBack();
Log.p("Previous form shown correctly");
});
}
));
// More info: https://www.codenameone.com/blog/pixel-perfect-text-input-part-2.html
TextModeLayout textModeLayout = new TextModeLayout(4, 1);
Container inputPersonData = new Container(textModeLayout);
TextComponent name = new TextComponent().label("Nome").errorMessage("Inserisci il tuo nome");
TextComponent surname = new TextComponent().label("Cognome").errorMessage("Inserisci il tuo cognome");
PickerComponent gender = PickerComponent.createStrings("Maschio", "Femmina", "altro").label("Genere");
pickerComponentSetUnselectedText(gender, "Seleziona genere");
PickerComponent date = PickerComponent.createDate(new Date()).label("Data di nascita").errorMessage("Hai almeno 13 anni?");
pickerComponentSetUnselectedText(date, "Seleziona data");
Validator validator = new Validator();
// to make better
// Codename One - Validate only one input at a time
// /10679420/codename-one-proveryat-tolko-odin-vvod-za-raz
validator.setShowErrorMessageForFocusedComponent(false);
validator.addConstraint(name, new LengthConstraint(2));
validator.addConstraint(surname, new LengthConstraint(2));
validator.addConstraint(date, new Constraint() {
@Override
public boolean isValid(Object value) {
boolean res = false;
// to do
// Codename One - addConstraint to PickerComponent date
// https://stackoverflow.com/questions/50249148/codename-one-addconstraint-to-pickercomponent-date
return res;
}
@Override
public String getDefaultFailMessage() {
return "You must be at least 13 years old";
}
});
inputPersonData.add(name);
inputPersonData.add(surname);
inputPersonData.add(gender);
inputPersonData.add(date);
add(inputPersonData);
setEditOnShow(name.getField());
super.show();
Log.p("Registry Form shown correctly");
}
/**
* Set a custom text for an unselected PickerComponent placed in a
* TextModeLayout
*
* @param picker
* @param text
*/
private void pickerComponentSetUnselectedText(PickerComponent picker, String text) {
if (picker.isOnTopMode()) {
picker.getPicker().setText(text);
} else {
picker.getPicker().setText("");
}
picker.getPicker().setUIID("TextHint");
picker.getPicker().addActionListener(l -> {
l.getComponent().setUIID("TextField");
});
}
}