3

我有一个 Jframe,它是我的应用程序的窗口(以下代码中的 appFrame),其中包含大量逻辑并且需要大约 1 秒才能加载。同时我想向我的用户展示一个非常好的加载框架(initFrame)。但是,当我运行此代码时,initFrame 确实出现了,但它上面的 JLabel 中的文本并没有立即出现——它实际上在加载应用程序框架之前的短暂时间内根本没有出现。

如果我注释掉所有 appFrame,只启动 initFrame,文本会立即加载,根本不需要等待时间。为什么会这样?这可能是并发问题吗?

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() { //as per best practice for concurrency in swing - see http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
        @Override
        public void run() {
            final JFrame initFrame = new InitFrame();
            initFrame.setVisible(true);
            final AppFrame appFrame = new AppFrame();
            appFrame.setVisible(true);
            initFrame.setVisible(false);
            initFrame.dispose();
        }
    });

}
4

3 回答 3

3

我会将框架的创建分成两个线程。第一,初始化InitFrame。运行此线程并在对象上调用isShowing() 。InitFrame当它返回 true 时,运行第二个线程来初始化并显示AppFrame

这将强制两个帧的可见性之间的关系发生之前。

class Main {
    JFrame initFrame = null;
    AppFrame appFrame = null;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
               initFrame = new InitFrame();
               initFrame.setVisible(true);
            }
        });

        while(!initFrame.isShowing()) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
            }
        }

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                appFrame = new AppFrame();
                appFrame.setVisible(true);
                initFrame.setVisible(false);
                initFrame.dispose();
            }
        });

    }
}
于 2013-05-02T19:29:07.463 回答
2

这是您的AppFrame.

您可以使用线程运行测试:

java SplashTest true

或没有

java SplashTest

启用线程后,您会看到每 250 毫秒更新一次SplashFrameAppFrame或多或少。

未启用线程时,您会看到SplashFrame没有显示组件,应用程序“挂起”4 秒钟,然后您会看到AppFrame.

这个例子有点做作,但可能会给你一些想法。

请注意, 与SplashFrame没有“直接”连接AppFrame。所有的通信都是通过AppFrameWorkListener接口进行的。

我也把“工作”放在了AppFrame. 但实际上,如果有很多处理要完成,它应该从 UI 代码中提取出来,在单独Thread的.AppFrameSplashFrame

import javax.swing.*;

class SplashTest {

    static boolean useThread = false;

    public static void main(String[] args) {
        // Pass true at the command line to turn on threading.
        // No args, or any value other than true will turn off threading.
        if (args.length > 0) {
            useThread = new Boolean(args[0]);
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                SplashFrame splashFrame = new SplashFrame();
                splashFrame.setVisible(true);
                new AppFrame(splashFrame).setVisible(true);
            }});
    }

    private static class BaseFrame extends JFrame {
        public BaseFrame() {
            setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            setSize(200, 200);
            setTitle(getClass().getSimpleName());
        }
    }

    private static class SplashFrame extends BaseFrame implements AppFrameWorkListener {
        JLabel status;

        public SplashFrame() {
            setLocation(0, 0);
            status = new JLabel("Splash Frame");
            getContentPane().add(status);
        }

        public void appFrameWorkStart() {
            status.setText("Work started");
        }

        public void appFrameWorkProgress(long timeElapsed) {
            status.setText("Work has taken " + timeElapsed + "ms so far");
        }

        public void appFrameWorkDone() {
            // http://stackoverflow.com/questions/1234912/how-to-programmatically-close-a-jframe
            setVisible(false);
            dispose();
        }
    }

    private static class AppFrame extends BaseFrame {
        JLabel status;
        AppFrameWorkListener listener;

        public AppFrame(AppFrameWorkListener listener) {
            setLocation(200, 200);
            status = new JLabel("App Frame");
            getContentPane().add(status);

            this.listener = listener;

            // None of this 'heavy lifting' should be in a constructor.
            if (useThread) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        doLotsOfWork(4);
                    }
                }).start();
            } else {
                doLotsOfWork(4);
                onWorkDone();
            }
        }

        private void doLotsOfWork(int workLengthSeconds) {
            // We're starting. Ensure onWorkStart is called on the EDT,
            // as this method may be called from a different Thread.
            invokeOnWorkStartOnEDT();

            long start = System.currentTimeMillis();

            // Hammer the CPU for "workLengthSeconds" number of seconds.
            // And do some contrived progress reporting.
            long workLengthMs = workLengthSeconds * 1000;
            while (System.currentTimeMillis() - start < workLengthMs) {
                long innerStart = System.currentTimeMillis();
                // Consume 250ms CPU before issuing progress update.
                while (System.currentTimeMillis() - innerStart < 250);
                invokeOnWorkProgressOnEDT(System.currentTimeMillis() - start);
            }

            // We're done now. Ensure onWorkDone is called on the EDT,
            // as this method may be called from a different Thread.
            invokeOnWorkDoneOnEDT();
        }

        private void invokeOnWorkStartOnEDT() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkStart();
                }
            });
        }

        private void invokeOnWorkProgressOnEDT(final long timeElapsed) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkProgress(timeElapsed);
                }
            });
        }

        private void invokeOnWorkDoneOnEDT() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    onWorkDone();
                }
            });
        }

        private void onWorkStart() {
            status.setText("Work Started");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkStart();
            }
        }

        private void onWorkProgress(long timeElapsed) {
            status.setText("Work has taken " + timeElapsed + "ms so far");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkProgress(timeElapsed);
            }
        }

        private void onWorkDone() {
            status.setText("Work Done");
            if (null != listener) {
                // Tell someone who's interested in the work status.
                listener.appFrameWorkDone();
            }
        }
    }

    interface AppFrameWorkListener {
        public void appFrameWorkDone();
        public void appFrameWorkStart();
        public void appFrameWorkProgress(long timeElapsed);
    }

}
于 2013-05-02T20:53:11.217 回答
1

您应该使用 Java Thread 并且您可以同时向您的用户显示交互式启动画面(定制),而您的代码正在生成您想要的任何内容这里是一个教程只是看看

您应该使用线程来实现良好和高效的并发,就是这样

于 2013-05-02T19:38:33.293 回答