为了账号安全,请及时绑定邮箱和手机立即绑定

如何从 no-gui 类实时更新 Java Jframe 控件

如何从 no-gui 类实时更新 Java Jframe 控件

繁星淼淼 2021-11-11 17:45:09
我的 Java JFrame 项目有一个乏味的问题。我想要做的(并寻找如何做)是从无ListBoxGUI 类实时添加元素,或者换句话说“异步”,而不冻结我的应用程序。这清楚吗?我试过SwingWorker和线程但没有结果。我所能做的就是在所有进程完成后更新列表框(显然我的应用程序冻结了,因为我的进程很长)。这是我的架构:控制器package controller;import business.MyBusiness;import util.MyLog;import view.MyView;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class MyController {    MyLog log;    MyBusiness business;    MyView view;    public MyController(){        log = new MyLog();        business = new MyBusiness(log.getLog());        view = new MyView(log.getLog());    }    public void runProcess() {        view.addButtonListener(new ActionListener() {             @Override             public void actionPerformed(ActionEvent e) {                 business.runProcess();            }}        );    }}商业package business;import MyLog;import java.util.List;import javax.swing.DefaultListModel;import javax.swing.SwingWorker;public class MyBusiness {    private int counter = 0;    private DefaultListModel<String> model;    private MyLog log;    public MyBusiness(DefaultListModel<String> model) {        this.model = model;a    }    public void runProcess() {        SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {            @Override            protected Void doInBackground() throws Exception {                for (int i = 0; i < 10; i++) {                    publish("log message number " + counter++);                    Thread.sleep(2000);                }                return null;            }            @Override            protected void process(List<String> chunks) {                // this is called on the Swing event thread                for (String text : chunks) {                    model.addElement("");                }            }        };        worker.execute();    }}
查看完整描述

2 回答

?
蓝山帝景

TA贡献1843条经验 获得超7个赞

这是一个过度简化的示例,String说明使用SwingWorker.


为了使 a 的基本使用SwingWorker更容易理解,它被过度简化。


这是一个单文件mcve,这意味着您可以将整个代码复制粘贴到一个文件 (MyView.java) 中并执行:


import java.awt.BorderLayout;

import java.awt.Dimension;

import java.awt.event.ActionListener;

import java.util.List;

import javax.swing.DefaultListModel;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JList;

import javax.swing.SwingWorker;


//gui only, unaware of logic 

public class MyView extends JFrame {


    private JList<String> loglist;

    private JButton log;


    public MyView(DefaultListModel<String> model) {


        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        log = new JButton("Start Process");

        add(log, BorderLayout.PAGE_START);

        loglist = new JList<>(model);

        loglist.setPreferredSize(new Dimension(200,300));


        add(loglist, BorderLayout.PAGE_END);

        pack();

        setVisible(true);

    }


    void addButtonListener(ActionListener listener) {

        log.addActionListener(listener);

    }


    public static void main(String args[]) {

        new MyController();

    }

}


//represents the data (and some times logic) used by GUI

class Model {


    private DefaultListModel<String> model;


    Model() {


        model = new DefaultListModel<>();

        model.addElement("No logs yet");

    }


    DefaultListModel<String> getModel(){

        return model;

    }

}


//"wires" the GUI, model and business process 

class MyController {


    MyController(){

        Model model = new Model();

        MyBusiness business = new MyBusiness(model.getModel());

        MyView view = new MyView(model.getModel());

        view .addButtonListener(e -> business.start());

    }

}


//represents long process that needs to update GUI

class MyBusiness extends SwingWorker<Void, String>{


    private static final int NUMBER_OF_LOGS = 9;

    private int counter = 0;

    private DefaultListModel<String> model;


    public MyBusiness(DefaultListModel<String> model) {

        this.model= model;

    }


    @Override  //simulate long process 

    protected Void doInBackground() throws Exception {


        for(int i = 0; i < NUMBER_OF_LOGS; i++) {


            //Successive calls to publish are coalesced into a java.util.List, 

            //which is by process.

            publish("log message number " + counter++);

            Thread.sleep(1000);

        }


        return null;

    }


    @Override

    protected void process(List<String> logsList) {

        //process the list received from publish

        for(String element : logsList) {

            model.addElement(element);

        }

    }


    void start() { 

        model.clear(); //clear initial model content 

        super.execute();

    }

}



查看完整回答
反对 回复 2021-11-11
?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

首先是一些规则和建议:

  • 您的模型或此处的“业务”代码应该不了解 GUI,并且不应依赖于 GUI 结构

  • 同样,所有 Swing 代码都应在事件线程上调用,所有长时间运行的代码都应在后台线程中调用

  • 如果您有长时间运行的更改状态的代码,并且如果该状态需要反映在 GUI 中,这意味着您需要某种回调机制,模型以某种方式通知重要方其状态已经改变。这可以使用 PropertyChangeListeners 或通过将某种类型的回调方法注入模型来完成。

  • 使视图变得愚蠢。它从用户那里获取输入并通知控制器,它由控制器更新。几乎所有的程序“大脑”都位于控制器和模型中。例外——一些输入验证通常在视图中完成。

  • 不要忽略基本的 Java OOP 规则——通过保持字段私有来隐藏信息,并允许外部类仅通过您完全控制的公共方法更新状态。

  • MCVE结构是一个很好的学习和使用,即使不问一个问题在这里。学习简化您的代码并隔离您的问题以最好地解决它。

例如,可以将此代码复制并粘贴到所选 IDE 中的单个文件中,然后运行:

import java.awt.event.ActionEvent;

import java.awt.event.KeyEvent;

import java.util.List;

import java.util.Random;

import java.util.concurrent.TimeUnit;

import java.util.function.Consumer;

import javax.swing.*;


public class Mcve1 {

    private static void createAndShowGui() {

        // create your model/view/controller and hook them together

        MyBusiness1 model = new MyBusiness1();

        MyView1 myView = new MyView1();

        new MyController1(model, myView);  // the "hooking" occurs here


        // create and start the GUI

        JFrame frame = new JFrame("MCVE");

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(myView);

        frame.pack();

        frame.setLocationRelativeTo(null);

        frame.setVisible(true);

    }


    public static void main(String[] args) {

        // start GUI on Swing thread

        SwingUtilities.invokeLater(() -> createAndShowGui());

    }

}

@SuppressWarnings("serial")

class MyView1 extends JPanel {

    private MyController1 controller;

    private DefaultListModel<String> logListModel = new DefaultListModel<>();

    private JList<String> logList = new JList<>(logListModel);


    public MyView1() {

        logList.setFocusable(false);

        logList.setPrototypeCellValue("abcdefghijklabcdefghijklabcdefghijklabcdefghijkl");

        logList.setVisibleRowCount(15);

        add(new JScrollPane(logList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,

                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));


        // my view's buttons just notify the controller that they've been pushed

        // that's it

        add(new JButton(new AbstractAction("Do stuff") {

            {

                putValue(MNEMONIC_KEY, KeyEvent.VK_D);

            }


            @Override

            public void actionPerformed(ActionEvent evt) {

                if (controller != null) {

                    controller.doStuff(); // notification done here

                }

            }

        }));

    }


    public void setController(MyController1 controller) {

        this.controller = controller;

    }


    // public method to allow controller to update state

    public void updateList(String newValue) {

        logListModel.addElement(newValue);

    }

}

class MyController1 {

    private MyBusiness1 myBusiness;

    private MyView1 myView;


    // hook up concerns

    public MyController1(MyBusiness1 myBusiness, MyView1 myView) {

        this.myBusiness = myBusiness;

        this.myView = myView;

        myView.setController(this);

    }


    public void doStuff() {

        // long running code called within the worker's doInBackground method

        SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {

            @Override

            protected Void doInBackground() throws Exception {

                // pass a call-back method into the method

                // so that this worker is notified of changes

                myBusiness.longRunningCode(new Consumer<String>() {                    

                    // call back code

                    @Override

                    public void accept(String text) {

                        publish(text); // publish to the process method

                    }

                });

                return null;

            }


            @Override

            protected void process(List<String> chunks) {

                // this is called on the Swing event thread

                for (String text : chunks) {

                    myView.updateList(text);

                }

            }

        };

        worker.execute();


    }


}

class MyBusiness1 {

    private Random random = new Random();

    private String text;


    public void longRunningCode(Consumer<String> consumer) throws InterruptedException {

        consumer.accept("Starting");

        // mimic long-running code

        int sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        consumer.accept("This is message for initial process");


        // ...

        // Doing another long process and then print log

        // ...

        sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        consumer.accept("This is not complete. Review");


        // ...

        // Doing another long process and then print log

        // ...

        sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        consumer.accept("Ok this works. Have fun");

    }


    public String getText() {

        return text;

    }


}

另一种做同样事情的方法是使用符合 Swing 的 PropertyChangeSupport 和 PropertyChangeListeners。例如:


import java.awt.event.ActionEvent;

import java.awt.event.KeyEvent;

import java.beans.*;

import java.util.Random;

import java.util.concurrent.TimeUnit;

import javax.swing.*;

import javax.swing.event.SwingPropertyChangeSupport;


public class Mcve2 {

    private static void createAndShowGui() {

        MyBusiness2 myBusiness = new MyBusiness2();

        MyView2 myView = new MyView2();

        new MyController2(myBusiness, myView);


        JFrame frame = new JFrame("MCVE");

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(myView);

        frame.pack();

        frame.setLocationRelativeTo(null);

        frame.setVisible(true);

    }


    public static void main(String[] args) {

        SwingUtilities.invokeLater(() -> createAndShowGui());

    }

}

@SuppressWarnings("serial")

class MyView2 extends JPanel {

    private MyController2 controller;

    private DefaultListModel<String> logListModel = new DefaultListModel<>();

    private JList<String> logList = new JList<>(logListModel);


    public MyView2() {

        logList.setFocusable(false);

        logList.setPrototypeCellValue("abcdefghijklabcdefghijklabcdefghijklabcdefghijkl");

        logList.setVisibleRowCount(15);

        add(new JScrollPane(logList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,

                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));

        add(new JButton(new AbstractAction("Do stuff") {

            {

                putValue(MNEMONIC_KEY, KeyEvent.VK_D);

            }


            @Override

            public void actionPerformed(ActionEvent evt) {

                if (controller != null) {

                    controller.doStuff();

                }

            }

        }));

    }

    public void setController(MyController2 controller) {

        this.controller = controller;

    }

    public void updateList(String newValue) {

        logListModel.addElement(newValue);

    }

}

class MyController2 {


    private MyBusiness2 myBusiness;

    private MyView2 myView;


    public MyController2(MyBusiness2 myBusiness, MyView2 myView) {

        this.myBusiness = myBusiness;

        this.myView = myView;

        myView.setController(this);


        myBusiness.addPropertyChangeListener(MyBusiness2.TEXT, new TextListener());

    }


    public void doStuff() {

        new Thread(() -> {

            try {

                myBusiness.longRunningCode();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }) .start();

    }


    private class TextListener implements PropertyChangeListener {

        @Override

        public void propertyChange(PropertyChangeEvent evt) {

            String newValue = (String) evt.getNewValue();

            myView.updateList(newValue);

        }

    }


}

class MyBusiness2 {

    public static final String TEXT = "text";

    private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);

    private Random random = new Random();

    private String text;


    public void longRunningCode() throws InterruptedException {

        setText("Starting");

        // mimic long-running code

        int sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        setText("This is message for initial process");


        // ...

        // Doing another long process and then print log

        // ...

        sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        setText("This is not complete. Review");


        // ...

        // Doing another long process and then print log

        // ...

        sleepTime = 500 + random.nextInt(2 * 3000);

        TimeUnit.MILLISECONDS.sleep(sleepTime);

        setText("Ok this works. Have fun");

    }


    public void setText(String text) {

        String oldValue = this.text;

        String newValue = text;

        this.text = text;

        pcSupport.firePropertyChange(TEXT, oldValue, newValue);

    }


    public String getText() {

        return text;

    }


    public void addPropertyChangeListener(PropertyChangeListener listener) {

        pcSupport.addPropertyChangeListener(listener);

    }


    public void removePropertyChangeListener(PropertyChangeListener listener) {

        pcSupport.removePropertyChangeListener(listener);

    }


    public void addPropertyChangeListener(String name, PropertyChangeListener listener) {

        pcSupport.addPropertyChangeListener(name, listener);

    }


    public void removePropertyChangeListener(String name, PropertyChangeListener listener) {

        pcSupport.removePropertyChangeListener(name, listener);

    }

}



查看完整回答
反对 回复 2021-11-11
  • 2 回答
  • 0 关注
  • 215 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信