Переключение между рисованием прямоугольника и от руки
У меня есть некоторые проблемы с моей программой рисования на Java.
у меня есть JComboBox
где я могу выбрать рисовать прямоугольник или от руки. Объекты добавляются в ArrayList
, Я хочу иметь возможность переключаться между рисованием прямоугольника и свободной рукой, а затем обратно к рисованию прямоугольника, а затем свободной рукой... и так далее.
Если я делаю так, как выглядит код, он сначала хорошо рисует прямоугольники, а затем, когда я переключаюсь на свободные руки, он хорошо рисует линии, но затем, когда я переключаюсь обратно на прямоугольники, он все равно рисует линии (или иногда линии вместе со странно выглядящими прямоугольниками).). Чем больше я переключаю, тем страннее становится.
Кто-нибудь может увидеть, что не так с кодом, потому что я не могу?
public abstract class Draw {
public int startX, startY, endX, endY, width, height, w, h;
public String color = "Black";
public Draw(int startX, int startY, int width, int height) {
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
}
public abstract void draw(Graphics2D g);
public int getX() {
return startX;
}
public void setX(int startX) {
this.startX = startX;
}
public int getY() {
return startY;
}
public void setY(int startY) {
this.startY = startY;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void setColor(String color) {
this.color = color;
}
}
public class Rectangle extends Draw {
public Rectangle(int x, int y, int width, int height) {
super(x, y, width, height);
}
@Override
public void draw(Graphics2D g2) {
g2.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
public class FreeHand extends Draw {
public FreeHand(int x, int y, int width, int height) {
super(x, y, width, height);
}
@Override
public void draw(Graphics2D g2) {
g2.drawLine(getX(), getY(), getWidth(), getHeight());
}
}
public class PaintProgram extends JFrame implements ActionListener {
public ArrayList<Draw> shapeList = new ArrayList<>();
int startX, startY, endX, endY, w, h;
private JPanel topPanel;
private JPanel bottomPanel;
private JPanel leftPanel;
private JPanel rightPanel;
private JComboBox comboBox;
private final String[] boxOptions = new String[] {"Rectangle", "Freehand"};
Container cp = getContentPane();
private int count = 0;
public JavaApplication30(String title) {
super(title);
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setSize(840, 500);
this.initComponents();
this.setVisible(true);
}
private void initComponents() {
cp.setBackground(Color.WHITE);
comboBox = new JComboBox(boxOptions);
topPanel = new JPanel();
bottomPanel = new JPanel(new GridLayout(1,2));
rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
comboBox.setSelectedIndex(0);
comboBox.addActionListener(this);
topPanel.setPreferredSize(new Dimension(0,40));
bottomPanel.setPreferredSize(new Dimension(0,30));
bottomPanel.setBackground(Color.LIGHT_GRAY);
topPanel.add(comboBox);
bottomPanel.add(leftPanel);
bottomPanel.add(rightPanel);
this.add(topPanel, BorderLayout.PAGE_START);
this.add(bottomPanel, BorderLayout.PAGE_END);
}
@Override
public void paint(Graphics g) {
if(count == 0) {
cp.repaint();
}
Graphics2D g2 = (Graphics2D) g;
for (Draw d : shapeList) {
d.draw(g2);
}
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(1));
if (startX != 0 && startY != 0 && endX != 0 && endY != 0) {
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
g2.setPaint(Color.WHITE);
g2.fillRect(r.getX(), r.getY(), r.getWidth(), r.getHeight());
r.setColor(pickedColor);
r.draw(g2);
}
}
@Override
public void actionPerformed(ActionEvent e) {
count++;
if (e.getSource().equals(comboBox)) {
JComboBox cb = (JComboBox)e.getSource();
if (cb.getSelectedItem().equals("Rectangle")) {
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
endX = startX;
endY = startY;
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = Math.abs(startX - endX);
int height = Math.abs(startY - endY);
int minX = Math.min(startX, endX);
int minY = Math.min(startY, endY);
Rectangle r = new Rectangle(minX, minY, width, height);
shapeList.add(r);
r.setColor(pickedColor);
startX = 0;
startY = 0;
endX = 0;
endY = 0;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
endX = e.getX();
endY = e.getY();
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Freehand")) {
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
addCoordinate(startX, startY);
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
Graphics g = getGraphics();
Graphics2D g2 = (Graphics2D) g;
FreeHand fh = new FreeHand(startX, startY, e.getX(), e.getY());
shapeList.add(fh);
fh.setColor(pickedColor);
fh.draw(g2);
startX = e.getX();
startY = e.getY();
}
});
}
}
}
public static void main(String args[]) {
new PaintProgram("Paint");
}
}
2 ответа
Вы добавляете MouseListeners, но не удаляете их. Каждый раз, когда вы выбираете что-то в выпадающем списке, добавляется новый слушатель. Поэтому, когда вы рисуете что-то, каждый слушатель применяется, и происходят странные вещи.
Вы должны удалить предыдущий MouseListener перед добавлением нового. Возможно, вам придется запомнить это в переменной экземпляра.
Кроме того, вы можете добавить всех слушателей в начале, но проверьте значение комбинированного списка внутри слушателя. Если значение не соответствует тому, для чего предназначен слушатель, он ничего не должен делать.
РЕДАКТИРОВАТЬ: Вот как вы можете удалить всех слушателей
for (MouseListener listener : this.getMouseListeners()) {
this.removeMouseListener(listener);
}
for (MouseMotionListener listener : this.getMouseMotionListeners()) {
this.removeMouseMotionListener(listener);
}
Поместите этот код перед тем, как добавить новых слушателей в метод actionPerformed()
Как было указано здесь и здесь ранее, не добавляйте MouseListener
в вашем ActionListener
вместо этого создайте MosueListener
и определите, что вы хотите сделать, основываясь на текущем выбранном элементе.
По сути, вы продолжаете добавлять новый MouseListener
каждый раз actionPerformed
называется... они накапливаются...
Решение было бы использовать один MouseListener
и завод какой-то...
Начнем с определения заводского интерфейса...
public interface DrawFactory {
public Draw createDrawing(int x, int y, int width, int height, Color color);
public void addPoint(Draw draw, int x, int y);
}
Создайте реализацию фабрики для каждого типа фигуры, которую вы хотите нарисовать...
public class RectangleFactory implements DrawFactory {
@Override
public Draw createDrawing(int x, int y, int width, int height, Color color) {
return new Rectangle(x, y, width, height);
}
@Override
public void addPoint(Draw draw, int x, int y) {
// Does nothing...
}
@Override
public boolean isMutable() {
return false;
}
@Override
public String getName() {
return "Rectangle";
}
@Override
public String toString() {
return getName();
}
}
public class FreeHandFactory implements DrawFactory {
@Override
public Draw createDrawing(int x, int y, int width, int height, Color color) {
return new FreeHand(x, y, width, height);
}
@Override
public void addPoint(Draw draw, int x, int y) {
if (draw instanceof FreeHand) {
FreeHand fh = (FreeHand)draw;
//fh.addPoint(x, y);
}
}
@Override
public boolean isMutable() {
return true;
}
@Override
public String getName() {
return "Free Hand";
}
@Override
public String toString() {
return getName();
}
}
Затем создайте пользовательский компонент, который простирается от JPanel
которая будет выступать в качестве основной поверхности рисования, это будет отвечать за мониторинг MouseLstener
и покраска Draw
случаи, как было упомянуто здесь
public class DrawSurface extends JPanel {
private DrawFactory factory;
private Draw currentDraw;
private List<Draw> shapeList = new ArrayList<>();
private Color drawColor;
public DrawSurface() {
shapeList = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
private Point pressPoint;
@Override
public void mousePressed(MouseEvent e) {
pressPoint = e.getPoint();
}
@Override
public void mouseReleased(MouseEvent e) {
DrawFactory factory = getDrawFactory();
if (factory != null) {
Point p = e.getPoint();
if (factory.isMutable() && currentDraw != null) {
factory.addPoint(currentDraw, p.x, p.y);
} else {
int x = Math.min(p.x, pressPoint.x);
int y = Math.min(p.y, pressPoint.y);
int width = Math.abs(p.x - pressPoint.x);
int height = Math.abs(p.y - pressPoint.y);
Draw draw = factory.createDrawing(x, y, width, height, getDrawColor());
shapeList.add(draw);
if (factory.isMutable()) {
currentDraw = draw;
}
}
}
}
};
}
public DrawFactory getDrawFactory() {
return factory;
}
public void setDrawFactory(DrawFactory factory) {
this.factory = factory;
currentDraw = null;
}
public Color getDrawColor() {
return drawColor;
}
public void setDrawColor(Color drawColor) {
this.drawColor = drawColor;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Draw draw : shapeList) {
draw.draw(g2d);
}
g2d.dispose();
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
Затем измените свой boxOptions
от String
в DrawFactory
это поможет вам определить, какую фабрику вам следует использовать. Не забудьте добавить ссылку на DrawSurface
private final DrawFactory[] boxOptions = new DrawFactory[]{new RectangleFactory(), new FreeHandFactory()};
private DrawSurface drawSurface;
В вашем initComponents
создать новый экземпляр DrawSurface
и добавить его в свой кадр...
private void initComponents() {
//...
drawSurface = new DrawSurface();
this.add(drawSurface);
}
Измени свой actionPerformed
способ выглядеть больше как...
@Override
public void actionPerformed(ActionEvent e) {
count++;
drawSurface.setDrawFactory((DrawFactory)comboBox.getSelectedItem());
}
Не уверен, как вы определяете текущий цвет, поскольку пример кода неполон, но в основном вы хотите установить drawColor
из DrawSurface
так же.
Избавиться от paint
метод в PaintProgram
как вы не должны переопределять paint
метод контейнеров верхнего уровня, против которого вам посоветовали хотя бы один раз, если не два раза.
Суть всего этого проста, когда вы хотите добавить новую "форму рисунка", вы создаете Draw
а также DrawFactory
для этого и добавить фабрику в поле со списком... работа сделана...