JFrame

       JFrame是个代表屏幕上window的对象。可以把button、checkbox、text字段等接口放在window上面。标准的menu也可以加到上面。

import javax.swing.*;
public class SimpleGui1 {

    public static void main(String[] args) {
        JFrame frame = new JFrame(); //创建frame
        JButton button = new JButton("click me");  //创建button

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //这一行程序会在window关闭时结束程序

        frame.getContentPane().add(button);

        frame.setSize(300, 300); //设定frame大小

        frame.setVisible(true);  //显示frame
    }

}

实现按钮功能

实现按钮功能:

  • 按钮要知道它的作用。
  • 按钮要在按键事件发生时调用执行功能的方法。

取得用户的事件

       在Java中,取得处理用户操作事件的过程称为even-handling。Java中有许多不同的事件类型,大多数都与GUI上用户的操作有关。如果用户按下了按钮,就会产生事件。

监听

       如果想要知道按钮的事件,就要监听事件的接口。
       监听接口是介于监听和事件源间的桥梁。

       Swing的GUI组件是事件的来源。以Java的术语说,事件来源是个可以将用户操作(点击鼠标、关闭窗口等)转换成事件的对象。对Java而言,事件几乎都是以对象来表示(事件类对象)。
       事件源(例如按钮)会在用户做出相关动作时(按下按钮)产生事件对象。你的程序在大多数情况下是事件的接受方而不是创建方。也就是说,你会花较多的时间当监听者而不是事件来源。
       每个事件类型都有相对应的监听者接口,想要接收MuoseEvent的话就实现Mouse Listener这个接口。记得接口的规则:要实现接口就得声明这件事,这代表你必须把接口中所有方法都实现出来。

监听和事件源的沟通

监听

       如果类想要知道按钮的ActionEvent,就要实现ActionListener这个接口。按钮需要知道你关注的部分,因此要通过调用addActionListener(this)并传入ActionListener的引用(下面的例子里就是你自己的这个程序,所以用this)来向按钮注册。按钮会在该事件发生时调用该接口上的方法。作为一个ActionListener,编译器会确保你实现此接口的actionPerformed()。

事件源

       按钮是ActionEvent的来源,因此它必须要知道有哪些对象是需要事件通知的。此按钮有个addActionListener()方法可以提供对事件有兴趣的对象(listener)一种表达此兴趣的方法。
       当按钮的addActionListener()方法被调用是(因为某个listener的调用),它的参数会被按钮存到清单中。当用户按下按钮时,按钮会通过调用清单上的每个监听的actionPerformed()来启动事件。

取得按钮的ActionEvent

  1. 实现ActionListener这个接口。
  2. 向按钮注册(告诉它你要监听事件)。
  3. 定义事件处理方法(实现接口的方法)。

import javax.swing.*;
import java.awt.event.*;
public class SimpleGui1B implements ActionListener {
    JButton button;

    public static void main(String[] args) {
        SimpleGui1B gui = new SimpleGui1B();
        gui.go();
    }

    public void go() {
        JFrame frame = new JFrame();
        button = new JButton("Click me");

        button.addActionListener(this); //向按钮注册

        frame.getContentPane().add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    public void actionPerformed(ActionEvent event) { //实现接口中的方法,真正处理事件的方法
        button.setText("I've been clicked");
    }
}

图形

在GUI上加东西的3种方法:

  1. 在frame上放置widget:
    加上按钮、窗体、radio button 等。
  2. 在widget上绘制2D图形:
    使用graphics对象来绘制图形。
  3. 在widget上放置JPEG图

创建绘图组件

       如果要在屏幕上放上自己的图形,最好的方式是自己创建出有绘图功能的widget。把它放在frame上,就像按钮或其他widget一样,不同之处是它会按照你所要的方式绘制。

创建Jpanel的子类并覆盖掉paintComponent()这个方法

       所有绘图程序代码都在paintComponent()里面。当你的panel所处的frame显示的时候,paintComponent()就会被调用。用户不能自己调用这个方法!它的参数是个跟实际屏幕有关的Graphics对象,用户无法取得这个对象,必须交由系统来交给你。然而,还是可以调用repaint()类要求系统重新绘制显示装置,然后产生paintComponent()调用。

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//创建JPanel的子类

