I'm trying to add a JScrollPane to an JPanel from a separate class. And thanks to some questions, which were asked so far, I could help myself create them. But my problem is still a little bit special.
I want to display an image on a JPanel and if the image is to large for the panel, I want to add scrollbars. But the scrollbars won't appear.
(When I set the ScrollPaneConstants to ****_SCROLLBAR_ALWAYS the frame of the bar appears, but without the bars to scroll).
I guess i have to connect the imagesize with the bars, so that they appear?
Some pieces of my code:
MainWindow
public class Deconvolutioner extends JFrame
{
Draw z;
Picturearea picturearea;
class Draw extends JPanel
{
public void paint(Graphics g)
{
}
}
public Deconvolutioner()
{
setTitle("Deconvolutioner");
setLocation(30,1);
setSize(1300,730);
super.setFont(new Font("Arial",Font.BOLD,11));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
FlowLayout flow = new FlowLayout(FlowLayout.CENTER);
this.setLayout(flow);
picturearea = new Picturearea();
picturearea.setLayout(new GridBagLayout());
JScrollPane scrollPane = new JScrollPane(picturearea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setPreferredSize(new Dimension(1000, 664));
getContentPane().add(scrollPane, flow); // add scrollpane to frame
add(z = new Draw());
setVisible(true);
}
}
JPanel Class
public class Picturearea extends JPanel
{
BufferedImage image;
int panelWidth, panelHeight, imageWidth, imageHeight;
public Picturearea()
{
setBackground(new Color(210,210,210));
setBorder(LineBorder.createBlackLineBorder());
setVisible(true);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
public void setPicture(BufferedImage picture)
{
try
{
image = picture;
}
catch (Exception e)
{
System.err.println("Some IOException accured (did you set the right path?): ");
System.err.println(e.getMessage());
}
repaint();
}
}
Thanks for your time.
The problem is that the JScrollPane has no way to know if it should display scroll bars or not, since the Picturearea it contains doesn't tell anything about its preferred size (or rather, it returns the preferred size based on its layout and on the components it contains. But since it doesn't contain any component, the returned preferred size is probably (0, 0)).
I would simply use a JLabel instead of the custom Picturearea class. A JLabel can display an image just fine, and it returns the appropriate Dimension when asked for its preferred size.
You can create a JLabel first and then add the Label to JPanel picturearea before creating instance for JScrollPane .
Have a try and it will work.
Example code is as follows:
JLabel imageLabel = new JLabel(new ImageIcon("d:\\099.jpg"));
picturearea.add(imageLabel);**
JScrollPane scrollPane = new JScrollPane(picturearea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
Related
I am struggeling with a little exercise I teached myself: I have a JPanel with an image drawn by g.paint(). On this JPanel I have another JPanel (componentsPanel) that contains the actual content: a JList and a JLabel that I want to be transparent, so only the content is visible (no background of those elements)
Here are my classes:
The JPanel and its components
private void initializeNormalPanel() {
normalPanel = new JPanel() {
#Override
public void paint(Graphics g) {
super.paint(g);
Image i, scaled = null;
try {
i = ImageIO.read(new URL("image-from-url"));
scaled = i.getScaledInstance(getWidth(), getHeight(), Image.SCALE_SMOOTH);
} catch (IOException e) {
e.printStackTrace();
}
g.drawImage(scaled, 0, 0, null);
}
};
CustomListModel listModel = new CustomListModel();
//m1-m10 is the sample data
listModel.addElement(m1);
listModel.addElement(m2);
listModel.addElement(m3);
listModel.addElement(m4);
listModel.addElement(m5);
listModel.addElement(m6);
listModel.addElement(m7);
listModel.addElement(m8);
listModel.addElement(m9);
listModel.addElement(m10);
JList list = new JList(listModel) {
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
};
list.setCellRenderer(new CustomListCellRenderer());
ComponentListener l = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
list.setFixedCellHeight(10);
list.setFixedCellHeight(-1);
}
};
list.addComponentListener(l);
normalPanel.setVisible(true);
normalPanel.setLayout(new BoxLayout(normalPanel, BoxLayout.PAGE_AXIS));
JPanel componentsPanel = new JPanel(new BorderLayout());
JLabel timeLabel = new JLabel("Aktuelle Zeit", SwingConstants.CENTER);
displayCurrentTime(timeLabel);
list.setOpaque(false);
componentsPanel.add(list, BorderLayout.WEST);
componentsPanel.add(timeLabel, BorderLayout.NORTH);
list.setOpaque(false);
timeLabel.setOpaque(false);
timeLabel.setFont(new Font("Arial", Font.PLAIN, 25));
normalPanel.add(componentsPanel);
}
I also have a custom ListCellRenderer:
#Override
public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean hasFocus) {
final Meeting text = (Meeting) value;
lt.setText(pre + value.toString());
setOpaque(false);
return p;
}
I cannot manage to achieve my goal, I always see the background of the List and Label. I tried every combination of setOpaque true and false, can someone help me to get my components transparent?
custom painting is done by overriding paintComponent() not paint.
Don't do I/O in a painting method. You don't want to keep reading the image every time the component needs to be repainted.
A JLabel is transparent by default (other Swing components are not).
I always see the background of the List and Label.
normalPanel.add(componentsPanel);
I don't see where you make the componentsPanel transparent. So the background of the components panel will paint over top of the normalPanel.
Do you even need the componentsPanel? Why can't you just add the JLabel and JList directly to your normalPanel after setting the layout manager.
So first make sure it works with the JLabel since it is transparent by default. Once this works you know you have the proper layout. Then if the JList doesn't work you know you have a renderer issue.
I have been struggling with this for some time. At first, I only used ActionListener, then I added the paintComponent, but I have no idea what to put there. I read some tutorials and used their code as an example, but it still doesn't work. Right now, the end result is the same as it was without PaintComponent.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Scream extends JPanel {
private JButton button = new JButton("OK");
private Color screenColor;
private JPanel panel = new JPanel();
private JFrame frame;
private Dimension screenSize;
private ImageIcon image;
private JLabel label = new JLabel(image);
private int x;
private int y;
private boolean mouseClicked;
public Scream() {
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e ) {
if (e.getSource() == button) {
mouseClicked = true;
frame.getContentPane().add(label);
frame.setSize(image.getIconWidth(), image.getIconHeight());
panel.repaint();
}
}
});
frame = new JFrame ("Existential angst");
screenColor = new Color(150, 100, 0);
panel.setBackground( screenColor );
frame.add(button, BorderLayout.PAGE_END);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1300, 700);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
image.paintComponent(this, g, 1300, 700);
}
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Scream scream = new Scream();
}
});
}
}
If you are trying to dynamically add an image to a panel then you need to add the label to the panel. There is no need for any custom painting.
The basic code for adding components to a visible GUI is:
panel.add(...);
panel.revalidate();
panel.repaint();
Also, don't attempt to set the size of the frame to the size of the image. A frame contains a titlebar and borders. Instead you can use frame.pack();
I noticed a couple of issues:
image is never initialized to anything so it is null, effectively making the label empty. I assume maybe your example was just incomplete?
Once I initialized the image to something, your example still did not work. Turns out adding label without specifying any constraint basically does nothing (I assume since adding a component to a border layout without a constraint puts it in the center where panel already is). When I added the label to BorderLayout.NORTH, everything worked (though resizing the frame to the size of the image makes it only partially visible since the frame includes the OK button)
First of all: sorry, if this question was asked before, but I cannot seem to find an answer anywhere, so here we go:
I am trying to get a canvas element to show while it being added to a panel with a titled border around the panel. Here is my code.
public class TestClass extends JFrame{
private TestClass() {
GuiCanvas canvas = new GuiCanvas();
setTitle("TestClass");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(1300, 800);
Border menuBorder = BorderFactory.createTitledBorder(
BorderFactory.createLineBorder(Color.LIGHT_GRAY), "Overview");
JPanel controlpanel = new JPanel();
JPanel panelCanvas = new JPanel();
panelCanvas.setBorder(menuBorder);
panelCanvas.add(canvas);
controlpanel.setLayout(new GridLayout(3, 1));
controlpanel.add(panelCanvas);
add(controlpanel);
setLocationRelativeTo(null);
setVisible(true);
System.out.println(canvas.getBounds());
}
private class GuiCanvas extends Canvas {
GuiCanvas() {
setBackground(Color.LIGHT_GRAY);
}
#Override
public void paint(Graphics g) {
g.drawLine(20, 20, 20, 200);
}
}
public static void main(String[] args) {
new TestClass();
}
}
The above code results in an empty panel with a titled border when it should show the defined line I draw in the GuiCanvas-Class. Am I missing something here? Is it even possible to add a canvas-element to a panel? Thanks for your help in advance :)
If you want the canvas to stretch to the size of the panel, change:
JPanel panelCanvas = new JPanel();
To:
JPanel panelCanvas = new JPanel(new GridLayout());
See also this answer:
It is indeed possible to add a Canvas object to a JPanel.
Your problem lies in the fact that your Canvas has no defined size.
What you need are the two following lines
canvas.setPreferredSize(new Dimension(1300,300));
/*
*
*/
this.pack();
This will place your canvas inside the panelCanvas border, displaying a black vertical line on a light gray background.
I would like to display more than one image on the screen in the same JPanel.
A for loop iterates over each element in the array and displays their corresponding image, but only seems to keep the last image.
The code:
import javax.swing.*;
import java.awt.*;
import java.io.*;
import javax.imageio.ImageIO;
public class GameGUI extends JFrame{
JPanel mainPanel = new JPanel();
JPanel buttonPanel = new JPanel();
int arrayLength;
public GameGUI() {
super("Gameplay");
//Set size of the frame.
setSize(650, 580);
//Location inside frame.
setLocation(10, 8);
SwingUtilities.isEventDispatchThread();
The methods that contain each individual panel:
createMainPanel();
createCentrePanel();
createNorthPanel();
createSouthPanel();
createWestPanel();
createEastPanel();
setVisible(true);
}
//creating panels
public void createMainPanel() {
//here is the main panel which the others will be nested in.
mainPanel.setLayout(new BorderLayout());
mainPanel.setBackground(Color.red);
add(mainPanel);
}
public boolean createCentrePanel() {
JPanel CENTRE = new JPanel(new BorderLayout());
CENTRE.setBackground(Color.WHITE);
CENTRE.setBorder(BorderFactory.createLineBorder(Color.black));
mainPanel.add(CENTRE, BorderLayout.CENTER);
return true;
}
This is the panel which i am using to print multiple images to the panel.
As you can see I have a for loop which is going through each item in the array and passing the value to the draw component. However it only seems to keep the last image on the screen eventhough each item in the array is being passed to it.
I have tried using repaint but it doesn't seem to work:
public boolean createNorthPanel() {
int[] array = {1, 8, 9, 10};
arrayLength = array.length;
int size = 0;
JPanel NORTH = new JPanel(new BorderLayout());
NORTH.setPreferredSize(new Dimension(100, 100));
NORTH.setBorder(BorderFactory.createLineBorder(Color.black));
mainPanel.add(NORTH, BorderLayout.NORTH);
for (int i = 0; i < arrayLength; i++) {
NORTH.add(new drawPanel(array[i], size, arrayLength));
size = size + 30;
//repaint();
}
return true;
}
public boolean createSouthPanel() {
JPanel SOUTH = new JPanel(new BorderLayout());
SOUTH.setPreferredSize(new Dimension(100, 100));
// SOUTH.add(new drawPanel(2, 0));
// SOUTH.add(new drawPanel(5, 30));
SOUTH.setBorder(BorderFactory.createLineBorder(Color.black));
mainPanel.add(SOUTH, BorderLayout.SOUTH);
SOUTH.add(buttonPanel, BorderLayout.EAST);
return true;
}
public boolean createWestPanel() {
JPanel WEST = new JPanel(new BorderLayout());
WEST.setPreferredSize(new Dimension(150, 100));
//WEST.add(new drawPanel(8, 0));
WEST.setBorder(BorderFactory.createLineBorder(Color.black));
mainPanel.add(WEST, BorderLayout.WEST);
return true;
}
public boolean createEastPanel() {
JPanel EAST = new JPanel(new BorderLayout());
EAST.setPreferredSize(new Dimension(150, 100));
EAST.setBorder(BorderFactory.createLineBorder(Color.black));
mainPanel.add(EAST, BorderLayout.EAST);
//EAST.add(new drawPanel(2, 0));
//EAST.add(new drawPanel(7, 60));
return true;
}
public static void main(String args[]){
new GameGUI();
}
}
Here is my class which draws the images on the screen
class drawPanel extends JPanel {
Image image = null;
int xPos;
public drawPanel(int x, int y, int length) {
xPos = y;
try {
File location = new File("src/Card_images/" + x + ".png");
image = ImageIO.read(location);
} catch (IOException e) {
System.out.println("Error: " + e);
}
}
/*public Dimension getPreferredSize() {
return new Dimension(71, 96);
}*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//draws image to screen at positions displayed
g.drawImage(image, xPos, 0, this);
}
}
You only have a single drawImage() statement that is executed in you paintCompent() method so you only ever see the last image drawn.
See Custom Painting Approaches for two different ways to draw multiple objects. You will obviously need to customize for your requirements, but the basic concepts will be the same.
Edit:
The above does not apply to your question, but is still good to know when you do need to do some custom painting.
Sorry, because of the custom painting I misread your question. You are making the code too complex.
The first problem is that you changed the layout manager of the "north" panel to a BorderLayout. You can only add a single component to any location of a BorderLayout. So that is why the last component added gets painted. Just use the default FlowLayout for the panel. Although your code still won't work because your components don't have a preferred size.
So the solution to your problem is:
a) create a panel using a FlowLayout
b) Use a JLabel to display your images. There is no need to do custom painting!. Add the labels to the panel, then add this panel to your frame.
Now the layout manager can do its job and you don't need to worry about the details.
Also, use standard Java naming conventions. Your code is too hard to read because you don't follow the standards.
NORTH is not a proper variable name. It should be "north". An upper cased name indicates a final static variable.
use proper class names. Classes should start with an upper case character. "drawPanel" should be "DrawPanel".
I'm currently trying to draw shapes on a JPanel, which is within another JPanel, within a JFrame.
I've searched Google and Youtube and found out how to draw shapes within a JFrame that has one panel, but have found nothing which can help me with what I'm doing. (maybe I'm not seeing something).
Code I've seen so far:
public class GameScreen
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.red);
g.drawRect(100, 10, 30, 40);
}
public static void main(String[] args)
{
GameScreen gs = new GameScreen();
JFrame f = new JFrame();
f.setTitle("");
f.setSize(400,400);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(gs);
}
This is all good for when I'm dealing with just one panel, but I wanna display shapes on a panel which is within the 1'st panel I've created.
Add a JPanel to the JFrame in the same way as you're doing now, but do it with your own subclass of JPanel.
class MyPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.drawRect(100, 10, 30, 40);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400,400); // As suggested by camickr
}
}
You can add this to a JPanel which sits within the JFrame
public static void main(String[] args)
{
MyPanel mp = new MyPanel();
JPanel jp = new JPanel();
jp.add(mp);
JFrame f = new JFrame();
f.setTitle("");
f.setSize(400,400);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(jp);
}
This can work for components within components, if you add them as children components. The key is to extend the component and override the methods you wish to change.
This is all good for when I'm dealing with just one panel,
The code you posted works fine because a frame uses a BorderLayout. So, when you add your panel to the frame is will increase in size and your custom painting will be seen.
I wanna display shapes on a panel which is within the 1'st panel I've created.
When you create a panel it uses a FlowLayout by default. When you add a custom painting panel to that panel it will be displayed at its preferred size, which will be (0, 0) since you didn't specify the preferred size.
On the panels that do custom painting you also need to implement the getPreferredSize() method so the layout manager knows how to arrange the panels. For example:
public Dimension getPreferredSize()
{
return new Dimension(200, 200);
}