Resizing Issue With Canvas Within Jscrollpane Within Jsplitpane

Resizing issue with canvas within jscrollpane within jsplitpane

After reading the comment by davidbuzatto I googled about mixing AWT and Swing components and I was a little surprissed to find out that it is such a bad practice.

I found the most accurate answer to my question here

Heavyweight components have their own Z-ordering. This is the reason why Swing and AWT cannot be combined in a single container. If they are, the AWT components will be drawn on TOP of Swing components.

For example: When AWT components are used with JtabbedPane, they do not disappear when the tabs are switched.

Thanks davidbuzatto for showing me the way :-)

Using JSplitPane with an AWT component

It is a dirty way but this will solve it:

When you call pack() without resizing the window, not much happens. So, when you first resize the window and then call pack(), your components are correcly drawn. This means you can put this dirty method in your divider moved listener method:

frame.setPreferredSize(frame.getSize()); // store the current size to restore it after packing.
frame.setSize(frame.getWidth() + 1, frame.getHeight()); // resize it!!
frame.pack();

I don't know what it is exactly but it is a strange behavour in Java...

Hope this helps until you've found a better solution...

JScrollPane minimum width inside JSplitPane

The getMinimumSize called on a JSplitPane returns a size which is actually taking into account the minimum size of its left and right Components, plus the divider size. So one way to maybe solve your problem would be to make your JSplitPane implement Scrollable (in order to make it respect the minimum size of itself) and add it to a JScrollPane. This way you can ensure that the minimum size is respected and when the user continues shrinking the Scrollable JSplitPane past its minimum size, then the scroll bars will show up.

Here is some working code:

import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class Main {

private static class MyScrollableSplitPane extends JSplitPane implements Scrollable {

private int maxUnitIncrement = 10;

public void setMaxUnitIncrement(final int pixels) {
maxUnitIncrement = pixels;
}

public int getMaxUnitIncrement() {
return maxUnitIncrement;
}

/**
* This is being constantly checked by the scroll pane instead of the
* getPreferredScrollableViewportSize...
*/
@Override
public Dimension getPreferredSize() {
final Dimension minSz = getMinimumSize(),
curSz = getSize();
curSz.width = Math.max(curSz.width, minSz.width);
curSz.height = Math.max(curSz.height, minSz.height);
return curSz;
}

/**
* This is only checked once (at the beginning).
*/
@Override
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}

/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}

//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}

/**
* Source: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html .
*/
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxUnitIncrement;
} else {
return visibleRect.height - maxUnitIncrement;
}
}

@Override
public boolean getScrollableTracksViewportWidth() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().width < ((JViewport) parent).getWidth());
}

@Override
public boolean getScrollableTracksViewportHeight() {
final Container parent = getParent();
return (parent instanceof JViewport) && (getMinimumSize().height < ((JViewport) parent).getHeight());
}
}

