Java 黑马程序员学习笔记(进阶篇18)

斗地主游戏

一、斗地主游戏1 -- 准洗发(控制台版)

  1. 静态成员变量 + 静态初始化块
java 复制代码
    static ArrayList<String> list = new ArrayList<>();

    static {
        String[] color = {"♦","♠","♥","♣"};
        String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};

        for (String c : color) {  // c依次表示每一种花色
            for (String n : number) {  // n依次表示每一个数字
                list.add(c + n);
            }
        }

        list.add("小王");
        list.add("大王");
    }  // 不太理解这个静态代码块的含义
(1) 为什么扑克牌的 list 要加 static

你的代码是这样的:

java 复制代码
static ArrayList<String> list = new ArrayList<>();
  • 数据不变:54 张牌的内容是固定的。
  • 大家共用:游戏中所有玩家操作的是同一副牌。
  • 一次生成:节省内存,提高效率。
(2) 不用 static 会发生什么?

假设不加 static

java 复制代码
class CardGame {
    ArrayList<String> list = new ArrayList<>();

    public CardGame() {
        list.add("♦3");
        list.add("♦4");
        // ... 生成一副牌
        System.out.println("生成了一副新牌");
    }
}

测试:

java 复制代码
CardGame p1 = new CardGame(); // 生成一副新牌
CardGame p2 = new CardGame(); // 又生成一副新牌

输出:

java 复制代码
生成了一副新牌
生成了一副新牌

➡ 结果:每多一个玩家,就多一副牌,浪费空间,而且牌不是同一副,没法玩游戏。

2. lookPoker 代码解析
java 复制代码
public void lookPoker(String name, ArrayList<String> list) {
    System.out.print(name + ":"); // 打印玩家名字,不换行
    for (String poker : list) {   // 遍历牌的集合
        System.out.print(poker);  // 打印每张牌,不换行
    }
    System.out.println();         // 所有牌打印完后换行
}

二、斗地主游戏2 -- 给牌排序1(利用序号进行排序)

java 复制代码
package demo1;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

public class PokerGame {

    static HashMap<Integer,String> hm = new HashMap<>();
    static ArrayList<Integer> list = new ArrayList<>();

    static {
        String[] color = {"♦","♠","♥","♣"};
        String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};

        int serialNumber = 1;
        for (String n : number) {
            for (String c : color) {
                hm.put(serialNumber,c + n);
                list.add(serialNumber);
                serialNumber++;
            }
        }

        hm.put(serialNumber,"小王");
        list.add(serialNumber);
        serialNumber++;
        hm.put(serialNumber,"大王");
        list.add(serialNumber);

    }  // 不太理解这个静态代码块的含义

    public PokerGame() {
        //洗牌
        Collections.shuffle(list);

        TreeSet<Integer> lord = new TreeSet<>();
        TreeSet<Integer> player1 = new TreeSet<>();
        TreeSet<Integer> player2 = new TreeSet<>();
        TreeSet<Integer> player3 = new TreeSet<>();  // 为什么使用的是TreeSet

        for (int i = 0; i < list.size(); i++) {
            int serialNumber = list.get(i);

            if (i <= 2) {
                lord.add(serialNumber);
                continue;
            }
            if (i % 3 == 0) {
                player1.add(serialNumber);
            } else if (i % 3 == 1) {
                player2.add(serialNumber);
            } else {
                player3.add(serialNumber);
            }
        }

        lookPoker("底牌",lord);
        lookPoker("知更鸟",player1);
        lookPoker("星期日",player2);
        lookPoker("阿格莱雅",player3);

    }

    public void lookPoker(String name, TreeSet<Integer> ts) {  // 不太理解
        System.out.print(name + ":");
        for (Integer serialNumber : ts) {
            String poker = hm.get(serialNumber);
            System.out.print(poker + " ");
        }
        System.out.println();
    }
}
关键逻辑 1:为什么用 TreeSet 存储牌
(1) TreeSet 的特点
  • 有序集合(默认从小到大排序),底层是红黑树实现。
  • 存储的元素会自动排序 ,并且不允许重复
