問題: メソッドが実行されるまで、グラフィックが再描画されません。
ボタンがクリックされると、2 つのメソッドが呼び出されます。各メソッド内には、このメソッドに関連付けられた (UI 内の) グラフィックの色を変更するコードがあります。メソッドが開始されると、グラフィックが黒から緑に変わります。メソッドが終了すると、色が緑から赤に変わります。次に、次のメソッドが呼び出され、そのグラフィックが緑色に変わり (メソッドが実行中)、メソッドが終了すると、そのグラフィックは赤色で塗りつぶされます (メソッドが終了しました)。
3 つの色の状態を持つ単純なステータス サークル グラフィック (塗りつぶし色の 30 ピクセルの円) を作成しました。緑はランニング用。赤は終了を意味します。
問題は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
}
編集
「再描画メソッドは、表示領域を更新する要求を提出し、すぐに戻ります。その効果は非同期です。つまり、別のスレッドで paintComponent メソッドを実行するのは JVM 次第です。」- Liang による Java プログラミングの紹介。
問題は、A) 私の無知のせいで、私のプログラム設計は正気のプログラマーがしないようなことをしている、および/または B) プログラムでグラフィックスの色を変更する方法がわからず、その後、それが発生した後、作業を続行することのいずれかであると思います作業が行われているスレッド(EDT、メインスレッド?)。
私は、物事が描かれるのを待つために「メインスレッド」を決して遅くしないことを示唆する答えに出くわしました。代わりに、各ステータスサークルのアイコンを作成してからアイコンを交換します-これは、アイコンを保持しているものをすぐに再描画することを強制すると思いますか? しかし、これは即時再描画を強制する方法があることを示唆しているのではないでしょうか?
思考実験: 100 回実行されるループがあり、各反復には 1 秒かかります。円の色を 100 の異なる色の 1 つに変更することによって、各反復をユーザーに表示したいとします。このために 100 個の異なるアイコンを作成する必要がありますか? または、私がやりたいことは、反復ごとに円の塗りつぶしの色を変更することです。...しかし、各反復で円の再描画を強制する方法は?
編集
それが「正しい」解決策かどうかはわかりませんが、プログラムは希望どおりに機能するようになりました。addedPanel.paintImmediately(0, 0, getWidth(), getHeight());
グラフィック色の変更を要求するメソッド呼び出しの直後にこれらを配置しました。上記の実際のコード例を更新しました。編集内容は「//EDIT: Make panel repaint its own」で示されています。
編集
今、私は自分が正しい道を進んでいるという自信を持っています。私は私に推奨されたことを実装したと信じています。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
}