Создайте простой редактор с графическим интерфейсом в Java Swing, используя компоненты Swing.

В настоящее время я работаю над проектом, в котором мне нужен очень простой редактор для объектов, подобных графическому интерфейсу. Этот редактор будет холстом, на котором можно разместить хорошо известные виджеты с графическим интерфейсом. Например, туда можно поместить кнопку и текстовое поле, перемещать их и изменять их размер. Никакого взаимодействия с самими виджетами не требуется.

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

Мне было интересно, могу ли я повторно использовать настоящие виджеты Swing на JPanel и позволить пользователю размещать, перемещать и изменять их размер. Если да, то на что мне следует обратить внимание?

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


person bobvdv    schedule 16.08.2012    source источник
comment
взгляните на javaFX2!   -  person invariant    schedule 16.08.2012
comment
Как мне в этом поможет javaFX2? Кажется, это просто еще один фреймворк. Я подумал, что мог бы использовать виджеты SWING с их включенной переменной в false и с использованием абсолютного позиционирования. Как мне заставить это взаимодействовать с мышью, мне немного неясно.   -  person bobvdv    schedule 16.08.2012
comment
Вы смотрели это?   -  person gontard    schedule 16.08.2012
comment
да, действительно gontard, но я не совсем уверен, как бы я справился с добавлением, перетаскиванием или изменением размера компонентов пользователем.   -  person bobvdv    schedule 16.08.2012


Ответы (3)


Я подумал, что должен вспомнить старые добрые времена Swing и немного повеселиться, написав вам доказательство концепции для этого. Он поддерживает добавление кнопки на главную панель, а затем перемещение и некоторые базовые изменения размера.

Это просто доказательство концепции, но оно отвечает на многие вопросы, которые у вас уже есть. Я добавил туда несколько TODO, типа вы будете знать, что делать дальше.

Наслаждаться...

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;

public class UIBuilder {
  private static final int NONE = -1;

  private static final int BORDER = 3;

  private JFrame frame = new JFrame("Builder");

  private JToolBar toolbar = new JToolBar();

  private JPanel main = new JPanel();

  private int startX = NONE;

  private int startY = NONE;

  private int prevX = NONE;

  private int prevY = NONE;

  private boolean resize = false;

  public UIBuilder() {
    frame.setBounds(100, 100, 600, 450);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(new BorderLayout());
    frame.getContentPane().add(toolbar, BorderLayout.PAGE_START);
    frame.getContentPane().add(main, BorderLayout.CENTER);
    frame.setVisible(true);
    buildToolbox();
    buildMainPanel();
  }

  private void buildToolbox() {
    JButton button = new JButton("Button");
    button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JButton btn = new JButton("Button");
        addComponent(btn);
      }
    });
    toolbar.add(button);
  }

  private void buildMainPanel() {
    main.setLayout(null);
  }

  private void addComponent(JComponent comp) {
    comp.setBounds(10, 10, 80, 24);

    comp.addMouseListener(new MouseAdapter() {
      public void mouseReleased(MouseEvent e) {
        startX = NONE;
        startY = NONE;
        ((JComponent) e.getSource()).setCursor(Cursor.getDefaultCursor());
      }

      public void mousePressed(MouseEvent e) {
        startX = e.getX();
        startY = e.getY();
      }
    });

    comp.addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        JComponent source = (JComponent) e.getSource();
        int x = e.getX();
        int y = e.getY();
        Rectangle bounds = source.getBounds();
        resize = x < BORDER || y < BORDER || Math.abs(bounds.width - x) < BORDER || Math.abs(bounds.height - y) < BORDER;
        if (resize) {
          // TODO: there are a lot of resize cursors here, this is just of proof of concept
          source.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
        } else {
          source.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        }
      }

      public void mouseDragged(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();
        if (startX != NONE && startY != NONE) {
          JComponent source = (JComponent) e.getSource();
          Rectangle bounds = source.getBounds();
          int deltaX = x - startX;
          int deltaY = y - startY;
          if (resize) {
            // TODO: handle all resize cases, left, right,...
            source.setSize(Math.max(10, bounds.width + x - prevX), Math.max(10, bounds.height + y - prevY));
          } else {
            source.setLocation(bounds.x + deltaX, bounds.y + deltaY);
          }
          // TODO: make sure you don't resize it as much as it disappears
          // TODO: make sure you don't move it outside the main panel
        } else {
          startX = x;
          startY = y;
        }
        prevX = x;
        prevY = y;
      }
    });
    main.add(comp);
    main.validate();
    main.repaint();
  }

  public static void main(String[] args) {
    new UIBuilder();
  }

}
person Dan D.    schedule 16.08.2012
comment
Это то, что я искал. Большое спасибо - person Juan C. V.; 04.12.2015

