5.Java事件處理機制
5.1小球移動案例
通過監聽鍵盤按鍵,實作小球的移動
例子:
package li.gui.even_;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author 李
* 演示小球通過鍵盤控制上下左右的移動-->講解Java事件的控制
*/
public class BallMove extends JFrame {
MyPanel mp = null;
public static void main(String[] args) {
BallMove ballMove = new BallMove();
}
public BallMove() {
mp = new MyPanel();
this.add(mp);//將面板加入框框中
this.setSize(400, 300);//設定框框的大小
//代表視窗JFrame物件可以監聽鍵盤事件,即可以監聽到面板發生的鍵盤事件
this.addKeyListener(mp);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//點擊框框的叉就可以退出程式運行
this.setVisible(true);
}
}
//面板,可以畫出小球
// KeyListener 是監聽器,可以監聽鍵盤事件
class MyPanel extends JPanel implements KeyListener {
//為了讓小球可以移動,把小球左上角的坐標設為x,y變數
int x = 10;
int y = 10;
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 20, 20);//默認為黑色
}
//使用快捷鍵Alt+Enter快速重寫方法
//keyTyped() :鍵盤有字符輸出時該方法就會快速觸發
@Override
public void keyTyped(KeyEvent e) {
}
//keyPressed():當某個鍵按下去的時候該方法就會觸發
@Override
public void keyPressed(KeyEvent e) {
// System.out.println((char)e.getKeyCode()+"被按下...");
// 根據用戶按下的鍵來處理小球的移動(上下左右的鍵)
//在Java中會給每一個鍵,分配一個值(int)
if (e.getKeyCode() == KeyEvent.VK_DOWN) {//KeyEvent.VK_DOWN就是向下的箭頭按鍵對應的code值
y++;
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
y--;
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
x--;
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
x++;
}
//讓面板重繪--repaint
this.repaint();
}
//keyReleased():當某個鍵釋放(松開)的時候該方法就會觸發
@Override
public void keyReleased(KeyEvent e) {
}
}
5.2事件處理機制介紹
基本說明:
Java事件處理是采取“委派事件模型”,當事件發生時,產生事件的物件,會把此"資訊"傳遞給"事件的監聽者"處理,這里所說的”資訊“實際上就是 java.awt.event 事件類別庫里某個類所創建的物件,把它稱為”事件的物件“,

