Changing Look And Feel by Swapping UIDefaults - java

I am writing a GUI Builder and want the user to be able to change the LookAndFeel of the GUI he builds. The LookAndFeel should only be changed for the Components inside the editor area. The rest of the Application should remain with the SystemLookAndFeel.
The great problem is, that the LookAndFeel is implemented as a Singleton and changing the LookAndFeel multiple times during the Application causes a lot of bugs.
I started experimenting with Buttons:
I tried setting the ButtonUI to MetalButtonUI, but they didn't render properly. So I debugged the default paintComponent method of JButton and saw that the ButtonUI still needed the UIDefaults, which were not complete since they were the WindowsUIDefaults.
My current solution is to set the MetalLookAndFeel, save the UIDefaults, then change the LookAndFeel to SystemLookAndFeel and save those UIDefaults aswell and everytime I draw a Button inside the editor I swap the UIDefaults.
Here is the Code:
public class MainClass{
public static Hashtable systemUI;
public static Hashtable metalUI;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
metalUI = new Hashtable();
metalUI.putAll(UIManager.getDefaults());
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
systemUI = new Hashtable();
systemUI.putAll(UIManager.getDefaults());
} catch (Exception e) {
e.printStackTrace();
}
/* ...
* Generate JFrame and other stuff
* ...
*/
}
});
}
}
public class MyButton extends JButton {
public MyButton(String text) {
super(text);
ui = MetalButtonUI.createUI(this);
}
#Override public synchronized void paintComponent(Graphics g) {
UIManager.getDefaults().putAll(Application.metalUI);
super.paintComponent(g);
UIManager.getDefaults().putAll(Application.systemUI);
}
}
As you can see here the result is pretty good. On the left is the MetalLaF as it should look and on the right, how it gets rendered in my application. The gradient is painted correctly, but the Border and the Font aren't.
So I need to know why not all elements of the LaF are beeing applied to the Button and how to fix that.
-
Edit:
I found an ugly solution. The LookAndFeel has to be changed before Button creation, because the Graphics object will be created in the Constructor. After the super constructor was called you can change the LookAndFeel back.
Next you need to change the LookAndFeel before the Component is painted/repainted. The only point I got it working was in paintComponent before super is called. You can change it back after super is called.
Code:
import javax.swing.*;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;
public class MainClass {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(f.getExtendedState() | JFrame.EXIT_ON_CLOSE);
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
}
f.setLayout(new FlowLayout());
f.add(new MyButton("MetalButton"));
f.add(new JButton("SystemButton"));
f.pack();
f.setVisible(true);
}
});
}
}
class MyButton extends JButton {
public MyButton(String text) {
super(text);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
ui = MetalButtonUI.createUI(this);
}
#Override public synchronized void paintComponent(Graphics g) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
super.paintComponent(g);
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
}
}
Edit 2:
Do not use this unless absolutely necessery!
This is extremely unstable. After a lot of testing I found it less buggy when I just swapped the UIDefaults instead of the whole LookAndFeel, but I do not recommend doing any of those.
Edit 3:
The best solution I found was using JavaFX as a GUI. I inserted a swing node into the Editor area and now can modify the Look and Feel of the swing components as often as I want without any noticeable side effects.
Rant:
If you can always choose JavaFX if you want to modify the style of your application. CSS makes it as easy as possible without any side effects ever!
Much Thanks
Jhonny

