Good afternoon! It is necessary to make a list (vertical) from lists of events (horizontal). There are at least 2 problems:
- the area of the list of events (horizontal scrolling) expands beyond the borders of the panel. Apparently, the problem is with Layout, but I can not find the right combination;
- horizontal scrolling does not work (probably due to the problem described above) and the scrollbar must be different for each group of events.
import ivank.components.EventAdd; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Random; public class WindowAddCameras extends JFrame { public static final List<JPanel> labels = new ArrayList<JPanel>(); public WindowAddCameras() { super("Добавить камеру"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panelButton = new JPanel(); JButton addButton = new JButton("+"); addButton.setFocusable(false); panelButton.add(addButton); JButton remButton = new JButton("-"); remButton.setFocusable(false); panelButton.add(remButton); JPanel externalPanel = new JPanel(); externalPanel.setLayout(new BorderLayout(0, 0)); JScrollPane scrollPaneGroupEvent = new JScrollPane( externalPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); JPanel internalPanel = new JPanel(); internalPanel.setLayout(new GridLayout(0, 1, 0, 0)); JScrollPane scrollPaneEvent = new JScrollPane(internalPanel); scrollPaneEvent.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); scrollPaneEvent.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); externalPanel.add(scrollPaneEvent, BorderLayout.NORTH); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int number = labels.size() + 1; EventAdd eventAdd = new EventAdd(); Dimension labelSize = new Dimension(80, 80); //add event to group event Random rand = new Random(); for(int a = 0; a < 20; a++) { //random color border event for TEST Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()); eventAdd.createEventLabel("Камера " + number, labelSize, randomColor); } labels.add(eventAdd); internalPanel.add(eventAdd, BorderLayout.NORTH); scrollPaneGroupEvent.revalidate(); } }); remButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(labels.size() > 0) { int index = labels.size() - 1; JPanel panel = labels.remove(index); internalPanel.remove(panel); internalPanel.repaint(); scrollPaneGroupEvent.revalidate(); } } }); this.getContentPane().setLayout(new BorderLayout()); this.getContentPane().add(panelButton, BorderLayout.NORTH); this.getContentPane().add(scrollPaneGroupEvent, BorderLayout.CENTER); this.setPreferredSize(new Dimension(600, 400)); this.pack(); this.setLocationRelativeTo(null); this.setVisible(true); } }
package ivank.components; import javax.swing.*; import java.awt.*; import java.util.ArrayList; public class EventAdd extends JPanel { public EventAdd() { super(new FlowLayout(FlowLayout.LEFT)); } public JComponent createEventLabel(String name, Dimension labelSize, Color randomColor) { this.setBorder(BorderFactory.createTitledBorder(name)); JLabel label = new JLabel(); label.setPreferredSize(labelSize); label.setHorizontalAlignment(JLabel.CENTER); label.setBorder(BorderFactory.createLineBorder(randomColor, 5)); this.add(label); return label; } }
What I have: enter image description here What I want to get: enter image description here
Advertisement
Answer
A scrolling area inside another scrolling area is a user interface antipattern (anti-design?). It should be avoided.
I would create a scrollable panel based on a vertical BoxLayout:
public class WindowAddCameras extends JFrame { private static final long serialVersionUID = 1; public static final List<JPanel> labels = new ArrayList<JPanel>(); private static class CameraListPanel extends JPanel implements Scrollable { private static final long serialVersionUID = 1; CameraListPanel() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public boolean getScrollableTracksViewportWidth() { return true; } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return getScrollableIncrement(30, visibleRect, orientation, direction); } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return getScrollableIncrement( orientation == SwingConstants.HORIZONTAL ? getWidth() : getHeight(), visibleRect, orientation, direction); } private int getScrollableIncrement(int amount, Rectangle visibleRect, int orientation, int direction) { if (orientation == SwingConstants.HORIZONTAL) { return Math.min(amount, direction < 0 ? visibleRect.x : getWidth() - (visibleRect.x + visibleRect.width)); } else { return Math.min(amount, direction < 0 ? visibleRect.y : getHeight() - (visibleRect.y + visibleRect.height)); } } } public WindowAddCameras() { super("Добавить камеру"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panelButton = new JPanel(); JButton addButton = new JButton("+"); addButton.setFocusable(false); panelButton.add(addButton); JButton remButton = new JButton("-"); remButton.setFocusable(false); panelButton.add(remButton); JPanel camerasPanel = new CameraListPanel(); JScrollPane scrollPaneGroupEvent = new JScrollPane(camerasPanel); addButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int number = labels.size() + 1; EventAdd eventAdd = new EventAdd(); Dimension labelSize = new Dimension(80, 80); //add event to group event Random rand = new Random(); for(int a = 0; a < 20; a++) { //random color border event for TEST Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()); eventAdd.createEventLabel("Камера " + number, labelSize, randomColor); } labels.add(eventAdd); camerasPanel.add(eventAdd); scrollPaneGroupEvent.revalidate(); } }); remButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(labels.size() > 0) { int index = labels.size() - 1; JPanel panel = labels.remove(index); camerasPanel.remove(panel); camerasPanel.repaint(); scrollPaneGroupEvent.revalidate(); } } }); this.getContentPane().setLayout(new BorderLayout()); this.getContentPane().add(panelButton, BorderLayout.NORTH); this.getContentPane().add(scrollPaneGroupEvent, BorderLayout.CENTER); this.setPreferredSize(new Dimension(600, 400)); this.pack(); this.setLocationRelativeTo(null); this.setVisible(true); } public static void main(String[] args) { EventQueue.invokeLater(() -> new WindowAddCameras()); } }
The CameraListPanel class is mostly a basic scrollable panel; the important part is that getScrollableTracksViewportWidth()
returns true, which will cause the panel’s width to match the width of any JScrollPane viewport. This eliminates any need for a horizontal scrollbar.
Of course, you will want to show all of your child components somehow. To do that, I would have the EventAdd class compute a height that can accommodate all of the children:
public class EventAdd extends JPanel { private static final long serialVersionUID = 1; private final FlowLayout layout; public EventAdd() { layout = new FlowLayout(FlowLayout.LEFT); setLayout(layout); } @Override public Dimension getPreferredSize() { Rectangle childSize = new Rectangle(); Component[] children = getComponents(); for (Component child : children) { childSize.add(new Rectangle(child.getPreferredSize())); } Insets insets = getInsets(); int hgap = layout.getHgap(); int vgap = layout.getVgap(); int childWidth = childSize.width + hgap; Dimension size; if (getParent() == null) { size = new Dimension( children.length * (childWidth * hgap) + hgap, childSize.height + vgap * 2); } else { int width = getParent().getWidth(); width -= insets.left + insets.right; int childrenPerRow = childWidth == 0 ? 0 : (width - hgap) / childWidth; int rows; if (childrenPerRow == 0) { rows = 0; } else { rows = children.length / childrenPerRow; if (children.length % childrenPerRow > 0) { rows++; } } size = new Dimension(width, vgap + rows * (childSize.height + vgap)); } size.width += insets.left + insets.right; size.height += insets.top + insets.bottom; return size; }