Drawing a circle and line onto a JFrame using JPanels without GridLayout - java

Currently I am trying to draw a line and a circle (which will become animated, like a wheel) onto my canvas.
I have a constructor called WheelAnimation().
Within this constructor, I have these two implementation classes, the first one is the circle:
class CircleComponent extends JComponent
{
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i < 200; i++)
{
// repaint();
g2.fillOval(i, 50, 50, 50);
}
}
}
final CircleComponent component2 = new CircleComponent();
panel.add(component2);
And this draws the line under the circle:
class LineComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(120, 120, 380, 120);
}
}
final LineComponent component = new LineComponent();
panel.add(component);
If I use a setLayout method such as: panel.setLayout(new GridLayout(1, 1));
I can make the two items show up (though their formatting is not good).
http://puu.sh/8fm9B/4f1dc1d0e5.png
But if I remove the setLayout method, nothing shows up onto my frame, despite the coordinates staying the same.
Could someone tell me why this is happening and give me a recommendation on how to set those layouts and make them show up?

I can make the two items show up (though their formatting is not good).
When you use a layout manager it is responsible for setting the size and location of the component added to the panel.
If you don't use a layout manager then your application code is responsible for setting the size and location of each component.

Related

How to stop JComponent.setLocation() from causing a repaint?

I'm trying to achieve camera shake for my game by randomly setting the location of the JPanel which everything in the game is drawn on. After a bit of experimentation, I am certain that JPanel.setLocation(Point p) triggers a repaint, which I don't want to happen.
So the way I create screen shake is by specifying the intensity and the frames it should last. However, the effect always wore off far too quickly, so I did some experimentation. I found that the paintComponent(Graphics g) method of the JPanel was triggered multiple times within one frame, but only while there was screen shake (how really does not add much to the point).
This is how the effect is generated:
public void display(){
framesAlive++; //<-- used to track when the effect has worn off
int intensityX = (int) (Math.random() * vals[0] - vals[0] / 2);
int intensityY = (int) (Math.random() * vals[0] - vals[0] / 2);
pane.setLocation(new Point(intensityX, intensityY));
}
And this is the simplified version of the paintComponent method:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
for (int i = 0; i < stockEffects.size(); i++) {
stockEffects.get(i).display(g);
}
}
Again, my guess is that setLocation() causes a repaint, which basically results in an infinite loop in which the paintComponent() method triggers the display() function, which triggers setLocation(), which triggers a repaint that starts the whole cycle again. This results in the framesAlive variable being incremented multiple times per frame, which throws the whole timing system off. Is there an elegant way to solve this?
you can use AffineTransform. this don't have to change objects real location.
it just change how to draw.
you can shake, rotate, flip, scale etc....
public static void main (String[] arg) {
MainFrame mainFrame = new MainFrame();
mainFrame.setVisible(true);
}
public static class MainFrame extends JFrame{
public MainFrame() {
this.setSize(600,600);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
this.add(mainPanel);
}
}
public static class MainPanel extends JPanel{
public void paint(Graphics g) {
super.paint(g);
// Panel Size = 400 X 400
g.drawLine(200, 0, 200, 400); // Y Axis
g.drawLine(0, 200, 400, 200); // X Axis
// Create Transform
AffineTransform at = new AffineTransform();
at.translate(200, 200); // Move Center Form (0, 0) To JPanel Center (200, 200)
// Change Transform
at.translate(-200, 0); // Move Center
// Set Transform To Graphics2D
Graphics2D g2d = (Graphics2D) g;
g2d.setTransform(at);
// Draw Rectangle By Graphics2D
g2d.fillRect(100, 100, 100, 100);
}
}

How to draw over a GLCanvas?

