반응형
Java SE 6.0(코드명 Mustang)에는 Swing
JTable
의 내용을 훨씬 쉽게 정렬하고 필터링할 수 있게 해주는 몇 가지 기능이 추가된다. (이 기능들이 최종적으로 포함되려면 JCP의 승인을 거쳐야 한다.) 최근의 테이블 중심 사용자 인터페이스는 대부분 사용자가 테이블 헤더를 클릭하여 칼럼을 정렬할 수 있도록 되어 있는데, 이는 Mustang 이전에 Swing JTable 지원을 통해 가능하게 되었다. 이 기능을 필요로 하는 각 테이블에 일일이 수동으로 기능을 추가해 주어야만 하는 불편이 따랐지만 Mustang은 최소한의 노력으로 이 기능을 사용할 수 있도록 해준다. 필터링은 사용자 인터페이스에서 일반적으로 이용할 수 있는 또 다른 옵션으로서, 테이블 내에서 사용자가 제공하는 기준에 부합하는 행만을 디스플레이할 수 있게 해준다. Mustang을 이용하면 JTable
컨텐츠 필터링이 훨씬 용이해진다. 행 정렬하기
Mustang에서 행을 정렬하고 필터링하는 기준이 되는 것이 바로 추상
RowSorter
클래스로서, 이 RowSorter
는 두 가지 매핑-JTable
내의 한 행을 기본 모델의 엘리먼트로, 그리고 다시 반대로-을 유지한다. 이는 하나의 행이 정렬과 필터링을 수행할 수 있게 해준다. 이 클래스는 TableModel
과 ListModel
모두에 적용될 만큼 포괄적이긴 하지만 TableRowSorter
에만 JTable
에 적용되는 Mustang 라이브러리가 제공된다. 가장 간단한 경우를 예로 들면,
TableModel
을 TableRowSorter
생성자에 패스한 다음 생성된 RowSorter
를 JTable
의 setRowSorter()
메소드로 패스한다. 다음은 이런 방식을 보여주는 예제 프로그램 SortTable
이다. import javax.swing.*; import javax.swing.table.*; import java.awt.*; public class SortTable { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Sorting JTable"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Object rows[][] = { {"AMZN", "Amazon", 41.28}, {"EBAY", "eBay", 41.57}, {"GOOG", "Google", 388.33}, {"MSFT", "Microsoft", 26.56}, {"NOK", "Nokia Corp", 17.13}, {"ORCL", "Oracle Corp.", 12.52}, {"SUNW", "Sun Microsystems", 3.86}, {"TWX", "Time Warner", 17.66}, {"VOD", "Vodafone Group", 26.02}, {"YHOO", "Yahoo!", 37.69} }; String columns[] = {"Symbol", "Name", "Price"}; TableModel model = new DefaultTableModel(rows, columns) { public Class getColumnClass(int column) { Class returnValue; if ((column >= 0) && (column < getColumnCount())) { returnValue = getValueAt(0, column).getClass(); } else { returnValue = Object.class; } return returnValue; } }; JTable table = new JTable(model); RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model); table.setRowSorter(sorter); JScrollPane pane = new JScrollPane(table); frame.add(pane, BorderLayout.CENTER); frame.setSize(300, 150); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

디스플레이된 테이블의 특정 칼럼을 클릭하고 칼럼의 내용이 재정리되는 것을 살펴본다.

커스텀 서브클래스를 생성하느니 차라리
DefaultTableModel
을 이용하면 안 되느냐고 질문을 던질지도 모른다. 그 대답은, TableRowSorter
가 칼럼 정렬 시 적용되는 일련의 규칙을 가진다는 것이다. 기본값으로, 테이블 내의 모든 칼럼은 Object
타입으로 간주된다. 따라서, toString()
을 호출함으로써 정렬이 수행되는 것이다. DefaultTableModel
의 기본값 getColumnClass()
비헤이비어를 오버라이드함으로써, RowSorter
는 Comparable
을 구현하는 것으로 가정하고 해당 클래스의 규칙에 따라 정렬한다. 또한, setComparator(int column, Comparator comparator)
를 호출하여 특정 칼럼을 위한 커스텀 Comparator
를 설치할 수도 있다. 다음은 정렬과 관련이 있는
SortTable
프로그램 내의 세 가지 주요 라인이다. JTable table = new JTable(model); RowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model); table.setRowSorter(sorter);첫 번째 라인은 모델을 테이블에 연결시키고, 두 번째 라인은 모델에 특정
RowSorter
를 생성한다. 세 번째 라인은 RowSorter
를 JTable
에 연결시킨다. 이로써 사용자는 칼럼 헤더를 클릭하여 해당 칼럼을 정렬할 수 있다. 같은 칼럼을 두 번 클릭하면 정렬 순서가 반대로 된다. 정렬 순서가 바뀔 때 각자의 액션을 추가하고 싶으면
RowSorter
에 RowSorterListener
를 첨부하면 된다. 인터페이스는 다음과 같은 하나의 메소드를 가진다. void sorterChanged(RowSorterEvent e)이 메소드는 상태 바에서 텍스트를 업데이트하거나 몇 가지 추가 태스크를 수행할 수 있게 해준다. 이 액션에 대한
RowSorterEvent
는 RowSorter
가 뷰 안팎의 행을 필터링한 경우, 정렬 전에 얼마나 많은 행이 존재했었는지 알아낼 수 있게 해준다. 테이블 행 필터링하기
RowFilter
를 TableRowSorter
에 연결시켜 테이블의 내용을 필터링하는 데 사용할 수 있다. 예를 들어, RowFilter
를 이용하여 이름이 A 자로 시작하거나 주가가 $50를 넘는 행만 테이블에 디스플레이되도록 하는 경우가 그것이다. 추상 RowFilter
클래스의 경우 다음과 같이 필터링에 사용되는 하나의 메소드를 가진다. boolean include(RowFilter.Entry<? extends M,? extends I> entry)
RowSorter
에 연결된 모델 내의 각 엔트리에 대해, 메소드는 지정된 엔트리가 모델의 현재 뷰에 표시되어야 할지 여부를 알려준다. 대개의 경우 여러분은 각자의 RowFilter
구현을 생성할 필요는 없으나, 대신 RowFilter
는 필터 생성을 위한 여섯 개의 정적 메소드를 제공한다.
andFilter(Iterable<? extends RowFilter<? super M,? super I>> filters)
dateFilter(RowFilter.ComparisonType type, Date date, int... indices)
notFilter(RowFilter<M,I> filter)
numberFilter(RowFilter.ComparisonType type, Number number, int... indices)
orFilter(Iterable<? extends RowFilter<? super M,? super I>> filters)
regexFilter(String regex, int... indices)
dateFilter
, numberFilter
, regexFilter
)를 가지는 RowFilter
팩토리 메소드의 경우에는 모델에서 지정된 인덱스에 일치하는 일련의 칼럼만을 확인하고, 지정된 인덱스가 없으면 모든 칼럼에 대해 일치 여부를 확인한다. dateFilter
는 날짜의 일치 여부를 확인할 수 있게 해주고, numberFilter
는 일치하는 수를 확인한다. notFilter
는 다른 필터를 반전시키는 데 사용된다. 즉, 제공 필터에 포함되지 않는 엔트리를 포함한다는 말인데, 이 필터는 가령 2005년 12월 25일까지 완료되지 않은 엔트리를 찾는다든지 하는 일에 사용할 수 있다. andFilter
와 orFilter
는 다른 필터들을 논리적으로 결합하는 데 사용되고, regexFilter
는 정규 표현식을 사용하여 필터링을 수행한다. 다음은 regexFilter
를 이용하여 테이블의 내용을 필터링하는 프로그램 FilterTable
이다. import javax.swing.*; import javax.swing.table.*; import java.awt.*; import java.awt.event.*; import java.util.regex.*; public class FilterTable { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Sorting JTable"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Object rows[][] = { {"AMZN", "Amazon", 41.28}, {"EBAY", "eBay", 41.57}, {"GOOG", "Google", 388.33}, {"MSFT", "Microsoft", 26.56}, {"NOK", "Nokia Corp", 17.13}, {"ORCL", "Oracle Corp.", 12.52}, {"SUNW", "Sun Microsystems", 3.86}, {"TWX", "Time Warner", 17.66}, {"VOD", "Vodafone Group", 26.02}, {"YHOO", "Yahoo!", 37.69} }; Object columns[] = {"Symbol", "Name", "Price"}; TableModel model = new DefaultTableModel(rows, columns) { public Class getColumnClass(int column) { Class returnValue; if ((column >= 0) && (column < getColumnCount())) { returnValue = getValueAt(0, column).getClass(); } else { returnValue = Object.class; } return returnValue; } }; JTable table = new JTable(model); final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model); table.setRowSorter(sorter); JScrollPane pane = new JScrollPane(table); frame.add(pane, BorderLayout.CENTER); JPanel panel = new JPanel(new BorderLayout()); JLabel label = new JLabel("Filter"); panel.add(label, BorderLayout.WEST); final JTextField filterText = new JTextField("SUN"); panel.add(filterText, BorderLayout.CENTER); frame.add(panel, BorderLayout.NORTH); JButton button = new JButton("Filter"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String text = filterText.getText(); if (text.length() == 0) { sorter.setRowFilter(null); } else { try { sorter.setRowFilter( RowFilter.regexFilter(text)); } catch (PatternSyntaxException pse) { System.err.println("Bad regex pattern"); } } } }); frame.add(button, BorderLayout.SOUTH); frame.setSize(300, 250); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }디스플레이는 어딘가에
SUN
이라는 문자가 포함된 모든 문자열에 대해 필터를 설정하는데, 이는 문자열 "SUN
"’에 의해 명시된다. 일치 여부를 정확하게 검사하려면 문자열의 시작과 끝 각각에 '^
'와 '$
'의 문자를 이용한다.

사용자가 아래쪽의 "
Filter
" 버튼을 누르면 필터는 자체적으로 Matcher.find()
를 사용하여 포함 여부를 검사한다. 
테이블에 표시된 일련의 행을 변경하려면 필터 텍스트를 변경하고, 테이블 내의 모든 행을 보고 싶으면 필터 텍스트를 삭제한다.
마지막으로 빼놓을 수 없는 것은, 정렬이나 필터링 시 선택은 뷰의 관점에서 이루어진다는 점이다. 따라서, 기본 모델에 매핑할 필요가 있다면
convertRowIndexToModel()
메소드를 호출해야 한다. 마찬가지로, 모델에서 뷰로 전환할 경우에는 convertRowIndexToView()
를 사용해야 한다. RowSorter
, TableRowSorter
, RowFilter
등에 관한 자세한 내용은 각 클래스에 관한 javadoc을 참조하기 바란다. RowSorter
TableRowSorter
RowFilter
"Java SE" 카테고리의 다른 글
- Java2D 소프트 클리핑 (댓글 6개 / 트랙백 0개) 2006/10/24
- JDESKTOP NETWORK COMPONENTS (JNDC)를 이용한 테이블 소개 (댓글 2개 / 트랙백 0개) 2005/05/31
- 가변 arity 메소드 (댓글 1개 / 트랙백 2개) 2005/11/15
- 스플래시 스크린과 MUSTANG (댓글 1개 / 트랙백 1개) 2005/12/20
- 제네릭(GENERIC) (댓글 3개 / 트랙백 1개) 2005/05/10
- 향상된 루프문 (댓글 2개 / 트랙백 1개) 2005/07/04
- HOTSPOT 가비지 콜렉션 구성 옵션 (댓글 3개 / 트랙백 0개) 2005/04/12
- 스윙 "괴담" (댓글 1개 / 트랙백 0개) 2005/09/13
- TIMEZONE CLASS이용하기 (댓글 2개 / 트랙백 2개) 2003/11/04
- 리스너 리스트를 위한 WEAKHASHMAP 사용하기 (댓글 1개 / 트랙백 0개) 2006/03/08