【问题标题】:Referencing from an inner class从内部类引用
【发布时间】:2015-09-03 15:50:12
【问题描述】:

我有以下代码

import javax.swing.*;
import java.awt.*;
import net.miginfocom.swing.MigLayout;
import Sorts.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.awt.Color;

public class SortsGui
{
    JFrame myMainWindow = new JFrame("Sorts");

    JPanel sortPanel = new JPanel();

    //first panel components
    public int nextTextBox = 20;
    JTextField[] allField = new JTextField [25];
    //end first panel

    public void runGUI()
    {
        myMainWindow.setBounds(10, 10, 800, 800);
        myMainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        myMainWindow.setLayout(new GridLayout(1,1));

        createSortTestPanel();

        myMainWindow.getContentPane().add(sortPanel);

        myMainWindow.setVisible(true);
    }

    public void createSortTestPanel()
    {
        MigLayout layout = new MigLayout("" , "[grow]");
        sortPanel.setLayout(layout);

        refreshScreen();
    }

    public void refreshScreen()
    {
        sortPanel.removeAll();

        for(int i = 0; i<nextTextBox;i++)
        {
            int fix = i+1;
            allField[i] = new JTextField("");
            sortPanel.add(allField[i],"growx");
            allField[i].addKeyListener(new KeyListener ()
            {
                public void keyPressed(KeyEvent e)
                {

                }

                public void keyReleased(KeyEvent e)
                {

                }

                public void keyTyped(KeyEvent e)
                {
                    char c = e.getKeyChar();
                    if(Character.isDigit(c))
                    {

                    }

                    else 
                    {
                        e.consume();
                    }

                    try
                    {
                        int parseTest = Integer.parseInt(allField[i].getText());
                    }

                    catch(Exception exc)
                    {
                        allField[i].setBackground(Color.RED);
                    }
                }
            });
        }
    }

    public static void main(String[] args)
    {
        SortsGui sG = new SortsGui();
        sG.runGUI();
    }
}

我的目标是创建一个 JTextField 数组,其中包含一个 keylistener。这个 keylistener 应该防止在 JTextField 中输入除数字以外的任何内容。如果输入的数字不是 int,它还应该更改 JTextField 的背景颜色。例如 2147483647554。 但是,当我编译这个时,我得到了错误

那么我该如何做到这一点,使其在所有 JTextField 上都是最终的或有效地最终的?

【问题讨论】:

  • “我的目标是创建一个 JTextField 数组,其中包含一个 keylistener。这个 keylistener 应该防止在 JTextField 中输入除数字以外的任何内容” - 然后使用DocumentFilter 而不是
  • *"如果输入的数字不是 int,它还应该改变 JTextField 的背景颜色" - 然后使用 InputVerifier
  • 您可以通过添加变量 final int index = i; 来修复错误。在你的循环中并引用它而不是 i
  • 您也可以使用e.getSource(),而不是直接使用allField[i]。这样,您也可以为所有 JTextField 重用相同的侦听器对象。

标签: java arrays swing keylistener miglayout


【解决方案1】:

我的目标是创建一个 JTextField 数组,其中包含一个 keylistener。这个 keylistener 应该防止在 JTextField 中输入除数字以外的任何内容

对此的简短回答是,不要使用KeyListener,它不会捕获用户将文本粘贴到字段中或以编程方式更新字段的用例

例如,您想使用DocumentFilter

public class IntFilter extends DocumentFilter {

    @Override
    public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {

        StringBuilder buffer = new StringBuilder(text.length());
        for (int index = 0; index < text.length(); index++) {
            if (Character.isDigit(text.charAt(index))) {
                buffer.append(text.charAt(index));
            }
        }
        super.insertString(fb, offset, buffer.toString(), attr);
        ValidationListener listener = getValidationListener();
    }

    @Override
    public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String string, AttributeSet attr) throws BadLocationException {
        if (length > 0) {
            fb.remove(offset, length);
        }
        insertString(fb, offset, string, attr);
    }
}

更多详情请参见Implementing a Document Filter,更多示例请参见DocumentFilter Examples

如果输入的数字不是 int,它还应该改变 JTextField 的背景颜色

您可以使用InputVerifier 进行后验证,但这可能无法满足您的需求。

这会产生问题。 DocumentFilter,不应该关心它应用到的字段,但是因为它正在做验证,它会知道什么时候出了问题,所以我们需要一些方法让过滤器在验证失败时提供通知。 .

首先,我们需要一些回调来告诉我们验证失败或通过的时间...

public interface ValidationListener {

    public void validationFailed();

    public void validationPassed();
}

然后我们需要更新过滤器以根据其规则引发这些通知...

public class IntFilter extends DocumentFilter {

    private ValidationListener validationListener;

    public void setValidationListener(ValidationListener validationListener) {
        this.validationListener = validationListener;
    }

    public ValidationListener getValidationListener() {
        return validationListener;
    }

    @Override
    public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {

        boolean validationFailed = false;
        StringBuilder buffer = new StringBuilder(text.length());
        for (int index = 0; index < text.length(); index++) {
            if (Character.isDigit(text.charAt(index))) {
                buffer.append(text.charAt(index));
            } else {
                validationFailed = true;
            }
        }
        super.insertString(fb, offset, buffer.toString(), attr);
        ValidationListener listener = getValidationListener();
        if (listener != null) {
            if (validationFailed) {
                listener.validationFailed();
            } else {
                listener.validationPassed();
            }
        }
    }

    @Override
    public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String string, AttributeSet attr) throws BadLocationException {
        if (length > 0) {
            fb.remove(offset, length);
        }
        insertString(fb, offset, string, attr);
    }
}

然后我们需要定义 ValidationListener 的实现来执行我们需要的操作...

public class DefaultValidationHandler implements ValidationListener {

    private JTextField field;

    public DefaultValidationHandler(JTextField field) {
        this.field = field;
    }

    public JTextField getField() {
        return field;
    }

    @Override
    public void validationFailed() {
        getField().setBackground(Color.RED);
    }

    @Override
    public void validationPassed() {
        getField().setBackground(UIManager.getColor("TextField.background"));
    }

}

这里,监听器维护了对我们要控制的字段的引用

然后我们将它完全绑定...

JTextField field = new JTextField(10);
DefaultValidationHandler handler = new DefaultValidationHandler(field);
IntFilter filter = new IntFilter();
filter.setValidationListener(handler);
((AbstractDocument)field.getDocument()).setDocumentFilter(filter);

这有点粗略,但基本概念已经理解了。

一些改进可能包括通过ValidationListener 的方法将DocumentFilter 的引用传回,然后您可以使用它来查找触发事件的字段并更新它,从而减少您可能需要的处理程序数量例如,创建。

例如

更新了 ValidationListener

public interface ValidationListener {

    public void validationFailed(DocumentFilter filter);

    public void validationPassed(DocumentFilter filter);
}

更新了 IntFilter

public class IntFilter extends DocumentFilter {

    private ValidationListener validationListener;

    public void setValidationListener(ValidationListener validationListener) {
        this.validationListener = validationListener;
    }

    public ValidationListener getValidationListener() {
        return validationListener;
    }

    @Override
    public void insertString(DocumentFilter.FilterBypass fb, int offset, String text, AttributeSet attr) throws BadLocationException {

        boolean validationFailed = false;
        StringBuilder buffer = new StringBuilder(text.length());
        for (int index = 0; index < text.length(); index++) {
            if (Character.isDigit(text.charAt(index))) {
                buffer.append(text.charAt(index));
            } else {
                validationFailed = true;
            }
        }
        super.insertString(fb, offset, buffer.toString(), attr);
        ValidationListener listener = getValidationListener();
        if (listener != null) {
            if (validationFailed) {
                listener.validationFailed(this);
            } else {
                listener.validationPassed(this);
            }
        }
    }

    @Override
    public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String string, AttributeSet attr) throws BadLocationException {
        if (length > 0) {
            fb.remove(offset, length);
        }
        insertString(fb, offset, string, attr);
    }
}

