看的見的算法 第二章 GUI界面

第二章

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());

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章