1. 美化界面
關鍵邏輯 1:
// 相對路徑:直接從項目的 src 目錄開始寫,不包含 D:\ 和個人名字
ImageIcon bg = new ImageIcon("src/image/background.png");
JLabel background = new JLabel(bg);
這兩行代碼是 Swing 中加載并顯示圖片的經典組合,核心是通過兩個類的協作完成 “讀取圖片→展示圖片” 的過程。
第一行:
ImageIcon bg = new ImageIcon("src/image/background.png");
(1) ImageIcon
?類:
它是 Swing 專門用于處理圖像的工具類,主要功能是加載圖像文件(支持 png、jpg、gif 等格式)并存儲圖像數據。你可以把它理解為 “圖像的容器”—— 它會幫你完成讀取文件、解析圖像數據的底層工作,最終提供一個可供 Swing 組件直接使用的圖像對象。
(2) new ImageIcon(路徑)
:
這是調用?ImageIcon
?的構造方法,參數是圖片的路徑(這里用的是相對路徑?src/image/background.png
)。執行這行代碼時:
- 程序會根據路徑找到?
background.png
?圖片文件; - 讀取圖片的像素、色彩等數據;
- 創建一個?
ImageIcon
?對象?bg
,這個對象內部就 “裝著” 這張圖片的所有數據。
第二行:
JLabel background = new JLabel(bg);
作用:創建一個標簽組件,將前面加載好的圖片設置為標簽的內容,讓圖片能在界面上顯示。
(1) JLabel
?類:
它是 Swing 中的 “標簽” 組件,本質是一個 “小型容器”,可以用來顯示文本或圖像(或兩者同時顯示)。它的作用類似網頁中的?<img>
?標簽 —— 本身不存儲圖像數據,而是作為 “展示載體”。
(2) new JLabel(bg)
:
這是調用?JLabel
?的構造方法,參數是前面創建的?ImageIcon
?對象?bg
(裝著圖片數據)。執行這行代碼時:
- 創建一個?
JLabel
?對象?background
; - 告訴這個標簽:“你的顯示內容是?
bg
?里的圖片”; - 此時標簽已經 “綁定” 了圖片,但還不會直接顯示在窗口上(需要后續步驟)。
關鍵邏輯 2:
this.getContentPane().add(background);
(1) this
:指當前的?JFrame
?對象(假設這句代碼寫在繼承自?JFrame
?的類中,比如之前的?MyJFrame3
)。
(2) this.getContentPane()
:
getContentPane()
?是?JFrame
?類的一個方法,作用是獲取當前窗口的 “內容面板”(ContentPane
?對象)。- 這個方法返回的?
ContentPane
?才是真正能存放組件的容器。
(3) .add(background)
:
- 調用?
ContentPane
?的?add()
?方法,將組件(這里是?background
?標簽,也就是包含圖片的標簽)添加到內容面板中。 - 只有添加到內容面板后,
background
?標簽(以及它包含的圖片)才能在窗口中顯示出來。
關鍵邏輯 3:
jLabel.setBorder(new BevelBorder(1));
這句話的作用是給?JLabel
?組件添加一個 “凹陷” 效果的立體邊框,讓組件(比如顯示圖片的標簽)看起來有 “凹下去” 的視覺層次感。
jLabel
:要添加邊框的目標組件(比如顯示圖片的標簽、文本標簽等)。setBorder(...)
:JLabel
?繼承的方法(來自父類?JComponent
),作用是給組件設置邊框(參數是一個邊框對象)。new BevelBorder(1)
:創建一個 “斜面邊框” 對象,參數?1
?對應?BevelBorder.LOWERED
,即凹陷效果。
2. 移動圖片
關鍵邏輯 1:為什么需要 else ?
for (int i = 0; i < tempArr.length; i++) { // 遍歷一維數組tempArr的每個元素if (tempArr[i] == 0) { // 如果當前元素是0(特殊值)x = i / 4; // 計算0在二維數組中的行索引xy = i % 4; // 計算0在二維數組中的列索引y} else { // 如果當前元素不是0(普通值)data[i / 4][i % 4] = tempArr[i]; // 將值存入二維數組的對應位置}
}
(1) 在拼圖游戲中,0
?通常代表?“空白塊”(沒有數字的空位),它的作用和其他帶數字的拼圖塊完全不同 —— 其他數字塊需要被顯示在界面上,而空白塊的核心作用是作為 “可移動的空位”,讓周圍的數字塊可以移動到這里。因此,0
?不需要像普通數字那樣被 “記錄到數據數組?data
?中”,而是需要單獨記錄它的位置(x,y
),這是由拼圖游戲的核心邏輯決定的。
(2) 功能定位:0
?是 “空位”,而非 “拼圖塊”
拼圖游戲的本質是:通過移動數字塊填補空白,最終讓所有數字塊回到正確位置。
- 普通數字(如?
1,2,3...
)是 “需要被顯示和移動的拼圖塊”,它們的位置需要被?data
?數組記錄(data[x][y] = 數字
),用于在界面上顯示對應的數字圖片。 0
?代表 “空白區域”(沒有拼圖塊的位置),它不需要被顯示(界面上這里是空的),因此不需要存入?data
?數組
關鍵邏輯 2:關于 x-- 的理解、關于 initImage( ); 的理解
System.out.println("向下移動");
data[x][y] = data[x - 1][y];
data[x - 1][y] = 0;
x--; //更新元素當前位置的行索引,記錄移動后的新位置
initImage(); //根據修改后的數據重新繪制界面,刷新顯示效果
(1) 這里的 x、y 表示空白方塊,假設?data
?數組中,0
?代表空白方塊,其他數字代表有內容的方塊。現在有一個空白方塊在?(x=2, y=1)
?位置,它上方(x-1=1, y=1
)有一個值為?5
?的方塊,我們要讓?5
?向下移動到空白位置。
(2) 代碼邏輯就變成了:
① System.out.println("向下移動");
提示:有元素要移動到下方的空白位置。
② data[x][y] = data[x - 1][y];
這里?x=2, y=1
?是空白位置,x-1=1, y=1
?是上方的有值元素(5)。
意思是:把上方的?5
?放到空白位置?(2,1)
?上。
執行后,空白位置被?5
?填充,原來的?5
?位置還沒清空。
③ data[x - 1][y] = 0;
把原來?5
?所在的位置?(1,1)
?設為?0
(變成新的空白位置)。
現在,5
?成功移動到了下方的空白位置,而原來的位置變成了新的空白。
④ x--;
空白位置原本在?x=2
,現在移動到了上方的?x=1
(原來?5
?的位置)。
用?x--
?更新空白位置的坐標(從 2 變成 1),這樣如果還要繼續移動(比如?5
?還能再往下移),程序就知道 “現在的空白位置在?x=1
”,可以繼續判斷下方是否還有空白。
⑤ initImage();
刷新界面,讓你看到 “5
?移動到了下方,原來的位置變成了空白” 的效果。
3. 查看完整圖片、作弊碼和判斷勝利
關鍵邏輯 1:整體邏輯鋪墊
int code = e.getKeyCode();
e
?是一個鍵盤事件對象(比如KeyEvent
),當用戶按下鍵盤時會觸發這個事件。getKeyCode()
?是事件對象的方法,用于獲取用戶按下的按鍵對應的 “鍵碼”(每個按鍵有唯一的數字編碼,比如 65 對應字母 A,40 對應下箭頭等)。- 這行代碼的作用是:把用戶按下的按鍵編碼存到變量
code
中,方便后續判斷按的是哪個鍵。
關鍵邏輯 2:二維數組的 “靜態初始化”
else if (code == 87) {data = new int[][]{{1, 2, 3, 4}, // 拼圖第1行(數組索引0行):對應1、2、3、4號拼圖塊{5, 6, 7, 8}, // 拼圖第2行(數組索引1行):對應5、6、7、8號拼圖塊{9, 10, 11, 12}, // 拼圖第3行(數組索引2行):對應9、10、11、12號拼圖塊{13, 14, 15, 0} // 拼圖第4行(數組索引3行):13、14、15號塊 + 0(空白塊)};initImage(); // 重新繪制界面,讓正確的拼圖布局和勝利圖片顯示出來
}
① 首先,這段代碼的本質是?創建一個 4 行 4 列的 int 類型二維數組,并直接給每個位置賦值,然后把這個 “正確排列的數組” 賦值給data
變量(覆蓋原來打亂的拼圖數據)。
②?二維數組的 “行” 和 “列”:
- 里面的每一組
{}
代表 “一行”,比如{1,2,3,4}
是第 1 行;每行里的數字(1、2、3、4)是 “列”,分別對應第 1 列、第 2 列、第 3 列、第 4 列。 - 整個數組是 4 行 4 列,正好對應你游戲里的 “4x4 拼圖格子”。
4. 計步和菜單業務實現
關鍵邏輯 1:應用場景和定位事件源頭
Object obj = e.getSource();
①?先鋪墊:這行代碼的 “上下文”—— 事件處理:
- 當用戶做了某個交互操作(比如點擊按鈕、選擇菜單、輸入文本),程序會生成一個 “事件對象”(通常用變量
e
表示,比如ActionEvent e
、MouseEvent e
),這個e
里包含了 “這個事件的所有信息”—— 而?e.getSource()
?就是從這些信息里,找到 “觸發這個事件的源頭組件”。 - 你的拼圖游戲有 “重新游戲”“關閉游戲” 兩個菜單按鈕(JMenuItem),如果給這兩個按鈕共用一個事件處理器(比如都綁定到同一個 ActionListener),當用戶點擊其中一個時,程序需要知道 “到底點的是哪個按鈕”—— 這時候就需要?
e.getSource()
?來定位 “點擊的源頭”。
②?Object obj
:用 Object 類型接收源頭,因為 “事件的觸發源頭” 可能是任何組件(比如 JButton 按鈕、JTextField 文本框、JMenuItem 菜單、JLabel 標簽等),這些組件都是不同的類,但它們有一個共同的父類 ——Object
。
假設沒有多態,我們要接收?e.getSource()
?的返回值會面臨什么問題?
- 如果源頭是?
JMenuItem
,就需要寫?JMenuItem obj = e.getSource();
; - 如果源頭是?
JButton
,又需要寫?JButton obj = e.getSource();
; - 如果有 10 種不同的組件觸發事件,就要寫 10 種不同類型的接收代碼 —— 這會導致代碼極度冗余,無法兼容多種場景。
關鍵邏輯 2:模態窗口(Modal Window)
jDialog.setModal(true);
它是 Swing 界面開發中控制 “用戶操作流程” 的關鍵設置,尤其適合像 “登錄彈窗”“確認提示框” 這類需要用戶 “先處理完當前窗口,才能操作其他窗口” 的場景。反之,如果設置為?setModal(false)
(非模態),彈窗彈出后,用戶可以隨意切換到其他窗口操作,不用管這個彈窗。