drawImage() on JPanel OR add Image on top of GridLayout - java

I have a 100 by 100 grid of labels. I've got a method which creates and populates an array of Strings. The next method creates an array of labels and then adds the String (created from previous method) to the labels using the setText() method. Some of the labels contain images too. Method after that takes those JLabels and adds them to a JPanel of Grid Layout(lets call this x1). Then I've added the JPanel to a JScrollPane(x2), the JScrollPane gets added to another JPanel(x3) with an empty border and this final JPanel(x3) gets added to the JFrame. So that's how I've created the grid and I'm happy with that, I don't want to change it.
I would like to add an image to x1 - the JPanel with Grid Layout.
For this I would have to add the paintComponent method and use the drawImage() method. My question is how will Eclipse know which panel to add the image to? I's prefer not to create a separate class for x1, I did that before and it just didn't work out right and I rather not go down that incredibly frustrating road again, I'm sorry!
I have considered using a Glass Pane however I would no longer be able to see the images of the JLabels - which is really important.
I think adding the image to the background of the JPanel will be best because I also want to have a button which shows/hides the grid lines - the borders of the JLabels.
I hope I'm making sense.
Below is the code. I understand it's a lot of code in one class. I did have it in two separate classes but it just didn't work me. I find this much easier. I hope you don't mind
package roverMars;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
public class MenuPanel extends JPanel {
private static final long serialVersionUID = -3928152660110599311L;
public JPanel frame, textfield, buttons, cpPanel;
public JTextField Commands;
public JButton Plot, Submit, Undo;
public JLabel Position, cpLabel;
public Border loweredetched;
public JCheckBox gridLines;
public SubmitButton sub;
static final int rows = 100, columns = 100;
// ******IMAGES******
static BufferedImage North, South, West, East;
public void ImageLoader() {
try {
North = ImageIO.read(this.getClass().getResource("North.png"));
South = ImageIO.read(this.getClass().getResource("South.png"));
West = ImageIO.read(this.getClass().getResource("West.png"));
East = ImageIO.read(this.getClass().getResource("East.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Error occured: " + e);
e.printStackTrace();
}
}
// ******IMAGES******
public void createMenu(JPanel p) {
// Text Field Panel
Commands = new JTextField(20);
textfield = new JPanel();
textfield.setPreferredSize(new Dimension(150, 50));
textfield.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
textfield.setBackground(new Color(204, 153, 255));
textfield.add(Commands);
// Have a button next to the Text Field to clear contents.
// Might need to give the JPanel a new Flow Layout.
// Buttons Panel
buttons = new JPanel();
buttons.setPreferredSize(new Dimension(150, 250));
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
buttons.setBackground(new Color(170, 051, 170));
// Create and Add buttons to the Buttons Panel
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Plot = new JButton("Plot");
Plot.setAlignmentX(Component.CENTER_ALIGNMENT);
Plot.setAlignmentY(Component.CENTER_ALIGNMENT);
buttons.add(Plot);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Submit = new JButton("Submit");
Submit.setAlignmentX(Component.CENTER_ALIGNMENT);
Submit.setAlignmentY(Component.CENTER_ALIGNMENT);
Submit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
sub = new SubmitButton();
sub.Submit(Commands);
cpLabel.setText("*****SET CURRENT POSITION*****");
labels[2][2].setIcon(new ImageIcon(North));
// I will be able to move the rover from here using for loops
// and if statements.
}
});
buttons.add(Submit);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Undo = new JButton("Undo");
Undo.setAlignmentX(Component.CENTER_ALIGNMENT);
Undo.setAlignmentY(Component.CENTER_ALIGNMENT);
buttons.add(Undo);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
gridLines = new JCheckBox();
gridLines.setText("Show gridlines");
gridLines.setAlignmentX(Component.CENTER_ALIGNMENT);
gridLines.setAlignmentY(Component.CENTER_ALIGNMENT);
gridLines.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// Set the colour of the JLabels array from here.
System.out.println("clicked");
}
});
buttons.add(gridLines);
buttons.add(Box.createRigidArea(new Dimension(30, 20)));
loweredetched = BorderFactory
.createEtchedBorder(EtchedBorder.RAISED);
cpLabel = new JLabel("Current position: ", JLabel.CENTER);
cpPanel = new JPanel();
cpPanel.setBackground(new Color(153, 153, 204));
cpPanel.add(cpLabel);
cpPanel.setBorder(loweredetched);
// Panel for the main window
JPanel frame = new JPanel();
frame.setPreferredSize(new Dimension(150, 350));
frame.setLayout(new BorderLayout());
frame.add(textfield, BorderLayout.NORTH);
frame.add(buttons, BorderLayout.CENTER);
// This Main Panel
p.setPreferredSize(new Dimension(350, 700));
p.setBackground(new Color(153, 153, 204));
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.setBorder(BorderFactory.createEmptyBorder(10, 50, 10, 25));
p.add(Box.createRigidArea(new Dimension(100, 100)));
p.add(frame);
p.add(Box.createRigidArea(new Dimension(15, 15)));
p.add(cpPanel);
p.add(Box.createRigidArea(new Dimension(100, 300)));
}
// From line 142 to 202 is everything to do with creating the Grid
public void StringArray(String[][] labelText) {
int x = 1; // increment rows
for (int i = 0; i < labelText.length; i++) { // x
for (int j = 0; j < labelText.length; j++) { // y
labelText[i][j] = Integer.toString(x); // populate string
x++;
}
}
}
public void JLabelArray(JLabel[][] label, String[][] labelText) {
for (int i = 0; i < label.length; i++) { // x
for (int j = 0; j < label.length; j++) { // y
label[i][j] = new JLabel();
label[i][j].setText(labelText[i][j]);
label[i][j].setOpaque(false);
label[i][j].setBorder(BorderFactory.createLineBorder(new Color(
0, 155, 200), 1));
// label[i][j].setBackground(Color.WHITE);
}
}
}
public void populateGrid(JPanel Grid, JLabel[][] label) { // Add Labels to
// Panel,
String x1[][] = new String[rows][columns];
StringArray(x1);
JLabelArray(label, x1);
Grid.setBackground(Color.RED);
int gHeight = label.length, gWidth = label.length;
Grid.setLayout(new GridLayout(gWidth, gHeight));
for (int i = 0; i < label.length; i++) { // x
for (int j = 0; j < label.length; j++) { // y
Grid.add(label[i][j]);
}
}
}
public void createGrid(JPanel finalPanel, JPanel Grid) {
// Add Grid to Scroll Pane
JScrollPane x4 = new JScrollPane(Grid);
x4.setPreferredSize(new Dimension(600, 600)); // DO NOT DELETE THIS.
x4.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
x4.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// Add Scroll Pane to another Panel with the Border
finalPanel.setBackground(new Color(153, 153, 204));
finalPanel.setBorder(BorderFactory.createEmptyBorder(50, 25, 50, 50));
finalPanel.add(x4);
}
// Variables for creaeteGUI method.
static MenuPanel t = new MenuPanel();
static JPanel menu = new JPanel();
static JPanel finalPanel = new JPanel();
static JPanel gridPanel = new JPanel();
static JLabel labels[][] = new JLabel[rows][columns];
public static void createGUI() {
t.createMenu(menu);
t.populateGrid(gridPanel, labels);
t.createGrid(finalPanel, gridPanel);
JFrame f = new JFrame();
f.setTitle("Project Testing");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setLocation(100, 100);
f.setAlwaysOnTop(true);
f.setSize(500, 500);
f.add(finalPanel, BorderLayout.CENTER);
f.add(menu, BorderLayout.WEST);
f.pack();
}
public static void main(String args[]) {
createGUI();
t.ImageLoader();
labels[2][2].setIcon(new ImageIcon(West));
}
}
Thank you so much! I really appreciate any help or suggestions :D

