度数分布図をSwingで出力
こんにちは。「Javaを復習する初心者」です。前回は度数分布図を「*」を使ってコンソール出力しました。今回はJavaのGUIツールキットのひとつSwingを使って描画しようと思います。グラフは横向きです。
点数の範囲は前回同様0~ 9点、10~19点、・・・、90~99点、100点とします。それぞれに人数が設定されている場合を想定し、ランダムに人数を設定します。その度数分布図を描画しようということです。
フレーム表示
Swingのフレーム表示の箇所は以下のような流れです。
- 変数frameに新規生成したインスタンス格納する。
- windowの閉じるボタンを押した動作を「終了」に設定する。(未設定だと閉じないです。)
- タイトルを「点数分布」に設定する。
- 横450、縦300に設定する。
- サイズ変更不可に設定する。
- GridLayoutを設定する。
- ラベルとグラフを配置する。
- 画面中央に表示するよう設定する。
- 表示する。
package practice; import java.awt.GridLayout; import javax.swing.JFrame; import javax.swing.JLabel; public class Hello20160803frame { public static void main(String[] args) { // フレーム表示 JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("点数分布"); frame.setSize(450, 300); frame.setResizable(false); frame.setLayout(new GridLayout(11, 3, 10, 10)); for (int i = 0; i < 11; i++) { frame.add(new JLabel("ラベル" + i)); frame.add(new JLabel("****")); } frame.setLocationRelativeTo(null); frame.setVisible(true); } }
この画像見ると各コンポーネントの間が空いていて良くないのですが、Swingのコンポーネント配置は難しくて、今の段階では見栄えはあきらめます。(専用のツールがあるのかもしれませんが。)GridLayoutというレイアウトマネージャーを使ってます。これはコンポーネントの大きさが自動調整されるようです。今回作るものはラベルの部分とグラフの部分2種類を規則的に並べるため、格子状に配置してくれるGridLayoutを使うことにしました。
グラフ描画
グラフの長さをどのように決定するかなのですが、これはコンポーネントの横幅を基準にしました。点数ごとの人数の最大値を取得し、[人数] * [最大値] / [横幅]という計算式を使って、横幅に対する割合のような感じでグラフを描画することにしました。
以下はソースと実行画面です。
package practice; import java.awt.Color; import java.awt.Graphics; import java.awt.GridLayout; import java.util.Random; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; public class Hello20160803 { // 出力用ラベル private static final String[] labels = { " 0~ 9点", "10~19点", "20~29点", "30~39点", "40~49点", "50~59点", "60~69点", "70~79点", "80~89点", "90~99点", " 100点", }; public static void main(String[] args) { // 点数をランダムで格納 int[] points = new int[1000]; Random random = new Random(); for (int i = 0; i < points.length; i++) { points[i] = random.nextInt(101); } // 集計 int[] counts = new int[11]; for (int point : points) { counts[ point / 10 ] ++; } // 最高人数取得 int maxCount = 0; for (int count : counts) { if (maxCount < count) { maxCount = count; } } // ラベル生成 JLabel[] jLabels = new JLabel[labels.length]; for (int i = 0; i < labels.length; i++) { jLabels[i] = new JLabel(String.format("%s: %4d人: ", labels[i], counts[i])); jLabels[i].setHorizontalAlignment(JLabel.RIGHT); } // グラフ用コンポーネント生成 JComponent[] jComponents = new JComponent[labels.length]; final int maxCountFinal = maxCount; for (int i = 0; i < labels.length; i++) { final double count = counts[i]; jComponents[i] = new JComponent() { @Override public void paint(Graphics g) { super.paintComponents(g); g.setColor(Color.ORANGE); g.fillRect(0, 0, (int)(count / maxCountFinal * getWidth()), getHeight()); } }; } // フレーム表示 JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("点数分布"); frame.setSize(450, 300); frame.setResizable(false); frame.setLayout(new GridLayout(labels.length, 3, 10, 10)); for (int i = 0; i < labels.length; i++) { frame.add(jLabels[i]); frame.add(jComponents[i]); frame.add(new JLabel("")); } frame.setLocationRelativeTo(null); frame.setVisible(true); } }
処理の流れですが、最初は前回同様に点数をランダムで格納しています。最高人数取得を先にやってますが、実際に使う場面はグラフ描画の箇所なので最後の方です。ラベル生成は書式文字列で書きましたが、文字列連結でもできます。JLabelクラスにはsetHorizontalAlignmentメソッドが用意されていて、右寄せにできます。そして、グラフ用コンポーネント生成でコンポーネントごとにグラフを描画してます。最後に、先ほどのプログラムのような感じでframeに配置して表示します。
配色については全然手が回りませんでした。とりあえず、グラフはオレンジ色にしただけです。