2013-03-09 53 views
5

Tôi có một số mã để vẽ hình chữ nhật. Nó được sử dụng để vẽ hình chữ nhật trên JPanel, để đánh dấu ranh giới của các tiện ích con. Ở đây mã đầu tiên, sau đó tôi sẽ giải thích vấn đề của tôi cq. câu hỏi.Java Swing - Cách tiếp cận hợp lệ để kéo hình chữ nhật vào một JPanel?

Trước hết, tôi có một lớp học (WidgetDrawingPanel) mở rộng JPanel.

public WidgetDrawingPanel(int width, int height) { 
    /*To make things visible at least*/ 
    widgets.add(new Widget(10,10,100,100, WidgetType.TextField)); 
    widgets.add(new Widget(50,50,100,200, WidgetType.TextField)); 
    this.width = width; 
    this.height = height; 
    this.setBackground(Color.BLUE); 
    addListener(); //adds both MouseMotionListener and MouseListener 
} 

Dưới đây bạn sẽ thấy tôi tham chiếu ch rất nhiều. Đây là CoordinateHolder, giữ các tọa độ bắt đầu và hiện tại của chuyển động của chuột.

private void addListener() { 
    this.addMouseMotionListener(new MouseMotionListener() { 
     @Override 
     public void mouseDragged(MouseEvent arg0) { 
      ch.currentX = arg0.getX(); 
      ch.currentY = arg0.getY(); 
      System.out.println("dragging " + ch.currentX + ","+ch.currentY); 
      WidgetDrawingPanel.this.repaint(); 
     } 
    }); 
    this.addMouseListener(new MouseListener() { 
     @Override 
     public void mouseReleased(MouseEvent event) { 
      ch.endX = event.getX(); 
      ch.endY = event.getY(); 
      try { 
       checkCoords(); 
      } catch (OutsidePanelException e) { 
       e.printStackTrace(); 
       JOptionPane.showMessageDialog(null, "drawn Outside Panel"); 
      } 
     } 

     @Override 
     public void mousePressed(MouseEvent event) { 
      ch = new CoordinateHolder(event.getX(), event.getY()); 
     } 
    }); 
} 

và, cuối cùng, phương pháp paintComponent(Grapics). Có vòng lặp qua Widgets, thực ra chỉ được vẽ Rects (thuộc tính x, y, w, h), nhưng có thêm một chút thông tin, không hữu ích trong phần bản vẽ của ứng dụng. Mỗi khi bạn nhả chuột, CoordinateHolder được chuyển đổi thành một Widget và được thêm vào widgets.

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    System.out.println("Paint"); 
    g.setColor(Color.BLUE); 
    g.fillRect(0, 0, width, height); //making the whole panel blue 
    g.setColor(Color.RED); 
    Graphics2D g2 = (Graphics2D)g; 
    g2.setStroke(new BasicStroke(3)); 
    for (Widget w : widgets) { 
     g.drawRect(w.getX(), w.getY(), w.getW(), w.getH()); 
    } 
    if (ch != null) 
     g.drawRect(ch.startX, ch.startY, ch.currentX - ch.startX, ch.currentY - ch.startY); 
} 

Mã này đang làm việc, nhưng tôi nghi ngờ điều này rất hiệu quả và inperformant, như trên đang liên tục làm mới JPanel trên kéo chuột, đó là, nói, một lần mỗi 10ms? Tôi cho rằng nó sẽ sớm bị chậm, đặc biệt là khi người dùng tạo ra một heck của một hình chữ nhật rất nhiều (cũng được liên tục vẽ lại, như được thấy trong painComponent(Graphics)).

Câu hỏi cq. Sự cố

Có phương pháp tiêu thụ tài nguyên ít hơn, tốt hơn, nơi người dùng có thể kéo hình chữ nhật trơn tru không?

Tôi đã đọc câu trả lời cho số Drag rectangle on JFrame in Java này, nhưng tác giả của câu trả lời đó dường như cũng giống như tôi. Nhưng một lần nữa, đó là cách không hiệu quả, phải không? Hay máy tính có thể dễ dàng vẽ lại thành phần liên tục được không, và đây có thực sự là một cách tiếp cận hợp lệ không?

Trả lời

4