(2) 为什么这里用 TreeSet

① 斗地主发牌后,每个人手里的牌需要按大小顺序排列方便查看。

② 牌的大小已经通过编号体现:

  • 3 的编号最小,然后依次增大,2 最大,小王次之,大王最大。
  • 所以只要把牌的编号放入 TreeSet,它就会自动从小到大排好序。

③ 这样在调用 lookPoker() 输出时,直接按顺序打印即可。

三、斗地主游戏2 -- 给牌排序2(给每一张牌计算价值)

java 复制代码
package demo3;

import java.util.*;

public class PokerGame {

    static HashMap<String,Integer> hm = new HashMap<>();
    static ArrayList<String> list = new ArrayList<>();

    static {
        String[] color = {"♦","♠","♥","♣"};
        String[] number = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};

        for (String c : color) {
            for (String n : number) {
                list.add(c + n);
            }
        }
        list.add(" 小王");
        list.add(" 大王");

        hm.put("J",11);
        hm.put("Q",12);
        hm.put("K",13);
        hm.put("A",14);
        hm.put("2",15);
        hm.put("小王",50);
        hm.put("大王",100);
    }

    public PokerGame() {
        //洗牌
        Collections.shuffle(list);

        ArrayList<String> lord = new ArrayList<>();
        ArrayList<String> player1 = new ArrayList<>();
        ArrayList<String> player2 = new ArrayList<>();
        ArrayList<String> player3 = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            String poker = list.get(i);
            if (i <= 2) {
                lord.add(poker);
                continue;
            }
            if (i % 3 == 0 ) {
                player1.add(poker);
            } else if (i % 3 == 1 ) {
                player2.add(poker);
            } else {
                player3.add(poker);
            }
        }

        order(lord);
        order(player1);
        order(player2);
        order(player3);

        lookPoker("底牌",lord);
        lookPoker("知更鸟",player1);
        lookPoker("星期日",player2);
        lookPoker("阿格莱雅",player3);
    }

    public void order(ArrayList<String> list) {
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                String color1 = o1.substring(0,1);  // 不太理解
                int value1 = getValue(o1);

                String color2 = o2.substring(0,1);
                int value2 = getValue(o2);

                int i = value1 - value2;
                return i == 0 ? color1.compareTo(color2) : i;
            }
        });
    }

    public int getValue(String poker) {
        String number = poker.substring(1);
        if (hm.containsKey(number)) {
            return hm.get(number);
        } else {
            return Integer.parseInt(number);  // 不太理解
        }
    }

    public void lookPoker(String name,ArrayList<String> list) {  // 不太理解
        System.out.print(name + ":");
        for (String poker : list) {
            System.out.print(poker + " ");
        }
        System.out.println();
    }

}

四、斗地主游戏3 (页面版)

Poker:

java 复制代码
package domain;


