【问题标题】:Triggering Java GUI to update触发 Java GUI 进行更新
【发布时间】:2023-04-11 12:19:01
【问题描述】:

对此可能有更好的问题/答案,但我一直在寻找的东西没有解决,我在为谷歌查询提出问题时遇到了麻烦。基本上,我有一个 JFrame,其中包含几个面板和组件,它们从 xml 文件中提取数据。我使用 JFrame 的实例变量 private Date focusDate = new Date(); 来存储我希望每个面板显示哪一天的信息,到目前为止一切顺利。

现在我的问题来了,我试图在更改“focusDate”后将导航组件的各种操作设置为更新。我在 JPanel NavButtons navPanel = new NavButtons(focusDate); 中有一个工具栏,我将其设置为内部类,控制台报告 focusDate 正在更改,但是当我调用 setFocus(Date d) 方法时,我无法将 JFrame 设置为 validate(), repaint(), etc...

如果有帮助,我可以包含更多我的代码,但这是有问题的方法:

public void setFocus(Date d) throws IOException {
    focusDate = d;

    dispose();
//  validate();  //Tried revalidate too, but DisplayView extends JFrame
//  repaint(); 
//  revalidate();
//  pack();
//  DisplayView view = new DisplayView(focusDate);
    setVisible(true); }

这是我在构造函数中设置 ActionListener 的方式:

public NavButtons(Date d) {
    newDate = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(d));

        weekBack.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) { 
                newDate = newDate.plusDays(-7); 
                try { setFocus(Date.from(newDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
                } catch (IOException e) {
                    e.printStackTrace(); } 
                //validate();
                //repaint(); 
                }
        });

我对摇摆不是很熟悉,所以我确定这是一些我没有得到的小细节,但如果有人可以解释如何重新触发参数传递并将框架的子组件更新为非常感谢的业余爱好者。

更新 这是整个 JFrame

package interfaceComponents;

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.io.IOException;
import java.text.*;
import java.util.*;
import java.time.*;

public class DisplayView extends JFrame {
    //instance variables
    private Date focusDate = new Date();

    //constructor
    public DisplayView(Date d) throws IOException {
        DisplayMenus menus = new DisplayMenus();
        setJMenuBar(menus);

        JPanel body = new JPanel();
        body.setLayout(new BoxLayout(body, BoxLayout.Y_AXIS));
        body.add(new DayView(focusDate));
        LocalDate focusNextDay = LocalDate.now();
        body.add(new DayView(Date.from(focusNextDay.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()))); 
        add(new JScrollPane(body), BorderLayout.CENTER);

        JPanel footer = new JPanel();
        NavButtons navPanel = new NavButtons(focusDate);
        JLabel focusPoint = new JLabel(new SimpleDateFormat("E, dd MMM yyyy").format(focusDate).toString());
        focusPoint.setForeground(Color.RED);
        footer.setLayout(new BorderLayout());
        footer.add(focusPoint, BorderLayout.CENTER);
        footer.add(navPanel, BorderLayout.EAST);
        footer.setBackground(Color.BLACK);
        add(footer, BorderLayout.SOUTH);

        pack(); }

    public DisplayView() throws IOException { this(new Date()); }

    public void setFocus(Date d) throws IOException { 
        focusDate = d;

        SwingUtilities.updateComponentTreeUI(this);
//      dispose();
//      invalidate();
//      validate();                                     //Tried revalidate too, but DisplayView extends JFrame
        repaint(); 
//      revalidate();
//      pack();
//      DisplayView view = new DisplayView(focusDate);
//      setVisible(true);
        }
    public Date getFocus() { return focusDate; }    

    class NavButtons extends JPanel {
        private JToolBar toolBar = new JToolBar("Navigation");
        private JButton weekBack = new JButton("<<");
        private JButton dayBack = new JButton("<");
        private JButton returnToday = new JButton("Today");
        private JButton nextDay = new JButton(">");
        private JButton nextWeek = new JButton(">>");
        private JButton calendar = new JButton("L");
        private LocalDate newDate = LocalDate.now();

        public NavButtons(Date d) {
            newDate = LocalDate.parse(new SimpleDateFormat("yyyy-MM-dd").format(d));

            weekBack.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) { 
                    newDate = newDate.plusDays(-7); 
                    try { setFocus(Date.from(newDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
                    } catch (IOException e) {
                        e.printStackTrace(); } 
//                  invalidate();
//                  validate();
//                  repaint(); 
                    }
            });
            dayBack.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(-1); }
            });
            returnToday.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) { newDate = LocalDate.now(); }
            });
            nextDay.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(1); }
            });
            nextWeek.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) { newDate = newDate.plusDays(7); }
            });

            toolBar.add(weekBack);
            toolBar.add(dayBack);
            toolBar.add(returnToday);
            toolBar.add(nextDay);
            toolBar.add(nextWeek);
            toolBar.add(new GalileoMode());
            toolBar.add(calendar);
            add(toolBar); }         
    }
}