    public void paintComponent(Graphics g) {//只能由系统调用
        g.setColor(Color.blue);

        g.fillRect(20, 50, 100, 100);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //创建frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}

其他在paintComponent()中可以做的事情

显示JPEG
import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//创建JPanel的子类

    public void paintComponent(Graphics g) {//只能由系统调用
        Image image = new ImageIcon("224888289124380709.jpg").getImage();//注意这里的根路径是project目录

        g.drawImage(image, 3, 4, this);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //创建frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}
在黑色背景上画随机色彩的圆圈
import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//创建JPanel的子类

    public void paintComponent(Graphics g) {//只能由系统调用
        g.fillRect(0, 0, this.getWidth(), this.getHeight());

        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);

        Color randomColor = new Color(red, green ,blue); //随机颜色
        g.setColor(randomColor); //设定画的颜色
        g.fillOval(400, 400, 200, 200);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //创建frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(300, 300);
            frame.setVisible(true);             
        }
}

使用Graphics2D对象

paintComponent()的参数被声明为Graphics类型:

public void paintComponent(Graphics g)

因此参数g是个Graphics对象,由于多态,g也有可能是Graphics的子类。事实上,由g参数所引用的对象实际上是个Graphics2D的实例

如果要调用Graphics2D类的方法,就不能直接使用g参数。要将其转换成Graphics2D变量:

Graphics2D g2d = (Graphics2D) g;

Graphics2D的方法更多:

import java.awt.*;
import javax.swing.*;
public class MyDrawPanel extends JPanel {//创建JPanel的子类

    public void paintComponent(Graphics g) {//只能由系统调用
        Graphics2D g2d = (Graphics2D) g;

        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);

        Color startColor = new Color(red, green ,blue);

        red = (int) (Math.random() * 255);
        green = (int) (Math.random() * 255);
        blue = (int) (Math.random() * 255);

        Color endColor = new Color(red, green ,blue);

        GradientPaint gradient = new GradientPaint(400,400, startColor,600,600, endColor);//两点之间坐标渐变
        g2d.setPaint(gradient);
        g2d.fillOval(400, 400, 200, 200);
    }
        public static void main (String[] args) {
            JFrame frame = new JFrame(); //创建frame
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            MyDrawPanel s = new MyDrawPanel();
            frame.getContentPane().add(s);

            frame.setSize(800, 800);
            frame.setVisible(true);             
        }
}

在获得事件时绘制图形

GUI布局:

frame默认有5个区域可以安置widget:

按下按钮圆圈就会改变颜色

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MySimpleGui3C implements ActionListener {
    JFrame frame;
    public static void main(String[] args) {
        MySimpleGui3C gui = new MySimpleGui3C();
        gui.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("change colors");
        button.addActionListener(this);

        MyDrawPanel s = new MyDrawPanel();
        frame.getContentPane().add(BorderLayout.SOUTH, button);//两个参数的add方法可以指定位置
        frame.getContentPane().add(BorderLayout.CENTER,s);

        frame.setSize(800, 800);
        frame.setVisible(true);     


    }

    public void actionPerformed(ActionEvent event) {
        frame.repaint();
    }

}

内部类

一个类可以嵌套在另一个类的内部。内部类可以使用外部所有的方法与变量,就算是private的也一样。

利用内部类实现两个按钮的程序

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

public class TwoButton {
    JFrame frame;
    JLabel label; //必须放在方法以外,否则是局部变量不能用
    public static void main(String[] args) {
        TwoButton gui = new TwoButton();
        gui.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton labelButton = new JButton("change label");
        labelButton.addActionListener(new LabelListener());

        JButton colorButton = new JButton("change color");
        colorButton.addActionListener(new ColorListener());

        label = new JLabel("I'm a label");
        MyDrawPanel drawPanel = new MyDrawPanel();

        frame.getContentPane().add(BorderLayout.SOUTH, colorButton);
        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
        frame.getContentPane().add(BorderLayout.WEST, label);

        frame.setSize(800, 800);
        frame.setVisible(true);
    }

    class LabelListener implements ActionListener {//内部类,可以调用label
        public void actionPerformed(ActionEvent event) {
            label.setText("Ouch!");
        }
    }

    class ColorListener implements ActionListener {//内部类,可以调用frame
        public void actionPerformed(ActionEvent event) {
            frame.repaint();
        }
    }

}

以内部类执行动画效果

import javax.swing.*;
import java.awt.*;

public class SimpleAnimation {
    int x = 70;
    int y = 70;

    public static void main(String[] args) {
        SimpleAnimation gui = new SimpleAnimation();
        gui.go();
    }

    public void go() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MyDrawPanel drawPanel = new MyDrawPanel();

        frame.getContentPane().add(drawPanel);
        frame.setSize(800, 800);
        frame.setVisible(true);

        for (int i = 0; i < 130; i++) {
            x++;//递增坐标值
            y++;//递增坐标值
            drawPanel.repaint();//重新绘制

            try {
                Thread.sleep(50);//加上延迟放缓过程
            } catch(Exception ex) { }
        }
    }

    class MyDrawPanel extends JPanel {
        public void paintComponent(Graphics g) {
            //去除痕迹
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());

            g.setColor(Color.green);
            g.fillOval(x, y, 40, 40);//x,y是椭圆左上角坐标,40,40是高度和宽度
        }
    }

}

这样使用内部类可以让外部类实现无法继承的子类的方法。