Swing界面刷新问题(转)

在Java Swing编程中,往往会遇到需要动态刷新界面的时候,例如动态刷新JLabel的文本,JTextField里的文本等等。但是往往却没有达到我们预期的效果,我相信很多朋友都遇到过本文将要说的这个问题。

如下图的Swing界面中,我们期望在点击按钮时,Jlabel和JTextField里的文本能不断的变化,并实时地显示出来。

这个例子中,我们期望点击按钮后,JLabel和JTextField中每隔一秒钟刷新一下文本,顺序的显示以下的几句文本:

Button clicked

Start to change text...

接着显示数字1到10

action end

很多人都会像下面的代码这样实现这个功能:

MainFrame.java

package com.longyg.test;

public class MainFrame extends javax.swing.JFrame {

    public MainFrame() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jLabel = new javax.swing.JLabel();
        labelText = new javax.swing.JLabel();
        jTextField = new javax.swing.JLabel();
        fieldText = new javax.swing.JTextField();
        button = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jLabel.setText("JLabel:");

        labelText.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        jTextField.setText("JTextField: ");

        button.setText("click");
        button.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                buttonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(10, 10, 10)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(button)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(labelText, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jTextField)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(fieldText, javax.swing.GroupLayout.PREFERRED_SIZE, 127, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(17, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(20, 20, 20)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel)
                    .addComponent(labelText, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTextField)
                    .addComponent(fieldText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(button)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        

    private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
        changeText("Button clicked");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        changeText("Start to change text...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        for (int i = 0; i < 10; i++) {
            changeText((i+1)+"");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
        changeText("action end");
    }                                      

    private void changeText(String text) {
        labelText.setText(text);
        fieldText.setText(text);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new MainFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton button;
    private javax.swing.JTextField fieldText;
    private javax.swing.JLabel jLabel;
    private javax.swing.JLabel jTextField;
    private javax.swing.JLabel labelText;
    // End of variables declaration
}

可以看到,在buttonActionPerformed方法中,我们多次调用了setText来期望改变JLabel和JTextField中的文本。

当我们运行这段代码,你会很遗憾的发现,点击click后,JLabel和JTextField中并没有如我们所期望的不断的更新并显示不同的文本。而是点击按钮后,界面仿佛被卡住一样,等过了一段时间后,显示出最后一句文本“action end”。

为什么会发生这样奇怪的现象呢?

Java Swing中,界面刷新是线程同步的,也就是说同一时间,只有一个线程能执行刷新界面的代码。如果要多次不断地刷新界面,必须在多线程中调用刷新的方法。

本例中,在buttonActionPerformed方法中多次调用了setText方法来试图刷新JLabel和JTextField的文本。buttonActionPerformed方法运行在主线程中,所以每次调用setText都是运行在主线程中,而且是顺序的执行的。在前面几次调用setText后,线程并没有退出,所以界面刷新线程不能获得执行刷新的机会。而当最后一次setText后,线程退出,界面才能执行刷新。所以我们只能看到最后一次setText的值。

因此,要解决这个问题,我们必须把buttonActionPerformed方法中的代码段放到一个单独的线程中执行。这样它就不会使线程阻塞,当每次setText后,界面刷新线程也能得到执行的机会,从而刷新界面。

下面是修改后的代码,只有buttonActionPerformed方法的代码被修改,其他部分的代码与上面的完全一致。

private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                changeText("Button clicked");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                changeText("Start to change text...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                for (int i = 0; i < 10; i++) {
                    changeText((i+1)+"");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
                changeText("action end");
            }
        }).start();
    }

我们可以看到,新的buttonActionPerformed方法中,仅仅是把整个代码段放在了一个线程中,并启动了线程。

我们在每次setText后,都睡眠了1秒钟,是为了看到界面真的实时的变化了,如果不睡眠,界面刷新会一闪而过,不利于观察。

再次运行代码,会发现,终于得到了我们期望的效果:JLabel和JTextField中的文本动态的变化了!