I wish to draw an HUD of sorts over a 3D OpenGL view, but it seems any drawing done in my panel will be overlooked, although it is done.
Here's some barebones code.
MyFrame;
public class MyFrame extends JFrame{
private static final long serialVersionUID = 1L;
public MyFrame(Labyrinth l){
super();
this.setTitle("My Frame");
this.setSize(512, 384);
this.setContentPane(new MyPanel());
//this.setVisible(true);//If needed here.
}
}
MyPanel;
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
public MyPanel(){
super();
this.setLayout(new BorderLayout());
MyCanvas mc=new MyCanvas(l);
mc.setFocusable(false);
this.add(this.mc, BorderLayout.CENTER);
//this.revalidate();//Doesn't seem needed in the instanciation.
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.mc.repaint();
g.setColor(new Color(128,128,128));
g.fillRect(0, 0, this.getWidth()/2,this.getHeight()/2);
//top-left quarter should be greyed away.
}
}
MyCanvas;
public class MyCanvas extends GLCanvas{
private static final long serialVersionUID = 1L;
public MyCanvas(){
super(new GLCapabilities(GLProfile.getDefault()));
}
}
The painting takes place, but isn't shown in the view. I've tried overriding repaint(), paint(Graphics), paintComponent(Graphics) and update(). I've been said that painting over "heavyweight" components was complicated, and that I should either paint directly in the component or use another type. I obviously need the GLCanvas to show a 3D render, and at the same time it does not seem to provide tools to draw an overlay. Someone told me to simply do my drawing in the JFrame's glassPane however that seems rather overkill, and I've been told never to play around the glassPane so I'm not planning on doing that.
I've seen many topics on the paintings call order but I cannot establish which would be correct while overriding such or such method, and I don't even know if or which method I should override. Is there an obvious way I'd have missed to have my simple JPanel paintings shown over its GLCanvas component?
First of all, I really wouldn't recommend getting a HUD through those means. As I can only imagine this hurting performance a lot. Granted I have never tried mixing Java, OpenGL and AWT's Graphics like that.
Now instead of using holding those classes together with duct tape, consider using JLayeredPane.
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(new MyCanvas());
layeredPane.add(new MyPanel());
frame.add(layeredPane);
Now the important part is that you must manually set the bounds of both components:
canvas.setBounds(x, y, width, height);
panel.setBounds(x, y, width, height);
If not you'll end up with the same problem as before:
The painting takes place, but isn't shown in the view
To demonstrate it working I created this small TestPanel class similar to your MyPanel.
public static class TestPanel extends JPanel {
private Color color;
public TestPanel(Color color) {
this.color = color;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
Then creating two instances like this:
JPanel panel1 = new TestPanel(new Color(255, 0, 0));
panel1.setBounds(25, 25, 100, 100);
JPanel panel2 = new TestPanel(new Color(0, 0, 255));
panel2.setBounds(75, 75, 100, 100);
Then adding them to a JLayeredPane and adding that to a JFrame and we see this:
I found using the JFrame glass-panel to be a good solution, I use it to draw debugging text on top of 3D graphics, I haven't experienced any problems with it. Using the glass-panel method is more convenient than using a JLayeredPane because the resizing of the 3D panel will be handled for you. Note the 3D graphics must be drawn in a GLJPanel component or the layering won't work (as opposed to GLCanvas which is not a Swing component). The paintComponent(Graphics g) will be called at the same rate as the frame-rate of the GLJPanel. Note also, the glass-pane is hidden by default so setVisible(true) must be called on it.
import javax.swing.JFrame;
import com.jogamp.opengl.awt.GLJPanel;
// ...
public class ApplicationWindow extends JFrame {
public ApplicationWindow(String title) {
super(title);
GLCapabilities gl_profile = new GLCapabilities(GLProfile.getDefault());
GLJPanel gl_canvas = new GLJPanel(gl_profile);
// ... code here to draw the graphics (supply a GLEventListener to gl_canvas)
setContentPane(gl_canvas);
StatusTextOverlayPanel myGlassPane = new StatusTextOverlayPanel();
setGlassPane(myGlassPane);
myGlassPane.setVisible(true);
setVisible(true);
}
class StatusTextOverlayPanel extends JComponent {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
g2d.setPaint(Color.BLACK);
String statusText = String.format("C-elev: %.2f S-view %.2f D-view %.2f", 1459.0, 17.0, 2.574691);
g2d.drawString(statusText, 10, 20);
}
}
}
Here is an example of what it could look like (You'll need additional code to draw the axis and the square shown)

Having images as background of JPanel

I am new in Java and I am currently creating a game with graphics. I have this class that extends from JFrame. In this class, I have many JPanels that needs an image as background. As I know, to be able to paint images in the JPanel, I need to have a separate class that extends from JPanel and that class's paintComponent method will do the work. But I don't want to make separate classes for each JPanel, I have too many of them; and with the fact that I am only concerned with the background. How can I do this? is it with an anonymous inner class? How?
For better understanding I provided some code:
public GUI extends JFrame {
private JPanel x;
...
public GUI() {
x = new JPanel();
// put an image background to x
}
Why not make a single class that takes a Image??
public class ImagePane extends JPanel {
private Image image;
public ImagePane(Image image) {
this.image = image;
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(0, 0) : new Dimension(image.getWidth(this), image.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
}
}
You would even provide hints about where it should be painted.
This way, you could simply create an instance when ever you needed it
Updated
The other question is, why?
You could just use a JLabel which will paint the icon for you without any additional work...
See How to use labels for more details...
This is actually a bad idea, as JLabel does NOT use it's child components when calculating it's preferred size, it only uses the size of the image and the text properties when determining it's preferred size, this can result in the component been sized incorrectly
You don't have to make another class for it? You could just do:
x = new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
//draw background image
}
};
You can do this in single line:
panelInstance.add(new JLabel(new ImageIcon(ImageIO.read(new File("Image URL")))));
I hope it will work for you.

How to set a transparent background of JPanel?

Can JPanels background be set to transparent?
My frame is has two JPanels:
Image Panel and
Feature Panel.
Feature Panel is overlapping Image Panel.
The Image Panel is working as a background and it loads image from a remote URL.
On Feature Panel I want to draw shapes. Now Image Panel cannot be seen due to Feature Panel's background color.
I need to make Feature Panel background transparent while still drawing its shapes and I want Image Panel to be visible (since it is doing tiling and cache function of images).
I'm using two JPanel's, because I need to seperate the image and shape drawing .
Is there a way the overlapping Jpanel have a transparent background?
Calling setOpaque(false) on the upper JPanel should work.
From your comment, it sounds like Swing painting may be broken somewhere -
First - you probably wanted to override paintComponent() rather than paint() in whatever component you have paint() overridden in.
Second - when you do override paintComponent(), you'll first want to call super.paintComponent() first to do all the default Swing painting stuff (of which honoring setOpaque() is one).
Example -
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(null);
CirclePanel topPanel = new CirclePanel();
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel();
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.drawArc(x, y, width, height, 0, 360);
}
}
}
Alternatively, consider The Glass Pane, discussed in the article How to Use Root Panes. You could draw your "Feature" content in the glass pane's paintComponent() method.
Addendum: Working with the GlassPaneDemo, I added an image:
//Set up the content pane, where the "main GUI" lives.
frame.add(changeButton, BorderLayout.SOUTH);
frame.add(new JLabel(new ImageIcon("img.jpg")), BorderLayout.CENTER);
and altered the glass pane's paintComponent() method:
protected void paintComponent(Graphics g) {
if (point != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2d.setColor(Color.yellow);
g2d.fillOval(point.x, point.y, 120, 60);
}
}
As noted here, Swing components must honor the opaque property; in this variation, the ImageIcon completely fills the BorderLayout.CENTER of the frame's default layout.
In my particular case it was easier to do this:
panel.setOpaque(true);
panel.setBackground(new Color(0,0,0,0,)): // any color with alpha 0 (in this case the color is black
(Feature Panel).setOpaque(false);
Hope this helps.
To set transparent you can set opaque of panel to false like
JPanel panel = new JPanel();
panel.setOpaque(false);
But to make it transculent use alpha property of color attribute like
JPanel panel = new JPanel();
panel.setBackground(new Color(0,0,0,125));
where last parameter of Color is for alpha and alpha value ranges between 0 and 255 where 0 is full transparent and 255 is fully opaque
public void paintComponent (Graphics g)
{
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.0f)); // draw transparent background
super.paintComponent(g);
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f)); // turn on opacity
g.setColor(Color.RED);
g.fillRect(20, 20, 500, 300);
}
I have tried to do it this way, but it is very flickery
As Thrasgod correctly showed in his answer, the best way is to use the paintComponent, but also if the case is to have a semi transparent JPanel (or any other component, really) and have something not transparent inside. You have to also override the paintChildren method and set the alfa value to 1.
In my case I extended the JPanel like that:
public class TransparentJPanel extends JPanel {
private float panelAlfa;
private float childrenAlfa;
public TransparentJPanel(float panelAlfa, float childrenAlfa) {
this.panelAlfa = panelAlfa;
this.childrenAlfa = childrenAlfa;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, panelAlfa));
super.paintComponent(g2d);
}
#Override
protected void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_ATOP, childrenAlfa));
super.paintChildren(g);
}
//getter and setter
}
And in my project I only need to instantiate Jpanel jp = new TransparentJPanel(0.3f, 1.0f);, if I want only the Jpanel transparent.
You could, also, mess with the JPanel shape using g2d.fillRoundRect and g2d.drawRoundRect, but it's not in the scope of this question.

Paint background of JPanel

How can I tell the paint method to draw background on JPanel only and not on the entire JFrame.
My JFrame size is bigger than the JPanel. When I try to paint a grid background for the JPanel, the grid seems to be painted all over the JFrame instead of just the JPanel.
Here parts of the code:
public class Drawing extends JFrame {
JPanel drawingPanel;
...........
public Drawing (){
drawingPanel = new JPanel();
drawingPanel.setPreferredSize(new Dimension(600,600));
}
public void paint(Graphics g)
{
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintBackground(g2); //call a METHOD to paint the for JPANEL
}
private void paintBackground(Graphics2D g2)
{
g2.setPaint(Color.GRAY);
for (int i = 0; i < drawingPanel.getSize().width; i += 300)
{
Shape line = new Line2D.Float(i, 0, i, drawingPanel.getSize().height);
g2.draw(line);
}
for (int i = 0; i < drawingPanel.getSize().height; i += 300)
{
Shape line = new Line2D.Float(0, i, drawingPanel.getSize().width, i);
g2.draw(line);
}
} //END private void paintBackground(Graphics2D g2)
}
If you want to do painting on the JPanel then override the JPanel, not the JFrame.
You should be overriding the paintComponent() method of JPanel. Read the section from the Swing tutorial on Custom Painting for a working example.
camickr is correct. So:
public class Drawing extends JFrame {
JPanel drawingPanel;
...........
public Drawing (){
drawingPanel = new MyPanel();
drawingPanel.setPreferredSize(new Dimension(600,600));
add(drawingPanel);
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
myBackgroundRoutine(g2);
}
}
You need to strictly separate your drawing from different components. Swing is already
managing subcomponents, so there is absolutely no need to implement drawings in your
Panel in the Frame (calling paintComponents() is a severe error).
And you should never override paint(), because only paintComponent()
is used in Swing. Don't mix both until you absolutely know what you are doing.
super.paintComponents(g);
I would suggest as your first point of investigation.
The code you posted is not complete, it's missing how the panel is added to the JFrame and which LayoutManager is being used.
The code seams to be correct. Are you sure the JPanel is not occupying the whole JFrame? Add a System.out.println(drawingPanel.getSize()) to check this.
If you are using the BorderLayout, the default for JFrame, and has just added the panel without any constraint, the panel will use the whole area. The PreferredSize is ignored.
Try this, just for testing:
public Drawing (){
drawingPanel = new JPanel();
drawingPanel.setPreferredSize(new Dimension(600,600)); // ignored
drawingPanel.setBounds(0, 0, 600, 600); // location and size
setLayout(null);
add(drawingPanel);
}
but IMO this is not the best or correct way to do it. I would prefer to override the paintComponent() method from the JPanel, as suggested by Thorsten and camickr.
But it will still use the whole area of the JFrame until other Component is added to the JFrame or the LayoutManager changed.
You should override the JPanel, not the JFrame to do painting. You can override the paintComponent() method of the JPanel

Categories