Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

JTombola

0.00/5 (No votes)
30 Sep 2016 1  
Desktop application with a tombola-like spinning label

JTombola screenshot

Introduction

I developed this tombola-like application in order to provide a simple yet visually attractive (and reusable) UI for a prize raffle at the CONAIS congress.

Around 500 participants attend the congress each year. At the end of the event, a small raffle of various items is held. These gifts are to be distributed among the assistants.

Thus, the organizers requested me a desktop application in which the names of the participants should be shown in a spinning label, a sort of electronic tombola, so the participants should be aware when their names would show up and when the tombola operator stops the tombola showing the name of the prize winner. The participants' names are stored in a plain text file, so the application should load the list when started. The list of winners should be saved in a plain text file at the end of the raffle.

I propose using Java as the programming language, because it has the facilities to develop desktop applications, it has multithreaded and platform-independent capabilities, and is an active and robust platform. I specifically use the OpenJDK 7 platform because it's free software. As a matter of fact, all the specified requirements could be easily met using this platform.

Using the Code

The solution is divided into the following packages:

ocb.jtombola.core
  +- NamesLoader.java
  +- TombolaLabel.java
ocb.jtombola.gui
  +- MainForm.java
  +- TombolaPanel.java

The package ocb.jtombola.core contains the foundation classes. The NamesLoader class performs I/O operations and the TombolaLabel class is the spinning label simulating the tombola.

The package ocb.jtombola.gui contains the MainForm class, which is the main window of the application; and the TombolaPanel class, which contains all the UI elements.

The Container Panel

The class TombolaPanel extends JPanel and implements ActionListener and MouseListener in order to manage user events:

public class TombolaPanel extends JPanel implements ActionListener, MouseListener

This class has the following main attributes used to set the background image, the congratulations label, the toggle button, and the spinning label, respectively:

private final ImageIcon background;
private final JLabel lblCongrats;
private final JButton btnGo;
private final TombolaLabel tombolaLabel;

In the constructor method of this class resides the code which outlines the overall operation of the application:

public TombolaPanel() {
  isSpinning = false;
  tombolaLabel = new TombolaLabel("input.txt");
  tombolaLabel.setMillis(80);
  tombolaLabel.setFont( new Font("Nimbus Sans L", Font.BOLD, 74) );
  tombolaLabel.setForeground(Color.WHITE);
  tombolaLabel.setBounds(50, 180, 930, 200);
  tombolaLabel.setHorizontalTextAlignment(JLabel.CENTER);
  tombolaLabel.setBorder(new LineBorder(Color.GRAY));
  /* Customization of TombolaPanel */
  /* Customization of btnGo */
  /* Customization of lblCongrats */
}

This code creates an instance of TombolaLabel with the file name containing the list pf names as a parameter, and then customizes it. The notable method here is setMillis(), which sets the delay in the label to show up the names. The methods setFont(), setForeground(), setBounds() and setHorizontalTextAlignment() are to customize the label. The method setBorder() is only used to debug the size and position of the label, although this border can be personalized in your custom UI.

The btnGo object is the toggle button used to fire up the tombola, and the lblCongrats is a sort of congratulations message shown after stopping the tombola and showing the name of the winner. Both components, and the panel container, are customized as shown in the screenshot of the application.

The btnGo code that starts up the tombola is:

@Override
public void actionPerformed(ActionEvent e) {
  if (e.getSource() == btnGo) {
    if (isSpinning) {
      isSpinning = false;
      tombolaLabel.stop();
      lblCongrats.setVisible(true);
    } else {
      isSpinning = true;
      tombolaLabel.go();
      lblCongrats.setVisible(false);
    }
  }
}

When the isSpinning flag is on, the button stops and the congratulations message is shown up. The opposite occurs on the other case.

The Spinning Label

The class TombolaLabel does the job. It inherits from the cool MultiLineLabel class by Samuel Sjoberg. It also implements the Runnable interface in order to display the spinning names in its own thread:

public class TombolaLabel extends MultiLineLabel implements Runnable

It is important to mention the volatile attribute used for controlling the tombola feature:

private volatile Thread tombolaThread;

The constructor takes the file name as an argument and creates an instance of the NamesLoader class and loads the names. If the list if empty, then the program terminates, otherwise the list of winners and the random number generator are created:

public TombolaLabel(String fileName) {
  inputList = new NamesLoader( fileName );
  lstNames = inputList.getList();
  if ( lstNames.isEmpty() ) {
    JOptionPane.showMessageDialog(null,
                                  "No names in the input file",
                                  "Error",
                                  JOptionPane.ERROR_MESSAGE);
    System.exit(1);
  }
  lstWinners = new ArrayList<>();
  random = new Random();
}