As you said what you need to do is to override the paintComponent method of the JPanel and put a drawImage(...) in there. So:
#Override
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
  g.drawImage(image, 0, 0, null);
}
Where image is an instance of the class Image that you loaded previously in the initialization code (don't load it in the paintComponent, that would be too slow and you only want to load it once).
There are 2 ways to accomplish that:
Make your own class extending JPanel and put that code there. You probably will want to create also a method setBackgroundImage(Image) that you can call from you main class to pass the image that you loaded from the disk.
Make an anonymous class, that is doing something similar but without explicitely defining a new class. To do so instead of creating the panel like this:
JPanel gridPanel = new JPanel();
do it like this:
JPanel gridPanel = new JPanel()
{
#Override
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
  g.drawImage(image, 0, 0, null);
}
};
Of course you must do this in the actual code (not as an static initialization) since you want to make sure that you load the image before.
Finally a couple of suggestions:
Variable names start in lower case by convention (as opposite to class names that start in upper case). You don't do this for example in the JPanel Grid argument and Comands field.
You are violating Swing's single threading rule. That is, you must call invokeLater in your main wrapping your GUI initializing code. For example look at Swing's Hello World. You can find a detailed explanation of this here.

Related

Why doesn't JPanel get rendered

I am trying to work with grid layout, and im trying to put few panels there that has some data, but nothing gets rendered at all.
Here is the current code that I have:
package main.cache.test;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ImageView {
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ImageView window = new ImageView();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ImageView() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
JFrame frmSpitePicker = new JFrame("Title");
frmSpitePicker.setSize(658, 395);
frmSpitePicker.setResizable(false);
frmSpitePicker.setLocationRelativeTo(null);
frmSpitePicker.getContentPane().setLayout(null);
frmSpitePicker.setVisible(true);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(12, 35, 620, 303);
frmSpitePicker.getContentPane().add(scrollPane);
JPanel panel = new JPanel();
scrollPane.setViewportView(panel);
File file = new File("images/");
panel.setLayout(new GridLayout((file.listFiles().length / 6), 6));
int i = 0;
// getting files name from folder
for (String name : file.list()) {
JPanel panel_1 = new JPanel();
panel_1.setBounds(209, 362, 82, 87);
frmSpitePicker.getContentPane().add(panel_1);
panel_1.setLayout(null);
// create label
JLabel lblNewLabel = new JLabel((i++) + "");
lblNewLabel.setBounds(12, 13, 56, 16);
lblNewLabel.setIcon(new ImageIcon(new ImageIcon("images/" + name).getImage().getScaledInstance(8, 8, 1)));
lblNewLabel.setHorizontalTextPosition(JLabel.CENTER);
lblNewLabel.setVerticalTextPosition(JLabel.BOTTOM);
panel_1.add(lblNewLabel);
// create button
JButton btnNewButton = new JButton("btn");
btnNewButton.setBounds(12, 42, 58, 25);
panel_1.add(btnNewButton);
// add to the panel
panel.add(panel_1);
}
}
}
I don't know what is wrong with this, and why Jpanel when being added, it doesn't get rendered but when adding a JLabel would work.
Thanks in advance!
Using layouts correctly, we can easily get something like this:
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
public class ImageView {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
ImageView window = new ImageView();
} catch (Exception e) {
e.printStackTrace();
}
});
}
public ImageView() {
initialize();
}
private void initialize() {
int num = 32; // number of images to show..
JFrame frmSpitePicker = new JFrame("Title");
frmSpitePicker.setResizable(false);
JPanel panel = new JPanel(new GridLayout(0, 6));
JScrollPane scrollPane = new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
frmSpitePicker.getContentPane().add(scrollPane);
for (int ii = 1; ii <= num; ii++) {
JPanel panel_1 = new JPanel(new BorderLayout());
// create label
JLabel lblNewLabel = new JLabel(ii + "");
lblNewLabel.setIcon(new ImageIcon(getImage()));
lblNewLabel.setHorizontalTextPosition(JLabel.CENTER);
lblNewLabel.setVerticalTextPosition(JLabel.BOTTOM);
panel_1.add(lblNewLabel, BorderLayout.CENTER);
// create button
JButton btnNewButton = new JButton("btn");
panel_1.add(btnNewButton, BorderLayout.PAGE_END);
// add to the panel
panel.add(panel_1);
// hack to ensure our scroll bar is active
// we require 3 rows to be visible..
if (ii==18) frmSpitePicker.pack();
}
frmSpitePicker.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Normally we'd call pack() here!
//frmSpitePicker.pack();
frmSpitePicker.setLocationRelativeTo(null);
frmSpitePicker.setVisible(true);
}
java.util.Random r = new java.util.Random();
private BufferedImage getImage() {
int s = 16;
BufferedImage bi = new BufferedImage(
s, s, BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
g.setColor(new Color(
r.nextInt(255), r.nextInt(255), r.nextInt(255)));
g.fillRect(0, 0, s, s);
g.dispose();
return bi;
}
}
Note that I would tend to use a JList for this type of case. The panel_1 would become a renderer for a POJO what encapsulates the label and button. But the button might not be needed, in that a list can have listeners for selection and activation. If that's what the button does, it'd be redundant in a list, and lblNewLabel could replace the entire panel_1.
BTW - please make sure all resources needed, are available to run the code. When it comes to images, we might hot link (load by URL) to images available on the net1 or generate them in the code (as done here).
One way to get image(s) for an example is to hot link to images seen in this Q&A. E.G. This answer hot links to an image embedded in this question.

JPanel not scrolling in JScrollPane

I have a JPanel that I create like so
JPanel statsKeysPanel = new JPanel(new MigLayout("insets 0", "[]", ""));
and populate with a dynamic number of JLabels stacked on top of each other. For the sake of an example:
for(int i = 0; i < 30; i++) {
statsKeysPanel.add(new JLabel("" + i + " key value"), "wrap");
}
I then create and add the scroller like so
JPanel panel = new JPanel(new MigLayout("insets 0", "[center][][center][]", "[][]"));
final JScrollPane keysScroller = new JScrollPane(this.statsKeysPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
keysScroller.setMaximumSize(new Dimension(100, 300));
panel.add(keysScroller, "cell 0 1");
The max of 300 is applied but the 15 out of 30 JLabels that don't fit in 300px are hidden, and scrolling doesn't work. What am I doing wrong? (image below)
final JScrollPane keysScroller = new JScrollPane(this.statsKeysPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
Why are you using NEVER for both the horizontal and vertical scrollbar? I would think this would prevent a scrollbar from appearing.
I generally don't set either property and just let the scrollpane determine when to display the scrollbar. Sometimes I use ALWAYS to reserve space for the scrollbar.
You are using unnecessarily two panels; one will suffice. I think that
you have omitted the code that caused your error.
Here is a working example:
package com.zetcode;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import net.miginfocom.swing.MigLayout;
public class StatsKeyEx extends JFrame {
public StatsKeyEx() {
initUI();
}
private void initUI() {
JPanel pnl = new JPanel(new MigLayout());
for (int i = 0; i < 60; i++) {
pnl.add(new JLabel("" + i + " key value"), "wrap");
}
JScrollPane spane = new JScrollPane(pnl);
spane.setPreferredSize(new Dimension(150, 200));
add(spane);
pack();
setTitle("Scrolling");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
StatsKeyEx ex = new StatsKeyEx();
ex.setVisible(true);
}
});
}
}
The scrollbars are shown as needed.

