4

问题:在方法运行之后才重新绘制图形。

单击按钮时会调用两个方法。每个方法内部都有代码,用于更改与此方法关联的图形颜色(在 UI 中);当方法启动时,图形从黑色变为绿色;当方法完成时,颜色从绿色变为红色。然后调用下一个方法,它的图形应该变成绿色(方法正在运行),当方法完成时,它的图形应该用红色填充(方法完成)。

我创建了一个简单的状态圆圈图形(一个 30 px 的圆圈,填充颜色),具有 3 种颜色状态:黑色表示准备就绪;绿色运行;红色表示完成。

我相信这个问题与repaint()在一个单独的线程上并计划在可能的时候运行有关吗?我尝试将更新图形的代码放在其自己的线程可运行中,然后使用线程.join()确保代码已完成运行,但这不起作用。

编辑

编辑:删除我用于演示的代码,并根据注释替换为单个可运行的代码示例。如果您运行代码,您将看到在您单击按钮后,当每个方法启动和停止时图形不会更新,它会等到两个方法都运行然后重新绘制图形。

    package graphicsUpdateDemo;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.beans.Transient;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

/**
 * Application entry
 */
public class App{
    public static void main(String[] args) {        
        SwingUtilities.invokeLater(new Runnable() {         
            @Override
            public void run() {
                new MainFrame();                
            }
        });
    }
}

/**
 * Main frame
 */
class MainFrame extends JFrame implements SomeListener{
    private AddedPanel addedPanel;

    // Constructor
    public MainFrame(){
        // Set frame properties
        setSize(500, 500);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        setLayout(new FlowLayout(FlowLayout.CENTER, 5, 20));

        // Create AddedPanel.
        addedPanel = new AddedPanel();
        add(addedPanel);

        // Set AddedPanel listener to this JFrame.
        addedPanel.setSomeListener(this);       
    }

    // AddedPanel listener method
    @Override
    public void doStuff() {
        // run simulated sort methods
        sort1();
        sort2();        
    }

    // Simulated sort 
    // .......graphic should turn green as soon as method starts
    // .......graphic should turn red as soon as method finishes.
    private void sort1() {
        // repaint graphic to show method is starting
        addedPanel.statusOne.setStatus(SortStatus.running);

        // EDIT: Make panel repaint itself.
        addedPanel.paintImmediately(0, 0, getWidth(), getHeight());

        // Simulate work being done.
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }       

        // repaint graphic to show methid is finished
        addedPanel.statusOne.setStatus(SortStatus.finished);
        // EDIT: Make panel repaint itself.
        addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
    }

    // Simulated sort
    // .......graphic should turn green as soon as method starts
    // .......graphic should turn red as soon as method finishes.
    private void sort2() {
        // repaint graphic to show method is starting (green)
        addedPanel.statusTwo.setStatus(SortStatus.running);
        // EDIT: Make panel repaint itself.
        addedPanel.paintImmediately(0, 0, getWidth(), getHeight());

        // Simulate work being done.
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }   

        // repaint graphic to show method is finished.
        addedPanel.statusTwo.setStatus(SortStatus.finished);
        // EDIT: Make panel repaint itself.
        addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
    }
}

/**
 * Panel to add to MainFrame
 */
class AddedPanel extends JPanel{
    // Button listener
    SomeListener listener;
    // Button
    private JButton aButton = new JButton("Click Me");

    // Create Status Circles for showing method state.
    public StatusCircles statusOne = new StatusCircles();
    public StatusCircles statusTwo = new StatusCircles();

    // Constructor.
    public AddedPanel(){
        setLayout(new BorderLayout(0, 15));

        // Add button to panel.
        add(aButton, BorderLayout.NORTH);

        // Make panel for holding graphics and labels.
        JPanel resultsPanel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(5, 5, 5, 5);
        resultsPanel.add(statusOne, c);
        c.gridx = 1;
        resultsPanel.add(new JLabel("Method A"), c);
        c.gridx = 0; c.gridy = 1;
        resultsPanel.add(statusTwo, c);
        c.gridx = 1;
        resultsPanel.add(new JLabel("Method B"), c);

        add(resultsPanel, BorderLayout.CENTER);

        aButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if(listener != null){
                    listener.doStuff();
                }               
            }
        });
    }

    public void setSomeListener(SomeListener listener){
        this.listener = listener;
    }
}

