Daily Archives: 15/02/2016

Java 2D – Các đối tượng hình học – phần 2

Trong phần này chúng ta tìm hiểu về một số đối tượng hình học cao cấp hơn và cách sử dụng màu sắc, ảnh texture…

Đa giác

Đầu tiên chúng ta vẽ một số đa giác cơ bản.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;


class Surface extends JPanel {
    
    private void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.setPaint(new Color(150, 150, 150));

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

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

        g2d.setRenderingHints(rh);

        g2d.fillRect(30, 20, 50, 50);
        g2d.fillRect(120, 20, 90, 60);
        g2d.fillRoundRect(250, 20, 70, 60, 25, 25);

        g2d.fill(new Ellipse2D.Double(10, 100, 80, 100));
        g2d.fillArc(120, 130, 110, 100, 5, 150);
        g2d.fillOval(270, 130, 50, 50);
   } 

    @Override
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        doDrawing(g);
    }    
}

public class BasicShapesEx extends JFrame {

    public BasicShapesEx() {

        initUI();
    }
    
    private void initUI() {
        
        add(new Surface());
        
        setTitle("Basic shapes");
        setSize(350, 250);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

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

Trong ví dụ trên chúng ta vẽ 6 đa giác là hình vuông, hình chữ nhật, hình chữ nhật có góc tròn, hình elip, hình quạt và hình tròn.

g2d.setPaint(new Color(150, 150, 150));

Các đa giác sẽ được vẽ màu xám.

g2d.fillRect(20, 20, 50, 50);
g2d.fillRect(120, 20, 90, 60);

Chúng ta dùng phương thức fillRect() để vẽ hình chữ nhật và hình vuông. Hai tham số đầu tiên là tọa độ góc trái-trên của hình chữ nhật, 2 tham số cuối cùng là chiều dài và chiều rộng của hình chữ nhật.

g2d.fillRoundRect(250, 20, 70, 60, 25, 25);

Phương thức fillRoundRect() dùng để vẽ hình chữ nhật có góc tròn, tham số cũng tương tự như fillRect() ngoại trừ 2 tham số cuối cùng là số đo độ cong của 2 đường ngang và 2 đường dọc. Bạn có thể thay đổi giá trị 2 tham số này để thấy sự khác biệt.

g2d.fill(new Ellipse2D.Double(10, 100, 80, 100));

Ở dòng code trên chúng ta dùng phương thức fill() để vẽ một đa giác cho trước là một elip.

g2d.fillArc(120, 130, 110, 100, 5, 150);

Phương thức fillArc() vẽ một hình quạt, 4 tham số đầu tiên là tọa độ góc trái-trên và góc phải-dưới của hình quạt. Tham số thứ 5 là hướng mà quạt quay tới, tham số thứ 6 là độ lớn của quạt.

g2d.fillOval(270, 130, 50, 50);

Phương thức fillOval() vẽ một hình tròn.

Capture

Đa giác lồi

Để vẽ các đa giác phức tạp hơn thì chúng ta dùng lớp GeneralPath. Lớp này vẽ đa giác bằng cách nối đoạn thẳng với tập các điểm cho trước.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private final double points[][] = { 
        { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, 
        { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, 
        { 40, 190 }, { 50, 125 }, { 0, 85 } 
    };
    
    private void doDrawing(Graphics g) {
        
        Graphics2D g2d = (Graphics2D) g.create();

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
                             RenderingHints.VALUE_RENDER_QUALITY);

        g2d.setPaint(Color.gray);
        g2d.translate(25, 5);

        GeneralPath star = new GeneralPath();

        star.moveTo(points[0][0], points[0][1]);

        for (int k = 1; k < points.length; k++)
            star.lineTo(points[k][0], points[k][1]);

        star.closePath();
        g2d.fill(star);        
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        doDrawing(g);
    }
}

public class StarEx extends JFrame {
    
    public StarEx() {

        initUI();
    }    
    
    private void initUI() {
        
        add(new Surface());
        
        setTitle("Star");
        setSize(350, 250);
        setLocationRelativeTo(null);           
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    public static void main(String[] args) {

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

Ở ví dụ trên chúng ta vẽ hình một ngôi sao từ một tập hợp điểm.

GeneralPath star = new GeneralPath();

Khởi tạo đối tượng GeneralPath.

star.moveTo(points[0][0], points[0][1]);

Đầu tiên chúng ta chuyển vị trí bắt đầu vẽ đến điểm đầu tiên.

for (int k = 1; k < points.length; k++)
    star.lineTo(points[k][0], points[k][1]);

Tiếp theo chúng ta duyệt qua tập điểm và dùng phương thức lineTo() để nối các điểm này lại.

star.closePath();
g2d.fill(star);

Sau khi đã nối xong chúng ta gọi phương thức closePath() để báo rằng việc nối đã hoàn tất và gọi phương thức fill() để tô màu ngôi sao.

Capture

Lớp Area

Lớp Area cho phép tạo các hình phức tạp hơn bằng cách trộn lẫn các hình có sẵn lại với nhau.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {
        
    public void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;
        
        RenderingHints rh = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

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

        g2d.setPaint(Color.gray);
        
        Area a1 = new Area(new Rectangle2D.Double(20, 20, 100, 100));
        Area a2 = new Area(new Ellipse2D.Double(50, 50, 100, 100));
        
        a1.subtract(a2);
        g2d.fill(a1);
        
        Area a3 = new Area(new Rectangle2D.Double(150, 20, 100, 100));
        Area a4 = new Area(new Ellipse2D.Double(150, 20, 100, 100));        
        
        a3.subtract(a4);
        g2d.fill(a3);
        
        Area a5 = new Area(new Rectangle2D.Double(280, 20, 100, 100));
        Area a6 = new Area(new Ellipse2D.Double(320, 40, 100, 100));        
        
        a5.add(a6);
        g2d.fill(a5);        
    }

    @Override
    public void paintComponent(Graphics g) {
                
        super.paintComponent(g);
        doDrawing(g);
    }           
}

public class AreasEx extends JFrame {

    public AreasEx() {

        initUI();
    }

    private void initUI() {
        
        add(new Surface());
        
        setTitle("Areas");
        setSize(450, 200);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                AreasEx ex = new AreasEx();
                ex.setVisible(true);
            }
        });
    }
}

Đoạn code trên tạo 3 hình khác nhau.

Area a1 = new Area(new Rectangle2D.Double(20, 20, 100, 100));
Area a2 = new Area(new Ellipse2D.Double(50, 50, 100, 100));

a1.subtract(a2);
g2d.fill(a1);

Hai dòng code trên tạo 1 hình chữ nhật và một hình elip rồi cắt một vùng trên hình chữ nhật bằng với hình elip bằng phương thức substract().

Area a5 = new Area(new Rectangle2D.Double(280, 20, 100, 100));
Area a6 = new Area(new Ellipse2D.Double(320, 40, 100, 100));        

a5.add(a6);
g2d.fill(a5); 

Đối với 2 hình a5a6 thì chúng ta cho chúng nằm đè lên nhau bằng phương thức add().

Capture

Tô màu

Lớp Color là lớp chuyên làm việc với màu. Chúng ta sẽ tìm hiểu về lớp này qua ví dụ sau.

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

class Surface extends JPanel {
        
    public void doDrawing(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.setColor(new Color(125, 167, 116));
        g2d.fillRect(10, 10, 90, 60);

        g2d.setColor(new Color(42, 179, 231));
        g2d.fillRect(130, 10, 90, 60);

        g2d.setColor(new Color(70, 67, 123));
        g2d.fillRect(250, 10, 90, 60);

        g2d.setColor(new Color(130, 100, 84));
        g2d.fillRect(10, 100, 90, 60);

        g2d.setColor(new Color(252, 211, 61));
        g2d.fillRect(130, 100, 90, 60);

        g2d.setColor(new Color(241, 98, 69));
        g2d.fillRect(250, 100, 90, 60);

        g2d.setColor(new Color(217, 146, 54));
        g2d.fillRect(10, 190, 90, 60);

        g2d.setColor(new Color(63, 121, 186));
        g2d.fillRect(130, 190, 90, 60);

        g2d.setColor(new Color(31, 21, 1));
        g2d.fillRect(250, 190, 90, 60);
    }

    @Override
    public void paintComponent(Graphics g) {
                
        super.paintComponent(g);
        doDrawing(g);
    }           
}

public class ColoursEx extends JFrame {

    public ColoursEx() {

        initUI();
    }

    private void initUI() {
        
        add(new Surface());
        
        setTitle("Colours");
        setSize(360, 300);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ColoursEx ex = new ColoursEx();
                ex.setVisible(true);
            }
        });
    }
}

