Java 2D – Basic drawing

Trong bài này, chúng ta cùng nhau tìm hiểu basic drawing trong Java 2D.

Point

Graphics đơn giản nhất là điểm. Nó chỉ 1 chấm trên cửa sổ. Java 2D có class ‘Point’ mô tả điểm trong hệ trục tọa độ, nhưng không có phương thức vẽ điểm. Để vẽ điểm, chúng ta sử dụng phương thức drawLine() với đối số truyền vào là 1 điểm.

PointsEx.java

package net.vncoding;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

class Surface extends JPanel implements ActionListener {

    private final int DELAY = 150;
    private Timer timer;

    public Surface() {

        initTimer();
    }

    private void initTimer() {

        timer = new Timer(DELAY, this);
        timer.start();
    }
    
    public Timer getTimer() {
        
        return timer;
    }

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.setPaint(Color.blue);

        int w = getWidth();
        int h = getHeight();

        Random r = new Random();

        for (int i = 0; i < 2000; i++) {

            int x = Math.abs(r.nextInt()) % w;
            int y = Math.abs(r.nextInt()) % h;
            g2d.drawLine(x, y, x, y);
        }
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }
}

public class PointsEx extends JFrame {

    public PointsEx() {

        initUI();
    }

    private void initUI() {

        final Surface surface = new Surface();
        add(surface);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                Timer timer = surface.getTimer();
                timer.stop();
            }
        });

        setTitle("Points");
        setSize(350, 250);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                PointsEx ex = new PointsEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Ví dụ này vẽ ngẫu nhiên 2000 điểm trên cửa sổ. Timer được sử dụng để vẽ điểm trong 1 chu kì.

private void initTimer() {

    timer = new Timer(DELAY, this);
    timer.start();
}

‘javax.swing.Timer’ được sử dụng để tạo dịch chuyển. Nó kích hoạt ‘ActionEvents’ sau mỗi khoảng thời gian được chỉ định.

g2d.setPaint(Color.blue);

Điểm được vẽ là màu Blue.

int w = getWidth();
int h = getHeight();

Lấy chiều rộng và chiều cao của cửa sổ.

Random r = new Random();
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;

Lấy số ngẫu nhiên nằm trong phạm vi kích thước cửa sổ.

g2d.drawLine(x, y, x, y);

Chúng ta vẽ điểm. Như đã đề cập, chúng ta sử dụng phương thức drawLine() và chỉ định tọa độ giống nhau.

@Override
public void actionPerformed(ActionEvent e) {
    repaint();
}

Sau mỗi sự kiện, chúng ta gọi phương thức repaint(), nó khiến toàn bộ vùng cửa sổ được redraw.

addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent e) {
        Timer timer = surface.getTimer();
        timer.stop();
    }
});

Khi cửa số sắp được đóng, chúng ta lấy timer và close nó với phương thức stop(). Timer không được gọi tường minh có thể giữ resource vô hạn. Hoạt động đóng mặc định EXIT_ON_CLOSE close JVM và tất cả các thread của nó. Do vậy, gọi stop() không cần thiết trong ví dụ này. Nhưng như 1 cách luyện tập lập trình, chúng ta vẫn gọi phương thức stop().

Java 2D - Point
Java 2D – Point

Line

Line là đối tượng graphics đơn giản, được vẽ bằng 2 điểm nối với nhau. Sử dụng phương thức drawLine() để vẽ line.

