Created
May 2, 2017 08:01
-
-
Save fmarot/87cf6642b5a555874d11242de899402a to your computer and use it in GitHub Desktop.
A Swing layout able to lay pages horizontally inside an horizontally scrollable panel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.BorderLayout; | |
import java.awt.Color; | |
import java.awt.Component; | |
import java.awt.Container; | |
import java.awt.Dimension; | |
import java.awt.Frame; | |
import java.awt.Insets; | |
import java.awt.LayoutManager; | |
import java.awt.Rectangle; | |
import java.awt.event.ComponentEvent; | |
import java.awt.event.ComponentListener; | |
import javax.swing.BorderFactory; | |
import javax.swing.JButton; | |
import javax.swing.JFrame; | |
import javax.swing.JPanel; | |
import javax.swing.JScrollPane; | |
import javax.swing.ScrollPaneConstants; | |
import javax.swing.Scrollable; | |
import javax.swing.SwingUtilities; | |
import lombok.extern.slf4j.Slf4j; | |
@Slf4j | |
public class PrintPreviewPagesTester { | |
public PrintPreviewPagesTester() { | |
JFrame frame = new JFrame(); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
PrintPreviewPagesController controller = new PrintPreviewPagesController(); | |
Container contentPane = frame.getContentPane(); | |
Component pagesPanel = controller.getPagesPanel(); | |
contentPane.add(pagesPanel, BorderLayout.CENTER); | |
Component actionPanel = controller.getActionPanel(); | |
contentPane.add(actionPanel, BorderLayout.SOUTH); | |
frame.setVisible(true); | |
frame.setSize(300, 400); | |
frame.setState(Frame.MAXIMIZED_BOTH); | |
} | |
public static void main(String[] args) { | |
SwingUtilities.invokeLater(() -> new PrintPreviewPagesTester()); | |
} | |
public class PrintPreviewPagesPanel { | |
private JScrollPane mainView; | |
private JPanel pagesPanel; | |
public PrintPreviewPagesPanel() { | |
pagesPanel = new ScrollablePanel(new PrintPreviewPagesLayout()); | |
mainView = new JScrollPane(pagesPanel); | |
mainView.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); | |
mainView.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); | |
mainView.addComponentListener(new ComponentListener() { | |
@Override | |
public void componentShown(ComponentEvent e) { | |
} | |
@Override | |
public void componentResized(ComponentEvent e) { | |
updatePagesPanelSize(); // first update the size of the pages panel... | |
// pagesPanel.doLayout(); // ... then resize the pages inside | |
} | |
@Override | |
public void componentMoved(ComponentEvent e) { | |
} | |
@Override | |
public void componentHidden(ComponentEvent e) { | |
} | |
}); | |
} | |
public Component getComponent() { | |
return mainView; | |
} | |
private void updatePagesPanelSize() { | |
int mainViewHeight = mainView.getHeight(); | |
int mainViewWidth = mainView.getWidth(); | |
log.info("mainViewWidth={} - mainViewHeight={}", mainViewWidth, mainViewHeight); | |
int pagesPanelTargetHeight = mainViewHeight; | |
int pagesPanelTargetWidth = -1; | |
if (pagesPanel.getComponentCount() >= 1) { | |
Rectangle lastComponentBounds = pagesPanel.getComponent(pagesPanel.getComponentCount() - 1).getBounds(); | |
pagesPanelTargetWidth = lastComponentBounds.width + lastComponentBounds.x; | |
} else { | |
pagesPanelTargetWidth = mainViewWidth; | |
} | |
Dimension pagesPanelTargetDimension = new Dimension(pagesPanelTargetWidth, pagesPanelTargetHeight); | |
log.info("pagesPanelTargetDimension={}", pagesPanelTargetDimension); | |
pagesPanel.setPreferredSize(pagesPanelTargetDimension); | |
pagesPanel.setSize(pagesPanelTargetDimension); | |
} | |
public void addPage(JPanel newPage) { | |
pagesPanel.add(newPage); | |
// mainView.invalidate(); | |
updatePagesPanelSize(); // first resize the panel (with wrong content but to position the height) | |
pagesPanel.doLayout(); // then do a layout pass to position the pages (their setBounds method will be called)... | |
updatePagesPanelSize(); // .. then resize the pagesPanel to match its content | |
} | |
} | |
public class PrintPreviewPagesLayout implements LayoutManager { | |
public PrintPreviewPagesLayout() { | |
} | |
@Override | |
public void addLayoutComponent(String arg0, Component arg1) { | |
} | |
@Override | |
public void layoutContainer(Container parent) { | |
Insets insets = parent.getInsets(); | |
// int maxWidth = parent.getWidth() - (insets.left + insets.right); | |
int targetHeight = parent.getHeight() - (insets.top + insets.bottom); | |
int currentHorizontalPosition = 0; | |
int nbComponents = parent.getComponentCount(); | |
for (int i = 0; i < nbComponents; i++) { | |
Component comp = parent.getComponent(i); | |
Dimension compPreferredSize = comp.getPreferredSize(); | |
log.info("targetHeight: {} ", targetHeight); | |
double targetWidth = (compPreferredSize.getWidth() * targetHeight) / compPreferredSize.getHeight(); | |
comp.setBounds(currentHorizontalPosition, 0, (int) targetWidth, targetHeight); | |
log.info("Component Bounds: {}+{} ", currentHorizontalPosition, targetWidth); | |
currentHorizontalPosition += targetWidth; | |
} | |
// parent.setPreferredSize(new Dimension(currentHorizontalPosition, targetHeight)); | |
} | |
@Override | |
public Dimension minimumLayoutSize(Container parent) { | |
return preferredLayoutSize(parent); | |
} | |
@Override | |
public Dimension preferredLayoutSize(Container parent) { | |
return new Dimension(10, 10); | |
} | |
@Override | |
public void removeLayoutComponent(Component parent) { | |
} | |
} | |
public class PrintPreviewPagesController { | |
/** 1st view of the controller */ | |
private PrintPreviewPagesPanel pagesPanel; | |
/** 2nd view of the controller */ | |
private JPanel actionPanel; | |
/** last color used to color a page */ | |
private Color lastColor = new Color(50, 100, 150); | |
public PrintPreviewPagesController() { | |
pagesPanel = new PrintPreviewPagesPanel(); | |
actionPanel = buildActionPanel(); | |
} | |
public Component getPagesPanel() { | |
return pagesPanel.getComponent(); | |
} | |
public Component getActionPanel() { | |
return actionPanel; | |
} | |
private JPanel buildActionPanel() { | |
JPanel panel = new JPanel(); | |
panel.setBackground(Color.darkGray); | |
JButton button = new JButton("add page"); | |
button.addActionListener(event -> addPage()); | |
panel.add(button); | |
return panel; | |
} | |
private void addPage() { | |
JPanel newPage = createNewPage(); | |
pagesPanel.addPage(newPage); | |
} | |
private JPanel createNewPage() { | |
lastColor = new Color( | |
modifyColorComponent(lastColor.getRed()), | |
modifyColorComponent(lastColor.getGreen()), | |
modifyColorComponent(lastColor.getBlue())); | |
JPanel newPage = new JPanel(); | |
newPage.setBorder(BorderFactory.createLineBorder(Color.black, 10)); | |
newPage.setBackground(lastColor); | |
newPage.setPreferredSize(new Dimension(20, 30)); | |
return newPage; | |
} | |
private int modifyColorComponent(int value) { | |
return (value + 50) % 255; | |
} | |
} | |
/** see http://stackoverflow.com/questions/10331129/jscrollpane-resize-containing-jpanel-when-scrollbars-appear */ | |
public class ScrollablePanel extends JPanel implements Scrollable { | |
public ScrollablePanel(PrintPreviewPagesLayout printPreviewPagesLayout) { | |
super(printPreviewPagesLayout); | |
setBackground(Color.cyan); | |
} | |
@Override | |
public Dimension getPreferredScrollableViewportSize() { | |
return new Dimension(200, 200); | |
} | |
@Override | |
public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) { | |
return 100; | |
} | |
@Override | |
public boolean getScrollableTracksViewportHeight() { | |
return true; | |
} | |
@Override | |
public boolean getScrollableTracksViewportWidth() { | |
return false; | |
} | |
@Override | |
public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) { | |
return 100; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment