通知表が複数ページ印刷できるようになりました。でも特定の生徒のものだけ先に印刷したいとか、逆に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(); } }