动态更改自定义图形的颜色

时间:2013-07-15 19:35:11

标签: java swing graphics jpanel repaint

问题:在运行方法之前,图形不会重新绘制。

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

我创建了一个简单的状态圆图形(带有填充颜色的30像素圆圈),有3种颜色状态:黑色表示准备好;绿色的跑步;红色完成。

我认为问题与repaint()在一个单独的线程上有关,并且计划在有能力时运行?我尝试将更新图形的代码放在自己的thread-runnable中,然后使用线程.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
}

修改

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

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

我确实遇到了一个答案,建议永远不要放慢“主线程”来等待事情的绘制;而是为每个状态圈制作图标然后交换图标 - 我想这会强制立即重绘任何持有图标的东西?但是,这不会表明有办法迫使立即重画吗?

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

修改

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

修改

现在我更有信心我走在正确的轨道上。我相信我已经实施了推荐给我的东西。一旦我理解它基本上像Android的asynTask()(这是我首先学习它的地方,这就是为什么我这样说),了解SwingWorker的速度非常快。通过睡眠模拟的工作发生在自己的线程中,在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
}

1 个答案:

答案 0 :(得分:5)

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

image

附录:我之前从未见过 Applet ,也没有 SwingWorker ...我不明白我为什么会这样做需要确定何时完成所有排序...我已编辑了问题。

  • 该示例也是hybrid

  • SwingWorker有助于避免阻止EDT

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

  • 尝试将PropertyChangeListener ColorIcon添加到标签中的setProgress()here。每当您PropertyChangeEvent在工作人员中时,您都会看到相应的{{1}}。