在之前的小球移動案例中,事件源就是鍵盤按鍵,事件物件就是KeyEvent
事件處理機制的深入理解:
前面我們提得到的幾個重要概念 事件源、事件、事件監聽器下面我們來全面地介紹它們,
-
事件源:事件源是一個產生事件的物件,比如按鈕,視窗等,
-
事件:事件就是承載事件源狀態改變時的物件,比如當鍵盤事件、滑鼠事件、視窗事件等等,會生成一個事件物件,該物件保存著當前事件很多資訊,比如KeyEvent物件含有被按下鍵的Code值,java.awt.event包和javax.swing.event包中定義了各種事件型別,
-
事件型別:[查閱jdk檔案]
| 事件類 | 說明 |
|---|---|
| ActionEvent | 通常在按下按鈕,或雙擊一個串列項或選中某個選單時發生 |
| AdjustmentEvent | 當操作一個滾動條時發生 |
| ComponentEvent | 當一個組件隱藏,移動,改變大小時發送 |
| ContainerEvent | 當一個組件從容器中加入或者洗掉時發生 |
| FocusEvent | 當一個組件獲得或是失去焦點時發生 |
| ItemEvent | 當一個復選框或是串列項被選中時,當一個選擇框或選擇選單被選中 |
| KeyEvent | 當從鍵盤的按鍵被按下,松開時發生 |
| MouseEvent | 當滑鼠被拖動,移動,點擊,按下 |
| TextEvent | 當文本區和文本域的文本 發生改變時 發生 |
| WindowEvent | 當一個視窗激活,關閉,失效,恢復,最小化 |
-
事件監聽器介面:
1)當事件源產生一個事件,可以傳送給事件監聽者處理
2)事件監聽者實際上就是一個類,該類實作了某個事件監聽介面,比如之前小球移動案例中的MyPanel就是一個類,它實作了KeyListener介面,它就可以作為一個事件監聽者,對接收到的事件進行處理
3)事件監聽器介面有多種,不同的事件監聽器介面可以監聽不同的事件,一個類可以實作多個監聽介面
4)這些介面在java.awt.event包和javax.swing.event包中定義,列出常用的事件監聽器介面,查看jdk檔案就行了,
5.3繪制坦克移動的圖案
讓坦克動起來
使用ava事件處理機制和java繪圖技術,讓坦克可以通過按鍵控制上下左右移動(使用WSAD按鍵表示)
例子:
Tank.java:
package li.TankGame.version02;
/**
* @author 李
* @version 2.0
*/
public class Tank {
private int x;//坦克的橫坐標
private int y;//坦克的縱坐標
private int direct;//坦克方向 0:向上 1:向右 2:向下 3:向左
private int speed = 1;//坦克的速度,初始值為 1
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//上下左右移動方法
public void moveUp() {
y -= speed;
}
public void moveRight() {
x += speed;
}
public void moveDown() {
y += speed;
}
public void moveLeft() {
x -= speed;
}
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
public int getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Hero.java:
package li.TankGame.version02;
/**
* @author 李
* @version 2.0
*/
public class Hero extends Tank {
public Hero(int x, int y) {
super(x, y);
}
}
MyPanel.java:
package li.TankGame.version02;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author 李
* @version 2.0
* 坦克大戰的繪圖區域
*/
//為了監聽鍵盤事件,要實作 KeyListener
public class MyPanel extends JPanel implements KeyListener {
//定義我的坦克
Hero hero = null;
public MyPanel() {
hero = new Hero(100, 100);//初始化自己的坦克
//hero.setSpeed(5); //改變坦克的速度
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 700, 550);//填充矩形,默認為黑色
//畫出坦克-封裝方法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
}
/**
* 撰寫方法,畫出坦克
*
* @param x 坦克的左上角橫坐標
* @param y 坦克的左上角縱坐標
* @param g 畫筆
* @param direct 坦克方向(上下左右)
* @param type 坦克的型別(我方,敵方)
*/
public void drawTank(int x, int y, Graphics g, int direct, int type) {
//根據不同型別的坦克設定不同的顏色
switch (type) {
case 0://敵方坦克
g.setColor(Color.cyan);//設定我方坦克顏色
break;
case 1://我方坦克
g.setColor(Color.yellow);//設敵方坦克顏色
break;
}
//根據坦克坐標方向,來繪制對應方向的坦克
//direct表示方向(0:向上 1:向右 2:向下 3:向左)
switch (direct) {
case 0://表示向上
g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊的輪子
g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊的輪子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克主體
g.fillOval(x + 10, y + 20, 20, 20);//畫出坦克艙體
g.drawLine(x + 20, y + 30, x + 20, y);//畫出炮管
break;
case 1://表示向右
//注意:以坦克左上角的(x,y)坐標為參考畫圖案
g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊的輪子
g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊的輪子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克方形主體
g.fillOval(x + 20, y + 10, 20, 20);//畫出坦克艙體
g.drawLine(x + 30, y + 20, x + 60, y + 20);//畫出炮管
break;
case 2://表示向下
//向下只需要將向上的炮筒的位置改變即可
g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊的輪子
g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊的輪子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克主體
g.fillOval(x + 10, y + 20, 20, 20);//畫出坦克艙體
g.drawLine(x + 20, y + 30, x + 20, y + 60);//畫出炮管
break;
case 3://表示向左
//向左只需要將向右的炮筒的位置改變即可
g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊的輪子
g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊的輪子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克方形主體
g.fillOval(x + 20, y + 10, 20, 20);//畫出坦克艙體
g.drawLine(x + 30, y + 20, x, y + 20);//畫出炮管
break;
default:
System.out.println("暫時沒有處理");
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//控制方向--處理 WSAD 鍵按下的情況
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {//按下W鍵-向上
//改變坦克的方向
hero.setDirect(0);
//修改坦克的坐標
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D鍵-向右
hero.setDirect(1);
hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S鍵-向下
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A鍵-向左
hero.setDirect(3);
hero.moveLeft();
}
//讓面板重繪
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
}
TankGame02.java:
package li.TankGame.version02;
import javax.swing.*;
/**
* @author 李
* @version 2.0
*/
public class TankGame02 extends JFrame {
//定義一個MyPanel
MyPanel mp = null;
public static void main(String[] args) {
TankGame02 tankGame02 = new TankGame02();
}
public TankGame02(){
mp = new MyPanel();
this.add(mp);//把面板(就是游戲的繪圖區域)添加進來
this.setSize(700,550);//設定大小
this.addKeyListener(mp);//讓JFrame監聽mp的鍵盤事件
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//點擊視窗的叉時停止運行
this.setVisible(true);//設定顯示
}
}
大致想法:(在day01的基礎代碼上改動)
1.實作各個方向的坦克繪圖:在 MyPanel類的drawTank方法中,畫出不同方向的坦克圖案,并使用direct引數控制方向的繪圖(這里因為繪圖是以矩形的左上點坐標為參考的,因此所有方向的圖案都以坦克最左上角的坐標為參考)然后在Tank類中創建一個direct引數,通過get、set方法獲取其值進行封裝,
2.接下來實作移動:在MyPanel類中實作介面KeyListener的keyPressed方法,大致的思路是:監聽當前按下的鍵,wasd鍵分別對應上左下右的移動,當按下d鍵時,首先繪出坦克向右的圖案,再實作向右移動,其他方向亦如此,
這里的改變坦克方向會用到Tank類中實作的setDirect方法,還可以在Tank類中再設定一個speed引數,初始值設為1,設定speed的set、get方法,再創建上下左右的移動方法,在移動方法中使用speed來改變坦克的坐標(x,y) , 在MyPanel類的keyPressed方法中呼叫移動方法,封裝speed還有一個好處就是可以很方便地改變坦克移動的速度,
最后在MyPanel類的paint方法中呼叫drawTank方法,使用封裝的各種get方法來獲取坦克的坐標值、方向以及型別等,
練習:在TankGame02版本的基礎上畫出三輛敵人的坦克,注意顏色
簡單分析:
-
因為敵人的坦克也是在面板上畫的,所以是代碼在MyPanel類上
-
因為敵人的坦克后面可能會有自己特殊的屬性和方法,所以新建一個Enemy類,繼承Tank類
-
敵人的坦克多,所以放入集合Vector(因為要考慮多執行緒問題)
Enemy.java:
package li.TankGame.version02;
public class Enemy extends Tank{
public Enemy(int x, int y) {
super(x, y);
}
}
MyPanel.java:
package li.TankGame.version02;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* @author 李
* @version 2.0
* 坦克大戰的繪圖區域
*/
//為了監聽鍵盤事件,要實作 KeyListener
public class MyPanel extends JPanel implements KeyListener {
//定義我的坦克
Hero hero = null;
//定義敵方坦克,放入到Vector集合中
Vector<EnemyTank> enemyTanks = new Vector<>();
int enemyTankNum = 3;//初始化坦克數量
public MyPanel() {
hero = new Hero(100, 100);//初始化自己的坦克
//hero.setSpeed(5); //改變坦克的速度
//初始化敵人的坦克
for (int i = 0; i < enemyTankNum; i++) {
//創建一個敵人的坦克
EnemyTank enemyTank =new EnemyTank(100 * (i + 1), 0);
//初始化敵人坦克方向向下
enemyTank.setDirect(2);
//將設定好的的敵人坦克放入到集合中
enemyTanks.add(enemyTank);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 700, 550);//填充矩形,默認為黑色
//畫出自己的坦克-封裝方法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
//畫出敵人的坦克,遍歷Vector
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
}
}
/**
* 撰寫方法,畫出坦克
*
* @param x 坦克的左上角橫坐標
* @param y 坦克的左上角縱坐標
* @param g 畫筆
* @param direct 坦克方向(上下左右)
* @param type 坦克的型別(我方,敵方)
*/
public void drawTank(int x, int y, Graphics g, int direct, int type) {
//根據不同型別的坦克設定不同的顏色
switch (type) {
case 0://敵方坦克
g.setColor(Color.cyan);//設定我方坦克顏色
break;
case 1://我方坦克
g.setColor(Color.yellow);//設敵方坦克顏色
break;
}
//根據坦克坐標方向,來繪制對應方向的坦克
//direct表示方向(0:向上 1:向右 2:向下 3:向左)
switch (direct) {
case 0://表示向上
g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊的輪子
g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊的輪子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克主體
g.fillOval(x + 10, y + 20, 20, 20);//畫出坦克艙體
g.drawLine(x + 20, y + 30, x + 20, y);//畫出炮管
break;
case 1://表示向右
//注意:以坦克左上角的(x,y)坐標為參考畫圖案
g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊的輪子
g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊的輪子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克方形主體
g.fillOval(x + 20, y + 10, 20, 20);//畫出坦克艙體
g.drawLine(x + 30, y + 20, x + 60, y + 20);//畫出炮管
break;
case 2://表示向下
//向下只需要將向上的炮筒的位置改變即可
g.fill3DRect(x, y, 10, 60, false);//畫出坦克左邊的輪子
g.fill3DRect(x + 30, y, 10, 60, false);//畫出坦克右邊的輪子
g.fill3DRect(x + 10, y + 10, 20, 40, false);//畫出坦克主體
g.fillOval(x + 10, y + 20, 20, 20);//畫出坦克艙體
g.drawLine(x + 20, y + 30, x + 20, y + 60);//畫出炮管
break;
case 3://表示向左
//向左只需要將向右的炮筒的位置改變即可
g.fill3DRect(x, y, 60, 10, false);//畫出坦克上邊的輪子
g.fill3DRect(x, y + 30, 60, 10, false);//畫出坦克下邊的輪子
g.fill3DRect(x + 10, y + 10, 40, 20, false);//畫出坦克方形主體
g.fillOval(x + 20, y + 10, 20, 20);//畫出坦克艙體
g.drawLine(x + 30, y + 20, x, y + 20);//畫出炮管
break;
default:
System.out.println("暫時沒有處理");
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//控制方向--處理 WSAD 鍵按下的情況
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {//按下W鍵-向上
//改變坦克的方向
hero.setDirect(0);
//修改坦克的坐標
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_D) {//按下D鍵-向右
hero.setDirect(1);
hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_S) {//按下S鍵-向下
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {//按下A鍵-向左
hero.setDirect(3);
hero.moveLeft();
}
//讓面板重繪
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
}
其他類不變,
6.本章小結
-
java坐標體系
-
java繪圖技術
-
java事件處理機制
-
隨著知識點的深入,坦克大戰的游戲也在不斷完善
- TankGame01.java的功能:
- 繪出了我方坦克
- TankGame02.java的功能:
- 繪出了我方坦克
- 通過按鍵可以控制我方坦克上下左右移動
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/503518.html
標籤:Java