Để tạo màu thì chúng ta dùng phương thức setColor() và đưa vào một đối tượng Color. Đối tượng này khi được tạo ra nhận 3 tham số là giá trị đỏ (Red), xanh lá (Green) và xanh lam (Blue).

Capture

Gradient

Gradient là dải màu từ màu này đến màu khác. Có thể ứng dụng để làm mô phỏng bóng tối và ánh sáng.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GradientPaint;
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();

        GradientPaint gp1 = new GradientPaint(5, 5, 
            Color.red, 20, 20, Color.black, true);

        g2d.setPaint(gp1);
        g2d.fillRect(20, 20, 300, 40);

        GradientPaint gp2 = new GradientPaint(5, 25, 
            Color.yellow, 20, 2, Color.black, true);

        g2d.setPaint(gp2);
        g2d.fillRect(20, 80, 300, 40);

        GradientPaint gp3 = new GradientPaint(5, 25, 
            Color.green, 2, 2, Color.black, true);

        g2d.setPaint(gp3);
        g2d.fillRect(20, 140, 300, 40);

        GradientPaint gp4 = new GradientPaint(25, 25, 
            Color.blue, 15, 25, Color.black, true);

        g2d.setPaint(gp4);
        g2d.fillRect(20, 200, 300, 40);

        GradientPaint gp5 = new GradientPaint(0, 0, 
             Color.orange, 0, 20, Color.black, true);

        g2d.setPaint(gp5);
        g2d.fillRect(20, 260, 300, 40);   
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {
        
        super.paintComponent(g);
        doDrawing(g);
    }
}

public class GradientsEx extends JFrame {
    
    public GradientsEx() {

        initUI();
    }    
    
