通知表が複数ページ印刷できるようになりました。でも特定の生徒のものだけ先に印刷したいとか、逆に2,3後回しにしたいということもあるでしょう。訂正があった生徒だけ印刷し直しという場面も考えられます。
下図のように選択して印刷したいというのが、このページのテーマです。

一般的にはJListの使用を思いつくかもしれません。一覧からShift,Ctrlキーを併用して追加選択、範囲選択が可能です。しかし、2つの理由からcheckboxを使うことにしました。
理由1: 右側の4つのボタンの機能の実現が難しそうであった
理由2: ユーザーにCtrlを併用する選択が思いの外不評であった
右側の4つのボタンの機能は次の通り
これを実現するためにSelectIdNo.javaを作ります。これは選択のためだけのクラスで、これまでの帳票印刷のプログラムから呼び出して使います。単体ではテスト用データを使って動作テストを行います。
1. 番号をキーにした名前のマップであるnamemapを引数にしてSelectIdNo.javaのインスタンスを作る
2. それをダイアログに乗せて表示をし、ユーザーにチェックボックスで選択させる
3. getSelectedAsList()メソッドを使って選択された番号をリストとして受け取る
LinkedHashMapはputした順序が変わることはありません。生徒の番号に抜けがなければどちらを使っても同じです。0から始まるか1から始まるかの違いはありますが、換算は簡単です。しかし、今回mapで管理することにした関係でchekboxのtextに書いた番号を読み取って返しています。mapで何番目ではなく、番号を返します
ボックスレイアウトの箱を組み合わせて配置しています。その中にチェックボックスの配列を15ずつ配置します。チェックされた項目のテキストから番号を取得しますので、チェックボックスの配置は動いても構いません。
Boxlayoutを使ったのは楽そうだったからですが、なかなか癖があって、GridBagの方がよかったかもしれません。

