0

我的问题似乎来自 Swing 在内部使用线程的方式。当应用程序执行一项耗时的任务时,我想显示一个繁忙的指示器,我称之为 Spinner。我有一个 Swing JDialog 并改编了Oracle 教程中的代码

我该如何解决这个问题?

我有一个 startSpinner()- 和一个 stopSpinner()- 方法。他们工作得很好。我有 2 个显示或隐藏它的 JButton。但问题是,当我调用 startSpinner() 方法时,在执行一个运行大约 5 秒的任务时,微调器没有出现。 我想我必须处理 SwingUtilities.invokeLater。但我没有这方面的经验。这是我开始显示微调器的方法。

StartSpinner 方法:

    private void startSpinner() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            layerUI.start();
            if (!stopper.isRunning()) {
                stopper.start();
                setTitle("Working...");
            }
        }
    });
}

停止微调器方法:

public void stopSpinner() {
    logger.info("stopSpinner");
    layerUI.stop();
}  

WaitLayerUI 类:

package view;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;

import javax.swing.JComponent;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.plaf.LayerUI;

@SuppressWarnings("serial")
class WaitLayerUI extends LayerUI<JPanel> implements ActionListener {
private boolean mIsRunning;
private boolean mIsFadingOut;
private Timer mTimer;

private int mAngle;
private int mFadeCount;
private int mFadeLimit = 15;

@Override
public void paint(Graphics g, JComponent c) {
    int w = c.getWidth();
    int h = c.getHeight();

    // Paint the view.
    super.paint(g, c);

    if (!mIsRunning) {
        return;
    }

    Graphics2D g2 = (Graphics2D) g.create();

    float fade = (float) mFadeCount / (float) mFadeLimit;
    // Gray it out.
    Composite urComposite = g2.getComposite();
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade));
    g2.fillRect(0, 0, w, h);
    g2.setComposite(urComposite);

    // Paint the wait indicator.
    int s = Math.min(w, h) / 5;
    int cx = w / 2;
    int cy = h / 2;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setStroke(new BasicStroke(s / 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
    g2.setPaint(Color.white);
    g2.rotate(Math.PI * mAngle / 180, cx, cy);
    for (int i = 0; i < 12; i++) {
        float scale = (11.0f - i) / 11.0f;
        g2.drawLine(cx + s, cy, cx + s * 2, cy);
        g2.rotate(-Math.PI / 6, cx, cy);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, scale * fade));
    }

    g2.dispose();
}

@Override
public void actionPerformed(ActionEvent e) {
    if (mIsRunning) {
        firePropertyChange("tick", 0, 1);
        mAngle += 3;
        if (mAngle >= 360) {
            mAngle = 0;
        }
        if (mIsFadingOut) {
            if (--mFadeCount == 0) {
                mIsRunning = false;
                mTimer.stop();
            }
        } else if (mFadeCount < mFadeLimit) {
            mFadeCount++;
        }
    }
}

public void start() {
    if (mIsRunning) {
        return;
    }

    // Run a thread for animation.
    mIsRunning = true;
    mIsFadingOut = false;
    mFadeCount = 0;
    int fps = 24;
    int tick = 1000 / fps;
    mTimer = new Timer(tick, this);
    mTimer.start();
}

public void stop() {
    mIsFadingOut = true;
}

@Override
public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
    if ("tick".equals(pce.getPropertyName())) {
        l.repaint();
    }
}
}

我正在使用教程中的代码,除了 main 方法,也许这就是问题的原因。解决方案应该是这样的:

private void save(){
startSpinner();
performTimeConsumingSave();
stopSpinner();
}

目前,微调器根本没有出现。但是,当我显示 JOptionPane (暂停当前线程)时,它会显示出来。我也试过 Thread.sleep(100) 但这不是一个好主意。

4

1 回答 1

1

感谢 Michal 的评论和这篇文章How do I use SwingWorker in Java? 我能够解决这个问题。可能长期运行的方法 performTimeConsumingSave() 现在由 SwingWorker 调用。我确实喜欢建议的教程。所以 SwingWorkers 方法 doInBackground() 现在是从 ActionListener 的 actionPerformed 方法中调用的。与直接从 EDT 调用 save() 不同,对 save() 的调用由 SwingWorker 完成。为了确定保存是否成功,我正在检查 retval。现在微调器出现了,系统正在同时执行耗时的任务(save())。这是 PostWorker 代码:

    class PostWorker extends SwingWorker<Integer, Integer> {
    @Override
    protected Integer doInBackground() throws Exception {
        // Do a time-consuming task.
        starteSpinner();
        int status = save();
        stopSpinner();
        return status;
    }


    @Override
    protected void done() {
        try {
            if (get()== SAVE_OK) {
                JOptionPane.showMessageDialog(PostAusDetailDialog.this, "Mail saved.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
于 2015-01-26T09:17:55.970 回答