Song 리스트를 표처럼 보여주기(2단계)
앞의 글에 이어서 Song 리스트를 JTable로 보여주는 방법을 살펴본다. 결과는 다음과 같은 화면으로 보여진다.
이전 버전에서 바뀌는 내용
- GUIMain의 가운데 있던 JLabel 대신에 JTable 객체가 스크롤패널에 들어가게 된다.
- 하단의 텍스트필드가 표의 컬럼 수만큼 생기고 그것을 통해 Song의 수정과 추가가 가능해진다.
- 한 곡의 정보를 엔진과 GUI 사이에 주고 받기 위해 String[]을 이용하게 된다.
song_table 패키지
- GUIMain : JFrame을 상속하여 메인 윈도우의 역할을 하는 클래스다.
- TablePanel: 실제 보여질 화면 내용을 가지는 클래스다. 테이블 부분(tableControlloer)과 아래쪽 패널(bottom)을 포함하여 화면을 구성하는 클래스다.
- TableController: JTable을 구성하고 수정/추가/삭제 버튼에 대한 테이블의 처리 기능을 가지는 클래스다.
- BottomPane: 하부의 텍스트 필드와 버튼들을 구성하고 그에 대한 액션리스너를 제공한다.
JTable은 복잡한 컴포넌트여서 다루기가 좀 어렵지만 보통 기본적인 코드를 그대로 따라하면 되기 때문에 적용하기는 크게 어렵지 않다. TableController의 JTable 초기화 부분을 살펴보면 다음과 같다.
public class TableController implements ListSelectionListener {
SongTableMgr dataMgr;
DefaultTableModel tableModel;
JTable table;
int selectedIndex = -1;
void init() {
dataMgr = GUIMain.engine; // 구체 클래스 엔진 객체가 설정됨
tableModel = new DefaultTableModel(dataMgr.getColumnNames(), 0);
loadData("");
table = new JTable(tableModel);
ListSelectionModel rowSM = table.getSelectionModel();
rowSM.addListSelectionListener(this);
table.setPreferredScrollableViewportSize(new Dimension(500, 220));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
JTable은 표의 행렬과 항들을 채우기 위해 TableModel이라는 것을 사용한다. 여기서는 DefaultTableModel을 써서 컬럼 개수만 지정해 준 tableModel 을 생성해서 쓰고 있다. 이것으로 JTable 생성자를 호출하고 행을 클릭했을 때 동작을 수행할 리스너를 달아준다. (ListSelectionListener.valueChanged) 그리고 크기와 선택 모드를 설정하고 있다. (한 행만 선택)
행이 선택되었을 때 동작은 해당 행의 모든 셀의 데이터를 가져와 하단의 텍스트 필드에 채우는 것이다.
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel lsm = (ListSelectionModel)e.getSource();
if (!lsm.isSelectionEmpty()) {
selectedIndex = lsm.getMinSelectionIndex();
String[] rowTexts = new String[tableModel.getColumnCount()];
for (int i = 0; i < rowTexts.length; i++)
rowTexts[i] = (String)tableModel.getValueAt(selectedIndex, i);
TablePanel.bottom.moveSelectedToEdits(rowTexts);
}
}
선택된 행번호로 tableModel 에게 getValueAt으로 값을 요청하면 Object 객체가 반환된다. 그것을 스트링으로 바꾸어 텍스트필드에 넣을 스트링배열을 만든다. (텍스트필드는 bottom 패널이 가지고 있음)
다음으로 테이블의 행을 채워줄 데이터를 엔진에게 받아오는 부분을 loadData 메소드가 담당한다.
void loadData(String kwd) {
List<?> result = dataMgr.search(kwd);
tableModel.setRowCount(0);
for (Object m : result)
tableModel.addRow(((Song)m).getUiTexts());
}
setRowCount는 JTable이 보여주는 데이터를 리셋하는 효과를 낸다. 엔진에게 받아온 result 에서 각 행에 들어갈 데이터를 꺼내는 것이 Song의 getUiTexts() 메소드다.
Song 클래스의 getUIText() 메소드는 다음과 같다. 각 필드를 스트링 배열로 바꾸어 넘겨준다.
public String[] getUiTexts() {
return new String[] {""+id, name, title, ""+year, lyric};
}
mgr 패키지 : 변동없음
song 패키지의 SongTableMgr
원래 있던 SongMgr에 테이블 지원 기능을 추가하기 위해 다음과 같이 수정한다.
테이블의 컬럼 개수와 컬럼 헤더를 정해주는 기능을 다음과 같이 추가한다.
public class SongTableMgr extends Manager<Song> {
// 테이블의 헤더 데이터 제공 부분
String[] labels = {"랭킹", "이름", "제목", "년도", "가사"};
// 테이블의 열 제목을 스트링 배열로 돌려줌
public int getColumnCount() {
return labels.length;
}
public String[] getColumnNames() {
return labels;
}
추가와 수정 기능에서 표에서 한 행의 여러 항목을 처리하기 위해 다음과 같이 스트링 배열을 UI로부터 넘겨받아 엔진의 데이터를 수정한다.
public void addNewItem(String[] editTexts) {
// TODO Auto-generated method stub
Song s = new Song();
s.set(editTexts);
mList.add(s);
System.out.println("추가: "+s);
}
public void update(String[] editTexts) {
// TODO Auto-generated method stub
Song s = (Song)find(editTexts[0]);
s.set(editTexts);
System.out.println("수정: "+s);
}
여기서 Song의 set 메소드는 스트링 배열을 받아 그것으로 곡의 정보를 수정할 수 있다. 이것을 이용하여 해당 곡의 정보를 추가 또는 수정한다.
public void set(Object[] row) {
id = Integer.parseInt((String)row[0]);
name = (String)row[1];
title = (String)row[2];
year = Integer.parseInt((String)row[3]);
}
한편 SongTableMgr 클래스의 remove 함수는 첫번째 항(곡번호)를 이용하여 검색하도록 수정하였다.
추가 삭제 버튼의 액션 처리
추가 버튼에 대한 액션 핸들러는 TableControlloer의 addRow 메소드를 호출한다. 이 때 다음과 같이 텍스트필드에 있던 값을 스트링 배열 매개변수로 넘긴다.
public void actionPerformed(ActionEvent e) {
String[] editTexts = getEditTexts();
clearEdits();
TableController tableController = TablePanel.tableController;
switch (e.getActionCommand()) {
case "추가":
tableController.addRow(editTexts);
break;
...
}
tableController.table.clearSelection();
}
그럼 컨트롤러에서는 엔진에게 추가를 요청하고 tabeModel에게도 추가하게 한다.
void addRow(String[] editTexts) {
try {
dataMgr.addNewItem(editTexts);
} catch (Exception ex) { // 추가 중 오류 발생
ex.printStackTrace();
JOptionPane.showMessageDialog(null, "추가 데이터 오류");
return;
}
tableModel.addRow(editTexts);
}