private static void createAndShowGUI() {

/*Since I don't add any Components to the 'left' and 'right' panels, I am going to set the
preferred size of them. This is only for demonstrating the concept. Setting the minimum size
though is somewhat required by the JSplitPane itself.*/

final JPanel left = new JPanel();
left.setMinimumSize(new Dimension(150, 100));
left.setPreferredSize(new Dimension(200, 200));

final JPanel right = new JPanel();
right.setMinimumSize(new Dimension(300, 100));
right.setPreferredSize(new Dimension(400, 200));

final JSplitPane split = new MyScrollableSplitPane();
split.setBorder(BorderFactory.createLineBorder(Color.CYAN.darker(), 3));
split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
split.setLeftComponent(left);
split.setRightComponent(right);

final JFrame frame = new JFrame("MyScrollableSplitPane demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(split));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}

2 Synchronized JScrollPanes inside JSplitPane not working properly

Scrollbars are displayed automatically when the preferred size of the component is greater than the size of the scroll pane.

You can't share the models because the state of each scrollbar will be different depending on how much space is allocation to each scroll pane. The only time they will be in sync is when the divider of the split pane is in the middle.

So you need to listen for changes in the scrollbar of each scroll pane and than attempt to adjust the scrollbar from the other scroll pane.

Something like:

import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import java.awt.event.*;

public class SplitPaneTest2 extends JFrame {

public SplitPaneTest2() {

setTitle( "Splits" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setSize( 400, 400 );

JPanel panel1 = new JPanel();
panel1.setLayout(new BorderLayout());

panel1.add( new JLabel( "Left panel!11111111111111111111" ) );
JScrollPane scrollPane1 = new JScrollPane(panel1);

JPanel panel2 = new JPanel();
panel2.setLayout(new BorderLayout());

panel2.add( new JLabel( "Right Panel11111111111111111111" ) );
JScrollPane scrollPane2 = new JScrollPane(panel2);

// scrollPane2.getVerticalScrollBar().setModel(scrollPane1.getVerticalScrollBar().getModel());
// scrollPane2.getHorizontalScrollBar().setModel(scrollPane1.getHorizontalScrollBar().getModel());
new ScrollBarSynchronizer(scrollPane1.getHorizontalScrollBar(), scrollPane2.getHorizontalScrollBar());

JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scrollPane1,
scrollPane2);
splitPane.setResizeWeight(0.5);
add(splitPane);

setVisible( true );

}

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

static class ScrollBarSynchronizer implements AdjustmentListener
{
JScrollBar[] scrollBars;

public ScrollBarSynchronizer(JScrollBar... scrollBars)
{
this.scrollBars = scrollBars;

for (JScrollBar scrollBar: scrollBars)
scrollBar.addAdjustmentListener( this );
}

@Override
public void adjustmentValueChanged(AdjustmentEvent e)
{
JScrollBar source = (JScrollBar)e.getSource();
int value = e.getValue();

for (JScrollBar scrollBar: scrollBars)
{
if (scrollBar != source)
{
scrollBar.removeAdjustmentListener( this );
scrollBar.setValue( value );
scrollBar.addAdjustmentListener( this );
}
}
}
}

}

You can also create a separate synchronizer for the vertical scrollbars.

Specifying the location of Canvas in Swing

"Swing programs should override paintComponent() instead of overriding paint()."—Painting in AWT and Swing: The Paint Methods. JPanel or JComponent are common choices, as suggested here. You can control placement using a suitable layout.

Addendum: How does this relate to Canvas?

The class java.awt.Canvas is an AWT component; instead use the Swing component javax.swing.JPanel. Here's a variation of your program that merely selects a random color, but it might give you an idea how to address your other properties. There's a related example here.

image

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class MainCode {

public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
MainView fc = new MainView();
}
});
}

private static class MainView implements ActionListener {

private JFrame f = new JFrame();
private JButton colorButton = new JButton("Color");
private JButton quitButton = new JButton("Quit");
private JButton infoButton = new JButton("Info");
private JLabel x_loc = new JLabel("X:");
private JLabel y_loc = new JLabel("Y:");
private JLabel w_label = new JLabel("Width:");
private JLabel h_label = new JLabel("Height:");
private JTextField x_loc_box = new JTextField("0");
private JTextField y_loc_box = new JTextField("0");
private JTextField w_loc_box = new JTextField("100");
private JTextField h_loc_box = new JTextField("100");
private JOptionPane info1 = new JOptionPane();
private JPanel panel1 = new JPanel();
private JPanel panel2 = new JPanel();
private GraphicsClass graphicsClass = new GraphicsClass();

public MainView() {
panel1.add(x_loc);
panel1.add(x_loc_box);
panel1.add(y_loc);
panel1.add(y_loc_box);
panel1.add(w_label);
panel1.add(w_loc_box);
panel1.add(h_label);
panel1.add(h_loc_box);
colorButton.addActionListener(this);
quitButton.addActionListener(this);
infoButton.addActionListener(this);
panel2.add(colorButton);
panel2.add(quitButton);
panel2.add(infoButton);
f.add(panel1, BorderLayout.NORTH);
f.add(graphicsClass, BorderLayout.CENTER);
f.add(panel2, BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Graphics Toolbox v2");
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}

@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == infoButton) {
JOptionPane.showMessageDialog(f, "hahahahahaha");
} else if (e.getSource() == quitButton) {
System.exit(0);
} else if (e.getSource() == colorButton) {
graphicsClass.randomColor();
graphicsClass.repaint();
}
}
}

private static class GraphicsClass extends JPanel {

private static final int SIZE = 128;
private static final Random r = new Random();
private Color color = Color.green;

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

public void randomColor() {
this.color = new Color(r.nextInt());
}

@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
int w = getWidth();
int h = getHeight();
g.fillArc(0, h / 4, w, h, 45, 90);
}
}
}

Java JComponent lag on resize

The example cited here scales well into the thousands of nodes and edges. It may serve as a useful reference example. A profiler, for example, may also offer some insight.

image



Related Topics



Leave a reply



Submit