正如@MadProgrammer 所指出的,我应该将日期存储在一个单独的对象中(如图所示):

package interfaceComponents;

import java.util.*;

public class FocusDate {
    //instance variables
    Date focus = new Date();

    //constructors
    //intentionally blank: public FocusDate() {}

    //methods:
    public void setFocus(Date d) {
        focus = d; }
    public Date getFocus() {
        return focus; }
}

我这样编辑了框架:

public class DisplayView extends JFrame {
    //instance variables
    FocusDate focus = new FocusDate();
//  private Date focusDate = new Date();

    //constructor
    public DisplayView(Date d) throws IOException {
//      focusDate = d;
        Date focusDate = focus.getFocus();

...

    public void setFocus(Date d) throws IOException { 
//      focusDate = d;
        focus.setFocus(d);

虽然还是没做对...

【问题讨论】:

  • 看看this question对你有没有帮助。
  • 考虑提供一个可运行的问题示例。它将消除猜测工作并产生更好的响应
  • 什么解决方案是将日期封装在模型中,然后传递给需要它的每个组件。该模型将允许感兴趣的各方注册一个回调,该回调将在日期更改时通知,以便他们可以采取适当的行动(也称为观察者模式)
  • 这听起来正是我需要的@MadProgrammer!我会在检查完 Aprendiz 的链接后立即尝试。感谢你们的快速响应(如果我仍然需要帮助,我会回复有效的或包含可运行的版本)。
  • 感谢@Aprendiz 的链接,但这似乎没有奏效。也许我在影响 JFrame 的实例而不是对象本身时遇到了麻烦?

标签: java swing model-view-controller repaint abstract-action


【解决方案1】:

因此,基本思想是将“当前”日期值包装在可观察模式中,并将其传递给每个相关方。这允许导航更改日期值,但不需要了解程序的任何其他部分,将其解耦并允许更灵活的结果。

所以,我从两个基本合同开始...

public interface DateModel {

    public LocalDate getDate();

    public void addObserver(Observer o);

    public void removeObserver(Observer o);
}

public interface MutableDateModel extends DateModel {

    public void setDate(LocalDate date);

}

一个是不可变的(对于那些不需要能够更改日期的程序部分),一个是可变的,对于那些可以更改日期的程序部分(如导航)

然后我创建了模型的“默认”实现...

public class DefaultDateModel extends Observable implements MutableDateModel {

    private LocalDate date;

    public DefaultDateModel(LocalDate date) {
        this.date = date;
    }

    @Override
    public void setDate(LocalDate date) {
        this.date = date;
        setChanged();
        notifyObservers();
    }

    @Override
    public LocalDate getDate() {
        return date;
    }

    @Override
    public void removeObserver(Observer o) {
        // I like the "remove" ;)
        deleteObserver(o);
    }

}

我比较懒,使用java.util 中的ObserverObservable API,您可以根据需要创建自己的,但这只是适合示例。

这意味着,我可以创建一个DefaultDateModel 的实例并将其传递给程序中需要DateModel 和需要MutableDateModel 的部分,而无需创建单独的实例,简洁。

因为我们正在处理接口契约,那些只需要DateModel 的程序部分将只能访问接口中定义的方法(如果强制转换它,那么他们做错了东西)...

举个例子……

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Observable;
import java.util.Observer;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class DisplayView extends JFrame {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                DisplayView view = new DisplayView();
                view.setDefaultCloseOperation(EXIT_ON_CLOSE);
                view.pack();
                view.setLocationRelativeTo(null);
                view.setVisible(true);
            }
        });
    }

    //constructor
    public DisplayView(LocalDate d) {
//      DisplayMenus menus = new DisplayMenus();
//      setJMenuBar(menus);

        DefaultDateModel model = new DefaultDateModel(d);

        JPanel body = new JPanel();
        body.setLayout(new BoxLayout(body, BoxLayout.Y_AXIS));

        DayView nowView = new DayView();
        nowView.setModel(model);
        DayView nextView = new DayView(1);
        nextView.setModel(model);

        body.add(nowView);
        body.add(nextView);
        add(new JScrollPane(body), BorderLayout.CENTER);

        JPanel footer = new JPanel();
        NavButtons navPanel = new NavButtons(model);
        JLabel focusPoint = new JLabel(DateTimeFormatter.ISO_DATE.format(model.getDate()));
        focusPoint.setForeground(Color.RED);
        footer.setLayout(new BorderLayout());
        footer.add(focusPoint, BorderLayout.CENTER);
        footer.add(navPanel, BorderLayout.EAST);
        footer.setBackground(Color.BLACK);
        add(footer, BorderLayout.SOUTH);

        pack();
    }

    public DisplayView() {
        this(LocalDate.now());
    }

    public interface DateModel {

        public LocalDate getDate();

        public void addObserver(Observer o);

        public void removeObserver(Observer o);
    }

    public interface MutableDateModel extends DateModel {

        public void setDate(LocalDate date);

    }

    public class DefaultDateModel extends Observable implements MutableDateModel {

        private LocalDate date;

        public DefaultDateModel(LocalDate date) {
            this.date = date;
        }

        @Override
        public void setDate(LocalDate date) {
            this.date = date;
            setChanged();
            notifyObservers();
        }

        @Override
        public LocalDate getDate() {
            return date;
        }

        @Override
        public void removeObserver(Observer o) {
            // I like the "remove" ;)
            deleteObserver(o);
        }

    }

    public class DayView extends JPanel implements Observer {
        private JLabel dateLabel;
        private DateModel model;
        private int offset;

        public DayView(int offset) {
            this.offset = offset;
            dateLabel = new JLabel("...");
            setLayout(new GridBagLayout());
            add(dateLabel);
        }

        public DayView() {
            this(0);
        }

        public void setModel(DateModel value) {
            if (model != null) {
                model.removeObserver(this);
            }
            this.model = value;
            if (model != null) {
                model.addObserver(this);
            }
            updateLabel();
        }

        public DateModel getModel() {
            return model;
        }

        protected void updateLabel() {
            DateModel model = getModel();
            if (model != null) {
                LocalDate offsetDate = model.getDate().plusDays(offset);
                dateLabel.setText(DateTimeFormatter.ISO_DATE.format(offsetDate));
            } else {
                dateLabel.setText("...");
            }
        }

        @Override
        public void update(Observable o, Object arg) {
            updateLabel();
        }

    }

    class NavButtons extends JPanel implements Observer {

        private JToolBar toolBar = new JToolBar("Navigation");
        private JButton weekBack = new JButton("<<");
        private JButton dayBack = new JButton("<");
        private JButton returnToday = new JButton("Today");
        private JButton nextDay = new JButton(">");
        private JButton nextWeek = new JButton(">>");
        private JButton calendar = new JButton("L");

        private MutableDateModel model;

        public NavButtons(MutableDateModel model) {
            weekBack.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    MutableDateModel model = getModel();
                    if (model != null) {
                        LocalDate newDate = model.getDate().minusDays(7);
                        model.setDate(newDate);
                    }
                }
            });
            dayBack.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    MutableDateModel model = getModel();
                    if (model != null) {
                        LocalDate newDate = model.getDate().minusDays(1);
                        model.setDate(newDate);
                    }
                }
            });
            returnToday.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    MutableDateModel model = getModel();
                    if (model != null) {
                        LocalDate newDate = LocalDate.now();
                        model.setDate(newDate);
                    }
                }
            });
            nextDay.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    MutableDateModel model = getModel();
                    if (model != null) {
                        LocalDate newDate = model.getDate().plusDays(1);
                        model.setDate(newDate);
                    }
                }
            });
            nextWeek.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    MutableDateModel model = getModel();
                    if (model != null) {
                        LocalDate newDate = model.getDate().plusDays(7);
                        model.setDate(newDate);
                    }
                }
            });

            toolBar.add(weekBack);
            toolBar.add(dayBack);
            toolBar.add(returnToday);
            toolBar.add(nextDay);
            toolBar.add(nextWeek);
