periodic update of JLabel using javax.swing.timer is not stable (fluctuating)

226 views Asked by At

I want to update a JLabel with a constant rate of about 300ms. Currently I am using the following code section to do this:

class setLabel {

    PdfHandler PdfHandling = new PdfHandler(pdfName);//as the name implies, this object is to handle pdf's
    Iterator<String> textIterator=PdfHandling.getPageStringList(pageNr).iterator();//extracting text from a pdf word by word

    ActionListener task = new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            centerLabel.setText(textIterator.next());//update the JLabel cenetrLabel with the next string in the textIterator
        }
    };

    public void startTask(){
            new javax.swing.Timer(rate, task).start();
    }

}

The problem is that using this code leads to a non constant update of the JLabel (centerLabel). This means that the time between two "new" labels is not constant (which you can see with your bare eyes) and some strings seem to be skipped because of the too short time between the updates. I have already tried several different things like using invokeLater or sheduledExecutorService but all seemed to have the same problem. My question is, if this would be the right way to do this and if using the JLabel approach for this task was a bad idea from the beginning? If so, what would be a good approach to display strings one after another in a smooth controlled way (somewhat like a video).

Thank you very much, any help and suggestions are appreciated.

Edit:

I have tried to use the following code instead of the Iterator, althoug I don't know if this is faster:

class stringBuffer {
    private ArrayList<String> buf;
    private final int size;
    private int index;

    stringBuffer(ArrayList<String> list){
            buf = list;
            size = buf.size();
    }

    public String getNext(){
            if(index < size){
                return buf.get(index++);
            } else {
                return null;
            }
    }

}

The problem still remained so I tried to just access the list straight away

centerLabel.setText(list.get(index++));

but this still didn't solve the problem.

2nd Edit:

Here is a runnable example which demonstrates the problem:

import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

import java.awt.event.*;
import java.awt.BorderLayout;

class JLabelTest {
    public static void main(String[] args){
        BuildGui gui = new BuildGui();
    }
}

class BuildGui {
    JFrame topLevelFrame;
    JLabel centerLabel;
    JButton startButton;

    BuildGui(){
        initFrame();
        addElements();
        addActionListeners();
    }

    public void initFrame(){
        topLevelFrame = new JFrame("JFrame");
        topLevelFrame.setBounds(200, 200, 400, 200);
        topLevelFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        topLevelFrame.setVisible(true);
    }

    public void addElements(){
        centerLabel = new JLabel("Center");
        startButton = new JButton("Start");

        centerLabel.setHorizontalAlignment(JLabel.CENTER);
        centerLabel.setVerticalAlignment(JLabel.CENTER);

        topLevelFrame.add(startButton, BorderLayout.SOUTH);
        topLevelFrame.add(centerLabel,BorderLayout.CENTER);
    }

    public void addActionListeners(){

        startButton.addActionListener( new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                new setLabel().startTask();
                startButton.setEnabled(false);
            }
        });

    }


    class setLabel {

        private final String[] stringArray =     {"one","two","one","two","one","two","one","two"};//just something to display
        private final int size = stringArray.length;
        private final ArrayList<String> list = new ArrayList<String>(Arrays.asList(stringArray));
        private int index = 0;

        ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
            centerLabel.setText(list.get(index++));
            if(index >= size) index = 0;
            }
        };

        public void startTask(){
            new javax.swing.Timer(300, task).start();
        }

    }

}

3rd EDIT

I have got told that the previous example had several issues. So I tried to fix them, I hope this example is better

import java.util.ArrayList;
import java.util.Arrays;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;

import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.EventQueue;

class JLabelTest2 extends JFrame {
    JLabel centerLabel;
    JButton startButton;

    public static void main(String[] args){
        EventQueue.invokeLater(() -> {
            JLabelTest2 gui = new JLabelTest2();
            gui.setVisible(true);
        });
    }

    JLabelTest2(){
        initUI();
    }

    public void initUI(){
        setBounds(200, 200, 400, 200);

        centerLabel = new JLabel("Center");
        startButton = new JButton("Start");

        centerLabel.setHorizontalAlignment(JLabel.CENTER);
        centerLabel.setVerticalAlignment(JLabel.CENTER);

        getContentPane().add(startButton, BorderLayout.SOUTH);
        getContentPane().add(centerLabel,BorderLayout.CENTER);

        setDefaultCloseOperation(EXIT_ON_CLOSE);

        startButton.addActionListener( new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent e) {
                new setLabel().startTask();
                startButton.setEnabled(false);
            }
        });

        pack();

    }

    class setLabel {

        private final String[] stringArray = {"one","two","one","two","one","two","one","two"};
        private final int size = stringArray.length;
        private final ArrayList<String> list = new ArrayList<String>(Arrays.asList(stringArray));
        private int index = 0;

        ActionListener task = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                centerLabel.setText(list.get(index++));
                if(index >= size) index = 0;
            }
        };

        public void startTask(){
            new javax.swing.Timer(300, task).start();
        }

    }

}

But somehow I have still got the problem on my machine. I have not got a lot running and the issue doesn't change even if I close all running programs. Are there maybe still some problems in the code I don't see?

4th Edit

Adding centerLabel.repaint() didn't change the behavior. I still see the labels change but with a unstable frequency. Infos of the environment:

OS: Ubuntu 14.04 LTS, 64bit

Procesor: Intel Core™ i5-2430M CPU @ 2.40GHz × 4

Memory: 5,7 GiB

Java Infos:

java version "1.8.0_102"

Java(TM) SE Runtime Environment (build 1.8.0_102-b14)

Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode)

0

There are 0 answers