I have a problem with showing specific component placed in JScrollPane. I have horizontal JScrollPane with GridLayout(1,0) and it contains variable number of JPanels - each containing image. It's like a preview of frames in GIF image. I use button to move among these JPanels (by changing borders and keeping index of chosen one), but I don't know how to force JScrollPane to show me JPanel if it's chosen (and center it if possible).
So I want this
force to do this:
Thanks in advance!
EDIT: almost working code with scrollRectToVisible() method
public class MiniatursPanel extends JPanel{
private int indexOfChosenFrame = 0;
private ArrayList<JPanel> frames;
private JScrollPane scrollPane;
private JPanel innerPanel;
public MiniatursPanel(){
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),BorderFactory.createLoweredBevelBorder()));
setPreferredSize(new Dimension(1200,170));
setLayout(null);
}
public void initialize(){
int width = GifImageStats.getInstance().getWidth();
int height = GifImageStats.getInstance().getHeight();
int numberOfFrames = GifImageStats.getInstance().getNumberOfFrames();
frames = new ArrayList(numberOfFrames);
for (int i = 0; i < numberOfFrames; i++) {
JPanel frameBox = new JPanel();
frameBox.setLayout(new FlowLayout(FlowLayout.CENTER));
JButton button = new JButton(String.valueOf(i+1));
button.setPreferredSize(new Dimension(2*width,2*height));
button.setBackground(Color.white);
button.setFocusable(false);
frameBox.add(button);
frames.add(frameBox);
}
innerPanel = new JPanel();
innerPanel.setLayout(new GridLayout(1,0,10,10));
for (JPanel button : frames) {
innerPanel.add(button);
}
scrollPane = new JScrollPane(innerPanel);
scrollPane.setBounds(10, 10, 1180, 145);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
highlightFrame(frames.get(0));
add(scrollPane);
}
public void nextFrame(){
if (indexOfChosenFrame == frames.size() - 1) {
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame = 0;
highlightFrame(frames.get(0));
}else{
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame++;
highlightFrame(frames.get(indexOfChosenFrame));
}
}
public void previousFrame(){
if (indexOfChosenFrame == 0) {
unhighlightFrame(frames.get(0));
indexOfChosenFrame = frames.size()-1;
highlightFrame(frames.get(indexOfChosenFrame));
}else{
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame--;
highlightFrame(frames.get(indexOfChosenFrame));
}
}
private void highlightFrame(JPanel frame){
Rectangle rect = frame.getBounds();
rect.setBounds(frame.getX()-550, frame.getY(), frame.getWidth()+1050, frame.getHeight());
innerPanel.scrollRectToVisible(rect);
frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}
private void unhighlightFrame(JPanel frame){
frame.setBorder(null);
}
The relevant method here is JComponent#scrollRectToVisible(Rectangle). It has to be called on the component that is in the viewport of the scroll pane. (In your case, this is the panel with the grid layout, which contains the other sub-panels).
The rectangle that is passed to this method can be the bounds of one sub-panel. In this case, the scoll pane will do the "minimum" scrolling that is necessary to make the given rectangle visible. If you want to make sure that the respective sub-panel is in the center, then you can increase the size of this rectangle - that is, you define a rectangle in a way that the desired sub-panel will be in the center.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ScrollToVisible
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int n = 20;
final JPanel panel = new JPanel(new GridLayout(1,0));
final List<JComponent> components = new ArrayList<JComponent>();
for (int i=0; i<n; i++)
{
JComponent component = new JLabel(String.valueOf(i), SwingConstants.CENTER);
component.setPreferredSize(new Dimension(100,100));
component.setBorder(BorderFactory.createLineBorder(Color.BLACK));
components.add(component);
panel.add(component);
}
final JScrollPane scrollPane = new JScrollPane(panel);
final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, n-1, 1));
spinner.addChangeListener(new ChangeListener()
{
JComponent selectedComponent = components.get(0);
#Override
public void stateChanged(ChangeEvent e)
{
selectedComponent.setBorder(BorderFactory.createLineBorder(Color.BLACK));
int index = (Integer)spinner.getValue();
JComponent component = components.get(index);
Rectangle bounds = component.getBounds();
// This would make the component "just" visible:
//panel.scrollRectToVisible(bounds);
// This will center the component:
int cx = bounds.x + bounds.width / 2;
int w = scrollPane.getViewport().getWidth();
Rectangle r = new Rectangle(cx-w/2, bounds.y, w, bounds.height);
panel.scrollRectToVisible(r);
selectedComponent = component;
selectedComponent.setBorder(BorderFactory.createLineBorder(Color.RED));
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.getContentPane().add(spinner, BorderLayout.NORTH);
f.setSize(800, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
EDIT: You should NOT use setLayout(null), and you should not do manual calls to setBounds, and you should rarely use setPreferredSize. And... when you post code that already is so close to a https://stackoverflow.com/help/mcve (or even was created from a runnable example of another post) then you should make it really runnable. It's annoying to re-insert the boilerplate code and waste some time with debugging until you realize that initialize() is not called at all...
However, change the code according to this:
private void highlightFrame(JPanel frame){
Rectangle rect = frame.getBounds();
int c = rect.x + rect.width / 2;
int w = scrollPane.getViewport().getWidth();
int x = c-w/2;
rect.setBounds(x, rect.y, w, rect.height);
innerPanel.scrollRectToVisible(rect);
frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}
private void unhighlightFrame(JPanel frame){
frame.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
The most important thing is to make sure that the size of the components is correct, by setting an empty border with the same size as the "highlighting" border.
Related
I am attempting to rotate a GridLayout filled with text labels to simulate a portrait orientation view due to an OS restriction. The JPanel they are inside of is not square, so when rotating 90 degrees the labels cut off based on dimensions of the JPanel. Is it possible to resize the layout based on the rotation to still fit within the JPanel? Researching into this showed many options for rotations, but only for square JPanels.
To further explain my problem: when I rotate the labels painted inside they stay formatted to the normal oriented x,y, and I want it to format the layout to fit into the 90 degree rotated x,y (so basically y and x are flipped). currently a portion of my grid is cut off after rotating. Also the final display should fit all 13 by 24 letters filled in the current JPnel.
edit: Using vague comments shows I need to paint after rotating, but doing so crops the grid and does not fill back to my preferred size.
JPanel code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Screen extends JPanel {
private JLabel[][] labels = new JLabel[13][24];
private GridLayout layout;
public Screen() {
//setLocation(315,35);
layout = new GridLayout(13, 24);
layout.preferredLayoutSize(this);
//setBounds(315, 65, 243, 350);
setBounds(315, 65, 243, 350);
setLayout(layout);
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 24; j++) {
labels[i][j] = new JLabel();
labels[i][j].setBackground(Color.BLACK);
add(labels[i][j]);
}
}
//testing new letter
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 24; j++) {
labels[i][j].setText("T");
labels[i][j].setForeground(Color.GREEN);
}
}
setBackground(Color.black);
setVisible(true);
repaint();
}
#Override
public void paintComponent(Graphics g) {
//Rotates screen graphics to correct orientation
Graphics2D g2d = (Graphics2D) g;
int w2 = getWidth() / 2;
int h2 = getHeight() / 2;
g2d.rotate(Math.PI / 2, w2, h2);
super.paintComponent(g);
setSize(243,350);
}
}
test code:
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
public class RotateTest {
public static class Frame extends JFrame {
public Frame() {
Screen screen = new Screen();
JLayeredPane pane = new JLayeredPane();
setUndecorated(false);
setSize(800, 480);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane.add(screen, 0, 0);
pane.setVisible(true);
add(pane);
}
}
public static void main(String[] args) {
Frame frame = new Frame();
}
}
The process of rotating a component is more complicated then just painting the rotated image. There are a number of interconnected layers which generate contractual obligations.
For example, the size of the clipping rectangle set to the Graphics context that is passed to your component for painting is determined by the current size of the component, this size is calculated by the layout manager, but may consider the preferred size of the individual component...
That's a lot of re-wiring that needs to be considered...call my lazy, but if I can find a ready made solution, I'd prefer to use it, so based on this example, I can generate the following...
The red LineBorder around the field panel is there to show that the entire component is been rotated, not just it's children. The use of pack also demonstrates that this solution is still honouring it's contractual obligations to the rest of the API
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class RotateExample {
public static void main(String[] args) {
new RotateExample();
}
public RotateExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ExamplePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ExamplePane extends JPanel {
private FieldPane fieldPane;
private DefaultTransformModel transformModel;
private JButton rotate;
private double angle;
public ExamplePane() {
setLayout(new BorderLayout());
rotate = new JButton("Rotate");
rotate.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
transformModel.setRotation(Math.toRadians((angle += 90)));
SwingUtilities.getWindowAncestor(ExamplePane.this).pack();
}
});
fieldPane = new FieldPane();
transformModel = new DefaultTransformModel();
transformModel.setRotation(Math.toRadians(0));
transformModel.setScaleToPreferredSize(true);
JXLayer<JComponent> rotatePane = TransformUtils.createTransformJXLayer(fieldPane, transformModel);
JPanel content = new JPanel(new GridBagLayout());
content.add(rotatePane);
add(rotate, BorderLayout.SOUTH);
add(content);
}
}
public class FieldPane extends JPanel {
public FieldPane() {
setBorder(new LineBorder(Color.RED));
setLayout(new GridBagLayout());
JTextField field = new JTextField(10);
field.setText("Hello world");
add(field);
}
}
}
Caveats
This requires JXLayer (I was using version 3), SwingX (I was using version 1.6.4) and Piet Blok's excellent examples, which no longer seem to be available on the net...
I've put all the source code of JXLayer (version 3) and Piet's examples into a single zip and I would suggest, if you are interested, you grab a copy and store it some where safe.
You will also need JHLabs filters
Updated
And using your Screen panel (without the custom painting)...
I couldn't explain the question in the title any better, so here goes -
I created a tiled background image. I then set the created background image to my JFrame. However, I added my JScrollPane to said background. Depending on the order I place my code in, 1 of two things will happen. When I have my JScrollPane like so -
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
public class TestingApp {
public static JFrame programFrame;
public static JLabel projectBackground;
public static JLabel projectLogo;
public static JPanel allContent;
public static JPanel fourRows;
public static JPanel centerPanel;
public static JScrollPane scrollPane;
// Tiled Background
public static void tiledBackground() {
#SuppressWarnings("serial")
class ImagePanel extends JPanel {
public Image image;
public boolean tile;
ImagePanel(Image image) {
this.image = image;
this.tile = true;
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int iw = image.getWidth(this);
int ih = image.getHeight(this);
if (iw > 0 && ih > 0) {
for (int x = 0; x < getWidth(); x += iw) {
for (int y = 0; y < getHeight(); y += ih) {
g.drawImage(image, x, y, iw, ih, this);
}
}
}
}
}
}
// Making the parts for the GUI
public static void createGUI() {
java.net.URL img1 = null;
try {
img1 = new URL("https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcSE6uHn-B_qKtLZOKjQNVeIxhOaxbmfio45VMUq-mVgGKvgmeghKw");
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
Image image = null;
try {
image = ImageIO.read(img1);
} catch (IOException e) {
e.printStackTrace();
}
// programFrame Title and Layout
programFrame = new JFrame("Organizer");
programFrame.setLayout(new BorderLayout());
Icon backgroundIcon = new ImageIcon(img1);
projectBackground = new JLabel(backgroundIcon);
// Logo JLabel
java.net.URL img2 = null;
try {
img2 = new URL("https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcTvXxBQRsJ5NgSb8VOSNU_Qfom6HRV_crcazhD6bSZUh_ux3VHbgQ");
} catch (MalformedURLException e) {
e.printStackTrace();
}
Icon logoIcon = new ImageIcon(img2);
projectLogo = new JLabel(logoIcon);
projectLogo.setBorder(new EmptyBorder(10, 10, 10, 10));
// New JPanel for GridLayouts to hold each JPanel with GridLayouts
fourRows = new JPanel(new GridLayout(1, 4));
fourRows.setLayout(new GridLayout());
fourRows.setOpaque(false);
fourRows.add(new JButton("Button"));
fourRows.add(new JButton("Button"));
fourRows.add(new JButton("Button"));
fourRows.add(new JButton("Button"));
// Makes the Initial BorderLayout (Using allContent JPanel)
allContent = new JPanel();
allContent.setLayout(new BorderLayout());
allContent.add(projectLogo, BorderLayout.NORTH);
allContent.setVisible(true);
allContent.setOpaque(false);
allContent.add(fourRows, BorderLayout.CENTER);
// Add ScrollPane
scrollPane = new JScrollPane(allContent);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.setOpaque(false);
scrollPane.getViewport().setOpaque(false);
// JFrame programFrame Constructors
programFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
programFrame.setLayout(new BorderLayout());
programFrame.setContentPane(new ImagePanel(image));
programFrame.add(scrollPane);
programFrame.pack();
programFrame.setVisible(true);
programFrame.setResizable(true);
programFrame.setSize(1280, 720);
programFrame.setLocationRelativeTo(null);
} // public static void createGUI() Closing
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
} // public void run() Closing
});
}
}
It doesn't scroll on the entire JFrame, just the contentStuff, and even then, it stretches beyond to the bottom of the content to where the scroll bar isn't there, and you can't scroll.
However, when I rearrange that code, and I put my
programFrame.setContentPane(new ImagePanel(image));
BEFORE
programFrame.add(scrollPane);
I just get the repeated background image, and no content.
EDIT - Added an SSCCE
EDIT2 - Here's a solution I was trying. I tried creating an empty panel to add in the content that should update based on JFrame size. Needless to say, it didn't work. Small snippet of the edited code, nothing else was changed -
// Makes the Initial BorderLayout (Using allContent JPanel)
allContent = new JPanel();
allContent.setLayout(new BorderLayout());
allContent.add(warlordsLogo, BorderLayout.NORTH);
allContent.setVisible(true);
allContent.setOpaque(false);
allContent.add(fourRows, BorderLayout.CENTER);
int widthForCenterPanel = programFrame.getWidth();
int heightForCenterPanel = programFrame.getHeight();
// Makes a Panel to add the ScrollPane to to center is properly
centerPanel = new JPanel();
centerPanel.setOpaque(false);
centerPanel.setBounds(0, 0, widthForCenterPanel, heightForCenterPanel);
centerPanel.add(allContent);
// Add ScrollPane
scrollPane = new JScrollPane(centerPanel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.setOpaque(false);
scrollPane.getViewport().setOpaque(false);
scrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
EDIT 3 - Fixed my SSCCE. If you notice when you try it, it's dependent on how large the JPanel is, not the JFrame.
I am trying to create a GUI that will take in the number of circles to draw, and draw them in drawPanel with random locations/sizes. On my actionListener, when I try to draw the circle, it gives me red lines on my drawOval
1st class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
*
* #author Chris
*
*/
public class CirclesPanel extends JPanel{
private JButton draw, clear;
private JTextArea textArea;
private JPanel panel, drawPanel, buttonPanel;
private int count;
/**constructor
* builds the frame
*/
public CirclesPanel(){
//creates buttons and textArea
draw = new JButton("Draw");
clear = new JButton("Clear");
textArea = new JTextArea(1,10);
textArea.setBorder(BorderFactory.createTitledBorder("Circles"));
//creats panel
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setPreferredSize(new Dimension(620, 425));
//creates subpanel drawPanel
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(450,400));
drawPanel.setBackground(Color.BLACK);
//creates subpanel buttonPanel
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(3,1));
//adds all the content to the frame
add(panel);
add(buttonPanel, BorderLayout.WEST);
add(drawPanel, BorderLayout.EAST);
buttonPanel.add(textArea);
buttonPanel.add(draw);
buttonPanel.add(clear);
//reads if the draw button is clicked
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count =Integer.parseInt(textArea.getText());//takes the count in
repaint();//repaints the picture to add the circles
}
});
//reads if the clear button is clicked
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count=0;//sets the count to 0 so nothing is painted
repaint();//repaints the window
}
});
}
/**Paint component
* draws the random circles
*/
public void paintComponent(Graphics g) {
Random generator = new Random();
int x, y, diameter;
for(int i = 0; i < count; i++){ //loop that takes the count and does this "x" times
g.setColor(Color.BLUE);//sets color to blue
x = generator.nextInt(90);//random location for x
y = generator.nextInt(90);//random location for y
diameter = generator.nextInt(30);//random size
g.fillOval(x, y, diameter, diameter);//draws the circle
}
}
}
2nd class
import javax.swing.JFrame;
public class Circles {
public static void main(String[]args){
JFrame frame = new JFrame("Cicles HW9");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new CirclesPanel());
frame.pack();
frame.setVisible(true);
}
}
So in your, I did little addition, first of all, I made the whole program in one class(CIRLCES PANEL), IF You want to use the second class, you can use it....
Problem is coming, your program is not the reading the ActionPerformed method for the drawing, means it is not located with the button, now I directly added it with your button(DRAW), now whenever you click on the button, it automatically reads the your textArea value, and draw your circles. I made your text area FINAL, So you can use it anywhere......
Now things that you need to do----
- this program is drawing circle on the whole frame, means not on your drawing Panel, you need to set the values, so it will draw on your draw panel area
- Also you need to add color for your oval, because it will either draw black color circle, which you will not able to see.....
and also one thing I forget to mentioned you, is that your, you also need to add code for your clear method...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class CirclesPanel extends JPanel{
private JButton draw, clear;
private JTextArea textArea;
private JPanel panel, drawPanel, buttonPanel;
private int count;
public CirclesPanel(){
JButton draw = new JButton("Draw");
JButton clear = new JButton("Clear");
final JTextArea textArea = new JTextArea(1,10);
textArea.setBorder(BorderFactory.createTitledBorder("Circles"));
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setPreferredSize(new Dimension(620, 425));
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(450,400));
drawPanel.setBackground(Color.BLACK);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(3,1));
add(panel);
add(buttonPanel, BorderLayout.WEST);
add(drawPanel, BorderLayout.EAST);
buttonPanel.add(textArea);
buttonPanel.add(draw);
buttonPanel.add(clear);
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count =Integer.parseInt(textArea.getText());
repaint();
}
});
}
public void paintComponent(Graphics g) {
Random generator = new Random();
int x, y, diameter;
for(int i = 0; i < count; i++){
x = generator.nextInt(90);
y = generator.nextInt(90);
diameter = generator.nextInt(30);
g.drawOval(x, y, diameter, diameter);
}
}
}
What you want to do is drawing some random circles on the drawPanel when button clicked. I write you a simplified version to show how things work.
I only keep the drawButton and paintPanel to keep things simple.
public class PaintFrame extends JFrame {
private JPanel content = new JPanel();
private JButton drawButton = new JButton("Draw");
private PaintPanel paintPanel = new PaintPanel();
public PaintFrame() {
getContentPane().add(content);
content.setLayout(new BorderLayout());
drawButton.setSize(100, 500);
drawButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// drawButton is fired, repaint the paintPanel
paintPanel.repaint();
}
});
content.add(drawButton, BorderLayout.WEST);
content.add(paintPanel, BorderLayout.CENTER);
}
}
You need a new class extending the JPanel and override the paintComponent method to do the paint job for you. This makes sure you are drawing on the panel.
class PaintPanel extends JPanel {
public PaintPanel() {
setSize(500, 500);
setBackground(Color.BLACK);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Random random = new Random();
g.setColor(Color.WHITE);
// draw 5 random circles
int count = 5;
for (int i = 0; i < count; i++) {
g.drawOval(random.nextInt(250), random.nextInt(250),
random.nextInt(250), random.nextInt(250));
}
}
}
Main class
public class DrawMain {
public static void main(String[] args) {
JFrame frame = new PaintFrame();
frame.setDefaultCloseOperation(PaintFrame.EXIT_ON_CLOSE);
frame.setSize(600, 500);
frame.setVisible(true);
}
}
I have a small problem when using JScrollPane in my Java application.
I have a JScrollPane containing a JPanel.
This JPanel is dynamically updated with buttons (vertically ordered) that can be of any width.
The JPanel automatically adjusts its width to the largest JButton component inside.
Now when the vertical scrollbar appears, it takes away some space on the right side of my JPanel, which causes the largest buttons not to appear completely. I don't want to use a horizontal scrollbar in addition to display the whole button.
Is there a way to resize my JPanel when a scrollbar appears, so it appears nicely next to my buttons? Or is there any other way to have the scrollbar appear next to my JPanel?
Thanks in advance!
EDIT: Here is a demo of my problem. When you resize the window to a smaller height, a little part of the buttons on the right side gets covered.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.GridLayout;
/**
* #author Dylan Kiss
*/
public class Demo {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame myFrame = new JFrame("Demo");
JPanel sideBar = new JPanel();
JPanel centerPanel = new JPanel();
centerPanel.add(new JLabel("This is the center panel."));
JPanel buttonContainer = new JPanel();
JButton myButton = null;
for (int i = 0; i < 20; i++) {
buttonContainer.setLayout(new GridLayout(20, 1, 0, 0));
myButton = new JButton("This is my button nr. " + i);
buttonContainer.add(myButton);
}
sideBar.setLayout(new BorderLayout(0, 0));
JScrollPane scrollPane = new JScrollPane(buttonContainer);
sideBar.add(scrollPane);
myFrame.getContentPane().add(sideBar, BorderLayout.WEST);
myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER);
myFrame.setLocationByPlatform(true);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.pack();
myFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
Here is a simple, not pretty, solution:
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
EDIT:
I thought that might not do the job in your case. Here is a better solution although it has quite a lot of boilerplate:
private class ButtonContainerHost extends JPanel implements Scrollable {
private static final long serialVersionUID = 1L;
private final JPanel buttonContainer;
public ButtonContainerHost(JPanel buttonContainer) {
super(new BorderLayout());
this.buttonContainer = buttonContainer;
add(buttonContainer);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension preferredSize = buttonContainer.getPreferredSize();
if (getParent() instanceof JViewport) {
preferredSize.width += ((JScrollPane) getParent().getParent()).getVerticalScrollBar()
.getPreferredSize().width;
}
return preferredSize;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width * 9 / 10, 1)
: Math.max(visibleRect.height * 9 / 10, 1);
}
#Override
public boolean getScrollableTracksViewportHeight() {
if (getParent() instanceof JViewport) {
JViewport viewport = (JViewport) getParent();
return getPreferredSize().height < viewport.getHeight();
}
return false;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width / 10, 1)
: Math.max(visibleRect.height / 10, 1);
}
}
It implements Scrollable to get full control of scrolling, does a fancy trick with tracking the viewport height to ensure the buttons expand when the space is available and adds on the width of the vertical scroll bar to the preferred width at all times. It could expand when the vertical scroll bar is visible but that looks bad anyway. Use it like this:
scrollPane = new JScrollPane(new ButtonContainerHost(buttonContainer));
It looks to me like this workaround is required because of a possible bug in javax.swing.ScrollPaneLayout:
if (canScroll && (viewSize.height > extentSize.height)) {
prefWidth += vsb.getPreferredSize().width;
}
Here extentSize is set to the preferred size of the viewport and viewSize is set to viewport.getViewSize(). This does not seem correct, AFAIK the size of the view inside the viewport should always equal the preferred size. It seems to me that the view size should be compared to the actual size of the viewport rather than its preferred size.
A simple workaround to meet your demands regarding
Is there a way to resize my JPanel when a scrollbar appears, so it
appears nicely next to my buttons?
is the use of EmptyBorder, this will let you achieve what you feel like, should happen, as shown in the image below :
I just added this line written below after this line JPanel buttonContainer = new JPanel();
ADDED LINE
buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
Here is your code with that added line :
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.GridLayout;
/**
* #author Dylan Kiss
*/
public class Demo
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
try
{
JFrame myFrame = new JFrame("Demo");
JPanel sideBar = new JPanel();
JPanel centerPanel = new JPanel();
centerPanel.add(new JLabel("This is the center panel."));
JPanel buttonContainer = new JPanel();
buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JButton myButton = null;
for (int i = 0; i < 20; i++)
{
buttonContainer.setLayout(new GridLayout(20, 1, 0, 0));
myButton = new JButton("This is my button nr. " + i);
buttonContainer.add(myButton);
}
sideBar.setLayout(new BorderLayout(0, 0));
JScrollPane scrollPane = new JScrollPane(buttonContainer);
sideBar.add(scrollPane);
myFrame.getContentPane().add(sideBar, BorderLayout.WEST);
myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER);
myFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
myFrame.pack();
myFrame.setLocationByPlatform(true);
myFrame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
}
You could resize the JPanel by calling setPreferredSize when the JPanel needs to be resized.
buttonContainer.setPreferredSize(Dimension d);
Users of my software need to be able to click on different tabs to see different types of data representations. However, the code I am including below does not show the requested data panel when the user clicks on a tab.
You can re-create the problem easily by running the code below, and then following these steps in the GUI which the code will produce:
1.) Select "New" from the file menu
2.) Click on "AnotherTab" in the internal frame which will appear
Depending on which line of code you comment out below, the tab will either just show a blank panel or will show a tiny red square in the middle of the top of the panel.
The lines of code that you can toggle/comment-out to recreate this problem are:
GraphPanel myGP = new GraphPanel();
//GraphPanel myGP = new GraphPanel(width,height);
These lines of code are in GraphGUI.java below.
Can anyone show me how to fix the code below so that myGP gets displayed at the full size of the panel containing it?
Here are the three java files required to recreate this problem:
ParentFrame.java
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
public class ParentFrame extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
JLayeredPane desktop;
JInternalFrame internalFrame;
public ParentFrame() {
super("Parent Frame");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(800, 400));
Panel p = new Panel();
this.add(p, BorderLayout.SOUTH);
desktop = new JDesktopPane();
setJMenuBar(createMenuBar());
this.add(desktop, BorderLayout.CENTER);
this.pack();
this.setSize(new Dimension(800, 600));
this.setLocationRelativeTo(null);
}
protected JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
//Set up the File menu.
JMenu FileMenu = new JMenu("File");
FileMenu.setMnemonic(KeyEvent.VK_F);
menuBar.add(FileMenu);
//Set up the first menu item.
JMenuItem menuItem = new JMenuItem("New");
menuItem.setMnemonic(KeyEvent.VK_N);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.ALT_MASK));
menuItem.setActionCommand("new");
menuItem.addActionListener(new OpenListener());
FileMenu.add(menuItem);
//Set up the second menu item.
menuItem = new JMenuItem("Quit");
menuItem.setMnemonic(KeyEvent.VK_Q);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
menuItem.setActionCommand("quit");
menuItem.addActionListener(this);
FileMenu.add(menuItem);
return menuBar;
}
class OpenListener implements ActionListener {
private static final int DELTA = 40;
private int offset = DELTA;
public void actionPerformed(ActionEvent e) {
// create internal frame
int ifWidth = 600;
int ifHeight = 300;
internalFrame = new JInternalFrame("Internal Frame", true, true, true, true);
internalFrame.setLocation(offset, offset);
offset += DELTA;
// create jtabbed pane
JTabbedPane jtp = createTabbedPane();
internalFrame.add(jtp);
desktop.add(internalFrame);
internalFrame.pack();
internalFrame.setSize(new Dimension(ifWidth,ifHeight));
internalFrame.setVisible(true);
}
}
private JTabbedPane createTabbedPane() {
JTabbedPane jtp = new JTabbedPane();
jtp.setMinimumSize(new Dimension(600,300));
createTab(jtp, "One Tab");
createTab(jtp, "AnotherTab");
createTab(jtp, "Tab #3");
return jtp;
}
private void createTab(JTabbedPane jtp, String s) {
if(s=="AnotherTab"){
jtp.getHeight();
jtp.getWidth();
GraphGUI myGraphGUI = new GraphGUI(jtp.getHeight(),jtp.getWidth());
jtp.add(s, myGraphGUI);
}
else{jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));}
}
public static void main(String args[]) {
ParentFrame myParentFrame = new ParentFrame();
myParentFrame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {if ("quit".equals(e.getActionCommand())){System.exit(0);}}
}
GraphGUI.java: This is the one where you can toggle comments to re-create the problem.
import javax.swing.*;
class GraphGUI extends JPanel{
GraphGUI(int height,int width) {
//REPRODUCE ERROR BY COMMENTING OUT EITHER ONE OF NEXT TWO LINES:
GraphPanel myGP = new GraphPanel();
// GraphPanel myGP = new GraphPanel(width,height);
this.add(myGP);
this.setVisible(true);// Display the panel.
}
}
GraphPanel.java:
import java.awt.*;
import javax.swing.*;
class GraphPanel extends JPanel {
Insets ins; // holds the panel's insets
double[] plotData;
double xScale;
GraphPanel(int w, int h) {
setOpaque(true);// Ensure that panel is opaque.
setPreferredSize(new Dimension(w, h));// Set preferred dimension as specfied.
setMinimumSize(new Dimension(w, h));// Set preferred dimension as specfied.
setMaximumSize(new Dimension(w, h));// Set preferred dimension as specfied.
}
GraphPanel() {
setOpaque(true);// Ensure that panel is opaque.
}
protected void paintComponent(Graphics g){// Override paintComponent() method.
super.paintComponent(g);// Always call superclass method first.
int height = getHeight();// Get height of component.
int width = getWidth();// Get width of component.
System.out.println("height, width are: "+height+" , "+width);
ins = getInsets();// Get the insets.
// Get dimensions of text
Graphics2D g2d = (Graphics2D)g;
FontMetrics fontMetrics = g2d.getFontMetrics();
String xString = ("x-axis label");
int xStrWidth = fontMetrics.stringWidth(xString);
int xStrHeight = fontMetrics.getHeight();
String yString = "y-axis label";
int yStrWidth = fontMetrics.stringWidth(yString);
int yStrHeight = fontMetrics.getHeight();
String titleString ="Title of Graphic";
int titleStrWidth = fontMetrics.stringWidth(titleString);
int titleStrHeight = fontMetrics.getHeight();
//get margins
int leftMargin = ins.left;
//set parameters for inner rectangle
int hPad=10;
int vPad = 6;
int numYticks = 10;
int testLeftStartPlotWindow = ins.left+5+(3*yStrHeight);
int testInnerWidth = width-testLeftStartPlotWindow-ins.right-hPad;
int remainder = testInnerWidth%numYticks;
int leftStartPlotWindow = testLeftStartPlotWindow-remainder;
System.out.println("remainder is: "+remainder);
int blueWidth = testInnerWidth-remainder;
int blueTop = ins.bottom+(vPad/2)+titleStrHeight;
int bottomPad = (3*xStrHeight)-vPad;
int blueHeight = height-bottomPad-blueTop;
g.setColor(Color.red);
int redWidth = width-leftMargin-1;
//plot outer rectangle
g.drawRect(leftMargin, ins.bottom, redWidth, height-ins.bottom-1);
System.out.println("blueWidth is: "+blueWidth);
// fill inner rectangle
g.setColor(Color.white);
g.fillRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
//write top label
g.setColor(Color.black);
g.drawString(titleString, (width/2)-(titleStrWidth/2), titleStrHeight);
//write x-axis label
g.setColor(Color.red);
g.drawString(xString, (width/2)-(xStrWidth/2), height-ins.bottom-vPad);
g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise
//write y-axis label
g.drawString(yString, -(height/2)-(yStrWidth/2), yStrHeight);
g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise
// plot inner rectangle
g.setColor(Color.blue);
g.drawRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
}
}
class GraphGUI extends JPanel {
.
.
GraphGUI(int height,int width) {
// components in a GridLayout are stretched to fit space available
setLayout(new GridLayout());