Codenameone Codename One-验证程序显示的带有有效输入的错误消息(更新)

Codenameone Codename One-验证程序显示的带有有效输入的错误消息(更新),codenameone,Codenameone,——更新了问题并提供了更多详细信息-- 我正在更新此问题以添加更多详细信息。很抱歉在这个问题中报告了多个bug,但是我在下面的代码中遇到了多个问题 首先,这是我测试TextModeLayout的(新)代码,很容易完整复制和测试(我更改了这个问题的前一个版本的代码): 我生成了apk和ipa,并在我手中的真实设备上测试了它们(不是在设备场中)。为了更好地理解这些行为,我录制了两个视频 Android 7:正如您在视频中看到的,无论我提供了有效的输入,都会始终显示只显示姓名(但不显示姓氏)的错误消息

——更新了问题并提供了更多详细信息--

我正在更新此问题以添加更多详细信息。很抱歉在这个问题中报告了多个bug,但是我在下面的代码中遇到了多个问题

首先,这是我测试TextModeLayout的(新)代码,很容易完整复制和测试(我更改了这个问题的前一个版本的代码):

我生成了apk和ipa,并在我手中的真实设备上测试了它们(不是在设备场中)。为了更好地理解这些行为,我录制了两个视频

Android 7:正如您在视频中看到的,无论我提供了有效的输入,都会始终显示只显示姓名(但不显示姓氏)的错误消息。此外,当我用java.lang.NullPointerException点击生日选择器时,应用程序崩溃。完整日志如下:

iPhoneX(iOS 11.2.2):正如您在视频中看到的,只有当输入无效时(如我所料),字段右侧的红色十字才会正确显示,但错误消息文本也始终显示为有效输入。选择性别也有问题,因为如果我点击性别然后按OK(不滚动选项),第一个选项(即“男性”)不会被选中(在我尝试过两次的视频中)

因此,同样的代码在Android上会产生崩溃,但在iOS上不会。此外,错误文本在两个操作系统上显示不正确,但行为不同

在iOS上还有一个奇怪的错误:如果你阻塞屏幕然后解锁(尝试使用iPhone X的右侧按钮,然后使用FaceID解锁),输入字段会重复,如下图所示:。这个bug很容易被复制

谢谢你的支持

——老问题---

请注意以下屏幕截图(指的是相同的代码):在Android上,
TextComponent name
errorMessage
以有效输入显示(而
TextComponent name
errorMessage
未按预期以有效输入显示);在iPhoneX上,
TextComponent name
TextComponent name
的错误消息以有效输入显示。为什么?怎么了

当然,预期的行为是没有显示带有有效输入的
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
        // https://stackoverflow.com/questions/50249453/codename-one-validate-only-one-input-at-a-time
        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");
        });
    }
}

您不应将其与验证器一起使用:

TextComponent name = new TextComponent().labelAndHint("Name").errorMessage("Insert your name");
由于您正在有效地将错误消息硬编码到组件中,因此它将被设置为null。您应该将消息放在验证器中:

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

谢谢你,谢。关于其他问题,我找到了以下解决方案:

  • 不要使用
    PickerComponent.createDate(null).label(“生日”),因为“null”参数导致报告的Android崩溃(请参阅问题中报告的日志)。如果我没记错的话,您在Facebook克隆的课程中使用了
    createDate(null)
    :只有在不使用验证器的情况下才可以(这个问题只影响Android,而代码在iOS上工作正常)
  • 不要在主类的同一个Java文件中使用扩展表单的类(如问题顶部的代码):这会导致在暂停和恢复应用程序后,所有表单内容都被复制(我不知道为什么)
  • 还有一些不影响功能的小问题,例如,在用户使已验证的输入无效后,验证器没有禁用提交按钮

    最后,这是一个(足够)有效的代码:

    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
            // https://stackoverflow.com/questions/50250572/codename-one-errormessages-by-the-validator-shown-with-valid-inputs-update
            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() {
        }
    
    }
    

    这是否只发生在设备上,而不是模拟器上?它会随着焦点的变化而更新吗?离开球场?不更新日期是一个缺陷,明天将修复。我将等待明天的日期修复,之后我将更新此问题的更多细节(可能还包括视频)。谢谢你,谢!我刚刚用更多的细节更新了这个问题,我添加了一个新的代码,可以轻松地用于重现这些问题。我还添加了两个视频。我给出了答案,但我可能错过了问题的某些方面,因为它变得很长,很难通读。我添加了一个答案来完成你的答案。谢谢Shai。我们刚刚为picker添加了新的轻量级模式,希望这会使它更合理。至少在模拟器上看起来更好…谢谢,我希望新的轻量级模式能解决一些问题。。。然而:
    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
            // https://stackoverflow.com/questions/50250572/codename-one-errormessages-by-the-validator-shown-with-valid-inputs-update
            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() {
        }
    
    }