import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class Poker extends JLabel implements MouseListener {

    //属性
    //1.牌的名字 格式:数字 - 数字
    private String name;

    //2.牌显示正面还是反面
    private boolean up;

    //3.是否可以被点击
    private boolean canClick = false;

    //4.当前的状态,表示当前的牌是否已经被点击
    //如果是没有被点击的状态,此时被点击了,会执行弹起的操作
    //如果是已经被点击的状态,此时被点击了,会执行降落的操作
    private boolean clicked = false;

    public Poker(String name,boolean up){
        this.name = name;
        this.up = up;
        //判断当前的牌是显示正面还是反面
        if(this.up){
            //显示正面
            turnFront();
        }else{
            //显示反面
            turnRear();
        }
        //设置牌的宽高
        this.setSize(71,96);
        //把牌显示出来
        this.setVisible(true);
        //给每一张牌添加监听
        this.addMouseListener(this);
    }

    //显示正面
    public void turnFront(){
        //给牌设置正面
        this.setIcon(new ImageIcon("farmerandlord\\image\\poker\\"+name+".png"));
        //修改成员变量
        this.up = true;
    }

    //显示反面
    public void turnRear(){
        //给牌设置反面
        this.setIcon(new ImageIcon("farmerandlord\\image\\poker\\rear.png"));
        //修改成员变量
        this.up = false;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        //点击
        //判断当前的牌是否可以被点击
        if(canClick){
            //当牌被点击之后,要么升起,要么降落
            //表示牌的位移像素
            int step = 0;
            if(clicked){
                //表示当前的牌已经被点击
                //降落(y 增加 20像素)
                step = 20;
            }else{
                //表示当前的牌还没有被点击
                //升起 (y 减少 20像素)
                step = -20;
            }
            //需要修改一下clicked变量记录的值
            clicked = !clicked;
            //修改一下牌的位置
            Point from = this.getLocation();
            //创建一个Point的对象,表示结束位置
            Point to = new Point(from.x,from.y + step);
            //把最新的位置设置给牌
            this.setLocation(to);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }


    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return up
     */
    public boolean isUp() {
        return up;
    }

    /**
     * 设置
     * @param up
     */
    public void setUp(boolean up) {
        this.up = up;
    }

    /**
     * 获取
     * @return canClick
     */
    public boolean isCanClick() {
        return canClick;
    }

    /**
     * 设置
     * @param canClick
     */
    public void setCanClick(boolean canClick) {
        this.canClick = canClick;
    }

    /**
     * 获取
     * @return clicked
     */
    public boolean isClicked() {
        return clicked;
    }

    /**
     * 设置
     * @param clicked
     */
    public void setClicked(boolean clicked) {
        this.clicked = clicked;
    }

    public String toString() {
        return "Poker{name = " + name + ", up = " + up + ", canClick = " + canClick + ", clicked = " + clicked + "}";
    }
}

GameJFrame

java 复制代码
package game;

import domain.Poker;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class GameJFrame extends JFrame implements ActionListener {

    //获取界面中的隐藏容器,现在统一获取了,后面直接用就可以了
    public static Container container = null;  // 不太理解

    //管理抢地主和不抢两个按钮
    JButton[] landlord = new JButton[2];

    //管理出牌和不要两个按钮
    JButton[] publishCard = new JButton[2];

    //游戏界面中地主的图标
    JLabel dizhu;

    //集合嵌套集合
    //大集合中有三个小集合
    //小集合中装着每一个玩家当前要出的牌
    //0索引:左边的电脑玩家
    //1索引:中间的自己
    //2索引:右边的电脑玩家
    ArrayList<ArrayList<Poker>> currentList = new ArrayList<>();

    //集合嵌套集合
    //大集合中有三个小集合
    //小集合中装着每一个玩家手上的牌
    //0索引:左边的电脑玩家
    //1索引:中间的自己
    //2索引:右边的电脑玩家
    ArrayList<ArrayList<Poker>> playerList = new ArrayList<>();

    //底牌
    ArrayList<Poker> lordList = new ArrayList<>();

    //牌盒,装所有的牌
    ArrayList<Poker> pokerList = new ArrayList();

    //三个玩家前方的文本提示
    //0索引:左边的电脑玩家
    //1索引:中间的自己
    //2索引:右边的电脑玩家
    JTextField time[] = new JTextField[3];

    public GameJFrame() {
        //设置任务栏的图标
        setIconImage(Toolkit.getDefaultToolkit().getImage("farmerandlord\\image\\poker\\dizhu.png"));
        //设置界面
        initJframe();
        //添加组件
        initView();
        //界面显示出来
        //先展示界面再发牌,因为发牌里面有动画,界面不展示出来,动画无法展示
        this.setVisible(true);

        //初始化牌
        //准备牌,洗牌,发牌,排序
        initCard();
        //打牌之前的准备工作
        //展示抢地主和不抢地主两个按钮并且再创建三个集合用来装三个玩家准备要出的牌
        initGame();
    }

    //初始化牌(准备牌,洗牌,发牌,排序)
    public void initCard() {
        //准备牌
        //把所有的牌,包括大小王都添加到牌盒pokerList当中
        for (int i = 1; i <= 5; i++) {
            for (int j = 1; j <= 13; j++) {
                if ((i == 5) && (j > 2)) {
                    break;
                } else {
                    Poker poker = new Poker(i + "-" + j, false);
                    poker.setLocation(350, 150);
                    pokerList.add(poker);
                    container.add(poker);
                }
            }
        }

        //洗牌
        Collections.shuffle(pokerList);

        //创建三个集合用来装三个玩家的牌,并把三个小集合放到大集合中方便管理
        ArrayList<Poker> player0 = new ArrayList<>();
        ArrayList<Poker> player1 = new ArrayList<>();
        ArrayList<Poker> player2 = new ArrayList<>();

        //发牌
        for (int i = 0; i < pokerList.size(); i++) {
            //获取当前遍历的牌
            Poker poker = pokerList.get(i);

            //发三张底牌
            if (i <= 2) {
                //把底牌添加到集合中
                lordList.add(poker);
                Common.move(poker, poker.getLocation(), new Point(270 + (75 * i), 10));
                continue;
            }

            //给三个玩家发牌
            if (i % 3 == 0) {
                //给左边的电脑发牌
                Common.move(poker, poker.getLocation(), new Point(50, 60 + i * 5));
                player0.add(poker);
            } else if (i % 3 == 1) {
                //给中间的自己发牌
                Common. move(poker, poker.getLocation(), new Point(180 + i * 7, 450));
                player1.add(poker);
                //把自己的牌展示正面
                poker.turnFront();
            } else if (i % 3 == 2) {
                //给右边的电脑发牌
                Common. move(poker, poker.getLocation(), new Point(700, 60 + i * 5));
                player2.add(poker);
            }

            //把三个装着牌的小集合放到大集合中方便管理
            playerList.add(player0);
            playerList.add(player1);
            playerList.add(player2);

            //把当前的牌至于最顶端,这样就会有牌依次错开且叠起来的效果
            container.setComponentZOrder(poker, 0);

        }

        //排序
        for (int i = 0; i < 3; i++) {
            order(playerList.get(i));
            Common.rePosition(this,playerList.get(i),i);
        }



    }

    //排序
    public void order(ArrayList<Poker> list) {
        //此处可以改为lambda表达式
        Collections.sort(list, new Comparator<Poker>() {
            @Override
            public int compare(Poker o1, Poker o2) {
                //获取o1的花色和价值
                int color1 = Integer.parseInt(o1.getName().substring(0, 1));
                int value1 = getValue(o1);

                //获取o2的花色和价值
                int color2 = Integer.parseInt(o2.getName().substring(0, 1));
                int value2 = getValue(o2);

                //倒序排列
                //细节:
                //图形化界面当中,牌倒着摆放
                int flag = value2 - value1;

                //如果牌的价值一样,则按照花色排序
                if (flag == 0){
                    return color2 - color1;
                }else {
                    return flag;
                }
            }
        });
    }

    //获取每一张牌的价值
    public int getValue(Poker poker) {
        //获取牌的名字 1-1
        String name = poker.getName();
        //获取牌的花色
        int color = Integer.parseInt(poker.getName().substring(0, 1));
        //获取牌对应的数字,同时也是牌的价值
        int value = Integer.parseInt(name.substring(2));

        //在本地文件中,每张牌的文件名为:数字1-数字2
        //数字1表示花色,数字2表示牌的数字
        //其中3~K对应的数字2,可以视为牌的价值
        //所以,我们单独判断大小王,A,2即可

        //计算大小王牌的价值
        if (color == 5){
            //小王的初始价值为1,在1的基础上加100,小王价值为:101
            //大王的初始价值为2,在2的基础上加100,大王价值为:102
            return value += 100;
        }

        //计算A的价值
        if (value == 1){
            //A的初始价值为1,在1的基础上加20,大王价值为:21
            return value += 20;
        }

        //计算2的价值
        if (value == 2){
            //2的初始价值为2,在2的基础上加30,大王价值为:32
            return value += 30;
        }

        //如果不是大小王,不是A,不是2,牌的价值就是牌对应的数字
        return value;
    }


    //打牌之前的准备工作
    private void initGame() {
        //创建三个集合用来装三个玩家准备要出的牌
        for (int i = 0; i < 3; i++) {
            ArrayList<Poker> list = new ArrayList<>();
            //添加到大集合中方便管理
            currentList.add(list);
        }

        //展示抢地主和不抢地主两个按钮
        landlord[0].setVisible(true);
        landlord[1].setVisible(true);

        //展示自己前面的倒计时文本
        for (JTextField field : time) {
            field.setText("倒计时30秒");
            field.setVisible(true);
        }

    }





    @Override
    public void actionPerformed(ActionEvent e) {

    }

    //添加组件
    public void initView() {
        //创建抢地主的按钮
        JButton robBut = new JButton("抢地主");
        //设置位置
        robBut.setBounds(320, 400, 75, 20);
        //添加点击事件
        robBut.addActionListener(this);
        //设置隐藏
        robBut.setVisible(false);
        //添加到数组中统一管理
        landlord[0] = robBut;
        //添加到界面中
        container.add(robBut);

        //创建不抢的按钮
        JButton noBut = new JButton("不     抢");
        //设置位置
        noBut.setBounds(420, 400, 75, 20);
        //添加点击事件
        noBut.addActionListener(this);
        //设置隐藏
        noBut.setVisible(false);
        //添加到数组中统一管理
        landlord[1] = noBut;
        //添加到界面中
        container.add(noBut);

        //创建出牌的按钮
        JButton outCardBut = new JButton("出牌");
        outCardBut.setBounds(320, 400, 60, 20);
        outCardBut.addActionListener(this);
        outCardBut.setVisible(false);
        publishCard[0] = outCardBut;
        container.add(outCardBut);

        //创建不要的按钮
        JButton noCardBut = new JButton("不要");
        noCardBut.setBounds(420, 400, 60, 20);
        noCardBut.addActionListener(this);
        noCardBut.setVisible(false);
        publishCard[1] = noCardBut;
        container.add(noCardBut);


        //创建三个玩家前方的提示文字:倒计时
        //每个玩家一个
        //左边的电脑玩家是0
        //中间的自己是1
        //右边的电脑玩家是2
        for (int i = 0; i < 3; i++) {
            time[i] = new JTextField("倒计时:");
            time[i].setEditable(false);
            time[i].setVisible(false);
            container.add(time[i]);
        }
        time[0].setBounds(140, 230, 60, 20);
        time[1].setBounds(374, 360, 60, 20);
        time[2].setBounds(620, 230, 60, 20);


        //创建地主图标
        dizhu = new JLabel(new ImageIcon("images/dizhu.png"));
        dizhu.setVisible(false);
        dizhu.setSize(40, 40);
        container.add(dizhu);

    }

    //设置界面
    public void initJframe() {
        //设置标题
        this.setTitle("斗地主");
        //设置大小
        this.setSize(830, 620);
        //设置关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置窗口无法进行调节
        this.setResizable(false);
        //界面居中
        this.setLocationRelativeTo(null);
        //获取界面中的隐藏容器,以后直接用无需再次调用方法获取了
        container = this.getContentPane();
        //取消内部默认的居中放置
        container.setLayout(null);
        //设置背景颜色
        container.setBackground(Color.LIGHT_GRAY);
    }
    
}
关键逻辑 1:成员变量设计
java 复制代码
public static Container container = null;
  • 作用 :保存窗体的内容面板 ContentPane,方便在其他地方直接添加组件,不用每次都 this.getContentPane()
  • 注意public static 意味着这个容器是全局共享的,其他类也能直接访问。
关键逻辑 2:核心方法:initCard()
java 复制代码
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 13; j++) {
        if ((i == 5) && (j > 2)) break;
        Poker poker = new Poker(i + "-" + j, false);
        poker.setLocation(350, 150);
        pokerList.add(poker);
        container.add(poker);
    }
}
  • i 表示花色(1~4 是普通花色,5 是大小王)。
  • j 表示牌面数字(1~13)。
  • 大小王只有两张(i=5 时 j 只到 2)。
  • 每张牌是一个 Poker 对象(应该是自定义的 JLabel 子类,能显示牌的正反面)。
