Codename One - сообщения об ошибках от валидатора, показанные с действительными входами (ОБНОВЛЕНИЕ) - PullRequest
0 голосов
/ 09 мая 2018

--- ОБНОВЛЕННЫЙ ВОПРОС С БОЛЕЕ ДЕТАЛЯМИ ---

Я обновляю этот вопрос, чтобы добавить больше деталей. Мне жаль сообщать о множественных ошибках в этом вопросе, но я столкнулся с несколькими проблемами, связанными со следующим кодом.

Прежде всего, это мой (новый) код для тестирования 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 (в моей руке): Android 7

Снимок экрана реального iPhoneX iOS11 (в ферме устройств): iPhoneX

Полный код:

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");
        });
    }
}

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Спасибо, Шай. Что касается других вопросов, я нашел следующие решения:

  1. Не используйте PickerComponent.createDate(null).label("Birthday");, потому что аргумент "null" вызвал сообщение о сбое Android (см. Журнал, указанный в вопросе). Если я правильно помню, вы использовали createDate(null) на уроке клона Facebook: это нормально, только если вы не используете Validator (эта проблема касается только Android, вместо этого код работает на iOS) .
  2. Не используйте класс, который расширяет Form внутри того же Java-файла основного класса (как в коде вверху этого вопроса) : это вызывает, что после приостановки и восстановления приложения, все содержимое формы дублируется (я не знаю почему).

Есть еще незначительные проблемы, которые не влияют на функциональность, например, тот факт, что Validator не отключает кнопку отправки после того, как пользователь аннулирует уже проверенный ввод.

Наконец, это (достаточно) рабочий код:

import com.codename1.io.Log;
import com.codename1.ui.Button;
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 MainClass {

    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;
        }

        // More info:
        // https://www.codenameone.com/blog/pixel-perfect-text-input-part-2.html
        // /10682544/codename-one-soobscheniya-ob-oshibkah-ot-validatora-pokazannye-s-deistvitelnymi-vhodami-obnovlenie
        Form hi = new Form("Anagrafica", BoxLayout.y());
        TextModeLayout textModeLayout = new TextModeLayout(4, 1);
        Container inputPersonData = new Container(textModeLayout);

        TextComponent name = new TextComponent().labelAndHint("Name");
        TextComponent surname = new TextComponent().labelAndHint("Surname");
        PickerComponent gender = PickerComponent.createStrings("Male", "Female", "Other", "Not specified").label("Gender");
        gender.getPicker().setSelectedString("Not specified");
        PickerComponent date = PickerComponent.createDate(new Date()).label("Birthday");

        Validator validator = new Validator();
        validator.setShowErrorMessageForFocusedComponent(true);
        validator.addConstraint(name, new LengthConstraint(2, "Insert your name"));
        validator.addConstraint(surname, new LengthConstraint(2, "Insert your surname"));

        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 "Are you at least 13 years old?";
            }
        });

        inputPersonData.add(name);
        inputPersonData.add(surname);
        inputPersonData.add(gender);
        inputPersonData.add(date);

        hi.add(inputPersonData);

        Button button = new Button("Send");
        hi.add(button);
        validator.addSubmitButtons(button);

        hi.setEditOnShow(name.getField());
        hi.show();

    }

    public void stop() {
        current = getCurrentForm();
        if (current instanceof Dialog) {
            ((Dialog) current).dispose();
            current = getCurrentForm();
        }
    }

    public void destroy() {
    }

}
0 голосов
/ 14 мая 2018

Вы не должны использовать это с валидатором:

TextComponent name = new TextComponent().labelAndHint("Name").errorMessage("Insert your name");

Поскольку вы фактически жестко запрограммировали сообщение об ошибке в компонент, и оно будет установлено в нуль.Вместо этого вы должны поместить сообщение в валидатор:

validator.addConstraint(name, new LengthConstraint(2, "Insert your name"));
...