Проблема рисования на java


Я новичок в java, и мне нужно реализовать приложение paint, и я как бы застрял в начале, мне удалось нарисовать линии в JPanel, который я добавил к JFrame, но каждая нарисованная линия сбрасывает весь рисунок, и в области рисования остается только последняя нарисованная линия. Надеюсь, я правильно понял, вот его код:

class Shapes extends JFrame {

    public JFrame mf = new JFrame("Paint");
    DrawArea da = new DrawArea();

        JToggleButton lineButton = new JToggleButton(new ImageIcon("line.gif"));
        JToggleButton brushButton = new JToggleButton();
        JToggleButton pencilButton = new JToggleButton();
        JToggleButton eraserButton = new JToggleButton(new ImageIcon("eraser_icon.png"));
        JToggleButton rectangleButton = new JToggleButton();
        JToggleButton ovalButton = new JToggleButton();

    Shapes() {


        da.setBounds(120, 50, 500, 350);
        da.setBackground(Color.YELLOW);
        mf.setSize(700, 500);
        mf.setLayout(null);

        lineButton.setBounds(0, 50, 40, 40);
        brushButton.setBounds(40, 50, 40, 40);
        eraserButton.setBounds(0, 90, 40, 40);
        pencilButton.setBounds(40, 90, 40, 40);
        rectangleButton.setBounds(0, 130, 40, 40);
        ovalButton.setBounds(40, 130, 40, 40);

        mf.setBackground(Color.red);
        mf.add(lineButton);
        mf.add(brushButton);
        mf.add(pencilButton);
        mf.add(eraserButton);
        mf.add(rectangleButton);
        mf.add(ovalButton);
        mf.add(da);
        mf.show();
        mf.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        mf.addMouseListener(new MouseAdapter() {

            public void mouseClicked(MouseEvent e) {
                System.out.println("x:" + e.getX() + "y:" + e.getY() + "n" + "x2:" + e.getXOnScreen() + "y2:" + e.getYOnScreen());
            }
        });
        eraserButton.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent e)
            {
            eraserButton.setSelectedIcon(new ImageIcon("eraser_icon_selected.png"));
        }
        });
        lineButton.addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent e)
            {
              lineButton.setSelectedIcon(new ImageIcon("line_selected.png"));
        }
        });
        da.addMouseListener(new MouseAdapter() {

            public void mousePressed(MouseEvent e) {
                da.setXvalue(e.getX());
                da.setYvalue(e.getY());



            }

            public void mouseReleased(MouseEvent e) {

                da.setX2value(e.getX());
                da.setY2value(e.getY());
                da.repaint();
            }
        });
        da.addMouseMotionListener(new MouseAdapter() {

            public void mouseDragged(MouseEvent e) {
                da.repaint();
                da.setX2value(e.getX());
                da.setY2value(e.getY());

            }
        });

    }
}



public class DrawArea extends JPanel {
    int x1value,y1value,x2value,y2value;

    public int getX2value() {
        return x2value;
    }

    public void setX2value(int x2value) {
        this.x2value = x2value;
    }

    public int getY2value() {
        return y2value;
    }

    public void setY2value(int y2value) {
        this.y2value = y2value;
    }
    public JPanel dra=new JPanel();

    public int getXvalue() {
        return x1value;
    }

    public void setXvalue(int xvalue) {
        this.x1value = xvalue;
    }

    public int getYvalue() {
        return y1value;
    }

    public void setYvalue(int yvalue) {
        this.y1value = yvalue;
    }

    public void paint(Graphics g)
    {
      super.paint(g);
      g.setColor(Color.red);
      g.drawLine(getXvalue(),getYvalue(),getX2value(),getY2value());

}
}

    class Paint extends JPanel

{ 

    public static void main(String args[])
            {
               Shapes s=new Shapes();

            }

}
5 3

5 ответов:

Смотритепользовательские подходы к рисованию для двух решений. Примеры рисуют прямоугольники, но концепция одинакова для линий.

Переопределение Paint component(), а не paint(). Прочитайте этот учебник . Когда панель должна быть перерисована, вы называете это панелями метод перекраски ().

Paint вызывается оконным менеджером всякий раз, когда он считает, что область 'unfresh'. Если вы будете делать это так, как вы делаете это прямо сейчас, вы будете рисовать последнюю линию, проведенную каждый раз.

Правильный способ сделать это-сделать BufferedImage в памяти и рисовать на этом. Затем, в методе краски, Блит BufferedImage на поверхность. Это также делает прокрутку и масштабирование довольно легко сделать.

Всякий раз, когда вы выполняете такое действие, аннулируйте поверхность, чтобы диспетчер окон вызывал метод краски для вас.

Вы сохраняете только одну строку и перезаписываете ее каждый раз, поэтому, когда компонент перерисовывается, старый стирается, а новый перерисовывается.

Ожидание paintComponent и тому подобное состоит в том, что ваша реализация будет рисовать каждый графический элемент, который вы хотите отобразить, каждый раз, когда он вызывается.

Вместо хранения x1, y1, x2, y2, Вы должны создать класс LineSegment или аналогичный, который хранит эти значения. Затем, когда вы рисуете, вы называете g.drawLine() для каждого LineSegment объект, который вы сохранили (предположительно в ArrayList или подобном). Затем, когда компонент будет перерисован, на экране должны появиться все сегменты вашей линии.

Немного не по теме, но у меня было несколько неудобных минут, потому что я использовал update() вместо repaint(). Я советую всем, кто работает с SWING, потратить некоторое время на проверку того, какие методы должны обрабатываться как потокобезопасные, а какие-на EDT (Event Dispatcher Thread), чтобы убедиться, что вы не получите неожиданных ошибок. это хорошая статья об этом.

Кроме того, сначала подумайте, хотите ли вы иметь систему отмены/повтора в вашем приложении... Если да, то как многие шаги, которые вы хотите разрешить, чтобы быть отозванным. Если вы хотите разрешить эту функцию, то вы не можете просто рисовать и забыть о том, что вы рисовали в прошлый раз. Кроме того, было бы не очень эффективно хранить в памяти все изображения, которые вы рисуете до сих пор. Я не эксперт и не говорю, что это лучшая практика, но я бы пошел по этому пути:

Я бы составил два списка.

  1. Один из них будет хранить применяемые действия рисования ,
  2. другой будет содержать изъятый чертеж. действия .

действие рисования будет интерфейсом, и некоторый класс будет реализовывать его для каждого конкретного вида действия рисования (LineDrawAction, CirceDrawAction...).

Когда вы рисуете новую строку или что-то еще, вы опустошаете список отозванных действий и добавляете его в список примененных действий. Когда кто-то отменяет последнее действие, я просто удаляю последние действия рисования из списка примененных и добавляю в список отозванных (прием...). В зависимости от того, хотите ли вы разрешить отменять только последнее действие x, когда список достигает этого предела x, я удалю первое действие рисования из списка или очереди и, наконец, нарисую рисунок - это означает постоянный рисунок, и это не может быть отменено.

Я надеюсь, что это ясно и полезно, даже если это не прямой ответ на ваш вопрос.