0

我正在使用以下代码编写贷款计算器,但是当我尝试验证用户的输入时,我无法让 setText 运行。我没有收到任何编译错误,它只是不起作用。我有以下代码:

public class LoanCalculator extends JFrame {
  // Create text fields for interest rate,
// year, loan amount, monthly payment, and total payment
private JTextField jtfAnnualInterestRate = new JTextField("5");
private JTextField jtfNumberOfYears = new JTextField("10");
private JTextField jtfLoanAmount = new JTextField("10000");
private JTextField jtfMonthlyPayment = new JTextField("106.07");
private JTextField jtfTotalPayment = new JTextField("12727.86");

// Create a Compute Payment button
private JButton jbtClear = new JButton("Reset Fields");

public LoanCalculator() {
// Panel p1 to hold labels and text fields
JPanel p1 = new JPanel(new GridLayout(5, 2));
p1.add(new JLabel("Annual Interest Rate"));
p1.add(jtfAnnualInterestRate);
p1.add(new JLabel("Number of Years"));
p1.add(jtfNumberOfYears);
p1.add(new JLabel("Loan Amount"));
p1.add(jtfLoanAmount);
p1.add(new JLabel("Monthly Payment"));
p1.add(jtfMonthlyPayment);
p1.add(new JLabel("Total Payment"));
p1.add(jtfTotalPayment);
p1.setBorder(new
TitledBorder("Enter loan amount, interest rate, and year"));
jtfTotalPayment.setEditable(false);
jtfMonthlyPayment.setEditable(false);

// Panel p2 to hold the button
JPanel p2 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p2.add(jbtClear);

// Add the panels to the frame
add(p1, BorderLayout.CENTER);
add(p2, BorderLayout.SOUTH);

// Register listeners
jbtClear.addActionListener(new ButtonListener());
jtfAnnualInterestRate.getDocument().addDocumentListener(new DocListener());
jtfNumberOfYears.getDocument().addDocumentListener(new DocListener());
jtfLoanAmount.getDocument().addDocumentListener(new DocListener());
}

/** Handle textfield changes */
class DocListener implements DocumentListener {
 public void insertUpdate(DocumentEvent e) { validate(); }
 public void removeUpdate(DocumentEvent e) { }
 public void changedUpdate(DocumentEvent e) { }

 public void validate() {
     //get values from text fields
     double interest = Double.parseDouble(jtfAnnualInterestRate.getText());
 int year = Integer.parseInt(jtfNumberOfYears.getText());
 double loanAmt = Double.parseDouble(jtfLoanAmount.getText());
 Loan loan = new Loan(interest, year, loanAmt);


 //validate field values and shift focus if needed
 if (! (interest >= 1 && interest <= 10)){
     JOptionPane.showMessageDialog(null, 
        "interest must be between 1 and 10 percent");
     jtfAnnualInterestRate.setText("5");                   //THIS DOESN'T RUN
     jtfAnnualInterestRate.requestFocus(true);
 }       
 //display monthly and total payments
 else {
      jtfMonthlyPayment.setText(String.format("%.2f", 
                          loan.getMonthlyPayment()));
      jtfTotalPayment.setText(String.format("%.2f", 
                  loan.getTotalPayment()));
 }
 }
}

/** Handle the Reset button Code not included */
}

public static void main(String[] args) {
 LoanCalculator frame = new LoanCalculator();
 frame.pack();
 frame.setTitle("LoanCalculator");
 Dimension screenSize =
   Toolkit.getDefaultToolkit().getScreenSize();
 int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
// Locate the upper-left corner at (x, y)
int x =  3 * (screenWidth - frame.getWidth()) / 4;
int y = (screenHeight - frame.getHeight()) /2;
frame.setLocation(x, y);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

知道我哪里出错了吗?

4

1 回答 1

3

您不能在其自己的 DocumentListener 中对 JTextComponent 进行变异,至少不能直接变异。我知道两种可能的解决方案:

1) 将文本更改排队到 Runnable 内的 EDT:

if (!(interest >= 1 && interest <= 10)) {
   JOptionPane.showMessageDialog(null, "interest must be between 1 and 10 percent");
   SwingUtilities.invokeLater(new Runnable() {
      public void run() {
         jtfAnnualInterestRate.setText("5"); // THIS DOESN'T RUN
         jtfAnnualInterestRate.requestFocus(true);
      }
   });

2)使用文档过滤器



关于您的问题的 编辑说明:

它只是不工作。我有以下代码:

它所做的不仅仅是“不工作”。它实际上抛出了一个重要的异常,它准确地描述了你的代码做错了什么以及在哪里

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Attempt to mutate in notification
    at javax.swing.text.AbstractDocument.writeLock(Unknown Source)
    at javax.swing.text.AbstractDocument.replace(Unknown Source)
    at javax.swing.text.JTextComponent.setText(Unknown Source)
    at pkg.LoanCalculator$DocListener.validate(LoanCalculator.java:78) // your numbers will be different
    at pkg.LoanCalculator$DocListener.insertUpdate(LoanCalculator.java:58) // ditto

下次您问类似的问题时,您可能希望将信息异常堆栈跟踪与您的问题一起发布。它将极大地帮助我们。


编辑 2
同样,下次尝试仅发布编译所需且与您的问题相关的代码。例如,您只需要发布您的 JTextField、您的 DocumentListener 并将其显示在一个简单的 JOptionPane 中。IE,

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class LoanCalcSscce {
   protected static final double MIN_INTEREST = 0;
   protected static final double MAX_INTEREST = 10;

   public static void main(String[] args) {
      final JTextField jtfAnnualInterestRate = new JTextField("5");
      jtfAnnualInterestRate.getDocument().addDocumentListener(
            new DocumentListener() {

               @Override
               public void removeUpdate(DocumentEvent e) {
                  validate(e);
               }

               @Override
               public void insertUpdate(DocumentEvent e) {
                  validate(e);
               }

               @Override
               public void changedUpdate(DocumentEvent e) {
                  validate(e);
               }

               public void validate(DocumentEvent e) {
                  final Document doc = e.getDocument();
                  try {
                     String text = doc.getText(0, doc.getLength()).trim();
                     double interest = Double.parseDouble(text);

                     if (!(interest >= MIN_INTEREST && interest <= MAX_INTEREST)) {
                        JOptionPane.showMessageDialog(null,
                              "interest must be between 1 and 10 percent");
                        SwingUtilities.invokeLater(new Runnable() {
                           public void run() {
                              jtfAnnualInterestRate.setText("5");
                              jtfAnnualInterestRate.requestFocus(true);
                           }
                        });
                     }
                  } catch (BadLocationException e1) {
                     e1.printStackTrace();
                  } catch (NumberFormatException nfe) {
                     // inform user and set interest to baseline.
                  }
               }
            });
      JOptionPane.showMessageDialog(null, jtfAnnualInterestRate);
   }
}

我们可以更轻松地分析您的代码并帮助您解决问题。这称为sscce


编辑 3
为了我的钱,我不会在 DocumentListener 中验证我的输入,因为它可能会在输入尚未完成时尝试验证。当用户决定通过按下提交(或任何你称之为的)按钮提交数据时,我会进行所有验证。换句话说,我会在 ActinoListener 中进行验证。例外:如果输入只能是数字,或者只能是小写字母,或者其他可以验证部分输入的内容,我会在 DocumentFilter 中验证。


编辑 4
正如 MadProgrammer 在评论中建议的那样,还认真考虑使用 InputVerifier 作为一种在用户离开输入字段(此处为 JTextField)时验证输入的好方法:InputVerifier 教程

于 2013-09-07T20:01:10.970 回答