I'm developping a sprite editor. I have a class that extends JPanel, in that class, I use a JLayeredPane as a container. On the bottom layer, there is a JLabel with an ImageIcon and on the top Layer there is a JPanel where I have drawn a grid. When the code is ran, I make some basic math to set the JLayeredPane's prefered size to be equal to about 85% of the height of the screen resolution.
My problem is that when the user wants a new canvas to draw on it, I ask with a JOptionPane the user the size of the canvas he wants. Then I call my class constructor to create a new canvas with the specified size. After that I apply the methods revalidate() and repaint() on the canvas. Unfortunately, it doesn't work. In addition, when I try to get the width and the height of my canvas, it gives 0 for both. However, when I set the size by myself directly into the code, it works well. Therefore, I would like to know how can I update the size of a JLayeredPane ?
On start with a size of 85% of the screen resolution
When I set the size to 640x640 directly inside my code
When I answer the JOptionPane with a size of 640x640, the grid has been adapted to the asking size. However, it's not the case for the JLayeredPane
Without more context, it's impossible to know what's going wrong with your code.
However, personally, I'd stop trying to reinvent the wheel and make use of the available layout management API to provide you with the support you need.
There's a number of ways you could approach the problem. Personally I'd wrap the rendering of the grid and the image up into a single component, but that's me. In that case, you wouldn't need the JLayeredPane.
Let's, for the moment, assume that the JLayeredPane is non-negotiable. I'd then apply a layout manager to the JLayeredPane, this makes interaction with the two other components (image and grid) simpler, as the layout manager takes over control. Because you want them overlaid onto of each other, I'd be tempted to use a GridBagLayout.
The problem here then becomes keeping the two component's sizes in sync with each other. To this end, I'd use setPreferredSize to change the size dynamically.
"insert internal screaming" - Any time I see setPreferredSize it sets of alarm bells. In this case, again, I'd fall back to using a single component and provide a "sizing" mechanism which worked with getPreferredSize, but at the end of day, this is moving us in the same direction.
For laying out the JLayeredPane at 85% height of the parent container, again, GridBagLayout would be my preferred choice.
The following example lays the JLayeredPane in such away as it will want to fill the available space of the parent container (up to 85% of the available height). It then allows the image and grid components to automatically position themselves within side this area.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private JLayeredPane lp;
private ImagePane imagePane;
private GridPane gridPane;
public TestPane() throws IOException {
setLayout(new GridBagLayout());
lp = new JLayeredPane();
lp.setLayout(new GridBagLayout());
imagePane = new ImagePane();
gridPane = new GridPane();
gridPane.setForeground(new Color(255, 255, 255, 64));
imagePane.setImg(ImageIO.read(...)));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
lp.add(imagePane, gbc);
lp.add(gridPane, gbc);
lp.setLayer(imagePane, 0);
lp.setLayer(gridPane, 10);
lp.setBackground(Color.RED);
lp.setBorder(new LineBorder(Color.RED));
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 0.85;
gbc.fill = gbc.BOTH;
add(lp, gbc);
applyDesiredSize(200, 200);
JButton btn = new JButton("Change");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weighty = 0.25;
add(btn, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JTextField widthTF = new JTextField(4);
JTextField heightTF = new JTextField(4);
JPanel panel = new JPanel();
panel.add(new JLabel("Size: "));
panel.add(widthTF);
panel.add(new JLabel("x"));
panel.add(heightTF);
JOptionPane.showMessageDialog(TestPane.this, panel, "Change Size", JOptionPane.PLAIN_MESSAGE);
try {
int width = Integer.parseInt(widthTF.getText());
int height = Integer.parseInt(heightTF.getText());
applyDesiredSize(width, height);
} catch (NumberFormatException numberFormatException) {
JOptionPane.showMessageDialog(TestPane.this, "Invalid dimensions", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
protected void applyDesiredSize(int width, int height) {
Dimension size = new Dimension(width, height);
// lp.setPreferredSize(size);
imagePane.setPreferredSize(size);
gridPane.setPreferredSize(size);
// Stop GridBagLayout from shrinking the components
imagePane.setMinimumSize(size);
gridPane.setMinimumSize(size);
lp.revalidate();
lp.repaint();
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public BufferedImage getImg() {
return img;
}
public void setImg(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
}
public class GridPane extends JPanel {
private int gridSize = 10;
public GridPane() {
setOpaque(false);
}
public int getGridSize() {
return gridSize;
}
public void setGridSize(int gridSize) {
this.gridSize = gridSize;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getForeground());
for (int x = 0; x < getWidth(); x += gridSize) {
g2d.drawLine(x, 0, x, getHeight());
}
for (int y = 0; y < getWidth(); y += gridSize) {
g2d.drawLine(0, y, getWidth(), y);
}
g2d.dispose();
}
}
}
So, before you shoot me down because it "doesn't fit with what you've done", understand that:
I've no context to go on as to what you've actually done, other then an overview of your problem description
I've provided a runnable example which demonstrates the basic concepts you "might" be able to use to solve your issue, which is more then you've done for us
This is not the only way you might achieve this, and by no means is it my preferred solution
Related
I am trying to make a simple Java program with GUI using Java Swing.
I have painting panel (gPanel) in the center of the screen, panel with buttons (buttonSet) in the west and panel with labels (labelPanel) in the east. To paint over gPanel I use paintComponent method and since I have two buttons, which are supposed to draw different things (and change label on the right of the screen), I decided to put switch case in paintComponent method for it to choose the correct actual painting method.
When I run the program everything looks fine - program uses the first method to paint and there is a sampletext.png image shown in the middle of the screen with yellow background, as it should be. Button number 1 also uses this method to draw over gPanel, so pressing it draws the same thing.
Now Button number 2 uses the second painting method and this is where things go wrong. It draws sampleimage.png over the gPanel, but also parts of left and right panels (i.e. buttons from left buttonSet panel and orange colour that is background colour of side panels) are drawn, though it shouldn't happen. Also the whole gPanel becomes gray (I think it happens because of label on the right that becomes very long after pressing Button number 2, because when the label was shorter gPanel didn't turn gray and left the previously drawn things instead).
Pressing Button number 1 paints things from first method properly, so pressing it after pressing Button number 2 "reverts" the changes.
What do I have to do to make my second painting method work properly?
Also why adding border to buttonSet and labelPanel works but adding it to gPanel doesn't?
package com.inferjus.drawingproject;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.border.*;
/**
*
* #author inferjus
*/
public class DrawingProject
{
private JFrame frame;
private graphicPanel gPanel;
private JPanel buttonSet;
private JPanel labelPanel;
private JLabel label;
private int painter=0;
public static void main(String[] args)
{
DrawingProject program=new DrawingProject();
program.prepareGUI();
}
public int getPainter()
{
return painter;
}
public void setPainter(int x)
{
painter=x;
}
public void prepareGUI()
{
//setting JFrame and icon
frame=new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
try { frame.setIconImage(ImageIO.read(getClass().getResource("/resources/sampleicon.png")));}
catch (IOException e) { e.printStackTrace(); }
//border for components
Border bigBlackBorder=new LineBorder(Color.black, 3);
//setting JPanel (graphicPanel) for drawing images
gPanel=new graphicPanel();
gPanel.setBorder(bigBlackBorder); // <--- why it does not work?
//setting JPanel for buttons on the left of the screen
buttonSet=new JPanel();
buttonSet.setLayout(new BoxLayout(buttonSet, BoxLayout.Y_AXIS));
buttonSet.setBorder(bigBlackBorder);
//setting JButtons
JButton buttonOne=new JButton("Button number 1");
buttonOne.addActionListener(new buttonOneListener());
buttonSet.add(buttonOne);
buttonSet.setBackground(Color.orange);
JButton buttonTwo=new JButton("Button number 2");
buttonTwo.addActionListener(new buttonTwoListener());
buttonSet.add(buttonTwo);
//setting JLabels on the right of the screen
label=new JLabel("Default label");
label.setFont(new Font("Consolas", Font.PLAIN, 20));
labelPanel=new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
labelPanel.setBackground(Color.orange);
labelPanel.setBorder(bigBlackBorder);
JLabel popeLabelTitle=new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font("Consolas", Font.BOLD, 24));
//adding JLabels to labelPanel
labelPanel.add(BorderLayout.NORTH, popeLabelTitle);
labelPanel.add(BorderLayout.CENTER, label);
//adding components to JFrame
frame.getContentPane().add(BorderLayout.CENTER, gPanel);
frame.getContentPane().add(BorderLayout.EAST, labelPanel);
frame.getContentPane().add(BorderLayout.WEST, buttonSet);
frame.setVisible(true);
}
class graphicPanel extends JPanel
{
private BufferedImage sampletext=null;
private BufferedImage sampleimage=null;
#Override
public void paintComponent(Graphics g)
{
//for Button One paint sampletext.png, for Button Two paint sampleimage.png
switch (painter)
{
case 0:
paintSampletext(g);
break;
case 1:
paintSampleimage(g);
break;
}
}
//paint yellow background and put sampletext.png in the middle
private void paintSampletext(Graphics g)
{
if (sampletext==null)
{
gPanel.setSampletextPNG();
}
g.setColor(Color.yellow);
g.fillRect(0,0, gPanel.getWidth(), gPanel.getHeight());
g.drawImage(sampletext, gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, this);
g.setColor(Color.black);
g.drawRect(gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, sampletext.getWidth(), sampletext.getHeight());
g.dispose();
}
//paint sampleimage.png over what is already displayed
private void paintSampleimage(Graphics g)
{
if (sampleimage==null)
{
gPanel.setSampleimagePNG();
}
int x=(int)((Math.random()*gPanel.getWidth())-sampleimage.getWidth());
int y=(int)((Math.random()*gPanel.getHeight())-sampleimage.getHeight());
g.drawImage(sampleimage, x, y, gPanel);
g.dispose();
}
public void setSampletextPNG()
{
try { sampletext=ImageIO.read(getClass().getResource("/resources/sampletext.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
public void setSampleimagePNG()
{
try { sampleimage=ImageIO.read(getClass().getResource("/resources/sampleimage.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
}
class buttonOneListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 1: change of label.");
setPainter(0);
gPanel.repaint();
}
}
class buttonTwoListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 2: change of label + drawing images over gPanel.");
setPainter(1);
gPanel.repaint();
}
}
}
Tree of my project:
DrawingProject
-JRE System Library
-src
--com.inferjus.drawingproject
---DrawingProject.java
--resources
---sampleicon.png
---sampleimage.png
---sampletext.png
what shows after running the program by default or after pressing Button One
what shows after pressing Button Two one time
what shows after pressing Button Two a few times
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
I went ahead and created the following GUI. I created two BufferedImages for the text image and the plain image so I wouldn't have to read any external files.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
Model
I created a model class to hold the button flag and the two BufferedImages. This is the class where you would read the resources.
You can add the JFrame icon back to this class.
Model classes are plain Java getter/setter classes.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
Class names are written in camel case and start with an upper case character. Method names are written in camel case and start with a lower case character. Field names follow the same rules as method names.
I separated the creation of the JFrame from the creation of the JPanels. This helps me to separate my concerns and makes it much easier to visually verify whether or not the code is correct. Aim to write short methods that do one thing and do it well.
You have to manually draw a border on a graphic JPanel. I added the code to your paintComponent method to paint a partial border.
Your paintComponent method should paint. Period. Nothing else. It must also start with a call to the super.paintComponent method to maintain the Swing paint chain.
I changed your JLabel in the right JPanel to a JTextArea. A JTextArea allows for longer messages to word wrap on multiple lines and not make your JFrame change size.
Controller
Your JButton controller classes were fine, except for the class names.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code in one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint border
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
switch (model.getPainter()) {
case 0:
paintSampleText(g);
break;
case 1:
paintSampleImage(g);
break;
}
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void paintSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) ((Math.random() * getWidth()) - image.getWidth());
int y = (int) ((Math.random() * getHeight()) - image.getHeight());
g.drawImage(image, x, y, this);
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
}
}
Update
In order to paint multiple images, you have to save the origin of the images in a List. I've modified the application model to hold a List of origin Point instances. I also corrected the code to create a random point.
Here's the GUI with multiple images.
Here's the modified code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintMyBorder(g);
if (model.getPainter() == 1) {
createSampleImage(g);
}
paintSampleText(g);
BufferedImage image = model.getSampleImage();
List<Point> origin = model.getImageOrigin();
for (Point point : origin) {
g.drawImage(image, point.x, point.y, this);
}
}
private void paintMyBorder(Graphics g) {
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void createSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) (Math.random() * (getWidth() - image.getWidth()));
int y = (int) (Math.random() * (getHeight() - image.getHeight()));
model.addNewImageOrigin(new Point(x, y));
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
private final List<Point> imageOrigin;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
this.imageOrigin = new ArrayList<>();
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public void addNewImageOrigin(Point point) {
this.imageOrigin.add(point);
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
public List<Point> getImageOrigin() {
return imageOrigin;
}
}
}
I'm trying to create a binary clock in Java using swing.
I encountered a problem on very beginning, where last element of label array is always appearing on the 0 x axis position and in the middle of y axis.
I don't want to use any layout menager since i want few circle images on my Panel.
Here's my code so far:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame extends JFrame {
public MainFrame()
{
setLayout(null);
JFrame frame = new JFrame("Binary Clock");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(600,400);
frame.setResizable(false);
JLabel label[] = new JLabel[5];
for(int i = 0; i < 5; i++)
{
label[i] = new JLabel();
label[i].setLocation(i * 10, i * 10);
label[i].setSize(30, 10);
label[i].setText("TEST");
frame.add(label[i]);
}
}
public static void main(String[] args)
{
new MainFrame();
}
}
i found similar question here:
setLocation of Label
but there is no answer how to do it without layout manager.
I Tried resizing and stuff, but only setText() can do any difference, nothing besides that works.
Thank you!
Pshemo's answer provides the answer as to why you're getting the odd behavior with the last label added, but I've marked up the code with a couple enhancements as well.
Remove the extends JFrame, this is rarely a good idea and you already saw why using an instance of a JFrame within a class that is a JFrame can cause...
Set the layout of the frame instance to null (rather than that of this) that you have originally. Note: Use of a layout manager is the preferred method to creating Swing UI's, and is generally discouraged to set this to null
You don't need the array of labels (unless you plan on using them later), you can just use the loop to add what you want.
Make the frame visible last
Then you have:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame {
public MainFrame()
{
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,400);
frame.setResizable(false);
for(int i = 0; i < 5; i++)
{
JLabel label = new JLabel();
label.setLocation(i * 10, i * 10);
label.setSize(30, 10);
label.setText("TEST");
frame.add(label);
}
frame.setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
I am not Swing specialist so I don't know what consequences will give you null layout manager but from what I remember it is nothing good so consider using proper layout manager instead of null.
Anyway it seems that your problem is caused by fact that you set your null layout in currently initialized by constructor this instance, rather than instance from form regerence.
So instead of
setLayout(null);// represents this.setLayout(..), not frame.setLayout(..)
JFrame frame = new JFrame("Binary Clock");
use
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
I am also not sure why your class extends JFrame. Your code doesn't actually look like it needs any of inherited features from JFrame.
I don't want to use any layout menager since i want few circle images on my Panel.
Swing was designed at the core to use layout managers, it how it's update process works and how it deals with the difference in rendering pipelines across multiple platforms. Sure, you can try and do without it, but in the end, you'd simply reinvent the wheel as you try and compensate for all the little niggly issues that are created while trying to do without it.
The following simply uses a GridBagLayout, which provides me with the ability to supply gridx/y positions to the components (and other nice things)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BinaryClock {
public static void main(String[] args) {
new BinaryClock();
}
public BinaryClock() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// Hour column
gbc.gridx = 0;
addColumn(2, gbc);
gbc.gridx = 1;
addColumn(4, gbc);
// Minute column
gbc.gridx = 3;
addColumn(3, gbc);
gbc.gridx = 4;
addColumn(4, gbc);
// Minute column
gbc.gridx = 6;
addColumn(3, gbc);
gbc.gridx = 7;
addColumn(4, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridheight = GridBagConstraints.REMAINDER;
add(new JSeparator(JSeparator.VERTICAL), gbc);
gbc.gridx = 5;
add(new JSeparator(JSeparator.VERTICAL), gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void addColumn(int count, GridBagConstraints gbc) {
for (int index = 0; index < count; index++) {
gbc.gridy = 3 - index;
add(new Dot(), gbc);
}
}
}
public class Dot extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setColor(Color.DARK_GRAY);
g2d.fillOval(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
}
This now frees you up to worry about how to make the clock update without need to continuously try and fix issues in the layout
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)...
This question already has answers here:
How to add JTable in JPanel with null layout?
(11 answers)
Closed 8 years ago.
I know this is horrible coding but I desperately need to fix this problem. I've tried multiple ways of trying to position the button but the button still stays in the top center with all the other buttons lined up after it.
import javax.imageio.ImageIO;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.io.IOException;
import java.net.URL;
public class Template extends JFrame {
/**
* #param args
*/
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
JFrame frame = new JFrame("The Impossible Quiz");//Construct JFrame
frame.setLayout(null);//manual setting for button placement
frame.setContentPane(new JPanel() {//sets panel as background
BufferedImage image = ImageIO.read(new URL("https://pbs.twimg.com/media/BoyFVfXIUAA0Tik.png"));//uses url image
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, 1360, 690, this);//sets image as jframe background
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//closes jframe when press exit
frame.setSize(1365, 730);//sets size of jframe
JPanel buttonpanel = new JPanel();//sets panel for all buttons
buttonpanel.setBounds(0, 0, 1460, 690);//sets placing and sizing of panel
buttonpanel.setOpaque(false);//makes panel transparent
JButton next = new JButton ("Start");//contructs correct start button
next.setBounds(10, 5, 40, 50);
buttonpanel.add(next);//adds button to panel
next.addActionListener(new ActionListener()//adds actionlistener to button
{
public void actionPerformed(ActionEvent e)
{
new Introduction();//continues to next question
}
});
JButton wrongstart = new JButton("Start!!");//constructs wrong start button
wrongstart.setSize(100, 400);//setes size of button
buttonpanel.add(wrongstart);//adds button to panel
wrongstart.addActionListener(new ActionListener()//adds actionlistener to button
{
public void actionPerformed(ActionEvent e)
{
new Wrong();//direct user to wrong panel
}
});
frame.add(buttonpanel);//adds panel to jframe
frame.setVisible(true);//sets jframe as visible
}
}
Your problem is that you're trying to use absolute positioning to position a component (your JButton) in a container (the containing JPanel) that uses FlowLayout as default, and FlowLayout completely ignores the components bounds. A quick solution is to set the JPanel's layout to null allowing for absolute positioning. A correct solution is to always avoid null layouts and absolute positioning and instead to nest JPanels, each using its own layouts, in order to create complex yet flexible and pleasing GUI's.
You're setting the JFrame contentPane's layout to null as well -- don't do that either.
And then adding a JPanel as the contentPane which uses a default FlowLayout -- don't do that. Let the contentPane's layout be BorderLayout.
Edit
For example, if we leave the contentPane alone with its BorderLayout, and add another image panel on top of it, one that uses GridBagLayout, we can easily position our JButton to the top left corner of the GUI if desired. ....
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class Template2 extends JPanel {
private static final int PREF_W = 1460;
private static final int PREF_H = 690;
private BufferedImage img;
private JButton startButton = new JButton("Start");
public Template2() {
setLayout(new GridBagLayout());
// TODO: .... read in your image here
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.insets = new Insets(5, 10, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
add(startButton, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
private static void createAndShowGui() {
Template2 mainPanel = new Template2();
JFrame frame = new JFrame("Some Horrendous Program");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
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.