关键逻辑 3:Poker poker = new Poker(i + "-" + j, false);

① Poker自定义的 Swing 组件类 (推测继承自JLabel),用于表示一张扑克牌。

② i + "-" + j是牌的唯一标识

  • i代表 "花色 / 类型"(比如1~4对应普通花色,5对应大小王);
  • j代表 "牌面数字"(比如1对应 A,11对应 J,13对应 K);
  • 示例:"1-1"可能是 "黑桃 A","5-1"是 "小王","5-2"是 "大王"。

③ 第二个参数false:控制牌的初始状态 (比如 "是否正面朝上",false可能表示 "反面朝上",后续发牌时自己的牌会翻转为正面)。

LoginJFrame

java 复制代码
package game;

import domain.User;
import domain.User;
import util.CodeUtil;
import game.GameJFrame;
import util.CodeUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

public class LoginJFrame extends JFrame implements MouseListener {

    static ArrayList<User> allUsers = new ArrayList<>();

    static {
        allUsers.add(new User("zhangsan","123"));
        allUsers.add(new User("lisi","1234"));
    }


    JButton login = new JButton();
    JButton register = new JButton();
    JTextField username = new JTextField();
    JPasswordField password = new JPasswordField();
    JTextField code = new JTextField();

    //正确的验证码
    JLabel rightCode = new JLabel();