package net.vncoding;

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.drawLine(30, 30, 200, 30);
        g2d.drawLine(200, 30, 30, 200);
        g2d.drawLine(30, 200, 200, 200);
        g2d.drawLine(200, 200, 30, 30);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class LinesEx extends JFrame {

    public LinesEx() {

        initUI();
    }

    private void initUI() {

        add(new Surface());

        setTitle("Lines");
        setSize(350, 250);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                
                LinesEx ex = new LinesEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Ví dụ này, chúng ta vẽ 4 line.

g2d.drawLine(30, 30, 200, 30);

Dòng kẻ thẳng được vẽ. Tham số của phương thức drawLine() là tọa độ x,y của 2 điểm.

Kết quả:

Java-2D - Lines
Java-2D – Lines

BasicStroke

Class ‘BasicStroke’ định nghĩa tập hợp các thuộc tính render cho graphics cơ bản. Các thuộc tính này bao gồm chiều rộng, end caps, line joins, miter limit, và dash

BasicStrokesEx.java

package net.vncoding;

import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        float[] dash1 = {2f, 0f, 2f};
        float[] dash2 = {1f, 1f, 1f};
        float[] dash3 = {4f, 0f, 2f};
        float[] dash4 = {4f, 4f, 1f};

        g2d.drawLine(20, 40, 250, 40);

        BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f);

        BasicStroke bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash2, 2f);

        BasicStroke bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash3, 2f);

        BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_ROUND, 1.0f, dash4, 2f);

        g2d.setStroke(bs1);
        g2d.drawLine(20, 80, 250, 80);

        g2d.setStroke(bs2);
        g2d.drawLine(20, 120, 250, 120);

        g2d.setStroke(bs3);
        g2d.drawLine(20, 160, 250, 160);

        g2d.setStroke(bs4);
        g2d.drawLine(20, 200, 250, 200);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class BasicStrokesEx extends JFrame {

    public BasicStrokesEx() {

        initUI();
    }
    
    private void initUI() {

        add(new Surface());

        setTitle("Basic strokes");
        setSize(280, 270);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                BasicStrokesEx ex = new BasicStrokesEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Trong ví dụ này, vẽ đường gạch với nhiều kiểu khác nhau. Thuộc tính của đường gạch là 1 mẫu, được tạo bởi phối hợp phần mờ và phần trong suốt.

Graphics2D g2d = (Graphics2D) g.create();

Chúng ta chuẩn bị thay đổi thuộc tính stroke của object ‘Graphics’. Do đó, chúng ta làm việc với bản copy của object ‘Graphics’. (Nhớ rằng bản copy phải được tạo nếu chúng ta thay đổi thuộc tính ngoài font, color hoặc rendering hints)

float[] dash1 = { 2f, 0f, 2f };
float[] dash2 = { 1f, 1f, 1f };
float[] dash3 = { 4f, 0f, 2f };
float[] dash4 = { 4f, 4f, 1f };

Định nghĩa 4 dòng gạch khác nhau.

BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, 
    BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f );

Khởi tạo object ‘BasicStroke’

g2d.setStroke(bs1);

Chúng ta sử dụng phương thức setStroke() để apply ‘BasicStroke’ cho ngữ cảnh graphics hiện tại.

g2d.drawLine(20, 80, 250, 80);

Sử dụng drawLine() để vẽ line.

g2d.dispose();

Cuối cùng, chúng ta loại bỏ bản copy của object ‘Graphics’

Kết quả:

Java2D - Basic strokes
Java2D – Basic strokes

Caps

Cap là cách trang trí được áp dụng cho đầu của đường rẽ phụ và đường gạch mở. Trong Java 2D có 3 loại đầu cuối: CAP_BUTT, CAP_ROUND, and CAP_SQUARE

– CAP_BUTT: 2 đầu của đường gạch không được trang trí
– CAP_ROUND: 2 đầu của đường gạch được trang trí bo tròn góc với bán kính bằng 1/2 độ rộng của bút vẽ.
– CAP_SQUARE: 2 đầu của đường gạch được trang trí 1 đoạn hình chữ nhật với độ dài bằng 1/2 độ rộng của đường kẻ.

CapsEx.java

package net.vncoding;

import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        RenderingHints rh = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        rh.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

        g2d.setRenderingHints(rh);

        BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs1);
        g2d.drawLine(20, 30, 250, 30);

        BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs2);
        g2d.drawLine(20, 80, 250, 80);

        BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_SQUARE,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs3);
        g2d.drawLine(20, 130, 250, 130);

        BasicStroke bs4 = new BasicStroke();
        g2d.setStroke(bs4);

        g2d.drawLine(20, 20, 20, 140);
        g2d.drawLine(250, 20, 250, 140);
        g2d.drawLine(254, 20, 254, 140);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class CapsEx extends JFrame {

    public CapsEx() {

        initUI();
    }
    
    private void initUI() {
        
        add(new Surface());

        setTitle("Caps");
        setSize(280, 270);
        setLocationRelativeTo(null); 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                CapsEx ex = new CapsEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:
Trong ví dụ, chúng ta tạo 3 end cap.

BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT,
        BasicStroke.JOIN_BEVEL);
g2d.setStroke(bs1);

Chúng ta vẽ 3 line theo chiều dọc để giải thích sự khác nhau của đầu mút (end cap). Các line với CAP_ROUND và CAP_SQUARE lớn hơn dòng với CAP_BUTT. Lớn hơn chính xác bao nhiêu còn phục thuộc vào kích thước line. Trong ví dụ này, line có chiều rộng là 8px. Line lớn hơn hơn 8px, trong đó 4px bên trái và 4px bên phải.

Kết quả:

Java 2D - Caps
Java 2D – Caps

Joins

Line join là việc trang trí tại các giao điểm của 2 line. Line join có 3 style: JOIN_BEVEL, JOIN_MITER, và JOIN_ROUND.

JOIN_BEVEL — nối các line với cắt góc ngoài
JOIN_MITER — nối các line bằng cách kéo dài đường viền ngoài cho đến khi chúng gặp nhau.
JOIN_ROUND — nối các line với bo góc tròn

JoinsEx.java

package net.vncoding;

import java.awt.BasicStroke;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g.create();

        BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_BEVEL);
        g2d.setStroke(bs1);
        g2d.drawRect(15, 15, 80, 50);

        BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_MITER);
        g2d.setStroke(bs2);
        g2d.drawRect(125, 15, 80, 50);

        BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_ROUND);
        g2d.setStroke(bs3);
        g2d.drawRect(235, 15, 80, 50);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        doDrawing(g);
    }
}

public class JoinsEx extends JFrame {

    public JoinsEx() {

        initUI();
    }
    
    private void initUI() {

        add(new Surface());

        setTitle("Joins");
        setSize(340, 110);
        setLocationRelativeTo(null);  
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                JoinsEx ex = new JoinsEx();
                ex.setVisible(true);
            }
        });
    }
}

Giải thích:

BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND,
        BasicStroke.JOIN_BEVEL);
g2d.setStroke(bs1);
g2d.drawRect(15, 15, 80, 50);

Vẽ hình chữ nhật với style JOIN_BEVEL

Kết quả:

Java 2D - Line Join
Java 2D – Line Join

Be the first to comment

Leave a Reply