Card类
/**
* 卡牌
* 继承 JButton,就是在JButton基础上进行扩展改造
*/
public class Card extends JButton {
public Card(String name){
//设置了大小
setSize(59, 66);
//设置不绘制边框
setBorderPainted(false);
//设置不填充内容区域,按钮是透明的!
setContentAreaFilled(false);
//设置按钮上的图片
setIcon(new ImageIcon("images/"+name+".png"));
//设置禁用时候的灰色图片
setDisabledIcon(new ImageIcon("images/"+name+"2.png"));
//设置按钮 的 名称 name
setName(name);
}
@Override
public String toString() {
return getName();
}
/**
* 判断当前牌是否盖上了 另外一张牌 card
* @param card 另外一张牌
* @return true 表示覆盖了,false 没有盖上
*/
public boolean covered(Card card){
int x1 = card.getX() - 59;
int x2 = card.getX() + 59;
int y1 = card.getY() - 66;
int y2 = card.getY() + 66;
return (getX() > x1 && getX() < x2) && (getY() > y1 && getY() < y2);
}
/**
* 比较当前卡牌和另外一个卡牌是否”一样“ 名称一样
* @param other 另外一张牌
*/
public boolean sameAs(Card other){
String name = getName();
if (name == null){
return false;
}
return name.equals(other.getName());
}
/**
* 删除全部的鼠标点击事件
*/
public void removeActionListeners(){
ActionListener[] actionListeners = getActionListeners();
for (ActionListener l:actionListeners){
removeActionListener(l);
}
}
}
Yang类
public class Yang extends JFrame {
/**
* 窗口中内容面面板
*/
private JPanel panel = new JPanel();
/**
* JLabel 是Swing中用于显示文字和图片的控件
*/
private JLabel background;
/**
* 利用List集合保存全部的卡牌
*/
private ArrayList<Card> cards = new ArrayList<>();
/**
* 利用List表示下方从槽子
*/
private ArrayList<Card> though = new ArrayList<>();
/**
* 利用数组存储全部的 名称
*/
private String[] names = {"刷子","剪刀","叉子","奶瓶",
"干草","手套","树桩","棉花",
"毛线","水桶","火","玉米",
"白菜","草","萝卜","铃铛"};
/**
* 创建一个定时器,Java中提供单线程,定时计划
*/
//private ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor();
private Timer timer = new Timer();
public Yang(){
//设置窗口标题
setTitle("羊了个羊");
//设置窗口大小
setSize(500, 850);
//设置窗口不能被 拖大
// setResizable(false);
//设置窗口居中
setLocationRelativeTo(null);
//设置关闭窗口时候退出程序
setDefaultCloseOperation(EXIT_ON_CLOSE);
//将内容面板添加到 窗口中
add(panel);
//关闭panel的布局管理器
panel.setLayout(null);
//加载图片
background = new JLabel(new ImageIcon("images/背景.jpg"));
//关闭布局以后,需要自定义元素位置才能显示
background.setLocation(0,0);
background.setSize(480,800);
//显示图片
panel.add(background);
init(6);
}
/**
* 创建全部卡牌,创建的牌放到集合 cards 中
* @param times 是次数,需要是3的整数倍
* 如 3 6 9 等
*/
private void createCards(int times){
for (int i=0; i<times; i++) {
for (String name : names) {
Card card = new Card(name);
cards.add(card);
}
}
}
/**
* 初始化游戏
*/
private void init(int times){
//利用 Card 简化代码
createCards(times);//必须为的倍数,表示每张牌出现的次数
//System.out.println(cards.size());
//System.out.println(cards);
//输出卡牌
//printCards();
//shuffle 洗牌,打乱
Collections.shuffle(cards);
// System.out.println(cards);
//输出卡牌
//printCards();
//摆放牌到屏幕上
deal(33,100,0,6, 7);
deal(33+30,100+33,42,5, 6);
deal(33, 100+66, 42+30, 4, 7);
deal(33+30, 100+33, 42+30+28, 5, 6);
deal(33,100+33,42+30+28+30,6,7);
deal(33+30,100+66,42+30+28+30+42,5,6);
checkCovered();
//添加动作: 为每张牌添加鼠标点击事件。
addAction();
}
/**
* 为每个卡牌添加鼠标点击事件
*/
private void addAction(){
for (Card card:cards){
//Card 从 JButton 继承了 添加鼠标事件方法。
//ActionListener 是一个只有一个方法的功能性接口,可以利用lamdba进行实现
card.addActionListener(e->{
//System.out.println("点击卡牌");
//事件来源就是被点击的牌, 称为被选定的牌对象
Card selected = (Card) e.getSource();
//System.out.println(selected);
//使用方法处理点击事件: 卡牌进入槽子里
cardToThough(selected);
});
}
}
private void cardToThough(Card selected) {
//从原有集合中删除卡牌
cards.remove(selected);
//从 cards 中删除的牌,去除鼠标事件
selected.removeActionListeners();
//移动显示位置到槽子中
//查找插入位置
int found = -1;
for (int i=0; i<though.size(); i++){
Card card = though.get(i);
if (card.sameAs(selected)){
found = i;
break;
}
}
if (found == -1){
//如果没有找到牌, 就插入到最后
though.add(selected);
}else{
//如果找到了 牌, 就插入到找到的位置
though.add(found, selected);
}
//删除重复的3张牌
boolean flag = tryRemoveCards(found, selected);//判断是否删除了3张的卡牌
//重新排列牌的显示位置
int x = 22;
for (Card card: though){
card.setLocation(x, 642);
x += 63;
}
panel.repaint();
checkCovered();
if (though.size() == 7 && flag == false){
JOptionPane.showMessageDialog(this, "卡槽满了!");
System.exit(0);
}
if (cards.size() == 0){
//System.out.println("you win!!!");
JOptionPane.showMessageDialog(this, "下一关!");
//System.exit(0);
init(9);
}
}
private boolean tryRemoveCards(int found, Card selected) {
boolean flag = false;
//在最后时候追加时候,不需要删除牌
if (found == -1){
return flag;
}
//当前位置开始,到最后要至少有3张牌
if (found + 3 <= though.size()){
//检查当前牌和下下张牌是否一样
Card nextNextCard = though.get(found+2);
if (selected.sameAs(nextNextCard)){
flag = true;
timer.schedule(new TimerTask() {
@Override
public void run() {
//连续删除3张牌
Card c1 = though.remove(found);
Card c2 = though.remove(found);
Card c3 = though.remove(found);
//从界面上删除 卡牌
panel.remove(c1);
panel.remove(c2);
panel.remove(c3);
int x = 22;
for (Card card: though){
card.setLocation(x, 642);
x += 63;
}
panel.repaint();
}
}, 200);
}
}
return flag;
}
/**
* 检查全都的牌相互遮挡关系
*/
private void checkCovered(){
//检查每个牌,是否被后续牌遮挡,如果遮挡就是 disable状态,否则是enable
for (int i=0; i<cards.size(); i++){
//找到当前牌
Card card1 = cards.get(i);
//检查后续牌是否遮盖了这张牌
card1.setEnabled(true);
for(int j=i+1; j<cards.size(); j++){
Card card2 = cards.get(j);
//检查后一张牌card2是否遮盖了牌card1
if (card2.covered(card1)){
card1.setEnabled(false);
}
}
}
}
private void printCards(){
int i=1;
for (Card card:cards){
System.out.print(card + " ");
if (i++ % 7 == 0){
System.out.println();
}
}
System.out.println();
System.out.println();
}
/**
* 摆放牌,摆放到没有牌就会自动结束
* @param x 左上角的坐标x x:33
* @param y 左上角的坐标y y:100
* @param start 开始位置:0 42 42+30
* @param rows 摆放行数 6
* @param cols 摆放列数 7
*/
private void deal(int x, int y, int start, int rows, int cols){
// 循环到固定次数 并且还有可以使用的牌
for (int i=0; i<rows * cols && i+start < cards.size(); i++){
int x0 = x + i % cols * 59;
int y0 = y + i / cols * 66;
Card card = cards.get(i + start);
card.setLocation(x0, y0);
card.setEnabled(false);
panel.add(card, 0);
}
}
public static void main(String[] args) {
Yang yang = new Yang();
yang.setVisible(true);
}
}