Codename One - Validator的errorMessages显示有效输入(UPDATE)

时间:2018-05-09 10:00:38

标签: codenameone

---更详细的更新问题---

我正在更新此问题以添加更多详细信息。很抱歉在此问题中报告了多个错误,但我在使用以下代码时遇到了多个问题。

首先,这是我的(新)代码来测试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解锁),输入字段将重复,如此屏幕截图所示:{ {3}}。这个bug很容易复制。

感谢您的支持。

---老问题---

请注意以下屏幕截图(引用相同的代码):在Android上errorMessage的{​​{1}}显示有效输入TextComponent name { {1}}未显示有效输入,如预期的那样);在iPhoneX上,errorMessageTextComponent surname的errorMessages显示有效输入。为什么?怎么了?

当然,预期的行为是没有TextComponent name显示有效输入。

在屏幕截图下方,我复制了表格的完整代码。

真正的Android 7(在我手中)的屏幕截图: error.jpg

真正的iPhoneX iOS11(在设备场中)的屏幕截图: Android 7

完整代码:

TextComponent surname

2 个答案:

答案 0 :(得分:1)

你不应该使用验证器:

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

由于您实际上将错误消息硬编码到组件中,因此它将设置为null。您应该将消息放在验证器中:

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

答案 1 :(得分:1)

谢谢Shai。关于其他问题,我找到了这些解决方案:

  1. 不要使用PickerComponent.createDate(null).label("Birthday");,因为" null"参数导致报告的Android崩溃(请参阅问题中报告的日志)。如果我记得正确,你在Facebook克隆的课程中使用了createDate(null):只有当你不使用Validator 时它才会正常(这个问题只影响Android,而不是代码适用于iOS)
  2. 不要使用在主类的同一个Java文件中扩展Form的类(如本问题顶部的代码所示):它会在暂停和恢复后导致应用程序,所有表单内容都是重复的(我不知道为什么)。
  3. 仍有一些小问题不会影响功能,例如,在用户使已经验证的输入无效后,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
            // 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() {
        }
    
    }