【问题标题】:Swing: JTextField inside JPanel, redraw issueSwing:JPanel 中的 JTextField,重绘问题
【发布时间】:2018-01-05 09:19:43
【问题描述】:

我最近开始使用 Java+Swing 构建 UI,但我目前在使用 FlowLayout 的 JPanel 上放置 JTextField 时遇到问题。

在我的示例中,我有一个窗口,其中包含带有按钮的面板。单击该按钮会添加一个从 JPanel 派生并包含 JTextField 的组件。

问题是当我输入 JTextField 时它没有得到更新(没有调整大小)。但是,当我调整窗口大小或执行任何其他强制窗口/面板重绘的操作时,文本字段的大小将被调整(这正是我期望自动发生的)。

当我将基类从 JPanel 更改为 JTextField 时,它以我尝试实现的方式工作,但我需要将 JPanel 作为基类,以便我可以利用将子组件放入其中的优势。

我在这里检查了不同的问题,并在 Google 上搜索过试图找到解决方案,但它对我不起作用。我已经尝试在不同的组合和不同的组件中验证/无效/重新验证/重新绘制,并尝试对每个键入的字符强制重新验证,这对我来说听起来不是正确的方式。到目前为止,我明白这与布局管理器有关。

谁能帮助我理解它是如何工作的,以及我应该阅读哪些关于 Swing UI、布局管理和重绘如何工作的内容?

另外,如果有人可以帮助我解决我的代码的特定问题,我会很高兴。

提前致谢!

下面是我的代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


class TagVisual extends JPanel /*JTextField*/ {

    private JTextField editField;

    public TagVisual() {

        FlowLayout layout = new FlowLayout();
        layout.setHgap(0);
        layout.setVgap(0);
        setLayout(layout);

        editField = new JTextField();
        editField.setBackground(Color.RED);

        editField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editField.setSize(editField.getSize());
                editField.revalidate();
                remove(editField);
                add(editField);
                revalidate();
                repaint();
            }
        });

        add(editField, FlowLayout.LEFT);
    }

    public void place(JPanel panel) {
        panel.add(this);

        editField.grabFocus();
    }
}

public class MainWindow {

    private JPanel mainPanel;
    private JButton btnPlace;
    private JFrame frame;

    public MainWindow(JFrame frame) {

        mainPanel = new JPanel(new FlowLayout());
        btnPlace = new JButton();
        btnPlace.setText("Place");
        mainPanel.add(btnPlace);

        this.frame = frame;
        btnPlace.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                TagVisual v = new TagVisual();
                v.place(mainPanel);
                mainPanel.revalidate();
                mainPanel.repaint();
                mainPanel.updateUI();
                frame.revalidate();
                frame.repaint();
            }
        });
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("TextFieldUpdateIssue");

        frame.setContentPane(new MainWindow(frame).mainPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }
}

【问题讨论】:

  • 所以您希望您的文本字段根据输入的文本增加其大小?这不是正常的行为,作为用户,我会觉得这很讨厌......
  • 是的,这就是我想要的。我正在创建标签编辑器组件,预计在键入时会增加其大小。
  • 欢迎来到 SO。请发帖minimal reproducible example。此代码不会运行(btnPlacemainPanel 永远不会初始化也不会添加到框架中)
  • 感谢指出,更新了代码。

标签: java swing jpanel jtextfield


【解决方案1】:

如果我是你,我不会在用户输入一些文本时尝试调整文本字段的大小。

我建议您使用 JTextField (int columns) 构造函数给它们一个固定大小,这将允许您创建一些“足够宽”的文本字段。

如果您仍然希望在输入某些文本时使它们更宽,则不能使用 ActionListener,因为它会在用户按下 ENTER 键时触发一个事件,而不是基于输入的文本。

为此,您可以在文本字段的文档中注册Document Listener

你也可以重写 getPreferredSize() 方法来计算并返回一个合适的大小。在下面的示例中,为了方便计算首选宽度,我使用 JLabel,但您可以使用 FontMetrics。

如果您要向面板添加多个标签,您还应该考虑使用 JScrollPane 以便在面板需要更多空间时显示滚动条。

