第二章
2-1 使用Jframe
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
//新建窗口文檔建議使用事件隊列EventQueue,事件分發線程
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame("Welcome");
frame.setSize(500, 500);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
}
2-2 創建自己的frame類,繼承JFrame
AlgoFrame類
import java.awt.*;
import javax.swing.*;
//窗口類
public class AlgoFrame extends JFrame{
private int canvasWidth;//
private int canvasHeight;
//構造函數,title,寬和高
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);//調用父類函數
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
setSize(canvasWidth, canvasHeight);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
}
Main類
import java.awt.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
AlgoFrame frame = new AlgoFrame("Welcome");
});
}
}
2-3設置畫布與圖形繪製基礎
畫板panel在框架frame中。
AlgoFrame類
import java.awt.*;
import javax.swing.*;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
//setSize(canvasWidth, canvasHeight);
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();//佈局整理,加載窗口內的內容整理,自動調整窗口大小
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
//畫板類,自己的Jpanel類
private class AlgoCanvas extends JPanel{
@Override
public void paintComponent(Graphics g) {//g爲繪製的上下文環境
super.paintComponent(g);//覆蓋父類的方法,繼承
g.drawOval(50, 50, 300, 300);//左上角座標,寬高
}
@Override
public Dimension getPreferredSize(){//畫布panel的大小
return new Dimension(canvasWidth, canvasHeight);
}
}
}
Main類
import java.awt.*;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
AlgoFrame frame = new AlgoFrame("Welcome",500,500);
});
}
}
2-4 使用Graphics 2D
Graphics太古老,使用Graphics2D類。
AlgoFrame.java
import java.awt.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
private class AlgoCanvas extends JPanel{
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
int strokeWidth = 5;
g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));//線條寬度
g2d.setColor(Color.RED);
Ellipse2D circle = new Ellipse2D.Double(50, 50, 300, 300);
g2d.draw(circle);
g2d.setColor(Color.BLUE);//顏色狀態
Ellipse2D circle2 = new Ellipse2D.Double(50, 50, 300, 300);
g2d.fill(circle2);
}
@Override
public Dimension getPreferredSize(){//創建AlgoCanvas自動調用,決定畫布的大小,不用顯式設置畫布的大小
return new Dimension(canvasWidth, canvasHeight);
}
}
}
2-5 整理繪製工具類
將繪製空心圓,實心圓,設置繪製顏色,線條寬度等方法放在AlgoVisHelper類中。
AlgoVisHelper.java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class AlgoVisHelper {
private AlgoVisHelper(){}
//畫線條圓
public static void strokeCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.draw(circle);
}
//畫實心圓
public static void fillCircle(Graphics2D g, int x, int y, int r){
Ellipse2D circle = new Ellipse2D.Double(x-r, y-r, 2*r, 2*r);
g.fill(circle);
}
//顏色
public static void setColor(Graphics2D g, Color color){
g.setColor(color);
}
//設置線條圓角寬度
public static void setStrokeWidth(Graphics2D g, int w){
int strokeWidth = w;
g.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
}
}
AlgoFrame.java
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.RenderingHints;
import java.awt.BasicStroke;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
private class AlgoCanvas extends JPanel{
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 具體繪製
AlgoVisHelper.setColor(g2d, Color.BLUE);
AlgoVisHelper.fillCircle(g2d, canvasWidth/2,canvasHeight/2,200);
AlgoVisHelper.setStrokeWidth(g2d,5);
AlgoVisHelper.setColor(g2d, Color.RED);
AlgoVisHelper.strokeCircle(g2d, canvasWidth/2,canvasHeight/2,200);
}
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
2-6 抗鋸齒和雙緩存
抗鋸齒在paintComponent方法中加兩行代碼
// 抗鋸齒
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.addRenderingHints(hints);
雙緩存是指使用兩塊畫布,圖形渲染技術。JPanel類中包括雙緩存。
只需要在自己的panel類構造實例方法中繼承就可以。
public AlgoCanvas(){
super(true);
}
AlgoFrame.java
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.geom.Ellipse2D;
import java.awt.RenderingHints;
import java.awt.BasicStroke;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
private class AlgoCanvas extends JPanel{
public AlgoCanvas(){
// 雙緩存
super(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 抗鋸齒
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.addRenderingHints(hints);
// 具體繪製
AlgoVisHelper.setColor(g2d, Color.BLUE);
AlgoVisHelper.fillCircle(g2d, canvasWidth/2,canvasHeight/2,200);
AlgoVisHelper.setStrokeWidth(g2d,5);
AlgoVisHelper.setColor(g2d, Color.RED);
AlgoVisHelper.strokeCircle(g2d, canvasWidth/2,canvasHeight/2,200);
}
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
2-7 動畫基礎
在 AlgoFrame類中加入render方法
// data
private Circle[] circles;
//repaint函數是JFrame中自帶,將JFrame的空間全部刷新,重新調用paintComponent。
public void render(Circle[] circles){
this.circles = circles;
repaint();
}
Circle.java
public class Circle {
public int x;
public int y;
private int r;
public int vx;//速度
public int vy;
public Circle(int x, int y, int r, int vx, int vy){
this.x = x;
this.y = y;
this.r = r;
this.vx = vx;
this.vy = vy;
}
public int getR(){return r;}
public void move(int minx, int miny, int maxx, int maxy){
x += vx;
y += vy;
checkCollision(minx, miny, maxx, maxy);
}
private void checkCollision(int minx, int miny, int maxx, int maxy){//碰到邊界反彈
if(x - r < minx) { x = r; vx = -vx; }
if(x + r >= maxx){ x = maxx - r; vx = -vx; }
if(y - r < miny) { y = r; vy = -vy; }
if(y + r >= maxy){ y = maxy - r; vy = -vy; }
}
}
AlgoFrame.java
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.RenderingHints;
import javax.swing.*;
public class AlgoFrame extends JFrame{
private int canvasWidth;
private int canvasHeight;
private JPanel canvas;
public AlgoFrame(String title, int canvasWidth, int canvasHeight){
super(title);
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
AlgoCanvas canvas = new AlgoCanvas();
setContentPane(canvas);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
}
public AlgoFrame(String title){
this(title, 1024, 768);
}
public int getCanvasWidth(){return canvasWidth;}
public int getCanvasHeight(){return canvasHeight;}
// data
private Circle[] circles;
//repaint函數是JFrame中自帶,將JFrame的空間全部刷新,重新調用paintComponent。
public void render(Circle[] circles){
this.circles = circles;
repaint();
}
private class AlgoCanvas extends JPanel{
public AlgoCanvas(){
// 雙緩存
super(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// 抗鋸齒
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.addRenderingHints(hints);
// 具體繪製
AlgoVisHelper.setStrokeWidth(g2d,1);
AlgoVisHelper.setColor(g2d, Color.RED);
for(Circle circle: circles)
AlgoVisHelper.strokeCircle(g2d, circle.x, circle.y, circle.getR());
}
@Override
public Dimension getPreferredSize(){
return new Dimension(canvasWidth, canvasHeight);
}
}
}
Main.java
import java.awt.EventQueue;
import java.util.Random;
public class Main {
public static void main(String[] args) {
int sceneWidth = 800;
int sceneHeight = 800;
// init data
int N = 10;
Circle[] circles = new Circle[N];
int R = 50;
for(int i = 0 ; i < N ; i ++ ) {
int x = (int)(Math.random()*(sceneWidth-2*R)) + R;//讓圓形在方框內部
int y = (int)(Math.random()*(sceneHeight-2*R)) + R;
int vx = (int)(Math.random()*11) - 5;
int vy = (int)(Math.random()*11) - 5;
circles[i] = new Circle(x, y, R, vx, vy);
}
EventQueue.invokeLater(() -> {
AlgoFrame frame = new AlgoFrame("Welcome", sceneWidth,sceneHeight);
new Thread(() -> {//GUI放在事件隊列方法中,繪製無法響應,因此必須要用線程。
while(true) {
// 繪製數據
frame.render(circles);
AlgoVisHelper.pause(20);
// 更新數據
for(Circle circle : circles)
circle.move(0, 0, sceneWidth, sceneHeight);
}
}).start();
});
}
}
2-8 算法可視化中的MVC
Circle類數據層,Frame爲視圖層,Visualizer爲控制層。
AlgoVisualizer.java
import java.awt.*;
public class AlgoVisualizer {
private Circle[] circles; // 數據
private AlgoFrame frame; // 視圖
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){
// 初始化數據
circles = new Circle[N];
int R = 50;
for(int i = 0 ; i < N ; i ++ ) {
int x = (int)(Math.random()*(sceneWidth-2*R)) + R;
int y = (int)(Math.random()*(sceneHeight-2*R)) + R;
int vx = (int)(Math.random()*11) - 5;
int vy = (int)(Math.random()*11) - 5;
circles[i] = new Circle(x, y, R, vx, vy);
}
// 初始化視圖
EventQueue.invokeLater(() -> {
frame = new AlgoFrame("Welcome", sceneWidth, sceneHeight);
new Thread(() -> {
run();
}).start();
});
}
// 動畫邏輯方法
private void run(){
while(true){
// 繪製數據
frame.render(circles);
AlgoVisHelper.pause(20);
// 更新數據
for(Circle circle: circles)
circle.move(0, 0, frame.getCanvasWidth(), frame.getCanvasHeight());
}
}
}
Main.java
public class Main {
public static void main(String[] args) {
int sceneWidth = 800;
int sceneHeight = 800;
int N = 10;
AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N);
}
}
2-9 鍵盤事件
設置標誌
private boolean isAnimated = true;
繼承KeyAdapter類
private class AlgoKeyListener extends KeyAdapter{
@Override
public void keyReleased(KeyEvent event){
if(event.getKeyChar() == ' ')
isAnimated = !isAnimated;
}
}
更新數據的時候設置判斷
public void run(){
while(true){
// 繪製數據
frame.render(circles);
AlgoVisHelper.pause(20);
// 更新數據
if( isAnimated)
for(Circle circle: circles)
circle.move(0, 0, frame.getCanvasWidth(), frame.getCanvasHeight());
}
}
監聽事件
frame.addKeyListener(new AlgoKeyListener());
AlgoVisualizer.java
import java.awt.*;
import java.awt.event.*;
public class AlgoVisualizer{
private Circle[] circles;
private AlgoFrame frame;
private boolean isAnimated = true;
public AlgoVisualizer(int sceneWidth, int sceneHeight, int N){
// 初始化數據
circles = new Circle[N];
int R = 50;
for(int i = 0 ; i < N ; i ++ ) {
int x = (int)(Math.random()*(sceneWidth-2*R)) + R;
int y = (int)(Math.random()*(sceneHeight-2*R)) + R;
int vx = (int)(Math.random()*11) - 5;
int vy = (int)(Math.random()*11) - 5;
circles[i] = new Circle(x, y, R, vx, vy);
}
// 初始化視圖
EventQueue.invokeLater(() -> {
frame = new AlgoFrame("Welcome", sceneWidth, sceneHeight);
frame.addKeyListener(new AlgoKeyListener());
new Thread(() -> {
run();
}).start();
});
}
public void run(){
while(true){
// 繪製數據
frame.render(circles);
AlgoVisHelper.pause(20);
// 更新數據
if( isAnimated)//鍵盤監聽判斷。
for(Circle circle: circles)
circle.move(0, 0, frame.getCanvasWidth(), frame.getCanvasHeight());
}
}
private class AlgoKeyListener extends KeyAdapter{
@Override
public void keyReleased(KeyEvent event){
if(event.getKeyChar() == ' ')
isAnimated = !isAnimated;
}
}
public static void main(String[] args) {
int sceneWidth = 800;
int sceneHeight = 800;
int N = 10;
AlgoVisualizer vis = new AlgoVisualizer(sceneWidth, sceneHeight, N);
}
}
2-10 鼠標事件
AlgoVisualizer.java
初始化視圖裏加入代碼:
frame.addMouseListener(new AlgoMouseListener());
定義私有類
private class AlgoMouseListener extends MouseAdapter{
@Override
public void mousePressed(MouseEvent event){
//System.out.println(event.getPoint());
event.translatePoint(0,-(frame.getBounds().height - frame.getCanvasHeight()));
for(Circle circle : circles)
if(circle.contain(event.getPoint()))
circle.isFilled = !circle.isFilled;
}
}
Circle.java
public boolean isFilled = false;//空心圓變實心圓
public boolean contain(Point p){
return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y) <= r * r;
}
AlgoFrame.java
// 具體繪製
AlgoVisHelper.setStrokeWidth(g2d,1);
AlgoVisHelper.setColor(g2d, Color.RED);
for(Circle circle: circles)
if(circle.isFilled)
AlgoVisHelper.fillCircle(g2d, circle.x, circle.y, circle.getR());
else
AlgoVisHelper.strokeCircle(g2d, circle.x, circle.y, circle.getR());