Disclarimer
Swing's Look And Feel isn't designed to be switched after it's first initalised, it's actually a kind of fluky side effect that it's possible. Some look and feels and some components might not like you doing this and may not behave as they might other wise under normal conditions.
Possible solution
For the love of sanity, DON'T change the UI defaults in the paintComponent method (don't change the state of the UI at all from within any paint method EVER, painting paints the current state only), that's just asking for no end of trouble.
Instead, when required use UIManager.setLookAndFeel(,,,) and SwingUtiltiies#updateComponentTreeUI and pass in the most top level container
For example...
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LookAndFeelSwitcher {
public static void main(String[] args) {
new LookAndFeelSwitcher();
}
public LookAndFeelSwitcher() {
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();
gbc.gridx = 0;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(2, 2, 2, 2);
add(new JLabel("I have a bad feeling about this"), gbc);
add(new JTextField("When this blows up in your face, don't blame me"), gbc);
UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
DefaultComboBoxModel model = new DefaultComboBoxModel(lafs);
JComboBox cb = new JComboBox(model);
cb.setRenderer(new LookAndFeelInfoListCellRenderer());
add(cb, gbc);
String name = UIManager.getLookAndFeel().getName();
for (int index = 0; index < model.getSize(); index++) {
UIManager.LookAndFeelInfo info = (UIManager.LookAndFeelInfo) model.getElementAt(index);
if (info.getName().equals(name)) {
model.setSelectedItem(info);
break;
}
}
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
UIManager.LookAndFeelInfo info = (UIManager.LookAndFeelInfo) cb.getSelectedItem();
String className = info.getClassName();
try {
UIManager.setLookAndFeel(className);
SwingUtilities.updateComponentTreeUI(SwingUtilities.windowForComponent(TestPane.this));
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
}
});
}
public class LookAndFeelInfoListCellRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof UIManager.LookAndFeelInfo) {
UIManager.LookAndFeelInfo info = (UIManager.LookAndFeelInfo) value;
value = info.getName();
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}
}
}

Related

Update jPanel background color when a button is pressed

I'm just starting with Java and I wanted to make a little program that opens a jFrame with a text field in which you can write a number. Then you press a button and another jFrame with a jPanel, which will turn green if the number is even, and black if it's odd. I edited the code of the jPanel so that the color changes depending on the number, but the problem is that it will only work once. If I write "2" and press the button, the jFrame will appear with a green panel, but then if I write another odd number and press it again, the frame will stay green.
How could I solve this so that the background color changes whenever I press the button? I should also say that I made an "if-else", so that you could only open the second jFrame once because I didn't know how to make it close and then open again, so maybe that has to do with the problem. Thanks!
This is the code in the Panel. To make it easier, I tried to just turn it green if a zero was introduced, and now it doesn't even work:
jPanel1 = new javax.swing.JPanel();
if ("0".equals(Taller2.opcion)) {
jPanel1.setBackground(new java.awt.Color(0, 255, 0));
}
else {
jPanel1.setBackground(new java.awt.Color(0, 0, 0));
}
jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(255, 255, 255)));
// Code of sub-components - not shown here
// Layout setup code - not shown here
// Code adding the component to the parent container - not shown here
Here is the pretty basic main class:
public class Taller2 {
/**
* #param args the command line arguments
*/
public static String opcion;
public static boolean panelabierto;
public static void main(String[] args) {
Pregunta a = new Pregunta();
a.setVisible(true);
opcion = null;
panelabierto = false;
}
}
The second jFrame (the one with the jPanel inside) only has the basic code generated by Netbeans on the designer. If you need the code for the jFrame with the text field, I could add it too, although I believe the problem lies within the jPanel.
Don't create more instances of JPanel, simply create one and change it's state.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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() {
JFormattedTextField field = new JFormattedTextField(NumberFormat.getInstance());
field.setColumns(4);
setLayout(new GridBagLayout());
add(field);
field.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long value = (Long)field.getValue();
if ((value % 2) == 0) {
setBackground(Color.GREEN);
} else {
setBackground(Color.RED);
}
}
});
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Are you sure your code is correct? My best guess (without you providing your code) is that your code for checking whether the number is odd or even does not work correctly.
The best way to determine whether a number is odd or even in Java is to use the modulus (%) operator:
if ((num%2)==0) {
// Number is even
} else {
// Number is odd
}
(Replace "num" with the number you want to test.)

Java swing tabbed pane add tab exception

I have a Java swing application that creates and displays a tabbedpane and creates an update thread. The update thread when triggered needs to add a set of tabs with content but I am getting an exception now and again "Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException".
Is this something to do with adding tabs in a different thread? If so how do I add a tab to it in a thread safe way please??
Here is an example application that illustrates the problem
public class Example extends JFrame implements Runnable {
private final JTabbedPane tabbedPane;
private final Rectangle bounds;
public Example() {
super("Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
bounds = new Rectangle(0, 0, 500, 500);
setBounds(bounds);
setLayout(null);
tabbedPane = new JTabbedPane();
tabbedPane.setBounds(bounds);
JPanel jp = new JPanel();
jp.setLayout(null);
jp.setBounds(bounds);
jp.add(tabbedPane);
add(jp);
new Thread(this).start();
}
#Override
public void run() {
while (true) {
for (int i = 0; i < 3; i++) {
tabbedPane.addTab("NEW" + i, new JPanel());
repaint();
}
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
Logger.getLogger(Example.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public static void main(String[] args) {
Example e = new Example();
e.setVisible(true);
}
}
Swing is not thread safe...
This means that you should never try to create or modify the UI from outside the context of the Event Dispatching Thread.
The likely issue is you've run into some kind of race condition. Take a look at Concurrency in Swing and How to use Swing Timers for more details
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.Timer;
public class Example extends JFrame {
private final JTabbedPane tabbedPane;
private final Rectangle bounds;
public Example() {
super("Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
bounds = new Rectangle(0, 0, 500, 500);
setBounds(bounds);
// setLayout(null);
tabbedPane = new JTabbedPane();
tabbedPane.setBounds(bounds);
// JPanel jp = new JPanel();
// jp.setLayout(null);
// jp.setBounds(bounds);
// jp.add(tabbedPane);
// add(jp);
add(tabbedPane);
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 3; i++) {
tabbedPane.addTab("NEW" + tabbedPane.getTabCount(), new JPanel());
}
tabbedPane.revalidate();
}
});
timer.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Example e = new Example();
e.setVisible(true);
}
});
}
}
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
Take a look at Laying Out Components Within a Container for more details

KeyListener won't work?

I am trying to make a very simple 2-frame program called Duck Simulator.
This has a JFrame and 2 pictures. If you want to know what it does, it just is a JFrame with a starting picture of a duck sitting in a pond. It has a JLabel saying "Press D to drink water!" And when you press D, it is supposed to set the image to the duck drinking.
It shows the opening image of the duck sitting in the pond in the JFrame, but when I press D, it doesn't do anything.
Here is the code:
package net.ducksimulator.classes;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class a implements KeyListener {
static JFrame f;
public static void main (String[] args) {
f = new JFrame("Duck Simulator ALPHA");
try {
f.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("images/duck-sitting.png")))));
} catch (IOException e) {
System.out.println("Image doesn't exist.");
}
f.setResizable(false);
f.setVisible(true);
JLabel l = new JLabel("Press D to drink water!");
l.setBounds(250,20,100,10);
f.add(l);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
}
public void drink() throws IOException {
f.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("images/duck-drinking.png")))));
}
public void sit() throws IOException {
f.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("images/duck-sitting.png")))));
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_D) {
try {
drink();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
#Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_D) {
try {
System.out.println("Bagels");
sit();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
#Override
public void keyTyped(KeyEvent e) {
}
}
Simply implementing KeyListener isn't enough, you need to specify which component you want to receive key events.
This is where the problems start. KeyListener will only raise events for components that have focus AND are focusable.
A better solution would be to use the key bindings API which provides you with better control over the level of focus a component needs before it will trigger key events
You should avoid using setBounds for, at least, two reasons.
Firstly, the container you adding the component to is under the control of a layout manager (BorderLayout in this instance), which makes the use of setBounds pointless and secondly, you don't control the factors which will alter the required amount of space a component might need when presented on different platforms, such as font metrics and rendering pipelines. Let the layout managers do there job

Intercepting drag and drop events in a compound component?

Say I have a JPanel containing some JTextFields. I would like to perform the same drop action for this JPanel and its children. In other words, I would like the drop action onto the children to be treated the same way as a drop action onto the JPanel.
Is there any other way other than setting the same DropTargetListener for the JPanel and its children?
I know that if I set the TransferHandler of those JTextFields to null, the JPanel will receive the drag and drop event. However, this will destroy the copy and paste functionality of the textfield.
I know that I can intercept mouse events with JLayer. Is there something like this for drag events?
In the end, I added listeners separately to the child components. Because I needed the drop location relative to the parent as well, I used SwingUtilities.convertToPoint() separately on the child components. Which means a lot of different listeners used -- more memory usage. But seems to be the best way for now.
If you want to be able to drop items on a panel but you want any components on the panel to be ignored for dropping purposes you can deactivate the drop target on each of the components added to the panel. You will still be able to cut and paste within them, and there is even a way to initiate a drag from them, but you won't be able to drop anything on them - the drop event goes straight through them to the drop target associated with the panel.
To do this, simply call
component.getDropTarget().setActive(false);
for each component on the panel.
I found this useful when building a calendar panel where I wanted to be able to drag appointments around but drop them on the panel even if it was (partially or completely) covered in other appointments.
Not sure if this is what you had in mind, but, I basically added the same DropTargetListener to all of my components, which meant that it didn't matter where I dragged/dropped the incoming request, all the components triggered the same events...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestDragNDrop100 {
public static void main(String[] args) {
new TestDragNDrop100();
}
public TestDragNDrop100() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel implements DropTargetListener {
public TestPane() {
DropTarget dt = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this, true);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
for (int y = 0; y < 4; y++) {
gbc.gridy = y;
for (int x = 0; x < 4; x++) {
gbc.gridx = x;
JTextField field = new JTextField(10);
DropTarget child = new DropTarget(field, DnDConstants.ACTION_COPY_OR_MOVE, this, true);
add(field, gbc);
}
}
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
System.out.println("DragEnter - " + dtde.getDropTargetContext().getComponent());
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
System.out.println("DragOver - " + dtde.getDropTargetContext().getComponent());
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
System.out.println("dropActionChanged" + dtde.getDropTargetContext().getComponent());
}
#Override
public void dragExit(DropTargetEvent dte) {
System.out.println("dragExit" + dte.getDropTargetContext().getComponent());
}
#Override
public void drop(DropTargetDropEvent dtde) {
System.out.println("drop" + dtde.getDropTargetContext().getComponent());
}
}
}
I should also note. I tested the fields cut/copy/paste functionality and had no issues.
Implement a custom TransferHandler on the container which delegates to its children as appropriate, something along the lines of:
for (int i = 0; i < 5; i++) {
parent.add(new JTextField("item" + i, 20));
};
TransferHandler handler = new TransferHandler() {
#Override
public boolean canImport(TransferSupport support) {
TransferHandler childHandler = getTargetHandler();
return childHandler.canImport(
getTargetSupport(support));
}
protected TransferSupport getTargetSupport(TransferSupport support) {
return new TransferSupport(getTarget(), support.getTransferable());
}
protected TransferHandler getTargetHandler() {
return getTarget().getTransferHandler();
}
protected JComponent getTarget() {
return (JComponent) parent.getComponent(0);
}
#Override
public boolean importData(TransferSupport support) {
return getTargetHandler().importData(getTargetSupport(support));
}
};
parent.setTransferHandler(handler);

sticking components onto a JScrollPane

I have a program that adds bunch of components to a JPanel (in JScrollbar). However, since it adds so many components, most of them don't fit into the visible area (Viewport).
When everything loads and I start to scroll down, I notice that components, as they get into the Viewport area, are aligning and setting their positions. That causes my JScrollPane to be higher than necessary. That makes it "snap" when I get to the end (components abruptly move up (align properly), and so does the viewport).
I tried calling repaint() and validate(), but with no effect whatsoever. What am I doing wrong?
I would suggest posting an SSCCE in order to exactly replicate your specific problem.
I did a short example that may lead you in the right direction.
Basically will just add 225 JButtons to JPanel with GridLayout which in turn is added to JScrollPane.
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class JScrollPaneOfComponents {
/**
* Default constructor for ScrollBarOfComponents.class
*/
public JScrollPaneOfComponents() {
initComponents();
}
/**
* Initialize GUI and components (including ActionListeners etc)
*/
private void initComponents() {
JFrame jFrame = new JFrame();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(15, 15));
//create 225 JButtons and add them to JPanel;
for (int i = 0; i < (15*15); i++) {
panel.add(new JButton(String.valueOf((i + 1))) {
//make buttons bigger for demonstartion purposes
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
});
}
JScrollPane scrollpane = new JScrollPane(panel) {
//size the JScrollPane purposelfully smaller than all components
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
//add scrollpane to frame
jFrame.add(scrollpane);
//pack frame (size JFrame to match preferred sizes of added components and set visible
jFrame.pack();
jFrame.setVisible(true);
}
public static void main(String[] args) {
/**
* Create GUI and components on Event-Dispatch-Thread
*/
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
//set nimbus look and feel
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
//create new instance of GUI
JScrollPaneOfComponents test = new JScrollPaneOfComponents();
}
});
}
}

Categories

Resources