動作テストのため、main()(から呼ばれるcreateAndShowGUI())に名前のデータを直に書いています。
package print01;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
//2017-5-6 from SelectHito Maximum-Minimum, add repaint() in reArrCboxes()
public class SelectIdNo extends Box implements ActionListener {
ArrayList<JCheckBox> cboxes;
LinkedHashMap<Integer,String> namemap;
Box[] c15boxs;
JButton allButton;
JButton nonButton;
JButton revButton;
JButton gykButton;
boolean gyakujun;
//int nmax;
public String ver = "5.1-for print01";
/** id->SeiMei のMapを使って選択パネルを作成する */
public SelectIdNo(LinkedHashMap<Integer,String> namemap) {
super( BoxLayout.X_AXIS);
this.namemap = namemap;
cboxes = new ArrayList<JCheckBox>();
c15boxs = new Box[3];
for (int i=0 ;c15boxs.length>i; i++){
c15boxs[i] = createVerticalBox();
c15boxs[i].setAlignmentY(JComponent.TOP_ALIGNMENT );
}
//nmax = namemap.size(); -->> cboxes.size()
gyakujun=false;
int n = 0;
for (int key : namemap.keySet()) {
String ctxt = String.format("%2d\uu00A0%s",key,namemap.get(key)); //U+00A0 uは2つでも可
cboxes.add(new JCheckBox(ctxt));
c15boxs[n/15].add(cboxes.get(n));
n++;
}
int cwidth = 0;
for (int i=0 ;c15boxs.length>i; i++){
cwidth = Math.max(c15boxs[i].getPreferredSize().width,cwidth);
add(c15boxs[i]);
}
Dimension csize = new Dimension(cwidth,c15boxs[0].getPreferredSize().height);
for (int i=0 ;c15boxs.length>i; i++){
c15boxs[i].setPreferredSize(csize);
}
add(Box.createHorizontalStrut(20));
Box buttonbox = new Box(BoxLayout.Y_AXIS);
buttonbox.setAlignmentY(JComponent.TOP_ALIGNMENT );
allButton = new JButton("全選択");
nonButton = new JButton("全解除");
revButton = new JButton("反転");
gykButton = new JButton("逆順");
buttonbox.add(allButton);
buttonbox.add(Box.createVerticalStrut(8));
buttonbox.add(nonButton);
buttonbox.add(Box.createVerticalStrut(8));
buttonbox.add(revButton);
buttonbox.add(Box.createVerticalStrut(8));
buttonbox.add(gykButton);
allButton.addActionListener(this);
nonButton.addActionListener(this);
revButton.addActionListener(this);
gykButton.addActionListener(this);
Dimension btnsize = allButton.getPreferredSize();
nonButton.setMaximumSize(btnsize);
revButton.setMaximumSize(btnsize);
gykButton.setMaximumSize(btnsize);
add(buttonbox);
}
/** checkboxの並びを逆にする。actionPerformed()からのみ呼ばれる */
private void reArrCboxes(){
//cboxes(n):seitos(n) still
for (int i=0 ;c15boxs.length>i; i++){
c15boxs[i].removeAll();
}
for ( int n=0; cboxes.size()>n; n++ ){
int cb = 0;
if (gyakujun) {
cb = cboxes.size() - n-1;
}else {
cb = n;
}
c15boxs[n/15].add(cboxes.get(cb));
}
validate();
for (int i=0 ;c15boxs.length>i; i++){
c15boxs[i].repaint();
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == gykButton) {
gyakujun = ! gyakujun;
reArrCboxes();
}
else if (e.getSource() == allButton) {
for ( int cb=0; cboxes.size()>cb; cb++ ){
cboxes.get(cb).setSelected(true);
}
}
else if (e.getSource() == nonButton) {
for ( int cb=0; cboxes.size()>cb; cb++ ){
cboxes.get(cb).setSelected(false);
}
}
else if (e.getSource() == revButton) {
for ( int cb=0; cboxes.size()>cb; cb++ ){
if (cboxes.get(cb).isSelected()==true){
cboxes.get(cb).setSelected(false);
}else{
cboxes.get(cb).setSelected(true);
}
}
}
}
/** checkの付いたものだけのMapを返す */
public LinkedHashMap<Integer,String> getSelected(){
LinkedHashMap<Integer,String> temp = new LinkedHashMap<>();
if (gyakujun) {
for (int i=cboxes.size()-1; i>=0; i--){
if (cboxes.get(i).isSelected()){
String[] tmp = cboxes.get(i).getText().split("\uu00a0",2); //U+00A0
//System.out.println("/"+tmp[0]+"/"+tmp[1]+"/");//test
temp.put(Integer.parseInt(tmp[0].trim()),tmp[1]);
}
}
}else {
for (int i=0; cboxes.size()>i; i++){
if (cboxes.get(i).isSelected()){
String[] tmp = cboxes.get(i).getText().split("\uu00a0",2); //U+00A0
//System.out.println("/"+tmp[0]+"/"+tmp[1]+"/");//test
temp.put(Integer.parseInt(tmp[0].trim()),tmp[1]);
}
}
}
return temp;
}
/** checkの付いた番号(mapのkey)をlistにして返す */
public LinkedList<Integer> getSelectedAsList(){
LinkedList<Integer> temp = new LinkedList<>();
if (gyakujun) {
for (int i=cboxes.size()-1; i>=0; i--){
if (cboxes.get(i).isSelected()){
String[] tmp = cboxes.get(i).getText().split("\uu00a0",2); //U+00A0
temp.add(Integer.parseInt(tmp[0].trim()));
}
}
}else {
for (int i=0; cboxes.size()>i; i++){
if (cboxes.get(i).isSelected()){
String[] tmp = cboxes.get(i).getText().split("\uu00a0",2); //U+00A0
temp.add(Integer.parseInt(tmp[0].trim()));
}
}
}
return temp;
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("CheckBoxDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize( 600, 400 );
frame.setVisible( true );
LinkedHashMap<Integer,String> namemap = new LinkedHashMap<>();
namemap.put(1,"赤石 美緒");
namemap.put(2,"荒川 早織");
namemap.put(3,"石村 千尋");
namemap.put(4,"岩崎 桃子");
namemap.put(5,"太田 舞");
namemap.put(6,"小山内 真紀子");
namemap.put(7,"小山 美奈子");
namemap.put(8,"兼平 綾乃");
namemap.put(9,"北村 香奈");
namemap.put(10,"工藤 彩香");
namemap.put(11,"工藤 由佳");
namemap.put(12,"古川 香");
namemap.put(13,"小柳 真衣");
namemap.put(14,"齋藤 弥生");
namemap.put(15,"佐々木 詔子");
namemap.put(16,"佐藤 亜希");
namemap.put(17,"ショパン フレデリック");
namemap.put(18,"ファインマン リチャード");
namemap.put(19,"壱弐三 四五六七八九");
namemap.put(20,"壱弐三 四五六七八");
namemap.put(21,"壱弐三 四五六七");
namemap.put(22,"相馬 静香");
namemap.put(23,"髙谷 由紀子");
namemap.put(24,"田中 恵美子");
namemap.put(25,"對馬 由希子");
namemap.put(26,"中田 範子");
namemap.put(27,"八木橋 美沙子");
namemap.put(28,"荒木 健吾");
namemap.put(29,"石川 和茂");
namemap.put(30,"葛西 俊則");
namemap.put(31,"舘山 健太郎");
namemap.put(32,"田中 誠");
namemap.put(33,"鳴海 裕也");
namemap.put(34,"西沢 和樹");
namemap.put(35,"長谷川 弘樹");
namemap.put(36,"原子 啓太");
namemap.put(37,"平山 拓斗");
namemap.put(38,"三浦 大樹");
SelectIdNo shbox = new SelectIdNo(namemap);
String[] printOrNot = {"印刷開始","中止"};
int rev = JOptionPane.showOptionDialog(
frame, shbox,
"印刷選択 ver"+shbox.ver,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
null,printOrNot,printOrNot[0]
);
if (rev==1) {
System.out.println("Canceled");
}
else if (rev==0 ){
LinkedHashMap<Integer,String> selnamemap;
selnamemap = shbox.getSelected();
for (int key:selnamemap.keySet()){
System.out.println(key+" "+selnamemap.get(key));
}
System.out.println("-------------------- ");
for (int no:shbox.getSelectedAsList()){
System.out.println(no+" "+namemap.get(no));
}
}
System.exit(0);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
ボタンの全選択、全解除、反転、逆順 の文字数が異なります。BorderLayoutやGridLayoutでは自動で広げて幅を揃えてくれますが、Boxでは本来の大きさらしく幅が違ってしまいます。合わせるために[全選択]の幅をgetPreferredSize().widthで取得し、setPreferredSize()で指定してみましたが機能してくれません。MinimumSize, MaximumSize も加えて実験してみます。
MinimumSize, MaximumSize, PreferredSize を指定の前後に調べます。[全選択]以外のボタンは全部2文字分とし、全部の効果を一度に比較しています
int height = allButton.getPreferredSize().height;
int width = allButton.getPreferredSize().width;
//allButton.setPreferredSize(Dimension(width, height));
//[解除]はsetPreferredSize()の効果を見ます
System.out.println("no-mi "+nonButton.getMinimumSize().width+":"+height);//test
System.out.println("no-ma "+nonButton.getMaximumSize().width+":"+height);//test
System.out.println("no-pr "+nonButton.getPreferredSize().width+":"+height +"/");//test
nonButton.setPreferredSize(new Dimension(width, height));
System.out.println("no-mi "+nonButton.getMinimumSize().width+":"+height);//test
System.out.println("no-ma "+nonButton.getMaximumSize().width+":"+height);//test
System.out.println("no-pr "+nonButton.getPreferredSize().width+":"+height +"/");//test
//[反転]はsetMinimumSize()の効果を見ます
System.out.println("rv-mi "+revButton.getMinimumSize().width+":"+height);//test
System.out.println("rv-ma "+revButton.getMaximumSize().width+":"+height);//test
System.out.println("rv-pr "+revButton.getPreferredSize().width+":"+height +"/");//test
revButton.setMinimumSize(new Dimension(width, height));
System.out.println("rv-mi "+revButton.getMinimumSize().width+":"+height);//test
System.out.println("rv-ma "+revButton.getMaximumSize().width+":"+height);//test
System.out.println("rv-pr "+revButton.getPreferredSize().width+":"+height +"/");//test
//[逆順]はsetMaximumSize()の効果を見ます
System.out.println("gy-mi "+gykButton.getMinimumSize().width+":"+height);//test
System.out.println("gy-ma "+gykButton.getMaximumSize().width+":"+height);//test
System.out.println("gy-pr "+gykButton.getPreferredSize().width+":"+height +"/");//test
gykButton.setMaximumSize(new Dimension(width, height));
System.out.println("gy-mi "+gykButton.getMinimumSize().width+":"+height);//test
System.out.println("gy-ma "+gykButton.getMaximumSize().width+":"+height);//test
System.out.println("gy-pr "+gykButton.getPreferredSize().width+":"+height +"/");//test
![]() |
[全選択]のPreferredSizeから他の3つにSetする値を取得します |
| [解除]はsetPreferredSize()効果がありません | |
| [反転]はsetMinimumSize()効果がありません | |
| [逆順]はsetMaximumSize()効果があります |
System.out.println()で得られる値は、それぞれ設定前は等しくなっていて、設定後はsetの通りにgetできます。ただ、効果として現れるのはsetMaximumSize()だけです。
adachi@debian64:/media/S1T/basicaka/akajava/javadabiyone$ java box.SelectIdNo no-mi 60:26 no-ma 60:26 no-pr 60:26/ no-mi 60:26 no-ma 60:26 no-pr 73:26/ rv-mi 60:26 rv-ma 60:26 rv-pr 60:26/ rv-mi 73:26 rv-ma 60:26 rv-pr 60:26/ gy-mi 60:26 gy-ma 60:26 gy-pr 60:26/ gy-mi 60:26 gy-ma 73:26 gy-pr 60:26/
checkboxの入る3つのBoxの幅を揃えたい。見栄えもありますが、逆順にした時に幅が動くのは困るのです。
そこでgetPreferredSize()で一番の広いものを調べておき、全部その幅に設定します。これは、setPreferredSize()でうまく行きます
int cwidth = 0;
for (int i=0 ;c15boxs.length>i; i++){
System.out.println(c15boxs[i].getMinimumSize().width+":"+cwidth);//test
System.out.println(c15boxs[i].getMaximumSize().width+":"+cwidth);//test
System.out.println(c15boxs[i].getPreferredSize().width+":"+cwidth);//test
cwidth = Math.max(c15boxs[i].getPreferredSize().width,cwidth);
//if (c15boxs[i].getComponentCount()>0) add(c15boxs[i]);
add(c15boxs[i]);
}
Dimension csize = new Dimension(cwidth,c15boxs[0].getPreferredSize().height);
for (int i=0 ;c15boxs.length>i; i++){
System.out.println("-----"+i+"-----");
//System.out.printf( "%2d : %4d\n",i,c15boxs[i].getComponentCount() ); //test
System.out.println("bf-mi "+c15boxs[i].getMinimumSize().width+":"+cwidth);//test
System.out.println("bf-ma "+c15boxs[i].getMaximumSize().width+":"+cwidth);//test
System.out.println("bf-pr "+c15boxs[i].getPreferredSize().width+":"+cwidth);//test
//c15boxs[i].setMinimumSize(csize);
//c15boxs[i].setMaximumSize(csize);
c15boxs[i].setPreferredSize(csize);
System.out.println("af-mi "+c15boxs[i].getMinimumSize().width+":"+cwidth);//test
System.out.println("af-ma "+c15boxs[i].getMaximumSize().width+":"+cwidth);//test
System.out.println("af-pr "+c15boxs[i].getPreferredSize().width+":"+cwidth +"/");//test
}
| setMinimumSize()使用 | 効果がありません |
| setMaximumSize()使用 | 効果がありません |
| setPreferredSize()使用 | 効果があります |
最初の9行は最大幅を見つける時のもの。131,196,118が初期値で、196が最大。Minimum,Maximum,Preferredのどれも同じ値。
そのあとは設定の前後。以下は効果のあった最後のsetPreferredSize()のもので、それだけが196になり、Minimum,Maximumは変化していない。
131:0 131:0 131:0 196:131 196:131 196:131 118:196 118:196 118:196 -----0----- bf-mi 131:196 bf-ma 131:196 bf-pr 131:196 af-mi 131:196 af-ma 131:196 af-pr 196:196/ -----1----- bf-mi 196:196 bf-ma 196:196 bf-pr 196:196 af-mi 196:196 af-ma 196:196 af-pr 196:196/ -----2----- bf-mi 118:196 bf-ma 118:196 bf-pr 118:196 af-mi 118:196 af-ma 118:196 af-pr 196:196/
Minimum,Maximumについても、指定したものだけが196になり他は変わらないのは同様。ただし効果がない。
ボタンの幅はsetMaximumSize()でなければなりません
Dimension btnsize = allButton.getPreferredSize(); nonButton.setMaximumSize(btnsize); revButton.setMaximumSize(btnsize); gykButton.setMaximumSize(btnsize);
Boxの幅はsetPreferredSize()でなければなりません
int cwidth = 0;
for (int i=0 ;c15boxs.length>i; i++){
cwidth = Math.max(c15boxs[i].getPreferredSize().width,cwidth);
add(c15boxs[i]);
}
Dimension csize = new Dimension(cwidth, c15boxs[0].getPreferredSize().height);
for (int i=0 ;c15boxs.length>i; i++){
c15boxs[i].setPreferredSize(csize);
}
各mapから選択されたものだけを抽出していままで通りのTsuuchiHRのインスタンスを作ります。
Windowsのダイアログにはcloseボタンがあります。これを使った時にも例外を起こさないようにする必要があります。

reqset.add(OrientationRequested.PORTRAIT); //この下に挿入
SelectIdNo shbox = new SelectIdNo(namemap); //ここから追加
String[] printOrNot = {"画面で確認","印刷開始","中止"};
int rev = JOptionPane.showOptionDialog(
null, shbox,
"印刷選択 ver"+shbox.ver,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE,
null,printOrNot,printOrNot[0]
);
LinkedList<Integer> selectedno = shbox.getSelectedAsList();
if (selectedno.size()==0) rev = 2;
if (rev==2 || 0>rev) { //中止:2 close:-1
System.out.println("Canceled");
}
else if (2>rev){ //"画面で確認"または"印刷開始"
//選択した生徒だけのデータにする
LinkedHashMap<Integer,String> selnamemap = new LinkedHashMap<>();
LinkedHashMap<Integer,LinkedHashMap<String,int[]>> selseisekimap = new LinkedHashMap<>();
LinkedHashMap<Integer,int[][]> selshukketsumap = new LinkedHashMap<>();
for (int no:selectedno){
selnamemap.put(no,namemap.get(no));
selseisekimap.put(no,seisekimap.get(no));
selshukketsumap.put(no,shukketsumap.get(no));
}
TsuuchiHR prevPrintable = new TsuuchiHR(
selnamemap,selseisekimap,selshukketsumap,
tanis,gakki); //printable instance この先はほとんど同じ
if (rev==0){//画面で確認(プレビュー)
JScrollPane scrollPane = new JScrollPane(prevPrintable);
JPanel sbPanel = new JPanel();
sbPanel.setLayout(new BorderLayout());
sbPanel.add(scrollPane,BorderLayout.CENTER);
JPanel btnPanel = new JPanel();
sbPanel.add(btnPanel,BorderLayout.SOUTH);
JButton upbtn = new JButton("次");
JButton dnbtn = new JButton("前");
upbtn.addActionListener(prevPrintable);
dnbtn.addActionListener(prevPrintable);
JLabel statlabel = new JLabel(String.format("%3d/%-3d",1,selseisekimap.size())); //ラベルの初期値
statlabel.setName("status");
prevPrintable.setStatLabel(statlabel);
btnPanel.add(dnbtn);
btnPanel.add(statlabel);
btnPanel.add(upbtn);
String[] goOrNot = {"印刷","中止"};
JOptionPane pane = new JOptionPane(
sbPanel,
JOptionPane.PLAIN_MESSAGE,
JOptionPane.DEFAULT_OPTION,
null,goOrNot,goOrNot[0]
);
//JDialog dialog = pane.createDialog( null, "印刷プレビュー" );
JDialog dialog = pane.createDialog("印刷プレビュー" );
int dh = dialog.getPreferredSize().height;
int dw = dialog.getPreferredSize().width;
int scrh = Toolkit.getDefaultToolkit().getScreenSize().height;
if (dh>scrh) {
dialog.setPreferredSize(new Dimension(dw,scrh-40));
}
dialog.pack();
dialog.setResizable(true);
dialog.setVisible(true);
dialog.dispose(); //needs for ends /set HIDE_ON_CLOSE by default
String svalue = (String)pane.getValue();
rev = 1;//印刷へ
if (svalue==null || svalue.equals("中止")) rev = 0; // System.exit(0);
}
if (rev ==1){//印刷
pj.setPrintable(prevPrintable); //printable instance
System.out.println(pj.getPrintService().getName()); //test
if (pj.printDialog( reqset )) {
try { pj.print( reqset ); }
catch (PrinterException e) {
System.err.println(e);
}
}
}
} //end of (2>rev)
//dialog.dispose();
}
}