Why do my components appear next to each other? - java

I'm currently trying to create a program that moves a rectangle over a background Image with keyboard keys. The problem I'm facing is that when I draw the components they are simply placed next to each other, instead of the square overlaying the background image. Here's the code to display both the components;
JLayeredPane panel = new JLayeredPane();
panel.setLayout(new FlowLayout());
add(panel);
paintBackground pb = new paintBackground(bimg);
panel.add(pb, 1, 0);
paintPlayer cc = new paintPlayer(startX, startY);
panel.add(cc, 2, 0);
pack();
setVisible(true);
I believe the problem is that the paintPlayer component is set to full size, and there seems to be a background. The paintPlayer component code looks like this:
public Dimension getMinimumSize() {
return new Dimension(800,600);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800,600);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(800,600);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
System.out.println(startX + startY );
g.fillRect(startX, startY, 30, 30);
}
I've had a go at setting the component size to just the size of the rectangle, but that way I can't move the rectangle by using the first two values in fillRect. The background for the rest of the space filled by the component (800x600) seems to be opaque.
When added, the components just display next to each other, like this: https://gyazo.com/57245c518e02778c36ffc89ba75d5a81. How do I go about adding the paintPlayer ontop of the paintBackground, so that it only covers the rectangle on the background Image.
I've done a fair bit of searching but I can't seem to work it out. Perhaps something to do with the layout? One other thing I've noticed is that by doing this, neither the frame or the pane benefit from a setBackground, as it's not visible.
Cheers for any help.

This is the default Constructor of JLayerdPane.
public JLayeredPane() {
setLayout(null);
}
You see it uses normaly AbsolutLayout. And if you read here:
Note: that these layers are simply a logical construct and LayoutManagers will affect all child components of this container without regard for layer settings.
You should understand what is wrong. Check OverlapLayout.

Related

Make JScrollPanel dynamically resizable with JPanel drawing

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.

Simple way of scrolling over a certain rectangle with a JScrollPane and a JPanel (custom)

I have created a custom JPanel class called ImagePanel. I override the paintComponent method like this...
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0,0, null);
}
The purpose of the custom panel is to simply draw an image.
In my JFrame, I create a ScollPane that is added to the JFrame. When I created the ScrollPane though, I pass in the instance of my imagePanel, like this...
ip = new ImagePanel();
JScrollPane jsp = new JScrollPane(ip);
this.add(jsp);
Now all I want as an easy to use way of using the scroll bars to scroll over my image. Right now the image is very large and scrollbars do not appear. I use the policy to make them visible, but the handles to the scrollbars are not there.
Does anyone know an easy way to do this?
Try with JPanel#setPreferredSize() that will force the JScrollPane to show the scroll bar if needed.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0,0, null);
// set the size of the panel based on image size
setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
}
EDIT
Setting setPreferredSize() inside overridden paintComponent() is not a good way.
You can do it in a simpler way using JLabel as suggested by #mKorbel. For more info have a look at the comments below.
BufferedImage image = ...
JLabel label = new JLabel(new ImageIcon(image)); // set the icon
JScrollPane jsp = new JScrollPane(label);
Screenshot:

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.

Creating a JToolTip-like Component

I'm writing a custom component that displays some bioinformatics data, and I'd like to be able to show additional information about the location the mouse is at when the user holds down a certain key. This seems like an obvious job for a tooltip, but there are a few problems that seem to be preventing this from working. First, I want to have the tooltip follow the mouse and change its text dynamically. This works somewhat by overriding getToolTipText and getToolTipLocation for the component, but the tooltip flickers as the mouse position is updated and doesn't display over the sub-components (it's a JPanel with some JTextPanes inside it). I also don't think there's any way to make it display immediately without a call to the ToolTipManager, which I believe would change the delay for all other components.
It looks like there are workarounds for some of these problems, but they're rather complicated and inelegant so I'm thinking a good solution would be to just create my own component, fill it with the relevant information and show it myself. However, this needs to be some kind of top-level component because it needs to be able to extend slightly beyond the borders of the parent component or even the containing JFrame and be drawn over everything else. The only objects I know of that have this functionality outside of JToolTip are JFrame and JDialog, which have borders with titles and close buttons which I don't want. Is there some way to accomplish this?
One option is to use a glass pane. In this case your tooltip won't be able to go outside of the frame, but you can easily position it relative to how near it is to a side of the frame. Some example code that draws a bubble (which you can fill with text in the paint method) that follows the mouse.
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(new Dimension(500, 500));
JPanel glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.setLayout(null);
frame.setGlassPane(glassPane);
frame.getGlassPane().setVisible(true);
final MyInfoBubble mib = new MyInfoBubble();
mib.setBounds(10, 30, 100, 50);
((JPanel)frame.getGlassPane()).add(mib);
frame.getContentPane().addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent me) {
mib.setBounds(me.getPoint().x, me.getPoint().y, 100, 50);
}
});
((JPanel)frame.getGlassPane()).validate();
((JPanel)frame.getGlassPane()).repaint();
frame.setVisible(true);
}
static class MyInfoBubble extends JPanel
{
public MyInfoBubble()
{
setVisible(true);
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
}
}