Để hiển thị nhiều hình nền không thay đổi, hãy vẽ chúng thành BufferedImage và sau đó hiển thị BufferedImage theo phương pháp paintComponent(...). Vì vậy, trong khi một hình dạng đang được rút ra, vẽ nó trong paintComponent(...) nhưng một khi hình dạng được thực hiện đang được rút ra, có lẽ trên mouseRelease, sau đó vẽ nó trong nền BufferedImage.

Lưu ý rằng điều sẽ làm chậm mã vẽ hiện tại của bạn nhiều nhất có thể là các báo cáo SOP gỡ lỗi của bạn, nhưng tôi giả định rằng những điều này sẽ bị xóa khỏi mã đã hoàn thành.

Ví dụ:

import java.awt.*; 
import java.awt.event.*; 
import java.awt.image.BufferedImage; 

import javax.swing.*; 

@SuppressWarnings("serial") 
public class DrawingPanel extends JPanel { 
    private static final int PREF_W = 600; 
    private static final int PREF_H = 400; 
    private static final Color DRAWING_COLOR = new Color(255, 100, 200); 
    private static final Color FINAL_DRAWING_COLOR = Color.red; 

    private BufferedImage backgroundImg; 
    private Point startPt = null; 
    private Point endPt = null; 
    private Point currentPt = null; 

    public DrawingPanel() { 
     backgroundImg = new BufferedImage(PREF_W, PREF_H, 
      BufferedImage.TYPE_INT_ARGB); 
     Graphics g = backgroundImg.getGraphics(); 
     g.setColor(Color.blue); 
     g.fillRect(0, 0, PREF_W, PREF_H); 
     g.dispose(); 

     MyMouseAdapter myMouseAdapter = new MyMouseAdapter(); 
     addMouseMotionListener(myMouseAdapter); 
     addMouseListener(myMouseAdapter); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     if (backgroundImg != null) { 
     g.drawImage(backgroundImg, 0, 0, this); 
     } 

     if (startPt != null && currentPt != null) { 
     g.setColor(DRAWING_COLOR); 
     int x = Math.min(startPt.x, currentPt.x); 
     int y = Math.min(startPt.y, currentPt.y); 
     int width = Math.abs(startPt.x - currentPt.x); 
     int height = Math.abs(startPt.y - currentPt.y); 
     g.drawRect(x, y, width, height); 
     } 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     return new Dimension(PREF_W, PREF_H); 
    } 

    public void drawToBackground() { 
     Graphics g = backgroundImg.getGraphics(); 
     g.setColor(FINAL_DRAWING_COLOR); 
     int x = Math.min(startPt.x, endPt.x); 
     int y = Math.min(startPt.y, endPt.y); 
     int width = Math.abs(startPt.x - endPt.x); 
     int height = Math.abs(startPt.y - endPt.y); 
     g.drawRect(x, y, width, height); 
     g.dispose(); 

     startPt = null; 
     repaint(); 
    } 

    private class MyMouseAdapter extends MouseAdapter { 
     @Override 
     public void mouseDragged(MouseEvent mEvt) { 
     currentPt = mEvt.getPoint(); 
     DrawingPanel.this.repaint(); 
     } 

     @Override 
     public void mouseReleased(MouseEvent mEvt) { 
     endPt = mEvt.getPoint(); 
     currentPt = null; 
     drawToBackground(); 
     } 

     @Override 
     public void mousePressed(MouseEvent mEvt) { 
     startPt = mEvt.getPoint(); 
     } 
    } 

    private static void createAndShowGui() { 
     DrawingPanel mainPanel = new DrawingPanel(); 

     JFrame frame = new JFrame("Drawing Panel"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.getContentPane().add(mainPanel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      createAndShowGui(); 
     } 
     }); 
    } 
} 
+0

Cảm ơn, tôi sẽ kiểm tra BufferedImage sớm, nhưng nó có thể mất một thời gian, bởi vì tôi khá bận rộn. Và giả định của bạn là chính xác. – stealthjong

+0

@ChristiaandeJong: vui lòng xem chỉnh sửa cho ví dụ [sscce] (http://sscce.org) –

+0

Ví dụ hay. Ngay cả thông qua RDP, điều này diễn ra suôn sẻ. Tôi nghĩ tôi có thể sử dụng nó cho mục đích của mình. – stealthjong

Các vấn đề liên quan