/**
 * Graphic for showing user state of method:
 *      black for ready
 *      green for running
 *      red for finished
 */
class StatusCircles extends JPanel{
    private SortStatus sortStatus;
    private Ellipse2D.Double statusCircle = new Ellipse2D.Double(2, 2, 25, 25);

    // Constructor
    public StatusCircles(){
        sortStatus = SortStatus.ready;
    }

    @Override
    protected void paintComponent(Graphics g) {     
        // Cast Graphics to Graphics2D
        Graphics2D g2 = (Graphics2D)g;

        // Turn on anti aliasing
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Set background
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, getWidth(), getHeight());

        // Fill status circle with color based on status field
        switch(sortStatus){
        case ready:
            g2.setColor(Color.BLACK);
            g2.fill(statusCircle);
            break;
        case running:
            g2.setColor(Color.GREEN);
            g2.fill(statusCircle);
            break;          
        case finished:
            g2.setColor(Color.RED);
            g2.fill(statusCircle);
            break;
        }
    }       

    @Override
    @Transient
    public Dimension getPreferredSize() {
        return new Dimension(30, 30);               
    }

    // Set state method is in.
    public void setStatus(SortStatus status) {
        this.sortStatus = status;       
        repaint();      
    }   
}

/**
 * Interface
 */
interface SomeListener{
    public void doStuff();
}

/**
 * Enum for depicting status of graphic.
 */
enum SortStatus {
    ready,
    running,
    finished
}

编辑

“repaint 方法提出更新查看区域的请求并立即返回。它的效果是异步的,这意味着由 JVM 在单独的线程上执行 paintComponent 方法。” - Liang 介绍的 Java 编程。

我认为问题是 A)在我的无知中,我的程序设计正在做一些理智的程序员不会做的事情,和/或 B)我不知道如何让程序改变图形颜色,然后发生这种情况,然后继续工作在任何线程上完成工作(EDT,主线程?)。

我确实遇到了一个建议永远不要减慢“主线程”来等待绘制的答案。而是为每个状态圈制作图标,然后交换图标 - 我想这会强制立即重绘任何持有图标的东西?但是,这是否表明有一种方法可以强制立即重新绘制?

思想实验:你有一个循环运行 100 次,每次迭代需要一秒钟。您希望通过将圆圈的颜色更改为一百种不同颜色中的一种来向用户展示每次迭代。你需要为此制作 100 个不同的图标吗?或者,我想要做的是每次迭代更改圆的填充颜色。...但是如何在每次迭代时强制重新绘制圆圈?

编辑

不知道这是否是“正确”的解决方案,但程序现在可以按我的意愿运行。我addedPanel.paintImmediately(0, 0, getWidth(), getHeight());在方法调用要求更改图形颜色之后直接放置了这些。我更新了上面的工作示例代码,编辑由“//EDIT:使面板重绘本身”描述。

编辑

现在我更有信心,我在正确的轨道上。我相信我已经实施了向我推荐的事情。一旦我理解 SwingWorker 基本上就像 Android 的一样,理解 SwingWorker 就来得非常快asynTask()(那是我第一次学习它的地方,这就是我这样说的原因)。并且通过睡眠的模拟工作发生在它自己的线程中,在 EDT 之外,所以现在可以(?)((不是我需要我的程序小睡))现在,是完整的工作代码:

package graphicsUpdateDemo;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.beans.Transient;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

/**
 * Application entry
 */
public class App{
    public static void main(String[] args) {        
        SwingUtilities.invokeLater(new Runnable() {         
            @Override
            public void run() {
                new MainFrame();                
            }
        });
    }
}

/**
 * Main frame
 */
class MainFrame extends JFrame implements SomeListener{
    private AddedPanel addedPanel;

    // Constructor
    public MainFrame(){
        // Set frame properties
        setSize(500, 500);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setLayout(new FlowLayout(FlowLayout.CENTER, 5, 20));

        // Create AddedPanel.
        addedPanel = new AddedPanel();
        add(addedPanel);

        // Set AddedPanel listener to this JFrame.
        addedPanel.setSomeListener(this);

        // Call setVisible last
        setVisible(true);
    }

    // AddedPanel listener method
    @Override
    public void doStuff() {
        // Call sort1(), when that finishes have it call sort2().
        sort1();

    }