The relevant code to start the tombola using the toggle button is:

public void start() {
  this.setUI(MultiLineLabelUI.labelUI);
  tombolaThread = new Thread(this);
  tombolaThread.start();
}

This method adds a slight border to the label, then creates a new thread using this class and then starts it, which in turn fires the following code:

@Override
public void run() {
  Thread thisThread = Thread.currentThread();
  while (tombolaThread == thisThread) {
    n = random.nextInt(lstNames.size());
    this.setText(lstNames.get(n));
    try {
      Thread.sleep(millis);
    } catch (InterruptedException ex) {
      ex.printStackTrace();
    }
  }
}

In this code, the label displays a name from the whole list, picked at random. The name is shown for a number of milliseconds in which we put the thread to sleep. When the thread wakes up, the process continues until the toggle button stops the tombola invoking the following code:

public void stop() {
  tombolaThread = null;
  this.setUI(MultiLineShadowUI.labelUI);
  lstWinners.add(lstNames.get(n));
  lstNames.remove(n);
}

This simply sets the tombolaThread to null so the tombola stops showing the name of the winner. We must add this name to the list of winners and also not forget to remove it from the list of participants because one assistant can only win one prize. The setUI() method adds a nice shadow highlighting the name.

This method to stop the thread is taken from the Sun (now Oracle) article, Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

Loading the Names of the Assistants

The NamesLoader class is relatively simple, as it loads the list of names from a given input file and stores it in a List<String> array list:

public void loadFile() {
  try {
    FileReader fr = new FileReader(this.fileName);
    BufferedReader br = new BufferedReader(fr);
    lstNames = new ArrayList<>();
    String name;
    while ((name = br.readLine()) != null) {
      lstNames.add(name);
    }
  } catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
  } catch (IOException ioe) {
    ioe.printStackTrace();
  }
}

Another relevant method in this class is:

public void saveWinners(List<String> list) {
  try {
    FileWriter fw = new FileWriter("winners.txt");
    for (String name : list) {
      fw.write(name);
      fw.write(System.getProperty("line.separator"));
    }
    fw.close();
  } catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
  } catch (IOException ioe) {
    ioe.printStackTrace();
  }
}

Which saves the list of winning names shown by the application.

The Main Form

The MainFrame class inherits from the JFrame class and implements the WindowListener interface in order to detect when the user closes the frame:

public class MainFrame extends JFrame implements WindowListener

It has an instance of class TombolaPanel:

TombolaPanel pnltombola;

I set the following properties to the frame in the constructor method:

public MainFrame() {
  this.pnltombola = new TombolaPanel();
  this.add(pnltombola);
  this.setTitle("Tómbola 2016");
  this.setResizable(false);
  this.setSize(1024, 750);
  this.setLocationRelativeTo(null);
  this.addWindowListener(this);
}

When the user closes the frame:

@Override
public void windowClosing(WindowEvent we) {
  pnltombola.saveWinners();
  System.exit(0);
}

We make sure that the list of winners is saved before the application exits.

Points of Interest

The code proposed is very simple, yet an effective approach to handle a single thread showing a name for a number of milliseconds emulating a tombola. This time can be set via the setMillis() method. We notice that 80 milliseconds is an ideal time to show up a name and read it by the audience, while fast enough to simulate the tombola effect. We stress-tested the JTombola with thousands of names and the performance was as expected.

The background image of the application used during the congress was drawed by a local designer, but the GIMP is really great at designing background images, like the one with the curved text that I designed for the sample application. I also used the awesome suite ImageMagick to create the (very simple) animated gif with the congratulations message via the convert -delay 100 -loop 0 yay*.png yay.gif command. By the way, the animated screenshot was made with silencast. I include a small list of names in the sample application. Can you guess who they are?

If you wish to use the JTombola in your own raffles, just replace the background and (if needed) the congratulations images under the /img folder and that's it. The background image is 1024x750 px and the congratulations one is 268x117 px; if you wish to use other image sizes then you must modify the relevant code in the TombolaPanel constructor and recompile the project. Changing the images of the toggle button are optional. Don't forget to replace the input.txt file with your own participants' names.

JTombola was a resounding success during the last edition of the congress, as the participants had a lot of fun (well, at least the winning participants).

History

  • v1.0.0 | Initial release | 30.sep.2016

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here