J2SE 5.0이전에는 간단한 JTable
을 출력하는 것도 어려운 일이었다. J2SE 5.0에는 API에 print()
메소드가 추가되어 테이블 출력, 결과물에 머리글/바닥글 삽입, 출력 페이지에 테이블크기 맞추기 등의 설정이 가능해졌다. 이번 테크팁에서는 이런 새로운 기능들을 사용하여 간단한 JTable
을 출력하는 법에 대해 알아본다. 또한 한 줄 걸러 음영 넣기, 그림자 유무에 따라 테이블 출력하기 등의 사용자 정의에 대해서도 설명한다.
Swing 튜토리얼에 나와있는 SimpleTableDemo program을 리팩토링하는 것부터 시작하자. 리팩토링은 이번 테크팁 내 모든 예제의 공통 코드를 취합하고 있다. 테이블에 대한 데이터는 private static final variables형의 columnNames
와 code>data(각 코드 리스팅의 마지막 부분에 나타남)에 포함되어 있다. 다른 공통 코드는 setUpJFrame()
과 getTablePanel()
에 포함되어 있다. 이 메소드들은 JTable
을 보여주는 데 사용하는 JFrame
과 JPanel
을 설정하는 데 사용된다.(공통 코드는 다음의 예제에 나타나지만, 그 후의 예제에는 포함되지 않는다.)
다음은 BaseTable
클래스이다.
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.Dimension;
import java.awt.GridLayout;
public class BaseTable extends JTable {
public BaseTable() {
super(data, columnNames);
setUpJFrame();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new BaseTable();
}
});
}
// Table Frame common to all examples in this tip
private void setUpJFrame() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( getTablePanel());
frame.pack();
frame.setVisible(true);
}
private JPanel getTablePanel(){
JPanel jPanel = new JPanel(new GridLayout(1,0));
jPanel.setOpaque(true);
setPreferredScrollableViewportSize(
new Dimension(500, 70));
jPanel.add(new JScrollPane(this));
return jPanel;
}
// Table Data common to all examples in this tip
private static final String[] columnNames =
{"First Name", "Last Name", "Sport",
"# of Years", "Vegetarian"};
private static final Object[][] data = {
{"Mary", "Campione", "Snowboarding",
new Integer(5), new Boolean(false)},
{"Alison", "Huml", "Rowing",
new Integer(3), new Boolean(true)},
{"Kathy", "Walrath", "Knitting",
new Integer(2), new Boolean(false)},
{"Sharon", "Zakhour", "Speed reading",
new Integer(20), new Boolean(true)},
{"Philip", "Milne", "Pool",
new Integer(10), new Boolean(false)}
};
}
BaseTable
은 invokeLater()
를 통해 이벤트 디스패치 쓰레드(event dispatch thread)에 GUI를 생성하고 보여주는 방법을 권장한다. BaseTable
을 컴파일하고 구동하자. data
에서 공급한 데이터들이 나타나는 5행 테이블이 나타난다.
한 줄 걸러 음영을 넣는 것은 의외로 어렵지 않다. 이는 javax.swing.JTable prepareRenderer()
메소드를 오버라이딩하여 만들 수 있다. 먼저, 사용자가 오버라이딩하는 prepareRenderer()
메소드로부터 현재 셀이 렌더링되도록 한다. 다음으로, rowIndex
가 짝수인지 홀수인지 결정한다. 홀수행은 배경색을 변경하고 짝수행은 현재색이 배경색이 되도록 한다. 이는 다음 ColorTable
클래스에 나와있다.
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.TableCellRenderer;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Component;
import java.awt.Color;
public class ColorTable extends JTable {
public ColorTable() {
super(data, columnNames);
setUpJFrame();
}
public Component prepareRenderer(
TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer,
row, col);
if (row % 2 == 0 && !isCellSelected(row,col)) {
c.setBackground(Color.LIGHT_GRAY);
} else {
c.setBackground(getBackground());
}
return c;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ColorTable();
}
});
}
// put common code here . . .
}
ColorTable
을 컴파일하고 구동한다. 다음과 같이 홀수행에 음영이 생기게 될 것이다.
이제
BaseTable
에 의해 보여지는 꾸며지지 않은 테이블을 출력하자. 이를 위해서는
JTable
에 추가된 출력 메소드를 호출해야한다. 이번 예제에서는
print()
가 컨스트럭터로부터 호출된다. 독립변수가 없는
print()
메소드는
JTable
을 머리글이나 바닥글 없이 출력하고,
FIT_WIDTH
출력 모드(
NORMAL
의 반대)를 선택한다. 나타날 수 있는 PrinterException을 핸들링해야하는데, 샘플에서는 스택 트래이스(stack trace)를 이용하여 나타날 수 있는 예외상황을 핸들링한다.
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.print.PrinterException;
public class BasePrintableTable extends JTable {
public BasePrintableTable() {
super(data, columnNames);
setUpJFrame();
try {
print();
} catch (PrinterException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new BasePrintableTable();
}
});
}
// put common code here . . .
}
BasePrintableTable
을 컴파일하고 구동하자. 테이블이 나타난다(이전의 예제보다는 나타나는 데 조금 더 오래 걸릴 것이다). 그 후 출력 다이얼로그가 나타난다.
프린터를 선택하고 Print 버튼을 누른다. 사용자가 선택한 프린터에 테이블이 보내질 것이다.
테이블의 질을 높이는 방법 중 하나는 출력물에 머리글과 바닥글을 추가하는 것이다. 이를 위해서는 MessageFormat
오브젝트 같이 머릿글과 바닥글을 입력하는 매개변수를 받는 print()
버전의 사용이 필요하다. 이 오브젝트들은 페이지의 윗부분과 아랫 부분에 가운데 정렬 텍스트들을 생성하도록 만들어졌으며, 첫번째 포지션에 제공된 데이터를 참고하여 페이지 숫자를 삽입할 수도 있다. 다음은 JTable
을 출력하는데 머리글과 바닥글을 추가하기 위해 필요한 컨스트럭터의 변경사항들이다.
public BasePrintableTable() {
super(data, columnNames);
setUpJFrame();
try {
print(PrintMode.NORMAL, new MessageFormat(
"Personal Info"),
new MessageFormat("Page {0,number}"));
} catch (PrinterException e) {
e.printStackTrace();
}
}
머리글은 굵은 글자로 가운데 정렬되며, "Personal Info" 텍스트로 구성될 것이다. 바닥글은 가운데 정렬된 "Page 1" 텍스트로 되어있으며, 여러 페이지의 문서에서는 {0, number}
이 현재 페이지의 숫자 형식으로 바뀔 것이다.
좀 더 고난도의 출력물을 원한다면,
print()
서명을 이용하여 프린트 다이얼로그의 표시 유무, 프린트가 되는 동안의 형식 다이얼로그 표시 유무 등을 지정할 수 있다. 또한 이 서명으로
PrintRequestAttributeSet
를 전달하여 프린트하는데 사용하는 속성들을 지정할 수도 있다.
프린트 기능들과 JTable
에서 한 줄 걸러 음영이 들어가는 ColorTable
버전을 어떻게 결합시킬 것인지 고려해보자. 간단한 방법으로는 다음과 같이 BasePrintableTable
과 ColorTable
을 PrintableColorTable로 동시에 포함시키는 것이다.
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.TableCellRenderer;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Component;
import java.awt.Color;
import java.awt.print.PrinterException;
public class PrintableColorTable extends JTable {
public PrintableColorTable() {
super(data, columnNames);
setUpJFrame();
try {
print();
} catch (PrinterException e) {
e.printStackTrace();
}
}
public Component prepareRenderer(
TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer,
row, col);
if (row % 2 == 0 && !isCellSelected(row,col)) {
c.setBackground(Color.LIGHT_GRAY);
} else {
c.setBackground(getBackground());
}
return c;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new PrintableColorTable();
}
});
}
// common code here . . .
}
PrintableColorTable
은 홀수행의 배경색을 옅은 회색으로 설정하여 JTable
을 화면에 나타내고 출력한다. 그러나 출력 테이블의 형식이 표시된 테이블과 다르도록 하고싶다면 다른 방법이 필요하다. 예를 들어, 표시되는 테이블에서는 줄무늬(한 줄 걸러 음영)를 유지하고 싶지만, 출력되는 테이블에서는 보이지 않도록 하고싶다고 가정하자. Swing 엔지니어인 Shannon Hickey는 다른 형식을 사용하여 JTable
을 화면에 나타내고 출력하는 데 다음의 방법을 제안한다. JavaDesktop 포럼 JTable.print() - different TableCellRenderer을 참고하기 바란다.
Shannon의 방법에서는 현재 출력 중인지 트래킹하는 boolean을 도입한다. 그 후 JTable.print(Graphics g)
메소드를 오버라이딩하여 boolean을 움직인다. 이는 다음과 같다.
public void print(Graphics g) {
printing = true;
try {
super.print(g);
} finally {
printing = false;
}
}
다음으로, 셀의 배경색을 설정할 때 boolean을 테스트한다.
if (row % 2 == 0 && !isCellSelected(row,col)) {
c.setBackground(Color.LIGHT_GRAY);
// cell is selected, use the highlight color
} else if (isCellSelected(row, col)) {
c.setBackground(getSelectionBackground());
} else {
c.setBackground(getBackground());
}
다음의 FancyPrintableColor
클래스에서는 이 단계들을 모두 포함한다. JTable
의 음영 버전을 화면에 나타내지만, 출력물에는 음영이 포함되어 있지 않다.
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.TableCellRenderer;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Component;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.print.PrinterException;
public class FancyPrintableColorTable extends JTable {
private boolean printing = false;
public FancyPrintableColorTable() {
super(data, columnNames);
setUpJFrame();
try {
print();
} catch (PrinterException e) {
e.printStackTrace();
}
}
public void print(Graphics g) {
printing = true;
try {
super.print(g);
} finally {
printing = false;
}
}
public Component prepareRenderer(
TableCellRenderer renderer,
int row, int col) {
Component c = super.prepareRenderer(renderer,
row, col);
// if printing, only use plain background
if (printing) {
c.setBackground(getBackground());
} else {
if (row % 2 == 0 && !isCellSelected(row,col)) {
c.setBackground(Color.LIGHT_GRAY);
// cell is selected, use the highlight color
} else if (isCellSelected(row, col)) {
c.setBackground(getSelectionBackground());
} else {
c.setBackground(getBackground());
}
}
return c;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new FancyPrintableColorTable();
}
});
}
// common code here . . .
}
테이블을 출력하는 새로운 기능들에 대해 좀 더 알고싶다면, Swing API changes in JDK 5.0의 JTable
needs printing support를 참고하기 바란다.