    public LoginJFrame() {
        //初始化界面
        initJFrame();
        //初始化组件,在这个界面中添加内容
        initView();
        //让当前界面显示出来
        this.setVisible(true);
    }

    //在这个界面中添加内容
    public void initView() {
        //1. 添加用户名文字
        Font usernameFont = new Font(null,1,16);
        JLabel usernameText = new JLabel("用户名");
        usernameText.setForeground(Color.white);
        usernameText.setFont(usernameFont);
        usernameText.setBounds(140, 55, 55, 22);
        this.getContentPane().add(usernameText);

        //2.添加用户名输入框
        username.setBounds(223, 46, 200, 30);
        this.getContentPane().add(username);

        //3.添加密码文字
        JLabel passwordText = new JLabel("密码");
        Font passwordFont = new Font(null,1,16);
        passwordText.setForeground(Color.white);
        passwordText.setFont(passwordFont);
        passwordText.setBounds(197, 95, 40, 22);
        this.getContentPane().add(passwordText);

        //4.密码输入框
        password.setBounds(263, 87, 160, 30);
        this.getContentPane().add(password);

        //验证码提示
        JLabel codeText = new JLabel("验证码");
        Font codeFont = new Font(null,1,16);
        codeText.setForeground(Color.white);
        codeText.setFont(codeFont);
        codeText.setBounds(215, 142, 55, 22);
        this.getContentPane().add(codeText);

        //验证码的输入框
        code.setBounds(291, 133, 100, 30);
        this.getContentPane().add(code);

        //获取正确的验证码
        String codeStr = CodeUtil.getCode();
        Font rightCodeFont = new Font(null,1,15);
        //设置颜色
        rightCode.setForeground(Color.RED);
        //设置字体
        rightCode.setFont(rightCodeFont);
        //设置内容
        rightCode.setText(codeStr);
        //绑定鼠标事件
        rightCode.addMouseListener(this);
        //位置和宽高
        rightCode.setBounds(400, 133, 100, 30);
        //添加到界面
        this.getContentPane().add(rightCode);

        //5.添加登录按钮
        login.setBounds(123, 310, 128, 47);
        login.setIcon(new ImageIcon("farmerandlord\\image\\login\\登录按钮.png"));
        //去除按钮的边框
        login.setBorderPainted(false);
        //去除按钮的背景
        login.setContentAreaFilled(false);
        //给登录按钮绑定鼠标事件
        login.addMouseListener(this);
        this.getContentPane().add(login);

        //6.添加注册按钮
        register.setBounds(256, 310, 128, 47);
        register.setIcon(new ImageIcon("farmerandlord\\image\\login\\注册按钮.png"));
        //去除按钮的边框
        register.setBorderPainted(false);
        //去除按钮的背景
        register.setContentAreaFilled(false);
        //给注册按钮绑定鼠标事件
        register.addMouseListener(this);
        this.getContentPane().add(register);


        //7.添加背景图片
        JLabel background = new JLabel(new ImageIcon("farmerandlord\\image\\login\\background.png"));
        background.setBounds(0, 0, 633, 423);
        this.getContentPane().add(background);

    }