How to create vertical TitledBorder in JPanel (javax swing)

I'm trying to figure out how to create a vertical TitledBorder in a JPanel.
I've got this situation:
I'd like to have "Actuators st..." placed vertically, so user can read it.
Is there a way to do it, or should I implement my own customized JPanel & TitledBorder?
maybe crazy idea but is possible with JSeparator too :-)
required proper LayoutManager, maybe GridBagLayout (JComponent placed without GBC can take PreferrredSize from JComponent, but isn't resiziable), not GridLayout
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
public class NestedLayout {
private JFrame frame = new JFrame();
private JPanel leftPanel = new JPanel();
private JSeparator sep = new JSeparator();
private JLabel label = new JLabel("<html> L<br>a<br>b<br>e<br>l<br></html>");
public NestedLayout() {
label.setOpaque(true);
sep.setOrientation(JSeparator.VERTICAL);
sep.setLayout(new GridLayout(3, 1));
sep.add(new JLabel());
sep.add(label);
sep.add(new JLabel());
leftPanel.setLayout(new BorderLayout());
leftPanel.setBorder(BorderFactory.createEmptyBorder(
10, //top
10, //left
10, //bottom
10)); //right
leftPanel.add(sep, BorderLayout.CENTER);
leftPanel.setPreferredSize(new Dimension(40, 220));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(leftPanel, BorderLayout.WEST);
//frame.add(label);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
NestedLayout nestedLayout = new NestedLayout();
}
});
}
}
As shown in How to Use Borders, you can create a compound border using an empty border and a titled border.
Addendum: As an alternative, you can use the border's getMinimumSize() method to ensure that the title is visible. See also this related Q&A.
f.add(createPanel("Actuator status"), BorderLayout.WEST);
f.add(createPanel("Indicator result"), BorderLayout.EAST);
...
private Box createPanel(String s) {
Box box = new Box(BoxLayout.Y_AXIS);
TitledBorder title = BorderFactory.createTitledBorder(null, s,
TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION);
box.setBorder(title);
for (int i = 0; i < 6; i++) {
JButton b = new JButton(null, UIManager.getIcon("html.pendingImage"));
b.setAlignmentX(JButton.CENTER_ALIGNMENT);
box.add(b);
}
box.validate();
Dimension db = box.getPreferredSize();
int max = Math.max(title.getMinimumSize(box).width, db.width);
box.setPreferredSize(new Dimension(max, db.height));
return box;
}

