2015-05-11 3 views
0

У меня есть сетка предметов, выложенная в определенном порядке. Нажатие на объект вызовет определенные действия, такие как отображение информации об этом объекте или переход на другой экран.Нарисовать кликнируемые линии между кнопками Swing

Каждый объект также имеет соединения с другими объектами. Эти соединения должны быть доступны для клика, поскольку при нажатии на них будет отображаться дополнительная информация об этом соединении.

Следующий пример показывает, к чему я стремлюсь. Каждый синий круг в сетке является кликабельным объектом, и каждая красная линия между ними является интерактивным соединением.

Example 1

Я пытался хранить как объекты и соединения, как JButtons в одном макете мешок сетки и имел некоторый успех в том, что соединения являются интерактивными.

Однако соединения охватывают несколько ячеек в одном направлении, а кнопки Jbuttons расширяются, чтобы заполнить ячейки макета. Это приводит к тому, что концы соединений проходят через центр объектов и заканчиваются на границе ячейки сетки.

Вот рисунок, что я имею в виду:

Example 2

Есть ли простой способ сделать этот вид интерфейса/кто-нибудь делал это раньше?

+0

[Возможная начальная точка] (http://stackoverflow.com/questions/28467411/java-drawing -a-line-between-the-center-of-components/28467636 # 28467636) – MadProgrammer

+0

Возможно, использовать сам компонент, чтобы нарисовать линии связи ... – MadProgrammer

ответ

4

Хорошо, очень просто, но идея состоит в том, чтобы вручную нарисовать линии между объектами.

В этом случае я создал простой объект Connection, который поддерживал отношения между кнопками. Затем я просто использовал метод paintComponent для JPanel для рисования фактических строк.

Я оставлю вам разработать лучший путь метод генерации;)

Path

import java.awt.Color; 
import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.Rectangle; 
import java.awt.geom.Line2D; 
import java.awt.geom.Path2D; 
import java.awt.geom.Point2D; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import javax.swing.JButton; 
import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class Test { 

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

    public Test() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.add(new TestPane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private JButton[] buttons = new JButton[]{ 
      new JButton("1"), 
      new JButton("2"), 
      new JButton("3"), 
      new JButton("4"),}; 

     private List<Connection> connections; 

     public TestPane() { 
      setLayout(new GridBagLayout()); 
      GridBagConstraints gbc = new GridBagConstraints(); 
      gbc.gridx = 0; 
      gbc.gridy = 0; 
      gbc.insets = new Insets(10, 10, 10, 10); 
      add(buttons[0], gbc); 

      gbc.gridx = 2; 
      gbc.gridy = 0; 
      add(buttons[1], gbc); 

      gbc.gridx = 1; 
      gbc.gridy = 1; 
      add(buttons[2], gbc); 

      gbc.gridx = 2; 
      gbc.gridy = 2; 
      add(buttons[3], gbc); 

      connections = new ArrayList<Connection>(25); 
      connections.add(new Connection(buttons[0], buttons[1])); 
      connections.add(new Connection(buttons[0], buttons[2])); 
      connections.add(new Connection(buttons[0], buttons[3])); 

      connections.add(new Connection(buttons[1], buttons[3])); 
      connections.add(new Connection(buttons[2], buttons[3])); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      g2d.setColor(Color.RED); 
      for (Connection connection : connections) { 
       JButton source = connection.getSource(); 
       JButton dest = connection.getDestination(); 

       if (source.getX() == dest.getX()) { 
        // Same column... 
        g2d.drawLine(source.getX() + source.getWidth()/2, source.getY(), 
            dest.getX() + source.getWidth()/2, dest.getY()); 
       } else if (source.getY() == dest.getY()) { 
        // Same row... 
        g2d.drawLine(source.getX(), source.getY() + source.getHeight()/2, 
            dest.getX(), dest.getY() + dest.getHeight()/2); 
       } else { 

        Path2D path = new Path2D.Double(); 
        path.moveTo(horizontalCenter(source), verticalCenter(source)); 
        path.curveTo(horizontalCenter(source), verticalCenter(dest), 
            horizontalCenter(source), verticalCenter(dest), 
            horizontalCenter(dest), verticalCenter(dest)); 
        g2d.draw(path); 

       } 
      } 
      g2d.dispose(); 
     } 

     protected double horizontalCenter(JComponent bounds) { 

      return bounds.getX() + bounds.getWidth()/2d; 

     } 

     protected double verticalCenter(JComponent bounds) { 

      return bounds.getY() + bounds.getHeight()/2d; 

     } 

     protected boolean hasIntersection(Line2D line, JComponent... exclude) { 
      List<JComponent> toExclude = Arrays.asList(exclude); 
      boolean intersects = false; 
      for (Component comp : getComponents()) { 
       if (!toExclude.contains(comp)) { 
        if (line.intersects(comp.getBounds())) { 
         System.out.println(line.getP1() + "-" + line.getP2() + " intersets with " + ((JButton)comp).getText() + "; " + comp.getBounds()); 
         intersects = true; 
         break; 
        } 
       } 
      } 
      return intersects; 
     } 

     protected Line2D lineDownTo(JComponent from, JComponent to) { 
      return new Line2D.Double(horizontalCenter(from), from.getY(), horizontalCenter(from), verticalCenter(to));  
     } 

     protected Line2D lineAcrossTo(JComponent from, JComponent to) { 
      return new Line2D.Double(from.getX(), verticalCenter(from), horizontalCenter(to), verticalCenter(from));   
     } 

     protected Point2D centerOf(Rectangle bounds) { 
      return new Point2D.Double(bounds.getX() + bounds.getWidth()/2, bounds.getY() + bounds.getHeight()/2); 
     } 

     protected boolean canGoDownTo(Point2D startPoint, Point2D endPoint, JComponent to, JComponent from) { 
      Point2D targetPoint = new Point2D.Double(startPoint.getX(), endPoint.getY()); 
      return !hasIntersection(new Line2D.Double(startPoint, targetPoint), to, from); 
     } 

     public class Connection { 

      private final JButton source; 
      private final JButton destination; 

      public Connection(JButton source, JButton destination) { 
       this.source = source; 
       this.destination = destination; 
      } 

      public JButton getSource() { 
       return source; 
      } 

      public JButton getDestination() { 
       return destination; 
      } 

     } 

    } 

} 
+0

как бы вы обнаружили щелчок мышью на вашем connetor? –

+2

О, извините, забудьте эту часть: P - вы могли (и должны) кэшировать «Path2D», который соединяет два объекта, вы должны иметь возможность использовать «Path2D # contains», чтобы получить общее представление о линии, нажав на – MadProgrammer

+0

спасибо, что освободил ^^, что было бы неплохо сделать ... –