    //初始化组件,在这个界面中添加内容
    public void initJFrame() {
        this.setSize(633, 423);//设置宽高
        this.setTitle("斗地主游戏 V1.0登录");//设置标题
        this.setDefaultCloseOperation(3);//设置关闭模式
        this.setLocationRelativeTo(null);//居中
        this.setAlwaysOnTop(true);//置顶
        this.setLayout(null);//取消内部默认布局
    }

    //点击
    @Override
    public void mouseClicked(MouseEvent e) {
        Object obj = e.getSource();
        if (obj == login) {
            //获取两个文本输入框中的内容
            String usernameInput = username.getText();
            String passwordInput = password.getText();
            //获取用户输入的验证码
            String codeInput = code.getText();

            //判断验证码是否为空
            if (codeInput.length() == 0) {
                showJDialog("验证码不能为空");
                return;
            }

            //判断用户名和密码是否为空
            if (usernameInput.length() == 0 || passwordInput.length() == 0) {
                showJDialog("用户名或者密码为空");
                return;
            }

            //判断验证码是否正确
            if (!codeInput.equalsIgnoreCase(rightCode.getText())) {
                showJDialog("验证码输入错误");
                return;
            }

            //判断集合中是否包含当前用户对象
            //其实就是验证用户名和密码是否相同
            //contains底层是依赖equals方法判断的,所以需要重写equals方法
            User userInfo = new User(usernameInput, passwordInput);
            if (allUsers.contains(userInfo)) {
                //关闭当前登录界面
                this.setVisible(false);
                //打开游戏的主界面
                new GameJFrame();
            } else {
                showJDialog("用户名或密码错误");
            }

        } else if (obj == register) {
            System.out.println("点击了注册按钮");
        } else if (obj == rightCode) {
            //获取一个新的验证码
            String code = CodeUtil.getCode();
            rightCode.setText(code);
        }
    }

