Переключение между рисованием прямоугольника и от руки

У меня есть некоторые проблемы с моей программой рисования на 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 для этого и добавить фабрику в поле со списком... работа сделана...

Другие вопросы по тегам