java - 动态改变自定义图形的颜色

标签 java swing graphics jpanel repaint

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

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

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

我认为问题与 repaint() 处于单独的线程上并计划在可能时运行有关?我尝试将更新图形的代码放在它自己的线程可运行程序中,然后使用 thread.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 方法提出更新 View 区域的请求并立即返回。它的效果是异步的,这意味着由 JVM 在单独的线程上执行 paintComponent 方法。” - Liang 的 Java 编程简介。

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

我确实遇到了一个答案,建议永远不要放慢“主线程”的速度来等待绘制的东西;而是为每个状态圈制作图标,然后交换图标——我猜这会强制立即重绘持有图标的任何东西?不过,这难道不表明有一种方法可以强制立即重绘吗?

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

编辑

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

编辑

现在我更有信心我走在正确的轨道上。我相信我已经实现了推荐给我的事情。一旦我理解了 SwingWorker 基本上就像 Android 的 asynTask()(那是我第一次学习它的地方,这就是我这样说的原因),对它的理解就变得非常快。通过 sleep 进行的模拟工作发生在它自己的线程中,不在美国东部时间,所以现在没问题了(?)((不是我需要我的程序小睡一下))现在,这里是完整的工作代码:

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
}

最佳答案

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

image

附录:我从来没有见过 Applet ,也没有 SwingWorker……我不知道我不明白为什么我需要确定何时完成所有排序……我已经编辑了问题。

  • 例子也是一个hybrid .

  • SwingWorker有助于避免阻塞 EDT .

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

  • 尝试添加一个 PropertyChangeListener,如图所示 here , 到 ColorIcon,看到 here , 在你的标签中。每次您在 worker 中 setProgress() 时,您都会看到相应的 PropertyChangeEvent

关于java - 动态改变自定义图形的颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17662380/

相关文章:

Java:尝试让我的代码打印一行而不是五行

java - 有状态 session Bean 的超时设置?

winforms - TextRenderer.MeasureText 的问题

java - 为什么 hasNextInt() 方法不适用于大整数?

java - 在有限的空间内显示图像的特定部分?

java - 使字体大小小于 1 Java GUI

java - 在 JScrollPane 中包含两个 JLabel(一个在另一个之上)

C# picturebox 绘制速度

c++ - 为什么核心 C 或 C++ 文本不提及声音或图形?

java - CXF & Camel : IllegalArgumentException: Get the wrong parameter size to invoke the out service