자바 Swing에서 경고창 혹은 의사 결정(확인/취소)을 위한 확인창으로 JOptionPane이 존재한다. 위 캡쳐에 나온 화면은 ErrorMessage 아이콘을 사용한 것인데 이 아이콘을 커스텀해보자.
위는 자바에서 기본적으로 제공하는 아이콘이다. 나는 개인적으로 쓰는 프로그램에 경고창을 띄우던 중 기본 아이콘들이 너무 옛날 느낌을 주기도 하고 상황에 맞게 사용할 수 있는 아이콘이 매우 제한적이라는 생각이 들어 커스텀을 찾아보게 되었다.
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
public class JOptionPaneTest {
public static void main(String[] args) {
ImageIcon originIcon = new ImageIcon("res//arrow_cycle.png");
Image resizeImg = originIcon.getImage().getScaledInstance(30, 30, Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(resizeImg);
JOptionPane.showConfirmDialog(null, "다시 실행하시겠습니까?", "확인", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, icon);
}
}
메시지 창을 띄우기 위해 굳이 JFrame까지 사용할 필요는 없다. 위처럼 해주기만 해도 메시지 창을 띄울 수 있다. 테스트에 사용된 이미지 파일은 프로젝트 폴더에 "res" 폴더를 생성한 뒤 안에 넣어주었다.
ImageIcon 객체를 생성하고 프로젝트 폴더에 추가한 이미지 파일을 불러온다. 예제에 사용한 이미지 파일은 메시지 창에 쓰기에는 꽤나 크기 때문에 그대로 사용하면 이미지 크기가 들어가는 만큼 메시지 창이 컵져 버리기 때문에 조절이 필요하다. 아이콘으로 쓰기 적당한 파일 크기는 30x30 ~ 50x50이기 때문에 적절히 값을 바꾸어 가며 맞는 값을 찾으면 된다. 코드에서 리사이즈 작업을 해줘도 되지만 그림판, 포토샵 등으로 이미지 파일의 자체 크기를 조정해줘도 된다.
아이콘을 설정했으면 JOptionPane에서 원하는 타입의 다이얼로그(메서드)를 선택한 다음, Icon 객체를 파라미터로 받는 (오버로딩된)메서드를 선택하고 인자로 ImageIcon 객체를 넘겨준다. 그리고 프로그램을 실행하면 아래와 같은 결과를 얻을 수 있다.
JTable에 있는 값을 더블 클릭했을 때 해당하는 값을 복사해보자. 위 gif에서는 커서가 보이지 않기 때문에 무엇을 클릭하는지 자세히 보이지 않을 수 있는데 자세히 보면 클릭되는 셀의 테두리가 굵게 되는 것을 볼 수 있다.
기본적으로 JFrame, JTable을 다룰 줄 알아야 하고 MouseListener 혹은 MouseAdapter를 사용할 줄 알면 좋다. 전체 코드를 포스팅에 포함시킬 예정이지만 테이블 생성에 관한 기본적인 설명은 하지 않는다.
MouseListener는 인터페이스이기 때문에 마우스의 핵심적인 이벤트(드래그, 이동, 클릭 전후) 메서드를 강제적으로 구현해야 하지만 아답터를 쓰면 필요한 메서드만을 오버라이드하여 사용할 수 있다. 현재 구현에 있어서는 클릭한 후 이벤트만 필요하기 때문에 리스너가 아닌 아답터를 사용하여 구현할 것이다. 아답터를 사용할 줄 모른다면 리스너로 구현해도 문제없다.
테이블에서 선택된 값, 문자열을 복사하면 클립보드(Clipboard)에 저장된다. 클립보드에 대해 깊게 알 필요는 없고 우리가 복사한 값이 클립보드에 저장되고 복사한 값을 읽어올 때에는 붙여넣기(혹은 Ctrl+V)를 통해 클립보드에서 꺼내온다는 것만 알면 되겠다.
아래는 코드다.
import java.awt.Color;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
public class MyFrame extends JFrame{
private JTable jtab;
private DefaultTableModel dtm;
private Clipboard clipboard;
private ArrayList<Person> list;
private final String[] TABLE_HEADER = {"No", "Name", "Age"};
public static void main(String[] args) {
MyFrame myFrame = new MyFrame();
myFrame.setVisible(true);
}
public MyFrame() {
setTitle("MyFrame");
setBounds(10, 10, 300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(Color.white);
setLayout(null);
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
setComponent();
setTable();
}
private void setComponent() {
dtm = new DefaultTableModel(TABLE_HEADER, 0) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
jtab = new JTable(dtm);
jtab.getTableHeader().setReorderingAllowed(false);
jtab.getTableHeader().setEnabled(false);
jtab.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jtab.setRowHeight(25);
jtab.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (e.getClickCount() == 2) {
int row = jtab.getSelectedRow();
int col = jtab.getSelectedColumn();
StringSelection value = null;
switch(col) {
case 0:
value = new StringSelection(list.get(row).no + "");
break;
case 1:
value = new StringSelection(list.get(row).name);
break;
case 2:
value = new StringSelection(list.get(row).age + "");
}
clipboard.setContents(value, value);
}
}
});
JScrollPane tablePan = new JScrollPane(jtab, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
tablePan.setBounds(10, 10, 265, 140);
add(tablePan);
}
private void setTable() {
list = new ArrayList<>();
list.add(new Person(1, "김철수", 21));
list.add(new Person(2, "이영미", 22));
list.add(new Person(3, "신지수", 19));
for(Person person : list)
dtm.addRow(person.toArray());
}
class Person {
int no, age;
String name;
public Person(int no, String name, int age) {
this.no = no;
this.name = name;
this.age = age;
}
public Object[] toArray() {
return new Object[] {no, name, age};
}
}
}
기본적인 코드만 존재하기 때문에 JTable을 한 번이라도 생성해봤다면 코드를 어렵지 않게 이해할 수 있을 것이다. 데이터 클래스인 Person의 경우 예제 코드를 줄이기 위해서 getter를 생략하였다. 핵심은 멤버 변수인 Clipboard 객체와 JTable 객체에 마우스 이벤트를 추가한 addMouseListener 메서드가 되겠다.
JTable에 추가된 값을 마우스로 클릭했을 때 이벤트를 발생시키기 위해서 MouseAdapter를 추가한 뒤 마우스 이벤트 중 클릭이 때어진 순간 이벤트를 발생시키는 mouseClicked 메서드를 오버라이드 한다. 여기서 더블 클릭을 구현하기 위해서 getClickCount를 통해 연속으로 클릭된 횟수를 체크하여 2번 클릭되었을 경우에 이벤트를 처리한다.
더블 클릭이 발생하면 먼저 클릭된 행(row), 열(column)의 인덱스를 구한다. 행은 리스트의 인덱스로 사용되고 열은 리스트에서 읽어온 객체에서 특정한 값을 읽는데 사용될 것이다. 값을 읽었으면 StringSelection 객체에 초기화한 후 해당 객체를 클립보드에 세팅한다. 여기까지 문제없이 수행되었다면 클립보드에 값이 복사되었을 것이다. 텍스트를 입력할 수 있는 곳(메모장 등)에다가 "붙여넣기" 혹은 Ctrl+v를 수행하면 값이 복사된 것을 확인할 수 있다.
예제는 JTextArea를 배치하여 드롭된 파일 목록을 출력한다. DropTargetListener는 이름에서 짐작할 수 있듯이 파일을 드래그해 올 때 이벤트를 감지하고 처리하는 인터페이스이고, DropTarget은 파일을 드롭시킬 Target Component와 이벤트를 설정할 수 있다. JFrame을 Target으로 하고 이벤트도 현재 클래스에서 implements 했으므로 (this, this)를 넘겨주었다.
파일을 Drop하게 되면 Drop 메서드에서 File 타입 객체들을 List로 읽어올 수 있다. 바로 처리할 것이 아니라면 File 타입의 리스트를 멤버 변수로 생성해두고 파일이 드래그될 때마다 누적시켜서 처리하는 것이 좋다.
Swing으로 화면을 구성했을 때 Frame에 배치한 컴포넌트들이 보이지 않고 마우스로 해당 자리를 클릭해야만 컴포넌트가 표시되거나 클릭해도 표시되지 않을 때가 있다.
public class Main {
public static void main(String args[]) {
GUI gui = new GUI();
}
}
class GUI extends JFrame {
public GUI () {
..
this.setVisible(true);
..
}
}
JFrame을 상속한 GUI 클래스가 있을 때 해당 클래스 내부에서 setVisible을 호출할 경우 위와 같은 현상이 발생한다.
public class Main {
public static void main(String args[]) {
GUI gui = new GUI();
gui.setVisible(true);
}
}