    //展示弹框
    public void showJDialog(String content) {
        //创建一个弹框对象
        JDialog jDialog = new JDialog();
        //给弹框设置大小
        jDialog.setSize(200, 150);
        //让弹框置顶
        jDialog.setAlwaysOnTop(true);
        //让弹框居中
        jDialog.setLocationRelativeTo(null);
        //弹框不关闭永远无法操作下面的界面
        jDialog.setModal(true);

        //创建Jlabel对象管理文字并添加到弹框当中
        JLabel warning = new JLabel(content);
        warning.setBounds(0, 0, 200, 150);
        jDialog.getContentPane().add(warning);
        //让弹框展示出来
        jDialog.setVisible(true);
    }

    //按下不松
    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getSource() == login) {
            login.setIcon(new ImageIcon("farmerandlord\\image\\login\\登录按下.png"));
        } else if (e.getSource() == register) {
            register.setIcon(new ImageIcon("farmerandlord\\image\\login\\注册按下.png"));
        }
    }

    //松开按钮
    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.getSource() == login) {
            login.setIcon(new ImageIcon("farmerandlord\\image\\login\\登录按钮.png"));
        } else if (e.getSource() == register) {
            register.setIcon(new ImageIcon("farmerandlord\\image\\login\\注册按钮.png"));
        }
    }

    //鼠标划入
    @Override
    public void mouseEntered(MouseEvent e) {

    }

    //鼠标划出
    @Override
    public void mouseExited(MouseEvent e) {

    }


}
关键逻辑 1:类的整体结构
java 复制代码
public class LoginJFrame extends JFrame implements MouseListener
  • 继承 JFrame:让这个类本身就是一个窗口。
  • 实现 MouseListener:监听鼠标事件(点击、按下、释放、移入、移出)。
  • 这是 Swing 图形界面开发的常见写法,但对于初学者来说,事件监听机制是一个难点:

你点击按钮,程序如何知道要执行哪段代码?→ 就是通过 addMouseListener(this) 把当前窗口对象注册为监听器,然后重写 mouseClicked() 等方法。

关键逻辑 2:按钮图片切换(按下 / 弹起)
java 复制代码
@Override
public void mousePressed(MouseEvent e) {
    if (e.getSource() == login) {
        login.setIcon(new ImageIcon("farmerandlord\\image\\login\\登录按下.png"));
    }
}

@Override
public void mouseReleased(MouseEvent e) {
    if (e.getSource() == login) {
        login.setIcon(new ImageIcon("farmerandlord\\image\\login\\登录按钮.png"));
    }
}
  • 通过鼠标按下 / 释放事件切换按钮图标,实现按钮点击的视觉反馈
  • 这种交互细节初学者容易忽略,但对用户体验很重要。

五、JButtonJTextFieldJPasswordField 的作用

1. JButton(按钮组件)

JButton 是用于用户交互的按钮组件,核心作用是接收用户的点击操作并触发相应的事件

  • 用户点击按钮后,程序可以通过绑定事件监听器(如 ActionListener)执行特定逻辑(例如提交表单、打开新窗口、执行计算等)。
  • 按钮上可以显示文本(如 "登录""保存")或图标,直观提示用户其功能。
2. JTextField(文本输入框组件)

JTextField 是用于输入和显示单行可见文本的组件,适用于需要用户输入普通文本(非敏感信息)的场景。

  • 允许用户直接输入文本(如用户名、搜索关键词、年龄等),也可以通过代码动态设置或获取文本内容。
  • 文本内容默认可见(直接显示输入的字符),支持基本的文本编辑(如复制、粘贴、删除)。
3. JPasswordField(密码输入框组件)

JPasswordField 是专门用于输入和处理敏感文本(如密码) 的组件,本质是 JTextField 的子类,但对文本显示做了特殊处理。

  • 输入的文本会被隐藏(通常显示为 "*""・" 等占位符),避免他人直接看到敏感信息,增强安全性。
  • 同样支持单行输入,通过代码获取时需通过 getPassword() 方法(返回字符数组,而非字符串,进一步降低内存中密码泄露的风险)。

典型场景示例

在登录界面中:

  • JTextField 用于输入 "用户名"(可见文本);
  • JPasswordField 用于输入 "密码"(隐藏文本);
  • JButton 作为 "登录" 按钮,点击后触发验证用户名和密码的逻辑。
相关推荐
洋洋的笔记3 小时前
银行测试学习计划
学习
桦说编程3 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t3 小时前
ZIP工具类
java·zip
lang201509283 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
Allan_20254 小时前
数据库学习
数据库·学习
报错小能手4 小时前
linux学习笔记(43)网络编程——HTTPS (补充)
linux·网络·学习
报错小能手4 小时前
linux学习笔记(45)git详解
linux·笔记·学习
pengzhuofan4 小时前
第10章 Maven
java·maven
百锦再5 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net