65.9K
CodeProject 正在变化。 阅读更多。
Home

JTombola

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (5投票s)

2016 年 9 月 30 日

CPOL

6分钟阅读

viewsIcon

12206

downloadIcon

294

带有彩票式旋转标签的桌面应用程序

JTombola screenshot

引言

我开发了这个类似抽奖机的应用程序,为 CONAIS 大会 的奖品抽奖提供了一个简单而具有视觉吸引力(且可重用)的用户界面。

每年约有 500 名与会者参加此次大会。活动结束时,会举行一个小型的各种物品抽奖。这些奖品将分发给与会者。

因此,组织者要求我开发一个桌面应用程序,其中参与者的姓名将显示在一个旋转的标签上,这是一种电子抽奖机,这样与会者就可以知道他们的名字何时出现,以及抽奖操作员何时停止抽奖机并显示获奖者的姓名。参与者姓名存储在一个纯文本文件中,因此应用程序启动时应加载该列表。获奖者名单应在抽奖结束时保存到纯文本文件中。

我建议使用 Java 作为编程语言,因为它具有开发桌面应用程序的便利性,它具备多线程和平台无关的功能,并且是一个活跃而健壮的平台。我特别使用了 OpenJDK 7 平台,因为它是免费软件。事实上,使用该平台可以轻松满足所有指定的要求。

Using the Code

解决方案分为以下几个包

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

ocb.jtombola.core 包包含基础类。NamesLoader 类执行 I/O 操作,TombolaLabel 类是模拟抽奖机的旋转标签。

ocb.jtombola.gui 包包含 MainForm 类,它是应用程序的主窗口;以及 TombolaPanel 类,它包含所有 UI 元素。

容器面板

TombolaPanel 类继承自 JPanel 并实现 ActionListenerMouseListener 以管理用户事件。

public class TombolaPanel extends JPanel implements ActionListener, MouseListener

此类具有以下主要属性,分别用于设置背景图片、祝贺标签、切换按钮和旋转标签。

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

此类构造方法中包含概述应用程序整体运行的代码。

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 */
}

这段代码创建了一个 TombolaLabel 实例,并将包含姓名列表的文件名作为参数传入,然后对其进行自定义。此处值得注意的方法是 setMillis(),它设置标签显示姓名的延迟。setFont()setForeground()setBounds()setHorizontalTextAlignment() 方法用于自定义标签。setBorder() 方法仅用于调试标签的大小和位置,尽管此边框可以在自定义 UI 中进行个性化设置。

btnGo 对象是用于启动抽奖机的切换按钮,lblCongrats 是一种祝贺消息,在停止抽奖机并显示获奖者姓名后显示。这两个组件以及面板容器都已按照应用程序截图所示进行了自定义。

启动抽奖机的 btnGo 代码如下:

@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);
    }
  }
}

isSpinning 标志开启时,按钮会停止,并显示祝贺消息。反之亦然。

旋转标签

TombolaLabel 类完成了这项工作。它继承自 Samuel Sjoberg 的炫酷的 MultiLineLabel 类。它还实现了 Runnable 接口,以便在其自己的线程中显示旋转的姓名。

public class TombolaLabel extends MultiLineLabel implements Runnable

重要的是要提到用于控制抽奖机功能的易失性属性。

private volatile Thread tombolaThread;

构造函数以文件名作为参数,创建 NamesLoader 类的实例并加载姓名。如果列表为空,则程序终止;否则,将创建获奖者列表和随机数生成器。

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();
}

使用切换按钮启动抽奖机的相关代码如下:

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

此方法为标签添加了一个细微的边框,然后创建一个使用此类的新线程并启动它,从而触发以下代码。

@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();
    }
  }
}

在此代码中,标签会显示从整个列表中随机选择的一个姓名。姓名会显示一段时间(以毫秒为单位),在这段时间内我们将线程休眠。当线程唤醒时,过程会继续,直到切换按钮停止抽奖机,调用以下代码。

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

这会简单地将 tombolaThread 设置为 null,以便抽奖机停止显示获奖者的姓名。我们必须将此姓名添加到获奖者列表中,并且不要忘记将其从参与者列表中移除,因为一名与会者只能赢得一件奖品。setUI() 方法添加了一个漂亮的阴影来突出显示姓名。

停止线程的方法取自 Sun(现为 Oracle)的文章 Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

加载与会者姓名

NamesLoader 类相对简单,因为它从给定的输入文件中加载姓名列表并将其存储在 List<String> 数组列表中。

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();
  }
}

此类中的另一个相关方法是

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();
  }
}

其中保存了应用程序显示的获奖者姓名列表。

主窗体

MainFrame 类继承自 JFrame 类,并实现 WindowListener 接口以检测用户何时关闭框架。

public class MainFrame extends JFrame implements WindowListener

它有一个 TombolaPanel 类的实例。

TombolaPanel pnltombola;

我在构造方法中为框架设置了以下属性:

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);
}

当用户关闭框架时

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

在应用程序退出之前,我们确保获奖者列表已保存。

关注点

建议的代码非常简单,但却是处理单个线程显示姓名一段时间(以毫秒为单位)以模拟抽奖机的有效方法。此时间可以通过 setMillis() 方法设置。我们注意到 80 毫秒是显示姓名并供观众阅读的理想时间,同时足够快以模拟抽奖机效果。我们对 JTombola 进行了数千名参与者的压力测试,性能符合预期。

大会期间使用的应用程序背景图是由本地设计师绘制的,但 GIMP 在设计背景图方面非常出色,例如我为示例应用程序设计的带有 弯曲文本 的背景图。我还使用了强大的 ImageMagick 套件,通过 convert -delay 100 -loop 0 yay*.png yay.gif 命令创建了(非常简单的)带有祝贺消息的 动画 GIF。顺便说一句,动画截图是用 silencast 制作的。我在示例应用程序中包含了一个小的姓名列表。你能猜出他们是谁吗?

如果您希望在自己的抽奖活动中使用 JTombola,只需替换 /img 文件夹下的背景图片和(如果需要)祝贺图片即可。背景图片尺寸为 1024x750 像素,祝贺图片尺寸为 268x117 像素;如果您希望使用其他图片尺寸,则必须修改 TombolaPanel 构造函数中的相关代码并重新编译项目。更改切换按钮的图片是可选的。不要忘记将 input.txt 文件替换为您自己的参与者姓名。

JTombola 在上一届大会期间大获成功,与会者们玩得很开心(至少获奖者们是如此)。

历史

  • v1.0.0 | 初始发布 | 2016 年 9 月 30 日
© . All rights reserved.