Noah Bender
Noah Bender

Reputation: 1

I am trying to create a Java program that will make the letters appear one at a time in a JLabel

I am trying to create a program in java that makes the letters of a string appear one at a time into a JLabel, but the text just appears all at once every time. The more the delay on the Thread.Sleep();, the longer it takes to appear. I think what is happening is that it is writing it all out and then printing it into the Label, but i still don't know what to do to fix it. The code is here:

package uiTesting;

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

public class ScrollingText extends JFrame {

private JPanel contentPane;

        //Variables and values

public static String ThingToBePrinted = "You look down the gigantic hallway, the cold breath of spirits breathing down your neck. "
        + "Its nothing you haven't felt before. The spirits of those long past were always closer here, where many of them met"
        + " their end. Maybe you would be one of them, someday. But not today. Today, there was still too much to be done to rest.";

public static String ThingPrinted = "";

public static int Mili = 100;

public String html1 = "<html><body style='width: ";

public String html2 = "px'>";
/**
 * Launch the application.
 */
public static void main(String[] args) {
    System.out.println(ThingToBePrinted.length());
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                ScrollingText frame = new ScrollingText();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}


/**
 * Create the frame.
 */
public ScrollingText() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 719, 504);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);
        //The only Label
    JLabel Scrolling_L1 = new JLabel("");
    Scrolling_L1.setFont(new Font("Tahoma", Font.PLAIN, 15));
    Scrolling_L1.setVerticalAlignment(SwingConstants.TOP);
    Scrolling_L1.setBounds(10, 11, 693, 354);
    contentPane.add(Scrolling_L1);

        //The only Button
    JButton Master_B1 = new JButton("Print Text");
    Master_B1.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            try {
                //scrolling function
                for (int i = 0; i < ThingToBePrinted.length(); i++) {
                    String x = String.valueOf(ThingToBePrinted.charAt(i));
                    ThingPrinted = ThingPrinted + x;
                    Scrolling_L1.setText(html1 + "500" + html2 + ThingPrinted); //Html for wrapping text
                    Thread.sleep(Mili); //Delay between letters
                }
            }catch (Exception e){
                JOptionPane.showMessageDialog(null, "Error");
            }
        }
    });
    Master_B1.setFont(new Font("Tahoma", Font.PLAIN, 25));
    Master_B1.setBounds(164, 385, 375, 70);
    contentPane.add(Master_B1);

}
}

I would really appreciate any solution at this point, I've been troubleshooting for hours

Upvotes: 0

Views: 56

Answers (2)

Karan Bhagat
Karan Bhagat

Reputation: 329

It is because you are updating the JLabel in the UI thread itself from the event handler. Better way is to start a new thread in the event handler and then update the JLabel from this new thread. Following is the section you need to use in your code. I have tested it, it works.

Master_B1.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
            try { // Start new thread here for updating JLabel.
                new Thread() {
                    public void run() {
                         //scrolling function
                        for (int i = 0; i < ThingToBePrinted.length(); i++) {
                            String x = String.valueOf(ThingToBePrinted.charAt(i));
                            ThingPrinted = ThingPrinted + x;
                            Scrolling_L1.setText(html1 + "500" + html2 + ThingPrinted); //Html for wrapping text
                            try {
                                Thread.sleep(Mili); // Delay between letters
                            } catch (Exception e) {
                            }
                        }
                    }
                }.start();
            }catch (Exception e){
                JOptionPane.showMessageDialog(null, "Error");
            }
        }
});

Upvotes: 1

jpw
jpw

Reputation: 44891

Your problem is related to how concurrency works in Swing. One (imperfect) solution is to use a SwingWorker. You could change your action listener to this:

Master_B1.addActionListener(event -> {
    SwingWorker<Object, Void> worker = new SwingWorker<Object, Void>() {
        @Override
        protected String doInBackground() throws InterruptedException {

            for (int i = 0; i < ThingToBePrinted.length(); i++) {
                ThingPrinted += ThingToBePrinted.charAt(i);
                Scrolling_L1.setText(html1 + "500" + html2 + ThingPrinted); // Html for wrapping text
                Thread.sleep(Mili); //Delay between letters
            }

            return null;
        }
    };

    worker.execute();

});

Read this tutorial: Lesson: Concurrency in Swing to get a better understanding of the topic. (You might want to read up on concurrency in Java in general also, see this tutorial for example).

Upvotes: 1

Related Questions