Canvas isn't resizing with window in Java?

I have a jFrame with a Canvas on it. When I run my program in Windows XP/Vista and resize the window the canvas resizes proportionately along with the window.
However, in Ubuntu linux, when I compile the same java application and resize my window, the Canvas stays the same size.
What do I need to do to make my Canvas resize with my window in both Windows and Linux? What is the deal with the discrepancy?
Main.java
public class Main {
public static void main(String[] args)
{
JFrame frame = new JFrame("BallBounce");
frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS));
BallCanvas ballCanvas = new BallCanvas();
frame.getContentPane().add(ballCanvas);
frame.getContentPane().add(controlPanel);
frame.pack();
frame.setVisible(true);
}
}
BallCanvas.java
public class BallCavnas extends Canvas {
public BallCanvas()
{
setPreferredSize(new Dimension(640, 400));
setIgnoreRepaint(true);
... various gui controls are wired up here
}
... rest of canvas code
}
Edit: My source code is zipped up here incase someone wants to take a look:
http://www.filedropper.com/ballbounce
I've done the suggestions made by Dave Ray, and it still isn't resizing the Canvas? Remember, it resizes for me fine when I compile this java program and run it in windows. Only in linux does it does this to me. I'm also running Java 6 Sun 1.6.0.10 JVM, if it matters.
alt text http://img158.imageshack.us/img158/7642/screenshotww0.png
Perhaps my canvas is resizing by my BufferStrategy/Graphics aren't resizing ?
Edit 2: From the screenshot, it is definitely set to CENTER:
frame.getContentPane().add(ballCanvas, BorderLayout.CENTER);
frame.getContentPane().add(controlPanel, BorderLayout.SOUTH);
Resolved
Apparently the "Canvas" was getting resized but I was doing something weird with it's buffer strategy that wasn't allowing IT to be resized. I fixed it. Thanks everyone!
Perhaps, the layout manager is just attempting to honor your preferred size.
I would:
A) remove the preferred just to see what happens ( not a very good idea anyway )
or
B) not use canvas in first place but JComponent. After all Canvas is AWT component, and I not pretty sure how they work as today anyway. JComponent is a light weight component and since you're using a JComponent as container they would... mmhhh work better together?
Gee.. I'm giving voodoo programming suggestions now. Better get to work.
C) What have always worked for me. Make an small proof of concept, by adding step by step the stuff in my code. Start with empty canvas, then add the preffered size, then etc. etc. Chances are, the bug is on the paint method :P
Good luck.
:)
Use a border layout instead:
frame.getContentPane().setLayout(new BorderLayout());
BallCanvas ballCanvas = new BallCanvas();
frame.getContentPane().add(ballCanvas, BorderLayout.CENTER);
frame.getContentPane().add(controlPanel, BorderLayout.SOUTH);
Also get rid of setPreferredSize() in favor of frame.setSize() to set the initial size of the display.
Another good solution is put the canvas into an JPanel,
like i did
panel.add(new JPanel() {
#Override
public void paint(Graphics g) {
// do not paint
}
}.add(capture));
and in the canvas function something like
#Override
public void paint(Graphics g) {
Dimension currentSize = getParent().getSize();
int width = currentSize.width;
int height = currentSize.height;
if (offscreenImage == null || !currentSize.equals(offscreenDimension)) {
// call the 'java.awt.Component.createImage(...)' method to get an
// image
offscreenImage = createImage(width, height);
offscreenGraphics = offscreenImage.getGraphics();
offscreenDimension = currentSize;
}
if (image != null) {
offscreenGraphics.drawImage(image, 0, 0, width, height, null);
} else {
offscreenGraphics.setColor(Color.BLACK);
offscreenGraphics.fillRect(0, 0, width, height);
}
//scratch image
g.drawImage(offscreenImage, 0, 0, this);
}
for example. This make it very simple. Take the parent Panel to fit to your needs.
If you followed Dave's advice, esp. putting the ballCanvas in the CENTER, then you must be setting a maximum size for ballCanvas.

Categories