1. 🔥 마우스 이벤트

HW와 OS의 관계: 사용자가 마우스를 USB포트로 연결하게 되면 Driver를 통해 os에 맞게 번역되어 OS로 전달하게 된다.

마우스는 프로그램이 여러개 돌아가도 현재 마우스가 위치한 프로그램에서만 이벤트가 발생한다.

이는os가 현재 마우스의 위치를 알려주기 때문이다.

위에 개념을 정리하자면

  1. 연결된 하드웨어가 드라이버를 통해 번역되어 os에게 신호를 전달해 준다
  2. os는 해당 하드웨어의 작동을 이벤트 큐에 등록하여 맞는 이벤트 리스너를 실행시킨다.

마우스 이벤트

java만 해당되는 것이 아닌 다른언어의 gui이벤트 메서드는 비슷비슷함

이벤트 발생 경우 리스너의 메서드 리스너
마우스가 컴포넌트위에 올라갈 때 void mouseEntered(MouseEvent e) MouseListener
마우스가 컴포넌트위에서 내려올 때 void mouseExited(MouseEvent e) MouseListener
마우스 버튼이 눌러졌을 때 void mousePressed(MouseEvent e) MouseListener
눌러진 버튼이 떼어질 때 void mouseReleased(MouseEvent e) MouseListener
마우스로 컴포넌트를 클릭하였을 때 void mouseClicked(MouseEvent e) MouseListener
마우스가 드래그되는 동안 void mouseDragged(MouseEvent e) MouseMotionListener
마우스가 움직이는 동안 void mouseMoved(MouseEvent e) MouseMotionListener
마우스 휠이 구르는 동안 void mouseWheelMoved(MouseWheelEvent e) MouseWheelListener
  • 마우스가 눌러진 위치에서 떼어지는 경우
    • mousePressed(), mouseReleased(), mouseClicked()
  • 마우스가 드래그될 때 호출되는 메소드 호출 순서
    • mousePressed(), mouseDragged(), mouseDragged()…mouseDragged(), mouseReleased()

1.1. 실습

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


class MouseAndKeyboardPanel extends JPanel{
    JLabel text;

    MouseAndKeyboardPanel(){ //생성자
        text = new JLabel("Mouse Test");
        text.addMouseListener(new MouseListener(){ //이벤트 리스너 등록(마우스)
        // 필요한 추상메서드들
            public void mouseClicked(MouseEvent e) { 
            }
            public void mousePressed(MouseEvent e) {
            }
            public void mouseReleased(MouseEvent e) { 
            }
            public void mouseEntered(MouseEvent e) {
            }
            public void mouseExited(MouseEvent e) {
            }
        });

        add(text);
    }
}

public class App extends JFrame{

    App(){
        setTitle("Mouse and keyboard");
        setSize(300,300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        add(new MouseAndKeyboardPanel());

        setVisible(true);
    }

    public static void main(String[] args) {
        new App();
        // JFrame쓰레드 만들고 main쓰레드 종료
    }
}

자바 이미지

노랑색 부분이 JLable영역 부분

  • JLable을 MouseAndKeyboardPanel클래스의 객체 내부 필드로 선언 하고 생성자로 이름 초기화 후 Panel에 추가 한 모습이다.

