GUI编程
什么是GUI?
图形用户界面编程,GUI的核心开发技术是Swing和AWT,但是快被淘汰了,因为界面不美观,需要JRE环境(200+M),用处是可以写出自己自己想要的小玩意,工作的时候可能会维护到swing界面(概率很小),了解MVC架构,了解监听!
AWT
AWT介绍
- 包含了很多类和接口
- 元素:窗口,按钮,文本框
- java.awt
窗口(Frame)
窗口的创建过程如下:
如果有哪里不清楚,我们可以通过new Frame(),查看Frame类的源码来解决,大部分方法还是比较好理解的
面板(Panel)
面板的位置是相对于窗口的
实验的时候发现了问题
这是添加了setLayout的结果
这是没有setLayout的结果
等学到布局再详细探究这个问题吧
布局(Layout)
Frame通过setLayout()来设置布局
布局类型有绝对布局(null),流式布局(FlowLayout),东西南北中布局(BorderLayout),表格布局(GridLayout)(…)
布局可以嵌套举例:
public class Myframe extends Frame{
static int id=0; //用于标记窗口和统计窗口数量
public Myframe(int x, int y, int h, int w, Color color) {
super("Myframe" + (++id));
setLayout(new GridLayout(2, 1));
setBounds(y, x, w, h);//设置窗口位置和宽高
setBackground(color);//设置窗口背景颜色
setResizable(true);//设置窗口能否重新调整大小
Panel panel1 = new Panel(new BorderLayout());//上面
Panel panel2 = new Panel(new GridLayout(2, 1));//上面中间
Panel panel3 = new Panel(new BorderLayout());//下面
Panel panel4 = new Panel(new GridLayout(2, 2));//下面中间
panel1.add(new Button("1"), BorderLayout.WEST);
panel2.add(new Button("2"));
panel2.add(new Button("3"));
panel1.add(panel2, BorderLayout.CENTER);
panel1.add(new Button("4"), BorderLayout.EAST);
add(panel1);
panel3.add(new Button("5"), BorderLayout.WEST);
for(int i=6;i<=9;i++)
panel4.add(new Button(Integer.toString(i)));
panel3.add(panel4, BorderLayout.CENTER);
panel3.add(new Button("10"), BorderLayout.EAST);
add(panel3);
panel1.setBackground(Color.red);
panel3.setBackground(Color.blue);
panel2.setBackground(Color.yellow);
panel4.setBackground(Color.green);
setVisible(true);//设置窗口能否被用户看到
pack();
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
}
一定要给每个panel设置布局…不然会出问题的…
监听事件(Listener)
某个事件发生时,程序干什么的问题
frame.addWindowListener(WindowListener name);
frame.addMouseListener(MouseListener name);
…
但是如图所见
WindowListener是个接口,要实现的话要把所有的方法都实现了…
所以我们可以采用JAVA自带的适配器模式 例如
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
其中
类的妙用!
我们只需要重写我们想要的方法就好了.
然后我们就可以关掉窗口啦!
按钮(Button)事件:addActionLinstener()点击触发ActionLinstener
文本框(Textfield)常用方法: addActionLinstener()回车触发ActionLinstener, setEchoChar()将字符改为想要的样子
ActionEvent.getSource()可以获得触发监听事件的组件,不过它的返回类型为Object,所以我们要强制类型转换(向下转型)才能更好地使用它
应用:
public class Myframe extends Frame {
public Myframe(){
setLayout(new FlowLayout());
//三个文本框
TextField textField1 = new TextField(10);
TextField textField2 = new TextField(10);
TextField textField3 = new TextField(15);
//一个按钮
Button button=new Button("=");
//一个lable
Label label1 = new Label("+");
//构建页面
add(textField1);
add(label1);
add(textField2);
add(button);
add(textField3);
pack();//自适应化
setVisible(true);//显示页面
frameClose(this);//添加关闭事件
button.addActionListener(new MyActionListener(textField1,textField2,textField3));
}
private void frameClose(Frame frame){
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
private class MyActionListener implements ActionListener{
TextField textField1;
TextField textField2;
TextField textField3;
public MyActionListener(TextField t1,TextField t2,TextField t3)
{
textField1=t1;
textField2=t2;
textField3=t3;
}
@Override
public void actionPerformed(ActionEvent e) {
int num1;
int num2;
try {
num1=Integer.parseInt(textField1.getText());
num2=Integer.parseInt(textField2.getText());
textField3.setText(Integer.toString(num1+num2));
}catch (Exception error){
System.out.println("something wrong!!!!");
return ;
}finally {
textField1.setText("");
textField2.setText("");
}
}
}
}
但是这么做不是oop的做法(不够抽象,方法的针对性太强)
于是我们可以对它做一下调整:
public class Myframe extends Frame {
TextField textField1;
TextField textField2;
TextField textField3;
public void loadMyframe(){
setLayout(new FlowLayout());
//三个文本框
textField1 = new TextField(10);
textField2 = new TextField(10);
textField3 = new TextField(15);
//一个按钮
Button button=new Button("=");
//一个lable
Label label1 = new Label("+");
button.addActionListener(new MyActionListener(this));
//构建页面
add(textField1);
add(label1);
add(textField2);
add(button);
add(textField3);
pack();//自适应化
setVisible(true);//显示页面
frameClose(this);//添加关闭事件
}
private void frameClose(Frame frame){
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
private class MyActionListener implements ActionListener{
Myframe myframe=null;
public MyActionListener(Myframe myframe)
{
this.myframe=myframe;
}
@Override
public void actionPerformed(ActionEvent e) {
int num1;
int num2;
try {
num1=Integer.parseInt(myframe.textField1.getText());
num2=Integer.parseInt(myframe.textField2.getText());
myframe.textField3.setText(Integer.toString(num1+num2));
}catch (Exception error){
System.out.println("something wrong!!!!");
return ;
}finally {
myframe.textField1.setText("");
myframe.textField2.setText("");
}
}
}
}
但是这依然不是最优解,最优解是内部类–>因为内部类最大的好处就是可以使用外部类定义的变量
public class Myframe extends Frame {
TextField textField1;
TextField textField2;
TextField textField3;
public void loadMyframe() {
//内部监听类
class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int num1;
int num2;
try {
num1 = Integer.parseInt(textField1.getText());
num2 = Integer.parseInt(textField2.getText());
textField3.setText(Integer.toString(num1 + num2));
} catch (Exception error) {
System.out.println("something wrong!!!!");
return;
} finally {
textField1.setText("");
textField2.setText("");
}
}
}
setLayout(new FlowLayout());
//三个文本框
textField1 = new TextField(10);
textField2 = new TextField(10);
textField3 = new TextField(15);
textField2.addActionListener(new MyActionListener());//oop的好处就是可以随手加上一个已经写过的功能hhhh
//一个按钮
Button button = new Button("=");
button.addActionListener(new MyActionListener());
//一个lable
Label label1 = new Label("+");
//构建页面
add(textField1);
add(label1);
add(textField2);
add(button);
add(textField3);
pack();//自适应化
setVisible(true);//显示页面
frameClose(this);//添加关闭事件
}
private void frameClose(Frame frame) {
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
}
画笔(paint)
这个不是类,而是Frame里一个方法,可以被重写,里面的Graphics g才是真正的画笔
例子:
public class Mypaint extends Frame {
public void loadFrame(){
setBounds(200,200,600,500);
setVisible(true);
frameClose(this);
}
//画笔
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.fillOval(100,100,100,100);//实心椭圆
g.drawOval(200,100,100,100);//空心椭圆
g.setColor(Color.green);
g.fillRect(150,200,200,200);//实心长方形
//画笔用完要洗掉-->变回最开始的颜色(黑色)
}
private void frameClose(Frame frame) {
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
}
加入一个计时器,每次都洗掉并重新画一次图形就可以做出类似动画的效果了
鼠标监听
例子:使用鼠标画画(其实是点点…)
效果图:
public class Mypaint extends Frame {
private ArrayList points;
//鼠标单击监听事件
private class MyMouseLinsener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
Mypaint mypaint=(Mypaint) e.getSource();//获取发生点击事件的窗体
Point now=new Point(e.getX(),e.getY());//记录点击发生的位置
points.add(now);//添加进所有发生点击的坐标中
mypaint.repaint();//这个函数会将窗体清空并重新画
}
}
public void loadFrame(){
points=new ArrayList();
setBounds(200,200,600,500);
setVisible(true);
frameClose(this);//给窗体设置关闭事件
addMouseListener(new MyMouseLinsener());//给窗体设置点击事件
}
//画笔
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);//设置画笔颜色慰绿色
Iterator iterator=points.iterator();
while(iterator.hasNext()){
Point now=(Point)iterator.next();
/*
这里记录一下iterator的用法:
可迭代对象.iterator()会返回一个指向头的迭代器,这个迭代器指向的是第一个元素的前一位
我们每调用一次.next()方法迭代器会先前进一位再返回值,所以我们一定要配合.hasNext()方法使用防止出现类似越界的异常
*/
g.fillOval(now.x,now.y,10,10);//在点集中点的位置画一个比较小的实心圆当做点
}
}
private void frameClose(Frame frame) {
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
System.exit(0);
}
});
}
}
窗口监听
常用的是激活窗口(windowActivated)和关闭窗口(windowClosed)
点×时触发关闭窗口事件,窗口从后台调到前台时触发激活窗口
其他的都和上面没啥区别…
键盘监听
这是打游戏用的!很重要!(敲黑板!)
其实也没啥就一个keyListener;
上面的会了,这个直接贴代码自己领会吧
总结
- Frame是一个顶级窗口
- panel无法单独显示(要放在某个容器中)
- 布局管理器!!!Frame和panel一定要添加,不然会出错
- 大小,颜色,定位,可见性,监听
swing
其实就是把AWT封装了一下,让他变得更好用了
窗口(JFrame)
一个基本的实现了窗口关闭的JFrame实例
要注意的是JFrame内置了一个容器我们可以通过getContainer()方法来获取它,下面是个例子(仔细看能看出有个红边,那是Jframe的background)
居中显示一个标签
JLabel jLabel=new JLabel("这是一个标签");
jLabel.setHorizontalAlignment(JLabel.CENTER);
add(jLabel);
弹窗(JDialog)
和窗体没啥区别,挺好玩的就是
代码:
public class MyDialog extends JDialog {
private String title="";
static private int num=0;
public void init(){
setTitle(title);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setBackground(Color.yellow);
JLabel label=new JLabel("这是第"+(++num)+"个弹窗");
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
setBounds(num*100%1020,num*100%680,100,100);
pack();
}
public MyDialog(){}
public MyDialog(String title){this.title=title;}
}
public class MyFrame extends JFrame {
private class MyActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
new MyDialog("hhh").init();
}
}
public void init(){
setTitle("hhh");
setVisible(true);
JButton jButton=new JButton("点击弹出一个弹窗");
jButton.addActionListener(new MyActionListener());
jButton.setHorizontalAlignment(JButton.CENTER);
add(jButton);
pack();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
标签(JLabe)
图标 ICON:
例子:
public class My_Icon extends JFrame implements Icon{
private int width;
private int height;
public My_Icon(){};
public My_Icon(int width,int height){
this.width=width;
this.height=height;
};
public void init(){
My_Icon myIcon=new My_Icon(15,15);
JLabel label=new JLabel("ICONtest",myIcon,SwingConstants.CENTER);
Container container=getContentPane();
container.add(label);
setVisible(true);
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(Color.green);
g.fillOval(x,y,width,height);
}
@Override
public int getIconWidth() {
return this.width;
}
@Override
public int getIconHeight() {
return this.height;
}
}
带图像的图标(ImageICON)
因为图片放的不和谐,就不放结果了
写的时候遇到了问题:明明路径下有资源而且也能打开,就是找不到,很玄学,重命名下就好了(而且有提示是否要把传的参数一起改了…无语)
另外可以通过(类名).class.getResource("").getpath的方法来找到当前类的路径,很方便
因为ImageIcon是Icon的封装,所以也就不用实现Icon接口了,直接用就OK
public class Image_Icon extends JFrame {
public Image_Icon(){
//获取图片地址
System.out.println(Image_Icon.class.getResource("").getPath());
URL url=Image_Icon.class.getResource("jiao.jpg");
System.out.println(url);
ImageIcon image=new ImageIcon(url);
JLabel label=new JLabel("imageIcontest",image,SwingConstants.CENTER);
add(label);
setVisible(true);
};
}
面板(JPanel)
没啥好说的…
实例:
public class Test extends JFrame {
public Test(){
Container container=getContentPane();
container.setLayout(new GridLayout(1,2,10,10));//1行两列行间距和列间距都是10,超出的组件/容器会自动扩展出列来存放
JPanel panel=new JPanel(new GridLayout(1,3));
JPanel panel2=new JPanel(new GridLayout(1,2));
JPanel panel3=new JPanel(new GridLayout(2,3));
JPanel panel4=new JPanel(new GridLayout(3,2));
panel.add(new JButton("1"));
panel.add(new JButton("1"));
panel.add(new JButton("1"));
panel2.add(new JButton("2"));
panel2.add(new JButton("2"));
panel3.add(new JButton("3"));
panel3.add(new JButton("3"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
panel4.add(new JButton("4"));
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setVisible(true);
add(panel);
add(panel2);
add(panel3);
add(panel4);
setSize(500,500);
pack();
}
}
有边框(滚动条)的面板:JScrollpane
外层的滚动条是JScrollpane的,内层的滚动条是TextArea的
代码:
public class Scrolltest extends JFrame {
public Scrolltest(){
super("titile");
setBounds(100,100,1000,600);
TextArea area=new TextArea(20,20);
area.setText("这是个s本域\n\n\n'");
area.setBounds(0,0,500,300);
JScrollPane pane=new JScrollPane(area);
Container container=getContentPane();
container.add(pane);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
列表
- 下拉框(JComboBox)
- 列表框(JList)
按钮
- 单选按钮(JRadioButton) 没啥好说的,记得用ButtonGroup吧JRadioButton包进去就行
- 复选按钮(JcheckBox)连ButtonGrop都不需要了…下面上代码自己看下就行,就是还不清楚和后台怎么联动
首先是图片按钮,和上面的标签没啥区别…,多学了一个方法,放在代码注释里了因为一些原因,不方便放效果图(逃~~)
public class MyButton extends JFrame {
public MyButton(){
Container container=getContentPane();
URL url=MyButton.class.getResource("jiao.jpg");
JButton button=new JButton();
button.setIcon(new ImageIcon(url));
button.setToolTipText("图片按钮");//鼠标悬停上去后显示的文字
container.add(button);
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
单选按钮例子:
注意!!!,当一个窗体/面板有多个组件而又不想用绝对定位时,一定要setLayout,否则多个组件会堆叠在一起看着就像是只有一个组件显示了一样
public class MyRadioButton extends JFrame {
public MyRadioButton(){
Container container=getContentPane();
container.setLayout(new FlowLayout());
JRadioButton[] radioButtons=new JRadioButton[4];
ButtonGroup group=new ButtonGroup();
for(int i=0;i<4;i++) {
radioButtons[i] = new JRadioButton("Button" + (i + 1));
group.add(radioButtons[i]);
}
for(int i=0;i<4;i++)
container.add(radioButtons[i]);
pack();
setVisible(true);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
多选框: