Is there a different way to draw on a JPanel? - java

So, I have been having trouble drawing multiple objects on a JFrame, and I know I need to use Layout managers, so I decided to test it with one object before I do multiple, however when I run this code:
fps = 30;
panel = new JPanel();
frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.addKeyListener(new key());
running = true;
update = true;
ball = new Ball(0,0,1);
//panel.setBackground(Color.BLACK);
panel.add(ball);
panel.setVisible(true);
frame.add(panel, BorderLayout.CENTER);
frame.requestFocus();
frame.setVisible(true);
startTime = System.currentTimeMillis();
nothing draws (There is more to the code, i just didnt want to inlcude all of it). However, when I remove the comment and set the background to black, the JFrame turns black. So why is it that it wont draw my Ball object (which i know works) but will change the background? Is there a specific way you need to draw on a JPanel?

panel = new JPanel();
By default a JPanel uses a FlowLayout which respects the size of any component you add to the panel.
ball = new Ball(0,0,1);
I have no idea what the code in your Ball class looks like, but I would guess the preferred size is (0, 0).
You need to override the getPreferredSize() method of your Ball class to return the size of the Ball so that your layout manager can do its job.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
I know I need to use Layout managers,
The problem with a layout manager in this case is that the layout manager will control the position of the Ball which may or may not be what you want. If you want balls in random positions, then you will need to use a null layout. Then you will need to use the setSize() and setLocation() methods to control each Ball component.
Another option is to do custom painting of all your Balls. In this case you would add an object that you want to paint to an ArrayList. Then the custom painting code would iterate through the object in the list and paint them individually. See Playing With Shapes for ideas on this approach.

Related

Can I add a rectangle to a Jpanel in java swing and specify constraints for it using GridBagConstraints?

I'm pretty new to GUI and Java as a whole so I hope that I can explain this well enough and understand people's answers.
For a school project, I need to put a bunch of stuff on some rectangles but I'm having issues even adding one rectangle properly.
From researching online, this is what I have (the JPanel and GridBagConstraints are just there to show what I'd like to use):
public class GUI extends JPanel
{
public static void main (String [] args)
{
GUI g = new GUI();
JFrame window = new JFrame("Java Window");
window.setSize(1280, 960);
window.add(g);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel layout = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
}
#Override
public void paintComponent(Graphics g)
{
Color boxColour = new Color(194, 190, 190);
super.paintComponent(g);
g.setColor(boxColour);
g.fillRect(10, 10, 100, 100);
}
}
So right now, the rectangle appears in the window. But how can I add constraints to it? Is that possible? I would think that I should use JPanel to keep everything more organized since there will be many components so I tried adding this:
layout.add(g);
window.add(layout);
window.setVisible(true);
However, the rectangle no longer appeared. What am I doing wrong and how can I fix it and add constraints to my shapes? Thanks!
In your first scenario, the default layout manager of the frame is the BorderLayout and you are adding your "g" panel to the BorderLayout.CENTER. So based on the rules of the BorderLayout your "g" panel will take up all the space available in the frame. So you have plenty of space to paint your rectangle.
However, in your second scenario, your "layout" panel is using the default layout manager of a JPanel which is a FlowLayout which respects the preferred size of any component added to it.
The preferred size of the "g" panel is 10 x 10. So, when you add the "g" panel to the "layout" panel there is nothing to see because all you custom painting is done outside the bounds of the panel.
You need to override the getPreferredSize() method of your GUI panel to return a preferred size of (120, 120) so you can see your rectangle painted with a 10 pixel border around all the edges.
Read the section from the Swing tutorial on Custom Painting for more information and working examples demonstrating how to override the `getPreferredSize() method.
You will also need to read the Swing tutorial on How to Use GridBagLayout for example of using the constraints to add multiple components.

Swing layering - transparent component ignores underlying AWT element

First, to get it out of the way, I absolutely NEED to use heavyweight AWT component with a swing application. I need features from both of them.
The task is simple - render a heavyweight AWT Canvas (or any other element), render OpenGL scene directly onto it, then display Swing buttons above it for the UI.
My problem is that it works half-way.
I don't seem to have problems with Z-ordering. I am using jLayeredPanes for it, and I can move Canvas between layers and it actually works, popping on top or below other elements.
The problems are with transparency.
The thing is, Swing elements have Opaque parameter, and when it's set to false (non-opaque) - it should basically be transparent and you should see the next element below it. In my case, however, the AWT Canvas gets ignored, and you instead only see the next underlying SWING element.
Here are a couple of screenshots. They are taken from a standalone test project of mine. The canvas is stretched to the size of the frame, and in the upper left there is a JLayeredPane dummy element that is a simplified version of the menu.
On the first screenshot, the JLayeredPane's Opaque setting is set to true, and you can see that it's background property is set to Blue color.
On the second screenshot, everything is exactly the same but Opaque is set to false. Instead of displaying whatever is on the Canvas - what gets drawn in empty grey jFrame background.
Lastly, on the third screenshot I have put Canvas into a jPanel instead of leaving it on its own. As you can see, the Panel's orange color is seen through the transparent jLayeredPane, but the Canvas is yet again hidden.
Here's the code for the Frame layout. I would not post my rendering/context code right now
frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(width, height));
canvas = new Canvas();
canvas.setSize(width,height);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(width,height);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
Could anyone please explain me what is going on and how to do what I need to do.
I will repeat again - I have to use heavyweight component as render target. I am aware of solutions like JOGL's GLPanel which is a lightweight Swing-compatible component. But I tried that method and the performance is really slow, because instead of directly rendering onto it as a context target - it reads FrameBuffer from memory, flips it, and then paints it as BufferedImage. This path is not fitting for the limited resources of an embedded system that I'll be running on.
c0der said: Please post minimal reproducible example
Errrrm.... Didn't I?
Here, you can have it in complete java class form if you want, but I literally changed some variables for constants.
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(500, 500));
Canvas canvas = new Canvas();
canvas.setSize(500,500);
canvas.setBackground(Color.RED);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(500,500);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
}
}
A little update:
I initially suspected that because Swing elements delegate all their drawing to the underlying heavyweight element (In my case JFrame), then what happens is that the frame generates a single frameBuffer for itself and then displays on top of Canvas. Canvas itself is not handled in this generation and thus the frame "covers" over the canvas.
That doesn't seem to be the case. I tried making the JFrame undecorated, all panels non-opaque, and display the picture. The result - canvas is still "cut", and through the hole you can see the underlying IDE menu.
This makes me think that somewhere during Drawing, the Canvas itself detects that it is obscured by another element, and that it doesn't need to draw that area. So it "optimizes" itself and doesn't update these pixels.
Maybe I'm wrong. But here's another screenshot. This is the same example as before, but I took out 3d rendering and simply trying to display Canvas with background set to Red.
Once again, going to reply to my own question.
It turned out that I need to do
setComponentMixingCutoutShape(paneMenu, new Rectangle());
for the menu pane that lies underneath the button. That essentially tells java not to cut out the element from the heavyweight underlying component.

How do I put JPanel on another JPanel?

I am new to the java (and programming in general) and I am trying to make my very first program. I'm stuck on the same problem for about 5 hours now, so I've decided to ask for help.
Basically I'm trying to make a program (2d game) that has about 20 positions on the board. Each position is either blue (owned by player1), red(owned by player2) or black(not owned by anyone).
The way I'm going about this is in main I've put a method that calls setup game, and then a method that plays game. I am working on the setup game, basically all it does is it makes an object of class Background (extends JPanel, and overrides paintComponent()) and 20 objects of class Position(extends JPanel, and overrides paintComponent()).
So far I'm stuck on putting those Position objects on top of Background object.
When I do:
Background background= new Background();
frame.getContentPane().add(background);
Position position1= new Position;
frame.getContentPane().add(position1);
frame.setVisible(true);
it shows only a circle and no background as I was hoping, if I first add position and then background, I only have background and no circle.
Anyway I'm new to the java and I am still having trouble founding my way around, however I've tried to search for solutions, and I've found many different solutions to this problem (such as adding position to background first, and then adding background to frame, etc.) but I couldn't make any of them to work.
I am aware that the way I am adding them both to frame is (very likely) completely wrong, but I wrote it that way so you would (hopefully) be sure that what I've wrote actually does show you that my code for each of those classes draws something on the screen.
PS: I didn't copy my code here as most of variable and method names aren't in English so it's fairly hard to read, but if you still think its needed, I will add it. Also I'm sorry for my probably stupid question, but I'm kinda hitting a wall here and I've no idea what else to try.
Basically I'm trying to make a program (2d game) that has about 20
positions on the board. Each position is either blue (owned by
player1), red(owned by player2) or black(not owned by anyone).
Painting in Swing by default never returns PreferredSize, is required to override getPreferedSize()
JPanel has implemented FlowLayout in API, this LayoutManager accepting only PreferredSize came from JComponents added to this container
after a.m. changes to post an SSCCE, short, runnable, compilable
Background background= new Background();
frame.getContentPane().add(background);
Position position1= new Position;
frame.getContentPane().add(position1);
A JFrame uses a BorderLayout by default. Also by default when you add a component to a Container that uses a BorderLayout the comopnent is added to the CENTER. Only one comonent can be added to the CENTER so your Position comonent replaces the Background component.
You want to add the Position to the Background and then add the Background to the frame. Something like:
Background background= new Background();
Position position1= new Position;
background.add(position1);
frame.add(background);
Note: there is no need to uses getContentPane() when adding a component to the frame.
The root panel should be a JFrame with a Container class underneath. When you call someRoot.window.container = yourJPanel, that loads the JPanel as the main component view of the JFrame. Note, a JFrame can only hold one JPanel but other JPanels can hold other JPanels. Just as you add the initial JPanel to the JFRam, a JPanel's own container can be another JPanel. Hope this helps.
Like this:
JPanel temp = new JPAnel();
frame.getContentPane().add(temp);
temp.getContentPane().add(new JPanel());
After these additions, there is a command that is illuding me but you call on JFrame to get it to refresh in real time. I think it is something like:
frame.validate(); //thanks #SMT
or something,
Try using something like
jPanelExampleName.validate();
jPanelExampleName.repaint();
after adding your JPanels.
It sounds like you want to use one JFrame and attach JPanels to it. This is how I personally would do it.
Declare your JFrame and JPanels
JFrame frame1 = new JFrame( "App Name");
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
JPanel panel3 = new JPanel();
JPanel panel4 = new JPanel();
Set the Background (I'm using colors but you get the idea)
panel1.setBackground(Color.orange);
panel2.setBackground(Color.orange);
panel3.setBackground(Color.orange);
panel4.setBackground(Color.orange);
Set your layout for the JFrame (I'm using BoxLayout not sure which would be best for you) You can find the best one for you and some sample code here. Also just set the default close operation.
frame1.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame1.setLayout( new BoxLayout( frame1.getContentPane(), BoxLayout.Y_AXIS ) );
Then Just attach your JPanels
frame1.add( panel1);
frame1.add( panel2);
frame1.add( panel3);
frame1.add( panel4);
frame1.pack();
frame1.setVisible( true );
This will allow you to use the JPanels you created and then change the colors via other methods.

Resizing a JPanel inside of a JScrollPane clears the things I've drawn on the JPanel

I've got a JPanel inside a JScrollPane. I draw things in the JPanel, and at some point I might draw past the width of the JScrollPane. In this case, I'd like the horizontal scroll bar to appear, and I'd like to be able to scroll around to view different parts of the JPanel. However, I end up clearing the JScrollPane.
frame = new JFrame();
frame.setBounds(100, 100, 1000, 800);
localScrollPane = new JScrollPane();
localScrollPane.setBounds(768, 6, 226, 350);
frame.getContentPane().add(localScrollPane);
localView = new JPanel();
localScrollPane.setViewportView(localView);
drawSomeThings(localView.getGraphics());
// wait for user input
int newWidth = drawThingsPastTheWidth(localView.getGraphics());
// these next two lines clear it
localView.setPreferredSize(new Dimension(newWidth, localView.getHeight()));
localView.revalidate();
What am I doing wrong? Thanks!
drawSomeThings(localView.getGraphics());
Don't use the getGraphics() method to do painting. The painting will be lost the next time Swing determines the components needs to be repainted.
Instead custom painting is done by overriding the paintComponent() method of your component.
Do not use setPreferredSize method, here's a related thread.
Do not specify explicetly the size of the JScrollPane with setBounds. Let the LayoutManager of it's parent take care of this.
JScrollPane should use a
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED by default. So
the scrollbar should appear automatically when the preferred size of
the child component is higher than the displayed area. Try to
revalidate the JScrollPane instead of the JPanel.
It would appear that redraw is being called at some point, I've not used swing for a while so I'm not entirely sure what the problem is but try debugging and running through the code step by step to see where redraw is being called would be a good starting place.
you should certainly use repaint instead of revalidate. revalidate only marks all the container upto the top level as invalid.

Moving and resizing JPanels object inside JFrame

Continuing my quest of learning Java by doing a simple game, i stumbled upon a little issue. My gameboard extends JPanel as well as each piece of the board. Now, this presents some problems:
Cant set size of each piece, therefore, each piece JPanel ocupy the whole JFrame, concealing the rest of the pieces and the background (gameboard).
Cant set the position of the pieces.
I have the default flow manager. Tried setbounds and no luck.
Perhaps i should make the piece to extend other JComponent?
Added image:
That's the piece, now the greyed area is also the piece! Checked that by making a mousePressed listener and assigning some stuff to it. Below the grey area, is the gameboard (or at least, should be!), another JPanel.
alt text http://img42.imageshack.us/img42/2227/screenshotvdy.png
Some code:
package TheProject;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameWindow extends JFrame {
public GameWindow() {
setSize(800, 600);
setLocationRelativeTo(null);
Map map = new Map(800, 600, 2);
add(map);
MilitaryUnit imperialRussia = new MilitaryUnit(30, Color.BLACK, Color.YELLOW, Color.WHITE);
imperialRussia.setPreferredSize(new Dimension(30, 30));
add(imperialRussia);
}
}
This happens when i apply the pack() method:
alt text http://img442.imageshack.us/img442/5813/screenshot2ml.png
Packs around the Unit, not the map which is bigger and fills the JFrame.
For a game that has random movement of pieces you would probably use a "null layout".
Read the section from the Swing tutorial on Absolute Positioning for more information.
I wrote a few games using JPanel. Basically the way I use JPanel is like I'm using a Canvas, viz I draw directly on it by overriding the paint() method. The reason why I use JPanel is because I can determine the size of my game world, then use the setPreferredSize() to set the size of the panel. I then add the panel to a JScrollPane. So this will take care of the panning, etc.
Say I'm writing a 2D game. This is how I use JPanel. I have a logical map (a 2D array) which holds my game map. Say each location is 32x32 pixel. So you start drawing the ground and what is on that ground in that location. eg in x=1, y=2 which is screen location x=32, y=64, you draw the ground first, then draw what is on the ground. So a rough outline of the render loop would be something like this
for (int y = 0; y < map.length; y++)
for (int x = 0; x < map[y].length; x++) {
drawGround(map[y][x])
for very element on on map[y][x] - render them
}
You set a MouseListener listener to the JPanel, so every mouse click you translate back to the map eg. mouse click x=54, y=72 would correspond to x=1, y=2. The calculation is a bit tricky if you have an isometric view.
The thing you have to be careful here is that everytime when you scroll the panel via the scroll panel, paint() will be called. So it is good to render your game board on a BufferedImage and then in the paint() method just draw the BufferedImage otherwise it'll be too slow.
Have you tried NetBeans' visual editor? You can use it to drag, drop, and resize to your convenience Swing objects in Design View, and can then switch to Code View to see the generated code. You can learn that way.
Have you tried setPreferredSize(Dimension d)?
Edit: You need to call pack() on your JFrame, after you've added your components:
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(800,600));
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(200,200));
frame.add(panel);
frame.pack();
frame.setvisible(true);
Also, you add a Map to your JFrame, which has the same dimensions as your JFrame - fine. But then afterwards, you add another component, to the default flowlayout. This cannot fit into your frame, as the Map already occupies 100% of the space.

Categories

Resources