    // Simulated sort 
    // .......graphic should turn green as soon as method starts
    // .......graphic should turn red as soon as method finishes.
    private void sort1() {
        // repaint graphic to show method is starting
        addedPanel.statusOne.setStatus(SortStatus.running);

        // Run sort in its own thread.
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){

            @Override
            protected Void doInBackground() throws Exception {
                // Simulate work being done.
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void done() {             
                // repaint graphic to show methid is finished
                addedPanel.statusOne.setStatus(SortStatus.finished);

                // Call sort2
                sort2();
            }

        };      
        worker.execute();       
    }

    // Simulated sort
    // .......graphic should turn green as soon as method starts
    // .......graphic should turn red as soon as method finishes.
    private void sort2() {
        // repaint graphic to show method is starting (green)
        addedPanel.statusTwo.setStatus(SortStatus.running);

        // Run sort in its own thread
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){

            @Override
            protected Void doInBackground() throws Exception {
                // Simulate work being done.
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            protected void done() {             
                // repaint graphic to show method is finished.
                addedPanel.statusTwo.setStatus(SortStatus.finished);
            }

        };      
        worker.execute();
    }
}

/**
 * Panel to add to MainFrame
 */
class AddedPanel extends JPanel{
    // Button listener
    SomeListener listener;
    // Button
    private JButton aButton = new JButton("Click Me");

    // Create Status Circles for showing method state.
    public StatusCircles statusOne = new StatusCircles();
    public StatusCircles statusTwo = new StatusCircles();

    // Constructor.
    public AddedPanel(){
        setLayout(new BorderLayout(0, 15));

        // Add button to panel.
        add(aButton, BorderLayout.NORTH);

        // Make panel for holding graphics and labels.
        JPanel resultsPanel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(5, 5, 5, 5);
        resultsPanel.add(statusOne, c);
        c.gridx = 1;
        resultsPanel.add(new JLabel("Method A"), c);
        c.gridx = 0; c.gridy = 1;
        resultsPanel.add(statusTwo, c);
        c.gridx = 1;
        resultsPanel.add(new JLabel("Method B"), c);

        add(resultsPanel, BorderLayout.CENTER);

        aButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                if(listener != null){
                    listener.doStuff();
                }               
            }
        });
    }

    public void setSomeListener(SomeListener listener){
        this.listener = listener;
    }
}

/**
 * Graphic for showing user state of method:
 *      black for ready
 *      green for running
 *      red for finished
 */
class StatusCircles extends JPanel{
    private SortStatus sortStatus;
    private Ellipse2D.Double statusCircle = new Ellipse2D.Double(2, 2, 25, 25);

    // Constructor
    public StatusCircles(){
        sortStatus = SortStatus.ready;
    }

    @Override
    protected void paintComponent(Graphics g) {     
        // Cast Graphics to Graphics2D
        Graphics2D g2 = (Graphics2D)g;

        // Turn on anti aliasing
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Set background
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, getWidth(), getHeight());

        // Fill status circle with color based on status field
        switch(sortStatus){
        case ready:
            g2.setColor(Color.BLACK);
            g2.fill(statusCircle);
            break;
        case running:
            g2.setColor(Color.GREEN);
            g2.fill(statusCircle);
            break;          
        case finished:
            g2.setColor(Color.RED);
            g2.fill(statusCircle);
            break;
        }
    }       

    @Override
    @Transient
    public Dimension getPreferredSize() {
        return new Dimension(30, 30);               
    }

    // Set state method is in.
    public void setStatus(SortStatus status) {
        this.sortStatus = status;       
        repaint();      
    }   
}

/**
 * Interface
 */
interface SomeListener{
    public void doStuff();
}

/**
 * Enum for depicting status of graphic.
 */
enum SortStatus {
    ready,
    running,
    finished
}
4

1 回答 1

5

使用此处显示的方法,让每个排序从单独的 更新其显示SwingWorker,而Supervisor工作人员监视 aCountDownLatch以确定所有排序何时完成。

图片

附录:我以前从未见过 Applet ,也从未见过 SwingWorker……我不明白为什么我需要确定何时完成所有工作……我已经编辑了这个问题。

  • 该示例也是混合的。

  • SwingWorker有助于避免阻塞EDT

  • 确定done告诉您何时(重新)启用开始按钮。

  • 尝试在标签中将a PropertyChangeListener, shown here添加到 a ColorIcon, seen here 。每次你setProgress()在worker中,你都会看到一个对应的PropertyChangeEvent.

于 2013-07-15T21:18:26.763 回答