【问题标题】:Java AWT/Swing Updating JPanel Continously Not WorkingJava AWT/Swing 更新 JPanel 持续不工作
【发布时间】:2020-07-31 08:31:39
【问题描述】:

我正在尝试制作一个程序,该程序使用 GridLayout 填充 JPanel,其中包含 JButton 值的字符串键的 HashMap 的内容。因为 HashMap 的大小可能会改变,所以我不能只为每个按钮使用setText()。到目前为止,我已经调用.removeAll() 来删除所有按钮的 JPanel,然后循环遍历 HashMap 以重新填充 JPanel。然后我在 JPanel 上调用 revalidate(),在 JFrame 上调用 repaint()

当前代码:

public class GUI implements Runnable, ActionListener
{
    private ToDo td;

    JFrame frame;
    Thread t=null;
    int fontsize = 18;
    private Container contentPane;
    private JPanel topPane;
    private JButton main;
    private JButton add;
    private JButton settings;
    private JPanel centerPane;
    private JScrollPane centerScroll;
    private JPanel scrollable;

    private HashMap<String, JButton> items;

    public static void main(String[] args) {  
        new GUI();  
    }  

    public GUI(){
        td = new ToDo();

        frame = new JFrame();

        t = new Thread(this);  
        t.start();

        frame.setMinimumSize(new Dimension(480, 640));
        frame.setLayout(null);  
        frame.setVisible(true);

        contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout());

        topPane = new JPanel();
        topPane.setLayout(new GridLayout(1, 3));
        topPane.setPreferredSize(new Dimension(480, 40));

        main = new JButton("View Tasks");
        main.setFont(new Font("Sans Serif", Font.PLAIN, fontsize));
        add = new JButton("Add Task");
        add.setFont(new Font("Sans Serif", Font.PLAIN, fontsize));
        settings = new JButton("Settings");
        settings.setFont(new Font("Sans Serif", Font.PLAIN, fontsize));

        topPane.add(main);
        topPane.add(add);
        topPane.add(settings);

        contentPane.add(topPane, BorderLayout.NORTH);

        centerPane = new JPanel();
        centerPane.setPreferredSize(new Dimension(480, 600));

        items = new HashMap<>();

        HashMap<String, Assignment> assignments = td.getAssignments();

        scrollable = new JPanel();
        scrollable.setLayout(new GridLayout(assignments.size(), 1));
        centerScroll = new JScrollPane(scrollable);

        for(String key: assignments.keySet()){
            Assignment a = assignments.get(key);
            JButton button = new JButton(a.getTitle() + " | " + a.getDetails() + " | " + a.getClassification().getCls() + " | " + a.getStatus().getStatus());
            button.addActionListener(this);
            items.put(key, button);
            scrollable.add(button);
        }
        centerPane.add(centerScroll);        
        contentPane.add(centerPane, BorderLayout.CENTER);

        frame.pack();
        frame.setVisible(true);
    }
    public void update(int i){
        HashMap<String, Assignment> assignments = td.getAssignments();
        scrollable.removeAll();
        scrollable.setLayout(new GridLayout(assignments.size(), 1));
        for(String key: assignments.keySet()){
            Assignment a = assignments.get(key);
            JButton button = new JButton(Integer.toString(i));
            button.addActionListener(this);
            items.put(key, button);
            scrollable.add(button);
        }
        scrollable.revalidate();
        frame.repaint();
    }
    @Override
    public void run(){
        int counter = 0;
        try {

            while (true) {
                update(counter);
                t.sleep( 1000 );  // interval given in milliseconds 
                counter++;
            }  
        }  
        catch (Exception e) {
            System.out.println();
        }
    }
    @Override
    public void actionPerformed(ActionEvent e){
        for(String s: items.keySet()){
            if(items.get(s) == e.getSource()){
                EventMenu em = new EventMenu(td, s);
            }
        }
    }
}

问题是按钮没有更新。我希望 JPanel 应该不断地重新填充具有不同文本的更新 JButton,但似乎程序挂起并且没有更新。 我尝试制作一个从here 修改的更简单的示例,但结果不同:

public class DigitalWatch implements Runnable{  
    JFrame f;  
    JPanel p;
    Thread t=null;  
    int hours=0, minutes=0, seconds=0;  
    String timeString = "";  
    JButton b;  

    DigitalWatch(){  
        f=new JFrame();  
        p = new JPanel();
        t = new Thread(this);  
            t.start();  

        b=new JButton();  
            b.setBounds(100,100,100,50);  

        p.add(b);
        f.add(p);
        f.setSize(300,400);  
        f.setLayout(null);  
        f.setVisible(true);  
    }  

    public void run() {  
        try {  
            while (true) {  

                Calendar cal = Calendar.getInstance();  
                hours = cal.get( Calendar.HOUR_OF_DAY );  
                if ( hours > 12 ) hours -= 12;  
                minutes = cal.get( Calendar.MINUTE );  
                seconds = cal.get( Calendar.SECOND );  

                SimpleDateFormat formatter = new SimpleDateFormat("hh:mm:ss");  
                Date date = cal.getTime();  
                timeString = formatter.format( date );  

                p.removeAll();
                b=new JButton(timeString);  
                b.setBounds(100,100,100,50); 
                p.add(b);
                f.add(p);
                p.revalidate();
                f.repaint();

                //printTime();  

                t.sleep( 1000 );  // interval given in milliseconds  
            }  
        }  
          catch (Exception e) {
          System.out.println(e);
        }  
    }

    public static void main(String[] args) {  
        new DigitalWatch();  

    }  
} 

这个 sn-p 无法绘制任何东西,不像第一个至少绘制在构造函数中创建的对象。

如何以程序方式实时更新列表或网格 JPanel 并填充按钮?我知道我可以每次更改每个按钮的文本,但按钮的数量可能随时更改。

完整代码here.

【问题讨论】:

    标签: java swing awt


    【解决方案1】:

    你违反了 Swing 的单线程规则——你不应该在 Swing 的事件调度线程之外做任何与 UI 相关的事情。

    阅读herehere

    下面是一个工作示例。不知道为什么他们选择使用按钮来显示时间。 :-)

    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.FormatStyle;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.Timer;
    
    public class DigitalWatch extends JFrame {
    
        private DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM);
    
        public DigitalWatch() {
            JButton btn = new JButton(getCurrentTime());
            this.getContentPane().setLayout(new FlowLayout());
            this.getContentPane().add(btn);
            this.setPreferredSize(new Dimension(200, 150));
            this.pack();
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setLocationRelativeTo(null); // center it on the screen
            new Timer(500, e -> btn.setText(getCurrentTime())).start();
        }
    
        private String getCurrentTime() {
            return formatter.format(LocalTime.now());
        }
    
        public static void main(String[] args) {
            new DigitalWatch().setVisible(true);
        }
    
    }
    

    【讨论】:

    • 该类实现了runnable,这意味着一切都应该在Swing事件调度线程下运行。我提出的第二个例子是基于这个项目:javatpoint.com/digital-watch。所做的唯一更改是更改正在修改的 GUI 项。
    • javatpoint.com/digital-watch 上的代码恐怕是错误的,抱歉。你正在做 t = new Thread(this);和 t.start();它启动一个新线程并且不在 AWT/Swing 事件调度线程上运行。如前所述,您所基于的代码在很多方面都是错误的和令人敬畏的 - 我建议您找到另一个/更好的示例。对于定期更新,您可能需要查看 javax.swing.Timer。
    • 我在回答中添加了一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 2017-02-18
    • 2013-06-08
    相关资源
    最近更新 更多