    private void initUI() {
        
        add(new Surface());
        
        setTitle("Gradients");
        setSize(350, 350);
        setLocationRelativeTo(null);            
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    public static void main(String[] args) {

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

Trong ví dụ này chúng ta tạo 5 hình chữ nhật với 5 gradient khác nhau.

GradientPaint gp4 = new GradientPaint(25, 25, 
    Color.blue, 15, 25, Color.black, true);

Để hiện dải màu thì chúng ta dùng lớp GradientPaint(x1, y1, Color1, x2, y2, Color2, cyclic), trong đó các tham số có ý nghĩa là tô màu từ điểm (x1, y1) với màu Color1 dần dần chuyển sang màu Color2 tại vị trí (x2, y2). Tham số cyclic cho biết các dải màu có được phép lặp lại hay không.

g2d.setPaint(gp4);

Sau khi đã thiết lập màu gradient, chúng ta gọi phương thức setPaint() để kích hoạt gradient.

Capture

Ảnh Texture

Ảnh texture là ảnh dùng để hiện lên trên đa giác. Để sử dụng texture thì chúng ta dùng lớp TexturePaint.

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Surface extends JPanel {

    private BufferedImage slate;
    private BufferedImage java;
    private BufferedImage pane;
    private TexturePaint slatetp;
    private TexturePaint javatp;
    private TexturePaint panetp;

    public Surface() {

        loadImages();
    }

    private void loadImages() {

        try {

            slate = ImageIO.read(new File("slate.png"));
            java = ImageIO.read(new File("java.png"));
            pane = ImageIO.read(new File("pane.png"));

        } catch (IOException ex) {

            Logger.getLogger(Surface.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    private void doDrawing(Graphics g) {

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

        slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));
        javatp = new TexturePaint(java, new Rectangle(0, 0, 90, 60));
        panetp = new TexturePaint(pane, new Rectangle(0, 0, 90, 60));

        g2d.setPaint(slatetp);
        g2d.fillRect(10, 15, 90, 60);

        g2d.setPaint(javatp);
        g2d.fillRect(130, 15, 90, 60);

        g2d.setPaint(panetp);
        g2d.fillRect(250, 15, 90, 60);
        
        g2d.dispose();
    }

    @Override
    public void paintComponent(Graphics g) {

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

public class TexturesEx extends JFrame {

    public TexturesEx() {

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

        setTitle("Textures");
        setSize(360, 120);
        setLocationRelativeTo(null);        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

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

Trong đoạn code trên chúng ta vẽ 3 hình chữ nhật với 3 texture khác nhau.

slate = ImageIO.read(new File("slate.png"));

Chúng ta dùng lớp ImageIO để load ảnh vào bộ nhớ.

slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));

Tiếp theo chúng ta tạo ảnh texture từ lớp TexturePaint.

g2d.setPaint(slatetp);
g2d.fillRect(10, 15, 90, 60);

Cuối cùng chúng ta vẽ hình chữ nhật.

Capture

Java 2D – Các đối tượng hình học – phần 1

Trong phần này chúng ta sẽ vẽ các đối tượng hình học cơ bản.

Điểm

Đối tượng hình học cơ bản nhất là điểm, là một dấu chấm trên màn hình. Trong Java có sẵn lớp java.awt.Point để biểu diễn điểm nhưng không có phương thức nào để vẽ. Chúng ta sẽ vẽ điểm bằng phương thức drawLine(), phương thức này vẽ một đoạn thẳng từ điểm đầu tới điểm cuối, chúng ta dùng nó để vẽ điểm với hai điểm đầu và cuối trùng nhau.

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

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

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

Đoạn code trên vẽ 2000 điểm ở vị trí ngẫu nhiên trên màn hình và thay đổi theo thời gian.

private void initTimer() {

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

Lớp javax.swing.Timer được dùng để tạo hiệu ứng chuyển động. Lớp này sẽ giải phóng sự kiện ActionEvents cứ sau một khoảng thời gian nhất định.

g2d.setPaint(Color.blue);

Điểm được vẽ sẽ có màu xanh lam.

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

Ở 2 dòng code trên, chúng ta lấy kích thước cửa sổ.

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

Sau đó tính vị trí ngẫu nhiên dựa vào kích thước đó.

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

Chúng ta dùng phương thức drawLine() để vẽ điểm.

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

Trong phương thức xử lý sự kiện, chúng ta gọi phương thức repaint() để chương trình cập nhật lại vị trí mới của mỗi điểm.

Capture

Đoạn thẳng

Đoạn thẳng là một đối tượng hình học cơ bản. Trong lập trình thì đoạn thẳng là một đối tượng kết nối giữa hai điểm. Chúng ta dùng phương thức drawLine() để vẽ đoạn thẳng.

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

Trong đoạn code trên chúng ta vẽ bốn đoạn thẳng.

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

Phương thức drawLine() nhận vào 4 tham số là tọa độ (x, y) của điểm đầu và điểm cuối.

Capture

Định dạng đoạn thẳng

Định dạng đoạn thẳng là cách mà đoạn thẳng đó được vẽ. Lớp BasicStroke là lớp định nghĩa một tập các cách vẽ khác nhau.

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

Trong đoạn code trên chúng ta vẽ một số kiểu đường thẳng khác nhau.

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

Ở đây chúng ta sẽ thay đổi thuộc tính stroke của đối tượng Graphics, do đó chúng ta nên tạo một bản sao của đối tượng này rồi làm việc với bản sao đó.

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

Thuộc tính dash định nghĩa kiểu vẽ giữa các phần hiện và các phần ẩn. Trong đoạn code trên thì dash1 có ý nghĩa là vẽ một đoạn dài 2f pixel, sau đó là khoảng trắng dài 0f pixel rồi lại vẽ đoạn thẳng dài 2f, cứ như thế.

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

Chúng ta tạo một đối tượng BasicStroke.

g2d.setStroke(bs1);

Phương thức setStroke() quy định kiểu vẽ cho đối tượng Graphics.

g2d.dispose();

Cuối cùng chúng ta gọi phương thức dispose() để hủy đối tượng graphics đã được copy.

Capture

Tham số CAP

Tham số CAP quy định phần viền ở góc đoạn thẳng. Tham số này có 3 giá trị là CAP_BUTT, CAP_ROUND, và CAP_SQUARE.

  • CAP_BUTT: capbutt
  • CAP_ROUND: capround
  • CAP_SQUARE: capsquare
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);
            }
        });
    }
}

Đoạn code trên ví dụ về 3 tham số CAP khác nhau.

Capture

Tham số JOIN

Tham số JOIN quy định kiểu đường cong nối giữa hai đoạn thẳng cắt nhau. Tham số này có 3 giá trị là JOIN_BEVEL, JOIN_MITER, và JOIN_ROUND.

  • JOIN_BEVEL: joinbevel
  • JOIN_MITER: joinmiter
  • JOIN_ROUND: joinround
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);
            }
        });
    }
}

Trong đoạn code trên chúng ta vẽ 3 hình chữ nhật với 3 kiểu JOIN khác nhau.

g2d.drawRect(15, 15, 80, 50);

Phương thức drawRect() vẽ hình chữ nhật với 4 tham số là tọa độ góc trái trên và góc phải dưới của hình chữ nhật.

Capture