ホバークラフトのすべてのコメントとともに
Graphics コンテキストはコンポーネント間で共有されます。のタスクの 1 つはsuper.paintComponent
、描画する前にグラフィックス コンテキストを「クリーン」にすることです。
これが、2 つのバージョンのボタンが表示される理由です...
また、さまざまなことを別の方法で行います。これにより、拡張性と長期的な再利用が促進され、ロジックが少し削減されます。
私は...するだろう...
- シェイプを、塗りつぶしとストロークの色、位置、サイズ、ストロークなどの最小要件を持つ基本的な「シェイプ」クラスに抽象化し、それ自体をペイントする方法を理解します。
- 責任の境界を分離して定義できるようなモデルを作成します。形状を「管理」するのはコンポーネントの責任ではなく、表面にペイントするだけです。同様に、コンポーネントは「形状」が何であるかを気にせず、それらがどのようにペイントされるかについて知りたいだけです...
- s を使用
Action
して、これらの形状を単純に作成し、モデルに追加します...

私は三角形の形を作成しただけです(場所とサイズ以外の属性はありません)が、一般的なアイデアは理解できると思います...(psアクションに独自の三角形アイコンを提供する必要があります;))
public class DrawMe {
public static void main(String[] args) {
new DrawMe();
}
public DrawMe() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
DrawModel model = new DefaultDrawModel();
model.addElement(new Triangle(new Rectangle(10, 10, 100, 100)));
DrawPane drawPane = new DrawPane(model);
JToolBar toolBar = new JToolBar();
toolBar.add(new AddTriangleAction(model));
frame.add(toolBar, BorderLayout.NORTH);
frame.add(drawPane);
frame.setSize(400, 400);
frame.setVisible(true);
}
});
}
/**
* Simple action used to add triangles to the model...the model acts
* as a bridge between the action and the UI.
*/
protected class AddTriangleAction extends AbstractAction {
private DrawModel model;
public AddTriangleAction(DrawModel model) {
// Supply your own icon
putValue(SMALL_ICON, new ImageIcon(getClass().getResource("/shape_triangle.png")));
this.model = model;
}
public DrawModel getModel() {
return model;
}
@Override
public void actionPerformed(ActionEvent e) {
// Randomly add the triangles...
int x = (int)(Math.random() * 400);
int y = (int)(Math.random() * 400);
model.addElement(new Triangle(new Rectangle(x, y, 100, 100)));
}
}
/**
* This is the background pane, from which the draw pane extends...
*/
protected class BackgroundPane extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() / 2;
int y = getHeight() / 2;
Graphics2D g2d = (Graphics2D) g.create();
RadialGradientPaint rgp = new RadialGradientPaint(
new Point(x, y),
Math.max(getWidth(), getHeight()),
new float[]{0f, 1f},
new Color[]{Color.GRAY, Color.WHITE}
);
g2d.setPaint(rgp);
g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
g2d.setBackground(Color.BLACK);
g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
/**
* This is a simple model, I stole the list model because it was quicker
* and easier to demonstrate (don't need to write all the listeners)
*/
public interface DrawModel extends ListModel<DrawMeShape> {
public void addElement(DrawMeShape shape);
public void removeElement(DrawMeShape shape);
}
/**
* A default implementation of the DrawModel...
*/
public class DefaultDrawModel extends DefaultListModel<DrawMeShape> implements DrawModel {
@Override
public void removeElement(DrawMeShape shape) {
removeElement((Object)shape);
}
}
/**
* The actually "canvas" that shapes are rendered to
*/
protected class DrawPane extends BackgroundPane {
// Should provide ability to setModel...
private DrawModel model;
public DrawPane(DrawModel model) {
this.model = model;
model.addListDataListener(new ListDataListener() {
@Override
public void intervalAdded(ListDataEvent e) {
repaint();
}
@Override
public void intervalRemoved(ListDataEvent e) {
repaint();
}
@Override
public void contentsChanged(ListDataEvent e) {
repaint();
}
});
}
public DrawModel getModel() {
return model;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw the shapes from the model...
Graphics2D g2d = (Graphics2D) g.create();
DrawModel model = getModel();
for (int index = 0; index < model.getSize(); index++) {
DrawMeShape shape = model.getElementAt(index);
shape.paint(g2d, this);
}
g2d.dispose();
}
}
/**
* A abstract concept of a shape. Personally, if I was doing it, I would
* generate an interface first, but this is just a proof of concept...
*/
public abstract class DrawMeShape {
private Rectangle bounds;
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
public Rectangle getBounds() {
return bounds;
}
protected abstract Shape getShape();
/**
* The shape knows how to paint, but it needs to know what to paint...
* @param g2d
* @param parent
*/
public void paint(Graphics2D g2d, JComponent parent) {
g2d = (Graphics2D) g2d.create();
Rectangle bounds = getBounds();
Shape shape = getShape();
g2d.translate(bounds.x, bounds.y);
g2d.setColor(Color.DARK_GRAY);
g2d.fill(shape);
g2d.setColor(Color.BLACK);
g2d.draw(shape);
g2d.dispose();
}
}
/**
* An implementation of a Triangle shape...
*/
public class Triangle extends DrawMeShape {
public Triangle(Rectangle bounds) {
setBounds(bounds);
}
@Override
protected Shape getShape() {
// This should be cached ;)
Path2D path = new Path2D.Float();
Rectangle bounds = getBounds();
path.moveTo(bounds.width / 2, 0);
path.lineTo(bounds.width, bounds.height);
path.lineTo(0, bounds.height);
path.lineTo(bounds.width / 2, 0);
path.closePath();
return path;
}
}
}
楽しいお絵描き…