Add a complex image in the panel, with buttons around it in one customized user interface

How can i have this image like below into the slavePanel and on top of that JPanel adjust the JButtons which looks like the image but having buttons correctly wrapped around? (Right now they are shaped in 1 row, 4 column)
//
// Shot Gun mover up/down/left/right, middle on is for zoom
//
public void GunMover(JPanel configPanel) throws IOException {
// Master Panel - holds everything
JPanel masterPanel = new Panel();
masterPanel.setLayout(new SpringLayout());
// Slave Panel - with image background
JPanel slavePanel = new Panel();
slavePanel.setLayout(new SpringLayout());
// Row 1
final JButton ptzLeft = new JButton("<");
masterPanel.add(ptzLeft, BorderLayout.WEST);
// Row 2
final JButton ptzRight = new JButton(">");
masterPanel.add(ptzRight, BorderLayout.CENTER);
// Row 3
final JButton ptzUp = new JButton("^");
masterPanel.add(ptzUp, BorderLayout.WEST);
// Row 4
final JButton ptzDown = new JButton("down");
masterPanel.add(ptzDown, BorderLayout.CENTER);
// How do i add slavePanel this background and add all the JButtons
// According to that image shape?
// Layout the panel.
SpringUtilities.makeCompactGrid(masterPanel,
1, 4, //rows, cols
6, 6, //initX, initY
6, 6);
configPanel.setLayout(new GridLayout(0,1));
configPanel.add(masterPanel);
}
Follow up: Excellent one from Andrew Thompson + at-least my broken method
package test;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import javax.swing.*;
public class New extends JFrame {
private static final long serialVersionUID = 1L;
private ImageIcon errorIcon =
(ImageIcon) UIManager.getIcon("OptionPane.errorIcon");
private Icon infoIcon =
UIManager.getIcon("OptionPane.informationIcon");
private Icon warnIcon =
UIManager.getIcon("OptionPane.warningIcon");
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
New t = new New();
}
});
}
public New() {
setLayout(new BorderLayout());
JPanel slavePanel = new NewPanel();
slavePanel.setLayout(new GridLayout(0, 2, 4, 4));
add(slavePanel);
JButton button = new JButton();
button.setBorderPainted(false);
button.setBorder(null);
button.setFocusable(false);
button.setMargin(new Insets(0, 0, 0, 0));
button.setContentAreaFilled(false);
button.setIcon((errorIcon));
button.setRolloverIcon((infoIcon));
button.setPressedIcon(warnIcon);
button.setDisabledIcon(warnIcon);
slavePanel.add(button);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
package test;
import java.awt.*;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;
public class NewPanel extends JPanel {
private Image imageGui;
private static Dimension screen;
public NewPanel() {
try {
imageGui =
ImageIO.read(
(InputStream) NewPanel.class.getResourceAsStream(
"/image/ptz.png"));
} catch (IOException e) {
e.printStackTrace(System.err);
}
Border border = BorderFactory.createEmptyBorder(11, 11, 11, 11);
setOpaque(true);
setBorder(border);
setFocusable(true);
setSize(getPreferredSize());
revalidate();
repaint();
setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(imageGui, 0, 0,
imageGui.getWidth(null), imageGui.getHeight(null), null);
revalidate();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(imageGui.getWidth(null), imageGui.getHeight(null));
}
}
Use a 3x3 GridLayout
For each of the 9 cells get a subimage:
For every second component, add a label with the subimage.
For every other component, add a JButton from which the space is removed. Use the subimage as icon, but you'll need alternate icons to indicate focus, activation etc. This example puts a red border around the 'pressed' icon.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class CompassButtons {
public CompassButtons(BufferedImage bi) {
int w = bi.getWidth();
int h = bi.getHeight();
int step = w / 3;
JPanel p = new JPanel(new GridLayout(3, 3));
p.setBackground(Color.BLACK);
int count = 0;
for (int ii = 0; ii < w; ii += step) {
for (int jj = 0; jj < h; jj += step) {
// This is it - GET THE SUB IMAGE
Image icon = bi.getSubimage(jj, ii, step, step);
if (count % 2 == 1) {
JButton button = new JButton(new ImageIcon(icon));
// make it transparent
button.setContentAreaFilled(false);
// remove border, indicate press using different icon
button.setBorder(null);
// make a 'pressed' icon..
BufferedImage iconPressed = new BufferedImage(
step, step, BufferedImage.TYPE_INT_ARGB);
Graphics g = iconPressed.getGraphics();
g.drawImage(icon, 0, 0, p);
g.setColor(Color.RED);
g.drawRoundRect(
0, 0,
iconPressed.getWidth(p) - 1,
iconPressed.getHeight(p) - 1,
12, 12);
g.dispose();
button.setPressedIcon(new ImageIcon(iconPressed));
button.setActionCommand("" + count);
button.addActionListener((ActionEvent ae) -> {
System.out.println(ae.getActionCommand());
});
p.add(button);
} else {
JLabel label = new JLabel(new ImageIcon(icon));
p.add(label);
}
count++;
}
}
JPanel center = new JPanel(new GridBagLayout());
center.setBackground(Color.BLACK);
center.add(p);
JOptionPane.showMessageDialog(null, center);
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/SNN04.png");
final BufferedImage bi = ImageIO.read(url);
SwingUtilities.invokeLater(() -> {
new CompassButtons(bi);
});
}
}
1) you have to prepare the Icons before and for every 5 JButtons (event here came from ButtonModel)
basic Icon without Focus
Icon for isRollover()
Icon for isPressed()
2) how to set Icons and to remove all "balast" from JButton
3) put these 5 JButtons to the JPanel with painted circles (RemoteSet)
Starting from this example, I got a start by changing MoveButton like this:
this.setBorderPainted(false);
You could give ControlPanel a Custom Layout Manager. I'd also add a background image and some kind of visual feedback based on the ButtonModel state, as suggested here.

JScrollPane resize containing JPanel when scrollbars appear

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);

Categories