http://www.cnblogs.com/longyg/archive/2012/07/03/2575482.html

时间: 2024-08-03 07:56:34

Swing界面刷新问题(转)的相关文章

android 单线程多任务断点排队下载(支持多界面刷新)

最近在做一个单线程多任务的断点排队下载的功能,网上确实有很多这样的demo.但是呢我发现大部分网上的demo都是很些不完整的要么就是有缺陷的,可能是我还没找到.今天我给大家带来的一个功能完整的并且可以多界面刷新,就比如:我当前界面点了下载放后台下载了,退出了当前界面在进来网上很多这样demo都没做继续更新界面.并且还做了排队. 首先我们先制定下计划: 1.我们做的是断点下载,断点下载肯定是需要记录当前下载的进度和文件总大小的.这个当然是放在数据库里面最好,当然肯定会是异步来操作数据库,这里我们就

Android界面刷新

Android程序中可以使用的界面刷新方法有两种,分别是利用Invalidate和利用postInvalidate()来实现在线程中刷新界面. 1.利用Handler刷新界面: Invalidate不能直接在子线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用.实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新,而在子线程中通过sendMessage发送界面更新消息. // 在o

Java Swing界面编程(24)---事件处理:键盘事件及监听处理

在Swing的事件处理中也可以对键盘的操作进行监听,直接使用KeyListener接口即可. package com.beyole.util; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import javax

Java Swing界面编程(25)---事件处理:鼠标事件及监听处理

如果想对一个鼠标的操作进行监听,如果鼠标按下.松开等,则可以使用MouseListener接口. package com.beyole.util; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JFrame; import

项目经验谈---IM新消息界面刷新异常处理记录

项目中使用到ContentObserver来观察Provider的变化,当对Uri做数据库操作时对应的Provider会发一条Notify消息调用UI上层的ContentChange方法,在这个ContentChange方法中是重新对本会话的消息表的Uri查询一次,查询完毕后更新界面并将listView滑到最底部,有发现如果删除某会话的最后一条数据再新增一条数据时界面没有刷新的异常.排查后发现每一条短消息对应一个ThreadId,一旦删除了某个会话中的最后一条消息,在新建一条信息会重新创建一个新

Java Swing界面编程(1)

写多了jsp,对于页面式的系统已经写烦了,本人也開始着手于java swing的学习,作为菜鸟,仅想用博客记录下我的swing学习的历程.话不多说,首先開始我的第一个窗体化程序. 下面给出源码: package com.beyole.util; import java.awt.Color; import javax.swing.JFrame; public class test1 { public static void main(String[] args) { JFrame frame=ne

Java Swing界面编程(23)---事件处理:编写用户验证登录用例

LoginCheck: package com.beyole.util; class LoginCheck {//编写登录验证类 private String userName;//用户名 private String password;//密码 public LoginCheck(String userName,String password)//复写构造方法 { this.userName=userName;//为用户名赋值 this.password=password;//为密码赋值 }

Java Swing界面编程(21)---事件处理:窗体事件

Splay树的插入操作,只需要处理好插入节点的孩子节点就可以了,最重要的是不要破坏了BST的基本规则. 因为高度并不是Splay树的首要因素,所以插入的时候也是使用splay操作,然后在根节点插入. 参考:http://www.geeksforgeeks.org/splay-tree-set-2-insert-delete/ 对比一下使用插入创建的树和手工创建数的区别,先序遍历的结果: #pragma once #include<stdio.h> #include <stdlib.h&g

WPF 数据绑定,界面刷新的两种方法-----INotifyPropertyChanged

.Netformwork4.0及以下版本 -------INotifyPropertyChanged 命名空间: System.ComponentModel 后台代码 public partial class DvrWnd : UserControl { public DvrWnd() { InitializeComponent(); } private void InitInfo() { for (int i = 0; i < 10; i++) { DvrInfo dvrInfo = new