看这个例子(我对你的代码做了一些修改,因为它无法编译并且总体设计很糟糕,现在我认为它更好,但仍然不是很好):

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class MainWindow
{
    public static void main (String [] a) {
        SwingUtilities.invokeLater (new Runnable () {
            @Override public void run () {
                try {
                    UIManager.setLookAndFeel (UIManager.getSystemLookAndFeelClassName ());
                    createAndShowGUI ();
                }
                catch (Exception e) {
                    JOptionPane.showMessageDialog (null, "An unexpected error occurred: " + e.getClass ().getSimpleName (), "Error", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
    }
    private static void createAndShowGUI () {
        JFrame frame = new JFrame ("TextFieldUpdateIssue");
        frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
        frame.setContentPane (new MainPanel ());
        frame.setExtendedState (JFrame.MAXIMIZED_BOTH);
        frame.setLocationRelativeTo (null);
        frame.setVisible (true);
    }
}
class MainPanel extends JPanel
{
    private JPanel tagsPanel;

    public MainPanel () {
        super (new BorderLayout (0, 10));
        add (new JButton (new AbstractAction ("Add tag") {
             @Override public void actionPerformed(ActionEvent e) {
                addNewTag ();
            }
        }), BorderLayout.NORTH);
        tagsPanel = new JPanel ();
        tagsPanel.setLayout (new FlowLayout (FlowLayout.CENTER, 10, 0));
        add (tagsPanel, BorderLayout.CENTER);
    }
    private void addNewTag () {
        TagVisual v = new TagVisual ();
        tagsPanel.add (v);
        v.grabFocusOnField ();
        revalidate ();
    }
}
class TagVisual extends JPanel
{
    private JTextField editField;

    public TagVisual() {
        super (new FlowLayout (FlowLayout.CENTER, 0, 0));
        add (editField = createNewTextField (null), FlowLayout.LEFT);
    }
    private JTextField createNewTextField (String text) {
        JTextField textField = new JTextField (text) {
            @Override public Dimension getPreferredSize () {
                Dimension d = super.getPreferredSize ();
                return new Dimension (new JLabel (getText ()).getPreferredSize ().width + 10, d.height);
            }
        };
        textField.setBackground (Color.RED);
        textField.getDocument ().addDocumentListener (new DocumentListener () {
            @Override public void changedUpdate (DocumentEvent e) {
                revalidate ();
            }
            @Override public void insertUpdate (DocumentEvent e) {
                revalidate ();
            }
            @Override public void removeUpdate (DocumentEvent e) {
                revalidate ();
            }
        });
        return textField;
    }
    public void grabFocusOnField () {
        editField.grabFocus ();
        editField.setCaretPosition (editField.getText ().length ());
    }
}

截图(短文本):

屏幕截图(长文本):

【讨论】:

    【解决方案2】:

    请查看代码并备注 cmets:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    public class MainWindow {
    
        private JPanel mainPanel;
        private JButton btnPlace;
    
        public MainWindow(){
    
            JFrame frame = new JFrame("TextFieldUpdateIssue");
            //you can't use components before initializing them
            btnPlace = new JButton("Button");
            frame.add(btnPlace, BorderLayout.NORTH);
            mainPanel = new JPanel();
            frame.add(mainPanel, BorderLayout.CENTER);
            btnPlace.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    TagVisual v = new TagVisual();
                    mainPanel.add(v); //add it to main panel
                    //v.place(mainPanel);
                    //mainPanel.revalidate();
                    //mainPanel.repaint();
                    //mainPanel.updateUI();
                    //frame.revalidate();
                    //frame.repaint();
                    frame.pack();
                }
            });
    
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
    
            new MainWindow();
        }
    }
    
    class TagVisual extends JPanel /*JTextField*/ {
    
        private JTextField editField;
    
        public TagVisual() {
    
            FlowLayout layout = new FlowLayout();
            layout.setHgap(0);
            layout.setVgap(0);
            setLayout(layout);
    
            editField = new JTextField();
            //give it a preferred size to be used by layout manager
            editField.setPreferredSize(new Dimension(150,25));
            editField.setBackground(Color.RED);
    
            editField.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    //not sure what you want to do here 
                    //not relevant to the question 
                }
            });
    
            add(editField, FlowLayout.LEFT);
        }
    }
    

    【讨论】:

    • "//give it a preferred size to be used by layout manager" -- 对文本组件从来都不是好事,因为它使用了一种粗暴的工具,对组件的固有行为进行粗暴。通过设置字体来“设置大小”要好得多和列属性。例如,如果组件曾经位于 JScrollPane 中,您的建议将保证它不会正确运行。
    • 谢谢,在我为此处描述的 JTextField 应用自动调整大小后,这对我有用:stackoverflow.com/questions/22128813/…
    猜你喜欢
    • 2013-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多