  • text.addMouseListener(new MouseListener()를 넣어 이벤트 리스너를 등록했지만 현재는 추상 메서드를 오버라이딩만 하고 따로 기능적인 부분을 추가하지 않았기 때문에 별다른 반응이 없다.

    • 하지만 앞서 말한 os에서는 모든 마우스의 이벤트들을 살펴보고 있음을 명심해야함(JLable에 대한 움직임, 클릭..등등)

1.2. 실습 - 이벤트 등록

this.addMouseListener(new MouseListener(){
    public void mouseClicked(MouseEvent e) {
        System.out.println("clicked!");
    }
    public void mousePressed(MouseEvent e) {
        System.out.println("pressed!");
    }
    public void mouseReleased(MouseEvent e) {
        System.out.println("released!"); 
    }
    public void mouseEntered(MouseEvent e) {
        System.out.println("enter!");
    }
    public void mouseExited(MouseEvent e) {
        System.out.println("exit!");
    }
});

추상메서드 구현부분만 가져옴

  • 마우스 클릭(pressed -> released -> 클릭이벤트 발생, 드래그 즉, pressed상태에서 움직이면 발생하지 않음)
  • 마우스 누름(pressed 드래그 클릭 상관없이 사용자가 마우스를 누르자 마자 즉시 호출)
  • 마우스 때기(pressed후 바로 released호출)
  • 방문, 퇴장(마우스가 컴포넌트위에 올라갈때 내려갈때 발생)

추가

조금 더 명시적으로

무명클래스(람다식)에서는 로컬변수가 접근가능하다..!(final변수와 같이 변하지 않는다는 조건이 붙음)

JPanel p = this;
this.addMouseListener(new MouseListener(){
    public void mouseClicked(MouseEvent e) {
        System.out.println("clicked!");
    }
    public void mousePressed(MouseEvent e) {
        System.out.println("pressed!");
    }
    public void mouseReleased(MouseEvent e) {
        System.out.println("released!"); 
    }
    public void mouseEntered(MouseEvent e) {
        p.setBackground(Color.BLACK);
    }
    public void mouseExited(MouseEvent e) {
        p.setBackground(Color.blue);
    }
});

위와 같이 p(JPanel)변수로 자신을 직접 참조하여 접근 가능하다.

1.3. 실습 - 이벤트(심화)

좀 더 편의성을 위하는 방법은 없을까?

  • 현재 클래스 즉, JPanel을 상속 받은 클래스 내부에서 따로 리스너를 무몀클래스로 구현하는 방법도 있지만, 좀 더 쉽게 MouseListener인터페이스를 해당 클래스에서 직접 상속받아 필드 메서드로 가져가는 방법이다.

위의 방식을 사용하면 자신을 참조하여 리스너를 등록하고 쉽게 자신의 메서드를 호출할 수 있다.

// MouseListener(인터페이스) 추가로 상속받음
class MouseAndKeyboardPanel extends JPanel implements MouseListener{
    JLabel text;

    MouseAndKeyboardPanel(){
        text = new JLabel("Mouse Test");
        add(text);
        
        addMouseListener(this); //자신의 필드메서드로 MouseListener추상메서드가 존재한다.
    }
    public void mouseClicked(MouseEvent e) {
        System.out.println("clicked!");
    }
    public void mousePressed(MouseEvent e) {
        System.out.println("pressed!");
    }
    public void mouseReleased(MouseEvent e) {
        System.out.println("released!"); 
    }
    public void mouseEntered(MouseEvent e) {
        setBackground(Color.BLACK); //이미 자신이 JPanel이기 때문에 p를 사용하여 명시적으로 가르킬 필요 없음
    }
    public void mouseExited(MouseEvent e) {
        setBackground(Color.blue);
    }
}

1.4. 실습 - 이벤트 정보 가져오기(이벤트 객체)

그렇다면 JButton과 같이 이벤트 객체를 통해 정보를 얻어오는 것이 가능할까?

public void mousePressed(MouseEvent e) {
    int x = e.getX(); // 이벤트 객체로 부터 즉, 마우스 event로 부터 좌표를 가져옴
    int y = e.getY();
    int b = e.getButton(); // 해당 마우스 버튼에 대한 정수값
    if (b == MouseEvent.BUTTON1){ // 왼쪽버튼 상수값
        text.setText("Mouse Pressed at ("+x+","+y+")");
        System.out.println(b);
    }
}

리터럴값을 바로 적는것은 안좋은 습관.. 해당 값에 대한 상수값필드 존재

직접 접근하여 정보를 참조할 수 있음

추가 이벤트 객체 메서드&&필드

  • getClickCount() : 해당 위치에서 동일한 곳을 더블클릭의 개념으로 여러번 클릭스 카운트가 증가 다른 곳이나 클릭 정지 시 카운트 초기화

1.5. 실습 - 레이아웃 변경

앞서 공부한 JFrame은 기본적으로 BorderLayout, JPanel은 FlowLayout

두가지의 컨테이너는 디폴트 레이아웃이 정해져 있다.

그렇다면 사용자 커스텀 레이아웃은 어떻게 사용할까?

MouseAndKeyboardPanel(){
    this.setLayout(null); //기본은 FlowLayout인데 null을 넣어서 위치가 존재하지 않음

    text = new JLabel("Mouse Test");
    add(text);
    text.setLocation(10,10); // 위치를 설정(x,y)
    text.setSize(70,20); // 크기를 설정(가로, 세로)
    // 위치 및 사이즈를 설정해줘야지 표시가 가능
    addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
    if(e.getButton() == MouseEvent.BUTTON1){ // 왼쪽 버튼을 눌렀을 때만
        text.setLocation(e.getX(), e.getY()); // 해당 Lable의 위치를 이벤트 객체 즉, 마우스 위치로 변경
    }

}

1.6. 마우스 모션 리스너

앞서 다룬 MouseListener는 마우스의 위치와 클릭이 중심였다면 MouseMotionListener는 mouseDragged, mouseMoved의 추상메서드를 가진 인터페이스다.

  • mouseMoved: 클릭하지 않고 움직이면
  • mouseDragged: 클릭하고 움직이면

1.7. 실습 - 모션리스너 + 전체 정리

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

class MouseAndKeyboardPanel extends JPanel implements MouseListener, MouseMotionListener{
    JLabel text;
    int mouseButton; // 마우스 클릭 id를 담을 변수 선언

    MouseAndKeyboardPanel(){
        this.setLayout(null); //기본은 FlowLayout인데 null을 넣어서 위치가 존재하지 않음

        text = new JLabel("Mouse Test");
        add(text);
        text.setLocation(10,10); // 위치를 설정(x,y)
        text.setSize(70,20); // 크기를 설정(가로, 세로)

        addMouseListener(this);
        addMouseMotionListener(this);
    }
    public void mouseClicked(MouseEvent e) {
    }
    public void mousePressed(MouseEvent e) {
        mouseButton = e.getButton();
    }
    public void mouseReleased(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    public void mouseDragged(MouseEvent e) {
        if(mouseButton == MouseEvent.BUTTON1) //마우스 왼쪽버튼만 처리
            text.setLocation(e.getX(), e.getY()); //해당 위치로 이동 즉, 드래그를 따라감
    }
    public void mouseMoved(MouseEvent e) {
    }
}

public class App extends JFrame{

    App(){
        setTitle("Mouse and keyboard");
        setSize(300,300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        add(new MouseAndKeyboardPanel());

        setVisible(true);
    }

    public static void main(String[] args) {
        new App();
    }
}

추가++ 색 변경 Color 클래스

public void mouseClicked(MouseEvent e) {
    if(e.getClickCount() == 2){
        Color x = new Color((int)(Math.random()*255),
        (int)(Math.random()*255),
        (int)(Math.random()*255));
        setBackground(x);
    }  
}

1.8. 정리

마우스 이벤트 리스너

  • MouseListener의 이벤트(마우스 위치 및 클릭)
    • mouseEntered(): 마우스가 컴포넌트위에 올라갈 때
    • mouseExited(): 마우스가 컴포넌트위에서 내려올 때
    • mousePressed(): 마우스 버튼이 눌러졌을 때
    • mouseReleased(): 눌러진 버튼이 떼어질 때
    • mouseClicked(): 마우스로 컴포넌트를 클릭하였을 때
  • MouseMotionListener의 이벤트(마우스 움직임)
    • mouseDragged(): 마우스가 드래그되는 동안
    • mouseMoved(): 마우스가 움직이는 동안

MouseMotionListener는 마우스 리스너에서 분리되었다. (움직임을 처리 즉, 메모리사용이 높음 필요할 경우에 따로 상속받아서 사용)

MouseEvent객체의 필드 및 메서드

  • 마우스 포인터의 위치
    • getX(),getY()
    • Point getPoint()
  • 입력된 마우스 버튼(id)
    • int getButton()
  • 마우스 클릭 횟수
    • int getClickCount()

Adapter클래스

  • 이벤트 리스너 구현에 따른 부담을 해소하기 위한 클래스
  • 인터페이스로 상속받으면 모든 추상 메서드의 구현을 강제함
  • 리스너 인터페이스를 상속받은 클래스
    • 해당 클래스 내부에서 인터페이스 디폴트 구현
    • 필요한 부분만 오버라이딩하여 사용 가능

2. 🔥 키보드 이벤트

마우스 이벤트와 동일하게 동작하지만 주의해야하는 점이 있다. 마우스 이벤트는 마우스 포인터 및에 있는 동작들을 OS가 처리하지만 키보드는 활성화 된 창에서만 이벤트를 보낸다.

즉, Focus를 가지고 있는 객체

key이벤트

  • 키 입력 시 다음, key이벤트 발생
    • 키를 누르는 순간
    • 누른 키를 떼는 순간
    • 누른 키를 떼는 순간(Unicode의 경우)
  • 키 이벤트를 받을 수 있는 조건
    • 모든 컴포넌트들은 가능 다만, 현재 포커스를 가진 컴포넌트
  • 포커스
    • 컴포넌트나 응용프로그램이 키이벤트를 독접하는 권한
    • component.setFocusable(true)코드 필요

2.1. 실습

class MouseAndKeyboardPanel extends JPanel{

    MouseAndKeyboardPanel(){
        addKeyListener(new KeyListener(){ // 무명클래스로 KeyListener객체를 넘김
            public void keyTyped(KeyEvent e) { //눌린 키 조합
            }
            public void keyPressed(KeyEvent e) { //눌린 경우
                System.out.println("Pressed!");
            }
            public void keyReleased(KeyEvent e) { //눌린키를 뗐을 경우
                System.out.println("Released");
            }
        });
        this.setFocusable(true); // focus를 받을 수 있게함
        this.requestFocus(); // 현재 포커스로 강제함
    }
}

public class App extends JFrame{

    App(){
        setTitle("Mouse and keyboard");
        setSize(300,300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        add(new MouseAndKeyboardPanel());

        setVisible(true);
    }

    public static void main(String[] args) {
        new App();
    }
}

앞서 작성한 코드에서 키이벤트 리스너로 수정

  • setFocusable는 컴포넌트마다 포커스를 받을 수 있는 경우와 받을 수 없는 경우가 달라서 기본적으로 동일하게 호출한다.
    • JPanel의 경우 컨테이너의 속성을 가지기 때문에 포커스를 받을 수 있게 만들어준다.
  • requestFocus포커스의 현재 위치를 현재 컨테이너로 강제한다.

2.2. 실습 - 백그라운드 색 변경

class MouseAndKeyboardPanel extends JPanel{

    MouseAndKeyboardPanel(){
        addKeyListener(new KeyListener(){
            public void keyTyped(KeyEvent e) { 
            }
            public void keyPressed(KeyEvent e) {
                setBackground(new Color((int)(Math.random() * 255),(int)(Math.random() * 255),(int)(Math.random() * 255)));
            }
            public void keyReleased(KeyEvent e) {
            }
        });
        this.setFocusable(true);
        this.requestFocus();
    }
}

Math의 정적 메서드 random과 Color클래스를 활용

2.3. 실습 - 무명클래스와 생성자의 관계

class MouseAndKeyboardPanel extends JPanel{

    int a = 0; // 객체 내부에 존재하는 필드 변수
    MouseAndKeyboardPanel(){
        addKeyListener(new KeyListener(){
            int b = 1; // 생성자에서 생긴 로컬 변수
            public void keyTyped(KeyEvent e) { 
            }
            public void keyPressed(KeyEvent e) {
                System.out.println(a); // 생성자 내부에서 사용 시 해당 변수 final로 정의
                System.out.println(b); 
            }
            public void keyReleased(KeyEvent e) {
            }
        });
        //a++; 에러나지 않음
        //b++; 에러 출력
        this.setFocusable(true);
        this.requestFocus();
    }
}

a변수와 b의 변수의 다른 점은 a는 객체 내부에서 계속 존재하는 멤버 필드 변수이지만, b는 생성자에서 잠깐 사용되는 로컬변수로 바로 생성자 종료시 없어지게 된다.

  • a,b 둘다 키보드 이벤트 리스너에서 사용이 가능하다
    • 클래스안에 클래스 즉 내부클래스의 개념이라 전역변수처럼 적용되어 호출이 가능
    • 하지만 생성자에서 호출 시 한번 실행되고 종료되기 때문에 크게 의미는 없음
  • 생성자에서 이벤트 리스너 등록 시
    • 이벤트 리스너의 성격상 사용자의 선택에 따라 프로그램의 플로우가 결정되기 때문에 생성자에서 이벤트 리스너를 연결하고 종료됨
    • 따라서 해당 이벤트 리스너에서 사용된 로컬변수들은 상수의 값을 가짐 b
    • 생성자에서 a의 값을 변경 후 종료는 가능하지만 b는 선언 후 이벤트 리스너에서 사용되었기 때문에 상수의 성격을 가지며 수정이 불가.
  • 생성자에서 필드 메서드 사용 가능.

이 경우는 일반적인 경우가 아닌 컴포넌트에 대해 간단한 이벤트에 해당 되며 사실 일반적인 사용방법이 아니다.

2.4. 실습 - 일반적인 사용 방법(복습)

class MouseAndKeyboardPanel extends JPanel implements KeyListener{
    MouseAndKeyboardPanel(){
        addKeyListener(this);
        this.setFocusable(true);
        this.requestFocus();
    }
    public void keyTyped(KeyEvent e) { 
    }
    public void keyPressed(KeyEvent e) {
    }
    public void keyReleased(KeyEvent e) {
    }
}

위와 같이 사용하면 위에서 다룬 문제점을 쉽게 해결할 수 있다.

2.5. 실습 - 입력 키 정보 받아오기(이벤트 객체)

class MouseAndKeyboardPanel extends JPanel implements KeyListener{
    JLabel text;

    MouseAndKeyboardPanel(){
        this.setLayout(null); //레이아웃 null설정
        text = new JLabel("text"); //레이블의 값 초기화
        add(text);
        text.setLocation(100,100); //위치 설정
        text.setSize(50,50); //사이즈 설정
        addKeyListener(this);
        this.setFocusable(true); //포커스를 가지게 함
        this.requestFocus(); //현재 포커스로 강제
    }
    public void keyTyped(KeyEvent e) { 
    }
    public void keyPressed(KeyEvent e) {
        Point point = text.getLocation(); // Point 클래스 x,y의 좌표를 저장
        int x = point.x;
        int y = point.y;

        switch (e.getKeyCode()) { //해당 키의 code 정수값
            case KeyEvent.VK_LEFT: //해당 키 같은지 확인
                x -= 10;
                text.setLocation(x,y); //좌표 변경
                break;
            case KeyEvent.VK_RIGHT:
                x += 10;
                text.setLocation(x,y);
                break;
            case KeyEvent.VK_DOWN:
                y += 10;
                text.setLocation(x,y);
                break;
            case KeyEvent.VK_UP:
                y -= 10;
                text.setLocation(x,y);
                break;
            default:
                break;
        }
    }
    public void keyReleased(KeyEvent e) {
    }
}

다른 이벤트 리스너와 마찬가지로 이벤트 객체로 부터 입력 키의 정보를 참조할 수 있다.

  • getKeyCode메서드로 이벤트 객체의 눌린 키 정보 참조
  • KeyEvent.VK_LEFT 키의 정보(정수)를 담고있는 상수값

2.6. 실습 - 타이핑(keyTyped)

타이핑 즉, 키로 입력을 할려는 경우에는 keyTyped이벤트 리스너를 정의해야 한다.

//생략
public void keyTyped(KeyEvent e) { 
    char c = e.getKeyChar();
    String str = text.getText();
    str += c;
    text.setText(str);
}

keyPressed, keyReleased가 불리고 나서 호출되는 keyTyped의 이벤트 리스너

  • keyTyped에서 getKeyChar()으로 키의 문자를 가져올 수 있다.
    • keyPressed, keyReleased에서 참조 불가능..

삭제 추가

public void keyTyped(KeyEvent e) { 
    // 가져온 키를 정수로 강제형변환시 그 값이 아스키코드값.
    if((int)e.getKeyChar() == KeyEvent.VK_BACK_SPACE) 
    {
        String str = text.getText(); //백스페이스 삭제
        text.setText(str.substring(0,str.length() - 1));
    }
    else // 문자 추가 
        text.setText(text.getText()+e.getKeyChar());
}
  • keyTyped에서 조건을 걸고 싶다면 int형으로 형변환해서 조건을 검사한다.
  • 스페이스 삭제는 substring을 사용하여 뒤에 마지막 문자를 자른다.

2.7. 실습 - JTextField

위에서는 JLabel + 키보드 이벤트 리스너를 사용해서 타이핑을 처리했지만 일반적으로 GUI프로그래밍에서는 textfield를 사용한다.

class MouseAndKeyboardPanel extends JPanel implements KeyListener{
    JTextField textField;

    MouseAndKeyboardPanel(){
        this.setLayout(null);
        addKeyListener(this);
        this.setFocusable(true);
        this.requestFocus();
        textField = new JTextField(20);
        add(textField);
        textField.setLocation(10, 10); //layout설정이 null이기 때문에 위치 설정
        textField.setSize(100,20); // 마찬가지
    }
}
  • 따로 이벤트 리스너를 등록하지 않아도 타이핑이 가능하다.

2.8. 추가

  • requestFocus호출
    • 기본적으로 requestFocus을 호출하게 되면 해당 포커스가 메인이 되기 때문에 마우스 클릭 이벤트에 requestFocus호출시 해당 컴포넌트가 포커스를 가지게 된다.(setFocusable(true)을 통해 포커스를 가진 경우)

3. 정리

  • KeyListener 메서드
    • keyPressed: 키를 누르는 순간 / getkeycode()
    • keyReleased: 누른 키를 떼는 순간 / getkeycode()
    • keyTyped: 누른 키를 떼는 순간(unicode의 경우만) / getkeychar()
    • 순서: pressed -> Typed -> Released
  • 가상키: 유니코드를 포함한 모든 키는 자바의 가상코드키에 저장이 되어 있다.

즉, 키의 문자코드(유니코드)는 getKeyChar()를 호출하고 키의 가상키값은 getKeyCode()를 사용한다.

  • 키의 문자열이름 리턴: getkeyText() -> “space”

태그: ,

카테고리:

업데이트:

댓글남기기