public PaintFrame() {
this.startX = 0;
this.startY = 0;
this.toolBoxPanel = new ToolBoxPanel();
initFrame();
this.graphics = getGraphics(); //Frame이 렌더링되기 전에 호출하면 null을 리턴함.
this.graphics2D = (Graphics2D) graphics;
}
private void initFrame() {
initFrame(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT);
}
private void initFrame(int width, int height) {
setLayout(null);
setBackground(Color.WHITE);
setSize(width, height);
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension screenSize = tk.getScreenSize();
setLocation((screenSize.width / 2) - (width / 2), (screenSize.height / 2) - (height / 2));
addWindowListener(new AdapterWindowListener() {
@Override
public void windowClosing(WindowEvent e) {
e.getWindow().dispose();
}
});
this.add(toolBoxPanel);
setVisible(true);
}
버튼 리스트를 만들어서 버튼마다 마우스 이벤트 리스너를 달아주었다.
버튼마다 actionPerforemd 이벤트 핸들러를 달아주기 위해 MouseMotionLister, MouseListener 두 개를 같이 구현해놓은 어댑터 클래스를 만들었다.
public class AdapterMouseListener implements MouseMotionListener, MouseListener {...}
버튼 패널 클래스가 어댑터를 멤버변수로 가지고 있는데, 이는 Frame에 마우스 리스너를 삭제할 때 메서드 인자로 넘기기 위한 것이다.
버튼을 클릭할 때마다 Frame에 등록된 마우스 리스너를 삭제하고, 선택한 도구에 맞게 새로운 리스너를 등록해준다(직선, 연필만 구현).
private final String[] buttonLabels = {"연필", "직선", "사각형", "원", "세모", "지우개"};
private final List<Button> toolBtns;
private AdapterMouseListener mouseListener;
public ToolBoxPanel() {
this.toolBtns = makeButtons();
initPanel();
addActionEventListenerToButtons();
}
private void initPanel() {
setLayout(new GridLayout(1, 6));
setBounds(0, 30, 1024, 100);
for (Button toolBtn : toolBtns) {
this.add(toolBtn);
}
}
public void addActionEventListenerToButtons() {
for (Button toolBtn : toolBtns) {
toolBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
removeMouseListenerFromPaintFrame(e);
addMouseListenerToPaintFrame(e);
}
});
}
}
private void removeMouseListenerFromPaintFrame(ActionEvent e) {
Button btn = (Button) e.getSource();
PaintFrame paintFrame = (PaintFrame) btn.getParent().getParent();
paintFrame.removeMouseMotionListener(mouseListener);
paintFrame.removeMouseListener(mouseListener);
}
private void addMouseListenerToPaintFrame(ActionEvent e) {
Button btn = (Button) e.getSource();
PaintFrame paintFrame = (PaintFrame) btn.getParent().getParent();
Graphics graphics = paintFrame.getPaintFrameGraphics();
Graphics2D graphics2D = paintFrame.getPaintFrameGraphics2D();
switch (btn.getLabel()) {
case "연필":
mouseListener = new AdapterMouseListener() {
@Override
public void mouseMoved(MouseEvent e) {
paintFrame.setX(e.getX());
paintFrame.setY(e.getY());
}
@Override
public void mouseDragged(MouseEvent e) {
if (e.getModifiersEx() != MouseEvent.BUTTON1_DOWN_MASK) {
return;
}
graphics.drawLine(paintFrame.getX(), paintFrame.getY(), e.getX(), e.getY());
paintFrame.setX(e.getX());
paintFrame.setY(e.getY());
}
};
paintFrame.addMouseMotionListener(mouseListener);
break;
case "직선":
mouseListener = new AdapterMouseListener() {
@Override
public void mouseDragged(MouseEvent e) {
//TODO: 마우스 드래그시 출발점부터 마우스 포인터까지 계속 선이 따라다니도록 구현. 선이 마구 생기면 안됨.
}
@Override
public void mousePressed(MouseEvent e) {
paintFrame.setX(e.getX());
paintFrame.setY(e.getY());
}
@Override
public void mouseReleased(MouseEvent e) {
graphics.drawLine(paintFrame.getX(), paintFrame.getY(), e.getX(), e.getY());
}
};
paintFrame.addMouseMotionListener(mouseListener);
paintFrame.addMouseListener(mouseListener);
break;
case "사각형":
mouseListener = new AdapterMouseListener() {
};
break;
case "원":
break;
case "세모":
break;
case "지우개":
mouseListener = new AdapterMouseListener() {
@Override
public void mouseDragged(MouseEvent e) {
}
};
break;
}
}
현재 마우스 리스너 등록 순서가 꼬여서 그림판이 로딩 되고 나서 버튼을 한번 클릭 해줘야 작동함.
오랜만에 디버깅으로 api를 뒤져보면서 삽질을 많이 했다. AWT 컴포넌트 자체가 익숙하지 않은 상태에서 클래스를 나눠보려고 하니, 뭐가 되고 안되는지를 몰라 썼다 지웠다를 많이 반복했다. 그래도 역시 삽질하면서 하나하나 구현해 나가는 게 재미있었다.