Это звучит как забавный проект.

Я бы определенно выбрал JPanel в качестве контейнера с нулевым макетом. Чтобы иметь возможность перемещать такие компоненты, как JButton, JLabel в контейнере, вам нужно будет прослушивать события MouseListener и MouseMotionListener добавленных компонентов и обрабатывать их соответствующим образом. Вам придется вызывать validate() и repaint() для контейнера всякий раз, когда один компонент перемещается или изменяется размер.

person Dan D.    schedule 16.08.2012
comment
Чтобы добавить новый компонент, возможно ли, чтобы пользователь перетаскивал Jpanel, во время которого компонент будет появляться и постоянно изменять размер, пока пользователь не отпустит мышь, и в этом случае он будет помещен на Jpanel? Я думаю об использовании MouseListener и MouseMotionListener на JPanel для достижения этого, я бы добавил компонент в нажатый метод и изменил его размер (изменил его границы) во время метода перетаскивания. - person bobvdv; 16.08.2012
comment
Чтобы перетащить компонент из панели инструментов, вам придется использовать материал из пакета java.awt.dnd. Я бы посоветовал в первой версии просто нажать кнопку на панели инструментов, и это добавит новый JButton в ваш контейнер, который просто ждет перемещения и изменения размера. - person Dan D.; 16.08.2012
comment
Похоже на справедливое обходное решение, я никогда не использовал пакет awt.dnd, поэтому я не буду изучать его, если у меня еще не работает первая часть. Спасибо за помощь, Дэн, я уже давно ищу решения. - person bobvdv; 16.08.2012
comment
Конечно. Я могу помочь вам, если вы застряли в разработке этого инструмента. - person Dan D.; 16.08.2012
comment
Мне все еще неясно одно: как мне отличить перетаскивание для перемещения компонента от перетаскивания для изменения его размера? - person bobvdv; 16.08.2012
comment
Если расположение мыши близко к границе, она изменяется, если нет, то перемещается. - person Dan D.; 16.08.2012
comment
Дэн, могу я установить это по событию? - person bobvdv; 16.08.2012
comment
Ты сможешь. См. мой другой ответ на вопрос с рабочим доказательством концепции. - person Dan D.; 16.08.2012

Я бы реализовал это в смешанном подходе: создайте класс, который реализует реакции мыши (перетаскивание, изменение размера). Этот класс отвечает за управление взаимодействием с пользователем.

Для реального рисования компонентов используйте настоящие компоненты Swing (класс взаимодействия с пользователем будет иметь ссылку на компонент, который он должен представлять, и делегировать фактическое отображение этому компоненту).

Этот подход позволяет избежать большинства сложностей, возникающих при расширении исходных компонентов Swing, но при этом вы можете повторно использовать весь их код рисования.

person Durandal    schedule 16.08.2012
comment
Кажется, что этот подход будет более сложным, но я не очень ясно вижу его преимущества, если сравнивать его с подходом, который предлагает Дэн. - person bobvdv; 16.08.2012
comment
В моем подходе вы реализуете взаимодействие с пользователем только один раз для компонента, который не имеет собственного встроенного поведения мыши. В подходе Дэна вам приходится иметь дело с присущими компонентам (Swing) реакциями мыши. Представьте себе, например, JScrollPane или JScrollBar, они будут реагировать на событие мыши самостоятельно. Или JButton реагирует на щелчок. Вот почему я предлагаю использовать ваш собственный перетаскиваемый компонент, чтобы делегат не мешал. - person Durandal; 16.08.2012
comment
но если вы установите переменную с включенными компонентами в false, разве это тоже не решит проблему? - person bobvdv; 16.08.2012
comment
Если вы не возражаете против того, чтобы компонент имел отключенный внешний вид, он должен избежать большинства, если не всех проблем. - person Durandal; 17.08.2012
comment
Кажется, что disabled не будет работать, потому что он также отключает запуск всех событий мыши. Так что мне действительно придется расширять виджеты. Спасибо, что предложили мне решение. - person bobvdv; 17.08.2012