【问题标题】:Codename One - errorMessages by the Validator shown with valid inputs (UPDATE)代号一 - 验证器的错误消息显示有效输入(更新)
【发布时间】:2018-05-09 10:00:37
【问题描述】:

--- 更新了更多细节的问题---

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

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

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文本也总是与有效输入一起显示。选择性别也有问题,因为如果我点击性别然后按确定(不滚动选项),则不会选择第一个选项(即“男性”)(在我尝试了两次的视频中)。

因此,相同的代码在 Android 上会导致崩溃,但在 iOS 上不会。此外,errorTexts 在两个操作系统上都显示不正确,但行为不同。

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

感谢您的支持。

---老问题---

请注意以下屏幕截图(引用相同的代码):在 Android 上,TextComponent nameerrorMessage 显示为有效输入 (而 TextComponent surnameerrorMessage 未显示为一个有效的输入,正如预期的那样);在 iPhoneX 上,TextComponent nameTextComponent 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
        // 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");
        });
    }
}

【问题讨论】:

  • 这是否只发生在设备上,而不是模拟器上?它会随着焦点的变化而更新吗?离开球场?日期不更新是bug,明天修复
  • 我明天会等待日期修复,之后我会用更多细节更新这个问题(可能还包括视频)。谢谢你!
  • 我刚刚用更多细节更新了这个问题,我添加了一个新代码,可以很容易地用来重现问题。我还添加了两个视频。
  • 我给出了答案,但我可能错过了问题的某些方面,因为它变得非常长且难以阅读

标签: codenameone


【解决方案1】:

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

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

当您有效地将错误消息硬编码到组件中时,它将被设置为 null。您应该将消息放在验证器中:

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

【讨论】:

  • 我添加了一个答案来完成你的。谢谢你。
  • 我们刚刚为选择器添加了新的轻量级模式,希望它会更合理。至少在模拟器上看起来好多了……
  • 谢谢,希望新的轻量模式能解决一些问题……不过:github.com/codenameone/CodenameOne/issues/2416
【解决方案2】:

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

  1. 不要使用PickerComponent.createDate(null).label("Birthday");,因为“null”参数导致报告的Android崩溃(参见问题中报告的日志)。如果我没记错的话,你在 Facebook 克隆的课程中使用了 createDate(null):只有不使用验证器才可以(这个问题只影响 Android,而代码在 iOS 上可以正常工作).
  2. 不要在主类的同一个 Java 文件中使用扩展 Form 的类(如本问题顶部的代码中所示):它会导致在暂停和恢复应用程序后出现这种情况,所有的Form内容都是重复的(不知道为什么)。

仍有一些不影响功能的小问题,例如验证器不会在用户使已验证的输入无效后禁用提交按钮。

最后,这是一个(足够的)工作代码:

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() {
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    相关资源
    最近更新 更多