How do I add components on top of other components? How to align them?

The problem is the organization of the panel, perhaps I will try to explain the situation. In general, I have the task of organizing a calculator for calculating sequences, you do not need to understand what it is and how they are calculated, the only thing you need to know is that their solution does not go through equal, but through solid lines upwards. First, the initial equation (sequence) is written, then through the defined formula that the user chooses, the transformed formula (sequence) is written on top, the problem is considered solved when there is an "| -" sign in each bar above (identically true).

The difficulty lies in the fact that with the help of these transformations the example is often divided into 2 or even 3, and turns into separate subtasks, where each needs to be solved (ie, they can be divided in turn until the user reaches necessary step).

How do I add components on top of other components? How to align them? (In terms of what can be 3 components above the line, and with it at the same level, for example, on the left, there are also 3 components, those that on the left can push out those that are on the right from their place and it will be difficult to understand to which subcomponent relate, will it have to somehow align all the components that are a level lower?) And I need to implement the selection of these components, which are at the top levels in my column, in order to further choose the formula and perform the conversion, which will be written a higher level.

Any implementation is interested in whatever curve it is. I will be glad to any advice on this issue.

decision

  • Layout manager does not solve your problem? GridLayout seems to me quite suitable for her. In each cell you place some container, for example JPanel, and in it you already have the components you need, or you draw directly what you need. - GreyGoblin

2 answers 2

You can use GridBagLayout - this is a grid with a "rubber" size of rows and columns, in which a component can occupy several cells. The advantage of this solution will be the alignment of lines in height in different branches of calculations. Disadvantages: you need to recalculate the size and position of the components on the grid when changing the tree, and you can not just implement the folding part of the calculations.

Example of implementation:

import java.awt.*; import java.awt.event.*; import java.awt.geom.Line2D; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.border.StrokeBorder; public class GridBagTreeDisplay { static class TopLineBorder extends StrokeBorder { private static final long serialVersionUID = 819507837355280534L; public TopLineBorder(BasicStroke stroke) { super(stroke); } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { float size = this.getStroke().getLineWidth(); Graphics2D g2d = (Graphics2D)g.create(); g2d.setStroke(this.getStroke()); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.draw( new Line2D.Float( x + size / 2, y + size / 2, width - size, y + size / 2 ) ); g2d.dispose(); } } /** * Класс, поддерживающий древовидное отображение с помощью GridBagLayout * * Манипуляции с узлами дерева осуществляются непосредственно с экземплярами * класса TreeView.TreeNode. * * Для отображения изменений нужно вызвать метод update() */ static class TreeView { private Container container; private TreeNode root; public TreeView( Container container ) { this.container = container; this.container.setLayout( new GridBagLayout() ); } public void update() { container.removeAll(); if ( root != null ) { root.computeAll( 0 ); root.layoutComponentsOn( container ); } container.revalidate(); container.repaint(); } public void setRootComponent( JComponent rootComponent ) { Objects.requireNonNull( rootComponent ); this.root = new TreeNode( null, rootComponent ); } public TreeNode root() { return root; } public Optional<TreeNode> findNodeByComponent( JComponent componentToFind ) { return root.findByComponent( componentToFind ); } class TreeNode { private JComponent component; private int position; private int depth; private int width; private int height; private TreeNode parent; private List<TreeNode> children = new ArrayList<>(); TreeNode( TreeNode parent, JComponent component ) { this.parent = parent; this.component = component; this.depth = parent != null ? parent.depth + 1 : 0; } /** * Обновляет высоту и ширину поддерева с корнем в этом узле, * рекурсивно обновляя высоту и ширину для поддеревьев */ private void computeSize() { if ( children.isEmpty() ) { width = 1; height = 1; } else { int sumChildWidth = 0; int maxChildHeight = 0; for ( TreeNode child : children ) { child.computeSize(); sumChildWidth += child.width; maxChildHeight = Math.max( maxChildHeight, child.height ); } width = sumChildWidth; height = maxChildHeight + 1; } } /** * Устанавливает горизонтальную позицию этого узла в сетке * и обновляет позиции в поддеревьях * * @param newPosition - новая позиция */ private void computePosition( int newPosition ) { this.position = newPosition; int childLeft = position; for ( TreeNode child : children ) { child.computePosition( childLeft ); childLeft += child.width; } } /** * обновляет размеры и горизонтальную позицию узла и * его поддеревьев * * @param newPosition */ private void computeAll( int newPosition ) { computeSize(); computePosition( newPosition ); } public void updateTreeView() { TreeView.this.update(); } /** * Заменяет текущих детей этого узла на новые, * содержащие переданные компоненты * * @param components */ public void setChildren( JComponent... components ) { children.clear(); for ( JComponent component : components ) { children.add( new TreeNode( this, component ) ); } } public TreeNode parent() { return parent; } public List<TreeNode> children() { return Collections.unmodifiableList( children ); } public JComponent component() { return component; } public TreeView treeView() { return TreeView.this; } public Optional<TreeNode> findByComponent( JComponent componentToFind ) { if ( this.component == componentToFind ) { return Optional.of( this ); } for ( TreeNode child : children ) { Optional<TreeNode> found = child.findByComponent( componentToFind ); if ( found.isPresent() ) return found; } return Optional.empty(); } /** * Размещает компоненты текущего узла и дочерних узлов в * переданном контейнере * * @param container */ private void layoutComponentsOn( Container container ) { if ( !(container.getLayout() instanceof GridBagLayout ) ) { throw new IllegalArgumentException( "container must use GridBagLayout" ); } layoutComponentsOnRecursive( container, this.height ); } private void layoutComponentsOnRecursive( Container container, int treeHeight ) { // параметры размещения компонента: GridBagConstraints gbc = new GridBagConstraints( position, // позиция по горизонтали treeHeight - depth, // позиция по вертикали width, // число ячеек по ширине 1, // число ячеек по высоте 1.0, // вес компонента по ширине, при ненулевом весе GridBagLayout // постарается занять всю ширину контейнера 0, // вес по высоте GridBagConstraints.CENTER, // компонент располагается в центре своей области GridBagConstraints.HORIZONTAL, // и растягивается по горизонтали new Insets( 2, 5, 2, 5 ), // отступы от краев области 0, 0 // минимальные ширина и высота ); // компонентам из узлов с детьми устанавливаем границу с линией component.setBorder( !children.isEmpty() ? new TopLineBorder( new BasicStroke( 1.0f ) ) : null ); container.add( component, gbc ); for ( TreeNode child : children ) { child.layoutComponentsOnRecursive( container, treeHeight ); } } } } static JLabel[] makeClickableLabels( TreeView display, String... captions ) { JLabel[] labels = Arrays.stream( captions ) .map( caption -> new JLabel( caption, SwingConstants.CENTER ) ) .toArray( JLabel[]::new ); for ( JLabel label : labels ) { label.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent event ) { display.findNodeByComponent( (JComponent)event.getComponent() ) .ifPresent( node -> { if ( event.isShiftDown() ) { node.setChildren(); } else { node.setChildren( makeClickableLabels( display, "left", "center", "right" ) ); } display.update(); }); } }); } return labels; } static void initUi() { JFrame frame = new JFrame( "" ); frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); JPanel content = new JPanel(); TreeView treeView = new TreeView( content ); treeView.setRootComponent( makeClickableLabels( treeView, "root" )[0] ); TreeView.TreeNode root = treeView.root(); root.setChildren( makeClickableLabels( treeView, "1-left", "1-right" ) ); root.children().get( 0 ).setChildren( makeClickableLabels( treeView, "2-left-left", "2-left-center", "2-left-right" ) ); root.children().get( 1 ).setChildren( makeClickableLabels( treeView, "2-right-center" ) ); root.children().get( 1 ).children().get( 0 ).setChildren( makeClickableLabels( treeView, "3-right-center-center" ) ); root.updateTreeView(); frame.add( content, BorderLayout.CENTER ); frame.setSize( 800, 600 ); frame.setVisible( true ); } public static void main(String[] args) { EventQueue.invokeLater( GridBagTreeDisplay::initUi ); } } 

Result:

work example

    1 option. Swing is able HTML, perhaps it will be the fastest and easiest way.
    Option 2. The actions are the following, let's conditionally imagine that the graphical representation can be divided into cells.
    The cell is limited by the possibility of placing in it only 2 or 1 component.
    Components in a cell can be only with horizontal (left and right) or vertical (top and bottom) separation, and also without separation.
    Now, after the rules above, we get the following (based on the numbers from the picture you give):
    Start (1) will be a vertical 2 component cell. In the "bottom" is placed a one-component cell in which we place the text.
    In the "top" we place a horizontal 2x component cell. In the "left" and "right" we place in a vertical 2-component cell.
    Now in each cell in the "bottom" we place a one-component cell with the text (2) . In the upper part, we can continue splitting the cells or insert the text end (3) .
    It turned out "matryoshka". If you need 3-4-5 blocks in step (2) , we expand them with horizontal cells (in each other).

    • The trick is that this is already a tree =) inside the cell there may be another cell in which there will be another bunch of cells. For example, so that at stage (2) there were 3 options you add a horizontal cell, in the "right" of this cell you add another horizontal cell and you get 3 places for the options. If you need, for example, to expand the space to 4x between 1 and 2m variant, then you take everything that lies in the cell "left" or "right" (there is not much difference), thrust it into the "right" slot in the new horizontal cell and insert this cell into the "left" place "or" right "(which is chosen). - Dmitriy