我们首先看一下实现的效果:
这是打包后的exe文件执行的效果,但是在编辑器中运行的效果和exe文件运行的效果还是有点出入的。
比如打包以后窗口生成的位置有所变化,字体有些许模糊了,视觉上来看按钮变大了,颜色也不鲜艳了。
案例源码:
package frame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.FileInputStream;
public class MyFrame extends JFrame{
//面板对象
private JPanel mainPanel = new JPanel();
//组件: JLabel标签 JButton按钮 JTextField文本框
//展示一个图片
private JLabel pictureLabel = new JLabel();
//展示一个问题
private JLabel questionLabel = new JLabel("你是不是喜欢我?");
//两个按钮
private JButton yesBtn = new JButton("YES");
private JButton noBtn = new JButton("NO");
public MyFrame(String title){
super(title);
this.init();
}
public void init(){
this.setFontAndSoOn();
this.addElements();
this.addListener();
this.setFrameSelf();
}
//设置字体 位置等
public void setFontAndSoOn(){
//宽500 高300
//先将mainPanel的默认布局清空
mainPanel.setLayout(null);
//然后自己去摆放每一个组件的位置
//布局: 流式、边界式、网格式,都不符合我们的要求,我们使用自定义布局
//设置图片位置
pictureLabel.setBounds(40,30,140,200);
//设置图片边框
pictureLabel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
//添加图片,需要一个ImageIcon对象,自定义了一个drawImage方法
pictureLabel.setIcon(this.drawImage("timg.gif",140,200));
//设置问题label的位置
questionLabel.setBounds(220,80,260,40);
//questionLabel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
questionLabel.setFont(new Font("黑体",Font.BOLD,22));
//设置字体居中
questionLabel.setHorizontalAlignment(SwingConstants.CENTER);
//设置yes按钮位置
yesBtn.setBounds(240,160,100,40);
yesBtn.setFont(new Font("黑体",Font.BOLD,14));
//设置鼠标点击该按钮时候的光标是一个小手
yesBtn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
//设置no按钮位置
noBtn.setBounds(360,160,100,40);
noBtn.setFont(new Font("黑体",Font.BOLD,14));
//设置鼠标点击该按钮时候的光标是一个小手
noBtn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
//添加元素
public void addElements(){
//将这些组件添加☞刚才的窗体内
mainPanel.add(pictureLabel);
mainPanel.add(questionLabel);
mainPanel.add(yesBtn);
mainPanel.add(noBtn);
this.add(mainPanel);
}
//设置窗体本身
public void setFrameSelf(){
//设置窗口出现的位置和宽高
this.setBounds(800,300,500,300);
//窗口的关闭按钮默认是使得窗体隐藏,现在设置让它不关闭
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//设置窗口大小不能拖拽
this.setResizable(false);
//设置窗口显示
this.setVisible(true);
}
//单独设计一个方法,用来画一个图片
public ImageIcon drawImage(String path, int width, int height){
//通过一个给定的路径创建一个ImageIcon对象
// ImageIcon imageIcon = new ImageIcon(path);
ImageIcon imageIcon = new ImageIcon(MyFrame.class.getResource(path));
//设置imageIcon对象的信息
imageIcon.setImage(imageIcon.getImage().getScaledInstance(width,height,Image.SCALE_DEFAULT));
return imageIcon;
}
//添加事件绑定(核心)
public void addListener(){
//yes按钮 单击,离开(失去焦点) MouseAdapter是一个适配器,可以仅仅选择几个方法去实现
yesBtn.addMouseListener(new MouseAdapter() {
//单击
public void mouseClicked(MouseEvent e) {
//弹出一个小窗口,画一个小心心
showHeart();
}
//离开
public void mouseExited(MouseEvent e) {
//让按钮还原
yesBtn.setFont(new Font("黑体",Font.BOLD,14));
//颜色变为黑色
yesBtn.setForeground(Color.BLACK);
}
});
//yes按钮 悬停(得到焦点)
yesBtn.addMouseMotionListener(new MouseMotionAdapter() {
//悬停
public void mouseMoved(MouseEvent e) {
//字体稍微变大一点
yesBtn.setFont(new Font("黑体",Font.BOLD,18));
//颜色变为红色
yesBtn.setForeground(Color.RED);
}
});
//----------------------------------------------------
//no按钮 单击,离开(失去焦点)
noBtn.addMouseListener(new MouseAdapter() {
//单击
public void mouseClicked(MouseEvent e) {
//弹出一个小窗口,画一个小心心,把当前窗口隐藏,把下一个窗口运行出来
showHeart();
}
//离开
public void mouseExited(MouseEvent e) {
//文本值变回去
noBtn.setText("NO");
//让按钮还原
noBtn.setFont(new Font("黑体",Font.BOLD,14));
//颜色变为黑色
noBtn.setForeground(Color.BLACK);
}
});
//no按钮 悬停(得到焦点)
noBtn.addMouseMotionListener(new MouseMotionAdapter() {
//悬停
public void mouseMoved(MouseEvent e) {
//文本值变为YES
noBtn.setText("YES");
//字体稍微变大一点
noBtn.setFont(new Font("黑体",Font.BOLD,18));
//颜色变为红色
noBtn.setForeground(Color.RED);
}
});
//---------------------------------------
//窗体关闭按钮的事件绑定
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int option = JOptionPane.showConfirmDialog(MyFrame.this,"你要把小可爱关掉嘛?嘤嘤嘤");
//option有三个值,是==0,否==1,取消==2
//逻辑上来讲,否和取消都是同一回事
if(option==0)
JOptionPane.showMessageDialog(MyFrame.this,"关掉就有用了么???");
else
JOptionPane.showMessageDialog(MyFrame.this,"哈哈哈,我就知道你舍不得我");
}
});
}
//点击yes按钮以后,弹出的新的窗口
public void showHeart(){
MyFrame.this.setVisible(false);//当前窗口隐藏
//弹出新的窗口
new HeartFrame();
}
}
package frame;
import javax.swing.*;
import java.awt.*;
public class HeartFrame extends JFrame {
//设置心形图案的宽度和高度
private static final int WIDTH = 520;
private static final int HEIGHT = 520;
//构造方法
public HeartFrame(){
super("我也喜欢你,爱心送给你");//窗口标题
this.setFrameSelf();
}
public void setFrameSelf(){
this.setBackground(Color.BLACK);
this.setBounds(800,200,WIDTH,HEIGHT);//设置窗体宽高和显示位置
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);//不可拖拽
this.setVisible(true);
}
//设计一个方法,用来画心形图案 规则,算法
//需要提供一支笔:Graphics,提供O点坐标(x,y),半径r
public void drawHeart(Graphics pic,int x,int y,int r){
//求直角边长 已知 根号2 = 1.414
int offset = (int)(r / 1.414);
int c1x = x - (r -offset) /2; //c1的x坐标 O的x 减去 (r - 直角边)/2
int c1y = y - (r -offset) /2; //c1的y坐标 O的y 减去 (r - 直角边)/2 -->小三角形两个直角
int c2x = c1x - offset *2 -1; //c2的x坐标 c1的x 减去2个直角边 -1 (因为像素点四舍五入)
int c2y = c1y + offset*2; //c2的y坐标 c1的y 减去2个直角边
int c3x = x + (r - offset) / 2;//c3的x坐标 O的x 加上 (r - 直角边)/2
int c3y = y - (r - offset) / 2;//c3的y坐标 O的y 减去 (r - 直角边)/2
int c4x = c3x + offset * 2 + 1;//c4的x坐标 c3的x 加上 2个直角边 + 1 (因为像素点四舍五入)
int c4y = c3y + offset * 2; //c4的y坐标 c3的y 加上2个直角边
int ex = x; //e的x坐标 就是O的x坐标
int ey = c2y + (x - c2x); //e的y坐标 c2的y 加上一个下面大的直角边(O的x - c2的x)
int leftArcX = c1x - r - offset; //Q的x坐标 c1的x 减去一个直角边 减去一个半径
int leftArcY = c1y - (r - offset); //Q的y坐标 c1的y 减去一个 (半径 - 直角边)
int rightArcX = c1x; //Q' 的x坐标 与c1的x坐标一致
int rightArcY = leftArcY; //Q' 的y坐标 与Q的y坐标一致
//设置画图的颜色
pic.setColor(Color.MAGENTA);//magenta 品红色
//画一条线,从O点到c1点
pic.drawLine(x,y,c1x,c1y);
//画一条线,从O点到c3点
pic.drawLine(x,y,c3x,c3y);
//画左边的半圆形
//从Q点的x和y坐标 长和宽(相等就变成圆形了) 水平开始逆时针偏移45度开始,画180度
pic.drawArc(leftArcX,leftArcY,r*2,r*2,45,180);
//画右边的半圆形
//从Q'点的x和y坐标 长和宽 水平方向 水平开始逆时针偏移315度开始,逆时针画180度
pic.drawArc(rightArcX,rightArcY,r*2,r*2,315,180);
//画一条线,从c2点到e点
pic.drawLine(c2x,c2y,ex,ey);
//画一条线,从c4点到e点
pic.drawLine(c4x,c4y,ex,ey);
}
//重写过来的paint方法 让它按照我们刚才设计的心形的规则描绘出这个窗体的面板
public void paint(Graphics g){
//一次性出来的
/* Image image = this.createImage(WIDTH,HEIGHT);
Graphics pic = image.getGraphics();
this.drawHeart(pic,WIDTH/2,HEIGHT/2-100,100);
g.drawImage(image,0,0,this);*/
//由小变大
/* for(int i = 1;i<=100;i++){
Image image = this.createImage(WIDTH,HEIGHT);
Graphics pic = image.getGraphics();
this.drawHeart(pic,WIDTH/2,HEIGHT/2-200+i,i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
g.drawImage(image,0,0,this);
}*/
//画10个心形图案,去叠加
for(int count = 1;count<=10;count++){
for(int i = 1;i<=100;i++){
Image image = this.createImage(WIDTH,HEIGHT);
Graphics pic = image.getGraphics();
this.drawHeart(pic,WIDTH/2,HEIGHT/2-200+i,i);
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//每一次画完之后,将最后那个心形留在页面上
for(int show = 1;show<=count;show++){
drawHeart(pic,WIDTH/2,HEIGHT/2-100-show,100-show);
}
//中间输出一个文字
pic.drawString("我也喜欢你",WIDTH/2-30,HEIGHT/2);
g.drawImage(image,0,0,this);
}
}
}
}
package frame;
public class TestMain {
public static void main(String[] args) {
new MyFrame("I Love You");
}
}
整个案例中就只有画心形图案的方法有些复杂,我至今也不是很懂。
画GUI界面和事件绑定虽然代码量多,但实际上难度并不大。
通过这个案例学到了: