Make JScrollPanel dynamically resizable with JPanel drawing - java

I have a JScrollPanel and a JPanel added to it. I would like to draw to the JPanel and make the scrollbars of the JScrollPane appear whenever the drawing exceeds the size of the panel and be able to scroll the drawing both vertically and horizontally.
I have tried consulting with various forums and the official docs and tried a few things (setting the borders, the preferred size, etc.) but none seems to yield the desired effects.
I have a JFrame (with GridBagLayout, btw.) :
JFrame frame1 = new JFrame("Application");
frame1.setVisible(true);
frame1.setMinimumSize(new Dimension(580,620));
frame1.setResizable(false);
frame1.setLocationRelativeTo(null);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
The relevant components are :
JPanel panel1 = new JPanel();
JScrollPane scrollPane = new JScrollPane(panel1);
frame1.add(scrollPane, gbc_panel1); //added with layout constraints
JPanel :
panel1.setBackground(Color.BLACK);
panel1.setPreferredSize(new Dimension(500,500));
panel1.setMinimumSize(new Dimension(360,360));
panel1.setMaximumSize(new Dimension(1000,1000));
JScrollPane :
scrollPane.setAutoscrolls(true);
The relevant code from the action event
of a button that does the drawing :
Graphics g;
g = panel1.getGraphics();
panel1.paint(g);
g.setColor(new Color(0,128,0));
/* this is followed by some more code that
does the drawing of a maze with g.drawLine() methods */
The code does the drawing perfectly, I just can't seem to figure it out how to make the scrolling and dynamic resizing happen.
I would appreciate any helpful comments or remarks!
Thank you!

Ultimately rewriting the paint method did the trick as #MadProgrammer suggested. I was just hoping that I could do the painting without having to define my custom JPanel class, but looks like it doesn't work that way.
The custom class looks like this:
class Drawing extends JPanel {
int mazeSize;
public Drawing(JTextField jtf)
{
try {
this.mazeSize = Integer.parseInt(jtf.getText());
}
catch (Exception e)
{
JOptionPane.showMessageDialog(this, "ERROR! Invalid size value!");
}
} // the constructor gets the size of the drawing from a textField
public Dimension getPreferredSize() {
return new Dimension(mazeSize*10,mazeSize*10);
} //getPreferredSize - this method is used by the scroll pane to adjust its own size automatically
public void drawMaze (Graphics g)
{
/* some irrelevant code that does the desired drawing to the panel by calling g.drawLine()*/
} // drawMaze method that does the de facto drawing
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawMaze(g);
}// paintComponent() #Override method - this was the tricky part
}//Drawing JPanel subclass
It is also worth noting (if some noob like myself happens to stumble upon this question), that after instantiating the new JPanel subclass in the action event, I had to add it to the JScrollPanel in the following way, instead of just simply using its add() method:
Drawing drawPanel = new Drawing(textfield1);
scrollPane.getViewport().add(drawPanel);
Again, thanks for the suggestion!
Once finished with the program (a random maze generator that uses a recursive backtracking algorithm), I will make the source code available at my github profile.

Related

How to paint in a split panel in Java swing

Recently, I've met an issue that the paintComponent function is not invoked in the function, and I found that when I use splitpane function, it will disable the paint function, and gives error:
cannot add to layout: unknown constraint: null
I think the paint function may not be added to the right way, below is my code(partly):
Class: test
public class Test extends JFrame{
public Test() throws IOException{
//JFrame jf = new JFrame("my frame");
this.add(new NewPanel(this));
this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
this.setBounds(300,200,1050,600);
this.setVisible (true);
}
public static void main (String[] args) throws IOException{
Test test = new Test ();
test.setTitle("Hello");
//frame.pack ();
}
}
Class: NewPanel
public class NewPanel extends JPanel{
public NewPanel(JFrame frame) throws IOException{
JTabbedPane jTabbedpane = new JTabbedPane();
JSplitPane splitPane = new JSplitPane();
JPanel p1 = new JPanel();
p1.setLayout(null);
p2.setLayout(new FlowLayout());
splitPane.setOneTouchExpandable(true);
splitPane.setContinuousLayout(true);
//splitPane.setPreferredSize(new Dimension (250,500));
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(p1);
splitPane.setRightComponent (p2);
splitPane.setDividerSize(3);
splitPane.setDividerLocation(250); //balance two panels width
jTabbedpane.addTab("ABC",p2);
jTabbedpane.addTab("AB",p3);
jTabbedpane.addTab("AC",p4);
jTabbedpane.addTab("BC",p5);
frame.setContentPane(splitPane);
frame.add(jTabbedpane);
}
}
public void paintComponent(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.drawLine(303, 90, 303, 200);
g.drawLine(583, 90, 583, 200);
g.drawLine(863, 90, 863, 200);
}
}
When I comment frame.add(jTabbedpane),the line could be drawn in the panel, BUT it is only available in one panel, I cannot draw it into another split panel, I don't know why.. And when I uncomment frame.add(jTabbedpane), it pops up the above mentioned error.
Your UI assembly doesn't make sense. You're calling 'setContentPane' to the splitpane, which is sort-of OK (but unusual), but then you're calling add() to the frame, which tries to then add something else to the contentPane (the JSplitPane). You should either add the JTabbedPane to the SplitPane before adding the splitPane to the JPanel, or set up your layout differently.
//These don't make sense together.
frame.setContentPane(splitPane);
frame.add(jTabbedpane);
Your second question about drawing the blue line is more complicated.
You're doing a bunch of crazy stuff - you're creating a NewPanel and trying to add it to the JFrame, but then you're setting the contentPane of the JFrame to a different component later. You need to go through the Swing Tutorial and lay out your UI better.
I think the paint function may not be added to the right way,
public void paintComponent(Graphics g){
super.paint(g);
You are overriding paintComponent(...), so why are you calling super.paint(...)?
Start by reading the Swing Tutorial for Swing basics. All sections in the tutorial have working examples you can download and test.
So you might start with:
How to Use Split Panes - it will show you how to add a split pane to a frame
Performing Custom Painting - it will explain how painting works and show how to override the paintComponent(...) method.

How can I place a JLabel on a specific position within a JFrame?

We're about to create an online based Space Invaders-game with implemented graphics. I've been reading about JFrames, JPanels and JLabels in order to create a window with a grid where the ships and monsters will be placed in.
So, here's the thing, I've been looking through the different layouts that exists, but nothing really seems to fit our purpose. I would like a simple JFrame with the possibility of placing JLabel-objects on a certain position (with setBounds(), setLocation() or something similar). This requires setLayout(null) which I've heard isn't a good solution? I was thinking of having objects in a fixed 30x30px size, and a fixed window size of 600x600px (giving me a grid of 20*20).
Anyhow, I've been trying to get it to work with setLayout(null), but without any results. If I apply a layout, say FlowLayout, the ship is visible, but stuck in either LEFT, CENTER or MIDDLE.
public class GUI extends JFrame {
JPanel p = new JPanel(null);
public GUI() {
try{
this.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("graphics/bg.png")))));
}catch(IOException e) {
System.out.println("Image does not exist");
}
this.setLayout(null);
this.setResizable(false);
this.setSize(600,600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void placeShip1() {
ImageIcon ship2 = new ImageIcon("graphics/ship2.png");
JLabel imageLabel = new JLabel(ship2);
imageLabel.setBounds(200,200,30,30);
p.add(imageLabel);
p.setOpaque(false);
p.setSize(600, 600);
this.add(p);
this.setVisible(true);
}
}
I really can't see why it doesn't work. I mean, all I want is my JFrame with a background image and with an object on a certain position, but instead the object doesn't show at all.
The main reason for wanting to use setLayout(null) is because I was thinking of using a method translating our grid coordinates to the JFrame, simply mapping each coordinate to respective cell.

Border affects components position java

So I have a JPanel that has an inner border (it's toggled based on MouseEnter/MouseExit, as a sort of a rollover effect). I also have a JLabel. The problem is that the JLabel seems to be positioned relative to the border - not the actual edge of the JPanel. So whenever I move my mouse over the panel, the label shifts over a couple of pixels. I would prefer it to stay stationary.
So I guess my question is, what's the best way to change the border of a panel without affecting the positions of the components inside the panel?
Here's the mouselisteners for the panel:
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
panel.setBorder(BorderFactory.createBevelBorder(1, Color.BLACK, Color.BLACK));
}
#Override
public void mouseExited(MouseEvent e) {
panel.setBorder(null);
}
});
The JLabel is added simply using borderlayout:
panel.setLayout(new BorderLayout());
JLabel label = new JLabel("testlabel");
panel.add(label,BorderLayout.PAGE_END);
You could try using an EmptyBorder when the bevel border is not in use. Give it the same width/height you would the bevel border.
I don't do a lot of messing around with layouts or their managers but that's what I would try.
Edit
Since it seems you may wish to have an overlay type effect instead of a border, you could create a custom JPanel class and include some code in the paintComponent(Graphics g) method to draw this overlay.
Something similar to:
public class OverlayBorderJPanel extends JPanel
{
boolean containsMouse = false; //set to true by mouseListener when contains
BufferedImage overlay = //you would need to load an image border here,
//rather than having a java created border
//You could have alpha so it is half see-through
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (containsMouse)
{
g.drawImage(//use 0,0 position with panel width/height)
}
}
}
I think it would work with something like that, but you may need to call the panel's repaint() method in the listener as well.

How do you add a JPanel onto another JPanel that has graphics?

Basically what im trying to do is to add a JPanel onto another JPanel that has painted graphics on it using the paintComponent() method. But the JPanel I am trying to add is not shown because it is covered up by the JPanel w/ Graphics on it.
How can I make it so that when I add the JPanel to the one w/ graphics it will show the JPanel in the front instead of being covered up by the Graphics?
All answers are appreciated! :)
If you need the code just tell me and I will gladly put it on here.
Alright here is the code:
package javavideogame;
public class Game extends JPanel implements ActionListener, Runnable
{
public Game(MainCharacter character)
{
setLayout(null);
setFocusable(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(ground, 0, 0, this);
g.drawImage(character.getImage(), character.getX(), character.getY(), this);
g.setColor(Color.RED);
g.drawRect(10, 10, character.getMaxHealth(), 10);
g.fillRect(10, 10, character.getHealth(), 10);
g.dispose();
}
public void getInventoryScreen()
{
Main.inv = new Inventory();
Main.game.add(Main.inv);
}
}
And here is the code for the JPanel that I'm adding to the Game JPanel
public class Inventory extends JPanel
{
public Inventory()
{
setLayout(null);
setSize(400, 300);
setBackground(Color.BLACK);
addKeyListener(this);
setFocusable(true);
}
}
Custom painting is done by overriding the paintComponent(...) method. My guess is that you are overriding the paint() method.
Read this section from the Swing tutorial on Custom Painting. Because you are overriding the wrong method, you end up painting the children first and then the custom painting is done on top.
If you need more help than post your SSCCE demonstrating the problem.
Just a reminder: java.awt and javax.swing don't mix well. When you're painting stuff on one panel AND trying to add a JPanel to it, you are bound to run into problems somewhere. A far better solution is to have 2 different JPanels, one for your graphics, and one for your inventory.
Something like this:
JPanel mainPanel = new JPanel(); //will hold BOTH panels
JPanel gamePanel = new Game(myCharacter); //declare game panel
JPanel inventoryPanel = new Inventory(); //declare inventory panel
//set up some layout
mainPanel.setLayout(new GridLayout(2, 1));
//add the graphics panel, then add the inventory
mainPanel.add(gamePanel);
mainPanel.add(inventoryPanel);
This will keep your swing and awt components from mixing and save you a lot of headaches.

java panel with png background

i found this link.. LINK what i want is there's a JPanel that has a background and another JPanel with half the size of the first JPanel but with an image that is transparent and with a face or a ball at the middle.. :) just like the screenshot from the link.. is that possible to code in java? :) im just thinking it like for web programming. just a sort of DIV's to have that but i dont know in java.. :) sorry for bad english.. :D i have this as a background..
package waterKing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class Main extends JFrame {
MainData data = new MainData();
public static void main(String[] args) {
Main frmMain = new Main();
frmMain.setExtendedState(Frame.MAXIMIZED_BOTH);
frmMain.setVisible(true);
}
public Main() {
data.tk = getToolkit();
data.d = data.tk.getScreenSize();
data.jP = new JPanel() {
protected void paintComponent(Graphics g) {
data.e = getSize();
data.iI = new ImageIcon("images/mainBG.png").getImage();
g.drawImage(data.iI,0, 0, data.d.width, data.d.height, null);
super.paintComponent(g);
}
};
data.jP.setOpaque(false);
data.jSp = new JScrollPane(data.jP);
data.jB = new JButton("EXIT");
data.jB.setBounds(10,10,200,40);
data.jB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
data.jP.setLayout(null);
data.jP.add(data.jB);
this.setTitle("Water King Inventory System");
this.setUndecorated(true);
this.getContentPane();
this.add(data.jSp);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
}
}
i dont know how to add another JPanel to show in the middle with this background
i dont know how to add another JPanel to show in the middle with this background
Its just like adding components to a panel. You need to use a layout manager and then the component will be positioned properly based on the rules of the layout manager. In your case you can set the layout manager of the background panel to be a BorderLayout. Then you can add a JLabel with the appropriate Icon to the center of the BorderLayout.
You will need to set the preferred size (or override the getPreferredSize() method of your panel since you add it to a scroll pane. Scrollbars will only appear when the preferred size of the panel is greater than the size of the scroll pane.
You should not be reading the image in your paintComponent() method since this method is called multiple times.
You should not be using the "screen size" to determine the width/height of the image because the frame will contain a border. You need to use the size of the panel.
Get rid of all the setBounds() code. Learn to use layout managers.
For a general purpose background panel that takes into account most of the suggestions made here check out Background Panel.

Categories