Swing:JaxtField里面的JPanel,重绘问题

时间:2017-07-29 10:26:42

标签: java swing jpanel jtextfield

我最近开始使用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);
    }
}

2 个答案:

答案 0 :(得分: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 ());
    }
}

屏幕截图(简短文字):

enter image description here

屏幕截图(更长文字):

enter image description here

答案 1 :(得分:0)

请查看代码并注释评论:

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);
    }
}