//          toolBar.add(new GalileoMode());
            toolBar.add(calendar);
            add(toolBar);
            setModel(model);
        }

        public void setModel(MutableDateModel value) {
            if (model != null) {
                model.removeObserver(this);
            }
            this.model = value;
            if (model != null) {
                model.addObserver(this);
            }
        }

        public MutableDateModel getModel() {
            return model;
        }

        @Override
        public void update(Observable o, Object arg) {
            // models data has change!!
        }

        protected void setFocus(LocalDate newDate) {
            // No idea what this is suppose to do...
        }

    }
}

哦,如果可以的话,我会避免在 java.util.Datejava.time.LocalDate 之间移动,出于您的目的,我会坚持使用 LocalDate,但那是我 ;)

【讨论】:

  • 太棒了!非常感谢你的这个以及我一直在阅读的所有其他答案。彻底清楚,并准确地向我展示了我所缺少的东西。
  • @JamesLingo 很高兴它可以提供帮助
  • 优秀。如果我想使用 ObserverObservable 以外的其他东西,你有什么建议?如果我想要自己的 API,我就不必扩展这些接口吗?
  • @Aprendiz 您可以制作自己的“侦听器”界面或重新使用 Swings 之一,例如 ChangeListener。通常,当我创建自己的“监听器”时,我会扩展 EventListener,因为我可以重用 EventListenerList(它在所有 Swing 组件中都具有受保护的访问权限),这使得它更易于使用。同样,您可以独立维护一个接口列表。另一种选择可能是查看 PropertyChangeListener 和 PropertyChangeSupport,这可以减少您管理侦听器和触发事件所需的工作量
猜你喜欢
  • 2011-12-02
  • 1970-01-01
  • 1970-01-01
  • 2015-11-17
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多