示例实现...

public class TestPane extends JPanel {

    private Map<DocumentFilter, JTextField> fields;

    public TestPane() {

        fields = new HashMap<>(25);
        ValidationListener listener = new ValidationListener() {

            @Override
            public void validationFailed(DocumentFilter filter) {
                JTextField field = fields.get(filter);
                if (field != null) {
                    field.setBackground(Color.RED);
                }
            }

            @Override
            public void validationPassed(DocumentFilter filter) {
                JTextField field = fields.get(filter);
                if (field != null) {
                    field.setBackground(UIManager.getColor("TextField.background"));
                }
            }
        };

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        for (int index = 0; index < 10; index++) {
            JTextField field = new JTextField(10);
            IntFilter filter = new IntFilter();
            filter.setValidationListener(listener);
            ((AbstractDocument) field.getDocument()).setDocumentFilter(filter);
            fields.put(filter, field);
            add(field, gbc);
        }

    }

}

【讨论】:

  • 如果按下退格键,我将如何告诉它更新。 IE。在此示例中,如果单击某个字母,则所选文本框的背景变为红色。因此,当他们单击退格键时,它会将相同的 JTextfield 背景设置为正常?
  • 基本上你不会知道,因为老实说,你不需要。如果有人选择了一段文本并将其剪切,这也是同样的事情。 DocumentFilter 将知道何时删除文本,这就是它所关心的。在这种情况下,退格字符不是非法字符,因为它不会在字段中生成新文本(它显然会删除它)。当字段失去焦点时,您可以使用InputVerifier 重置字段的背景,验证字段的内容
  • 有没有办法知道 DocumentFilter 中的任何方法一般何时删除文本?
  • 我在想,由于示例的工作方式,您实际上不能输入无效值,根本没有办法,而不是将字段永久变为红色,您可以将其设置为红色,然后使用 Swing Timer,在一小段延迟后将其重置为正常
  • JavaDocs 告诉你什么 ;) ?但是从我的猜测来看,您正在追求错误的行动方案,因为如果过滤器检测到无效字符,则不会将任何内容添加到基础文档中,因此按退格键将简单地删除最后一个有效字符
【解决方案2】:

你有一个范围问题,这就是为什么会出现这个错误:

for(int i = 0; i<nextTextBox;i++) //you are declaring i here

KeyListener 内部使用,这是被禁止的。因为localnon-final变量不能从内部类访问,所以访问它必须是final的。在你的情况下,我认为不可能使 i 最终

所以,一个快速的解决方法是在类范围内声明 i 与 JFrameJPanel

JFrame myMainWindow = new JFrame("Sorts");

JPanel sortPanel = new JPanel();
int i; 

然后在任何地方使用它

 for(i = 0; i<nextTextBox;i++)

【讨论】:

  • “这是被禁止的” 不是真的......肯定很麻烦。我建议一个更好的解决方案,但整个方法只是,好吧,垃圾
  • 当循环完成时,所有字段都将始终引用最后一个文本字段是否存在问题,因为它们都查看相同的i 引用而不是它目前的值被实例化了?我建议您在实例化中通过JTextField myfield = allField[i] 在内部类范围内复制变量
  • This is prohibited 我的意思是在编译器禁止的情况下从内部类访问非最终局部变量。如果我仍然错了,请点亮我一点。而且我知道它是一种垃圾:(我只是想保持 OP 代码不变并修复当前的错误。@MadProgrammer
  • 是的,这是一种可能的情况,但在此代码中,除了循环内部之外,OP 没有在任何地方使用 i。 @MichaelDibbets
  • "fix the current bug" - 当可怜的 OP 以错误的方向或违反 API 的方式工作时,不要浪费他们的时间也许会很好,尤其是当API 的存在是为了实现他们想要的结果(恕我直言)。此外,他们应该使用KeyEvent#getSource,而根本不理会i,但这无助于他们解决他们正在/将要面临的更大问题
猜你喜欢
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多