Malen in Swing Teil 2: ein einfaches Malprogramm
Inhaltsverzeichnis
Dieses Beispielprogramm erlaubt das Malen von farbigen Linien verschiedener Stärke. Das Malen geschieht freihändig durch Ziehen mit der Maus. Dabei lernen wir:
- wie mehrfache repaint Anträge zusammengefasst werden
- wie wir den Bereich, der dargestellt wird, einschränken können
Mehrfache repaint Anträge zusammenfassen
Jede gemalte Linie setzt sich zusammen aus einer Folge von vielen Kreisen (Graphics#fillOval), die jeweils durch Interpolation zwischen zwei Mauspunkten festgelegt werden. Dabei wird repaint(x,y,brushSize,brushSize) mehrfach in einer Schleife aufgerufen:
<code=java> @Override
public void mouseDragged(final MouseEvent e) { double xDelta = e.getX() - lastPoint.getX(); double yDelta = e.getY() - lastPoint.getY(); double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta)); double xIncrement = xDelta / delta; double yIncrement = yDelta / delta; double xStart = lastPoint.getX(); double yStart = lastPoint.getY(); for (int i = 0; i < delta; i++) { Point interpolated = new Point((int) xStart, (int) yStart); draw(interpolated); xStart += xIncrement; yStart += yIncrement; } draw(e.getPoint()); lastPoint = e.getPoint(); }
private void draw(final Point start) { int brushSize = drawingPanel.getBrushSize(); int x = start.x - (brushSize / 2) + 1; int y = start.y - (brushSize / 2) + 1; drawingPanel.getG2d().fillOval(x, y,brushSize, brushSize); drawingPanel.repaint(x, y,brushSize, brushSize); }
</code=java>
Erfolgen mehrfache repaint() Aufrufe auf einer Komponente bevor der erste Antrag verarbeitet wird, kann der Swing Repaintmanager die mehrfachen Anträge zu einem einzigen Aufruf zusammenfassen. Wenn mehrfache Anträge zusammengefasst werden, ist das resultierende Cliprechteck gleich der Vereinigung der Rechtecke, die in den zusammengefassten Anträgen enthalten sind.
Den Bereich, der dargestellt wird, einschränken
Während Swing versucht, den Prozess der Darstellung der Komponenten so leistungsfähig wie möglich zu machen, kann die paintComponent() Implementierung einer Komponente selbst eine bedeutende Auswirkung auf die gesamte Leistung haben. Wir können diesen Prozess beeinflussen, indem wir die Clipinformationen verwenden, um den Bereich, der dargestellt wird, einzuschränken.
Wenn deine Komponente einfach ist -- zum Beispiel, wenn es eine Drucktaste ist -- dann ist sie die Mühe nicht wert, die Darstellung zu bearbeiten, um nur den Teil zu malen, der das Cliprechteck überschneidet; es ist vorzuziehend, die gesamte Komponente zu malen und die Graphics passend einschränken zu lassen. Wenn du jedoch eine Komponente mit komplizierter Ausgabe erzeugt hast, wie eine Textkomponente, dann ist es notwendig, dass dein Code die Clipinformationen gebraucht, um den Umfang der Darstellung einzuschränken. Die Methode Graphics#getClipBounds erlaubt uns den Zugriff auf das Cliprechteck:
<code=java> @Override
public void paintComponent(final Graphics g) { super.paintComponent(g); // initialises the image with the first paint // or checks the image size with the current panelsize if (image == null || image.getWidth(this) < getSize().width || image.getHeight(this) < getSize().height) { resetImage(); } Rectangle r = g.getClipBounds(); g.drawImage(image, r.x, r.y, r.width + r.x, r.height + r.y, r.x, r.y, r.width + r.x, r.height + r.y, null); }</code=java>
Gesamter Quellcode
<code=java>import javax.swing.*; import java.awt.*; import java.awt.event.*;
public class DrawOnImage extends JFrame implements ActionListener {
private DrawingPanel drawingPanel; private JPanel buttonPanel; private JButton clearButton, upSize, downSize;
public DrawOnImage() { super("DrawOnImage"); drawingPanel = new DrawingPanel(); buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(2, 0, 2, 2)); addButton(Color.BLACK); addButton(Color.BLUE); addButton(Color.GREEN); upSize = addButton(null); upSize.setText("+"); addButton(Color.RED); addButton(Color.ORANGE); clearButton = addButton(null); clearButton.setText("Clear"); downSize = addButton(null); downSize.setText("-"); getContentPane().add(new JScrollPane(drawingPanel)); getContentPane().add(buttonPanel, BorderLayout.SOUTH); }
private JButton addButton(final Color color) { JButton button = new JButton(); button.setBackground(new Color(230, 240, 250)); button.setBorder(BorderFactory.createEtchedBorder()); if (color != null) { button.setForeground(Color.WHITE); button.setBackground(color); } button.setText("Paint"); buttonPanel.add(button); button.addActionListener(this); return (button); }
public void actionPerformed(final ActionEvent e) { String s = e.getActionCommand(); if (s.equals("Paint")) { JButton button = (JButton) e.getSource(); drawingPanel.setPaintColor(button.getBackground()); } else if (s.equals("Clear")) { drawingPanel.clearPaint(); } else if (s.equals("+")) { drawingPanel.increaseBrushSize(); } else { drawingPanel.decreaseBrushSize(); } }
public static void main(final String[] args) { Runnable gui = new Runnable() {
@Override public void run() { JFrame frame = new DrawOnImage(); frame.setDefaultCloseOperation(EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }; //GUI must start on EventDispatchThread: SwingUtilities.invokeLater(gui); }
}</code=java> <code=java>import java.awt.Point; import java.awt.event.MouseEvent; import javax.swing.event.MouseInputAdapter;
class MouseHandler extends MouseInputAdapter {
private DrawingPanel drawingPanel; private Point lastPoint;
MouseHandler(DrawingPanel drawingPanel) { this.drawingPanel = drawingPanel; }
@Override public void mousePressed(final MouseEvent e) { lastPoint = e.getPoint(); draw(lastPoint); }
@Override public void mouseDragged(final MouseEvent e) { double xDelta = e.getX() - lastPoint.getX(); double yDelta = e.getY() - lastPoint.getY(); double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta)); double xIncrement = xDelta / delta; double yIncrement = yDelta / delta; double xStart = lastPoint.getX(); double yStart = lastPoint.getY(); for (int i = 0; i < delta; i++) { Point interpolated = new Point((int) xStart, (int) yStart); draw(interpolated); xStart += xIncrement; yStart += yIncrement; } draw(e.getPoint()); lastPoint = e.getPoint(); }
private void draw(final Point start) { int brushSize = drawingPanel.getBrushSize(); int x = start.x - (brushSize / 2) + 1; int y = start.y - (brushSize / 2) + 1; drawingPanel.getG2d().fillOval(x, y,brushSize, brushSize); drawingPanel.repaint(x, y,brushSize, brushSize); }
}</code=java> <code=java>import java.awt.*; import javax.swing.*;
class DrawingPanel extends JComponent {
private Image image; private Graphics2D g2d; private int brushSize = 5;
public DrawingPanel() { super(); setPreferredSize(new Dimension(300, 300)); MouseHandler mh = new MouseHandler(this); addMouseListener(mh); addMouseMotionListener(mh); }
@Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); if (image != null) { size.width = image.getWidth(this); size.height = image.getHeight(this); } return size; }
public void setPaintColor(final Color color) { g2d.setColor(color); }
public void clearPaint() { g2d.setColor(Color.white); g2d.fillRect(0, 0, getWidth(), getHeight()); repaint(); g2d.setColor(Color.black); }
public void increaseBrushSize() { brushSize += 2; }
public void decreaseBrushSize() { brushSize -= 2; if (brushSize <= 0) { brushSize = 1; } }
@Override public void paintComponent(final Graphics g) { super.paintComponent(g); // initialises the image with the first paint // or checks the image size with the current panelsize if (image == null || image.getWidth(this) < getSize().width || image.getHeight(this) < getSize().height) { resetImage(); } Rectangle r = g.getClipBounds(); g.drawImage(image, r.x, r.y, r.width + r.x, r.height + r.y, r.x, r.y, r.width + r.x, r.height + r.y, null); }
private void resetImage() { Image saveImage = image; Graphics2D saveG2d = g2d; image = createImage(getWidth(), getHeight()); g2d = (Graphics2D) image.getGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(Color.white); g2d.fillRect(0, 0, getWidth(), getHeight()); g2d.setColor(Color.black); if (saveG2d != null) { g2d.setColor(saveG2d.getColor()); g2d.drawImage(saveImage, 0, 0, this); } }
public Graphics2D getG2d() { return g2d; }
public int getBrushSize() { return brushSize; }
}</code=java>