How to change/edit a JPanel object at startup/init - java

I want to change a JPanel-object in an applet at startup/init. I can't figure out how to do this. I've made a simple example of my problem in which I clear the JPanel. It does not work when it is called by the init() method but it works when I press the button. What can I do to change the JPanel at startup/init?
import javax.swing.JApplet;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TestStartUpApplet extends JApplet {
JPanel panel;
#Override
public void init() {
System.out.println("Init");
erasePanel();
}
private void erasePanel() {
Graphics g = panel.getGraphics();
g.clearRect(0, 0, 117, 48);
}
public TestStartUpApplet() {
getContentPane().setLayout(null);
panel = new JPanel();
panel.setBackground(Color.RED);
panel.setBounds(35, 36, 117, 48);
getContentPane().add(panel);
JButton btnTest = new JButton("Test");
btnTest.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
erasePanel();
}
});
btnTest.setBounds(35, 108, 117, 25);
getContentPane().add(btnTest);
}
}

Works just for me:
public class AppletPaintTest extends JApplet {
#Override
public void init() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
setLayout(new BorderLayout());
ImagePane pane = new ImagePane();
pane.setBackground(Color.RED);
pane.setOpaque(false); // Little trick
add(pane);
}
});
}
#Override
public void start() {
super.start();
}
public class ImagePane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 2 - (insets.top + insets.bottom);
g2d.setColor(getBackground());
g2d.fillRect(x + 10, y + 10, width - 20, height - 20);
g2d.dispose();
}
}
}

question
no idea why do you want to clear empty JPanel without any custom painting
what's wrong with JPanel with red Background
you clear JApplet before became visible on the screen
doesn't works correctly, because doesn't works anything
suggestions
don't use AbsoluteLayout, use LayoutManager instead
Graphics g = panel.getGraphics(); is usefull for printing to the printer, or save Graphics to the image, don't use this method for another reason
read how JApplet works
maybe to use JFrame in the case that you don't want to publish your GUI to the web browser
maybe to read tutorial about 2D Graphics

Whenever you do any custom painting outside of the usually system calls to your paint() or paintComponent() methods, you must call invalidate() on any components you wish to repaint. So for your erasePanel() method, I would suggest setting some flag then calling panel.invalidate(). Then inside your panel's paintComponent() method, you can check the flag to see if you need to draw the introductory picture or just leave the panel blank.

Ok, it seems that the problem was that my coding was just bad. I wanted to change a panel object by a method from another class and that's not the way to do it. I rewrote my code and made a panel class in which the painting is done using paintcomponent. I now use objects of this panel class and it shows the graphics I want at startup.
Thanks for the help!

Related

Java Applet - How to add double-buffering to JButton

I am currently using the Applet class to create a simple game. Because there is a flickering effect, I've added double-buffering for Graphics components by creating an off-screen buffer like so:
public class AppletTest extends Applet implements Runnable {
Thread thread;
Image img;
Graphics gfx;
public final int WIDTH = 700, HEIGHT = 500;
public void init() {
this.resize(WIDTH, HEIGHT);
thread = new Thread(this);
thread.start();
img = createImage(WIDTH, HEIGHT); // off-screen buffering
gfx = img.getGraphics();
}
public void draw(Graphics g) {
gfx.setColor(Color.BLACK);
gfx.fillRect(0, 0, WIDTH, HEIGHT);
gfx.setColor(Color.WHITE);
gfx.fillRect(50, 50, 100, 100);
gfx.setFont(new Font("Century", Font.BOLD, 30));
gfx.drawString("I feel good sometimes I don't", 200, 200);
g.drawImage(img, 0, 0, this); // draws the off-screen image
}
public void update(Graphics g) {
draw(g);
}
public void run() {
while(true) {
repaint();
try {
Thread.sleep(5);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
If you run the application, all the Graphics (.fillRect, .drawString, etc.) components/methods are drawn on the off-screen buffer. However, my goal is to add a JButton to the applet - and as expected, there's no off-screen loading for the JButton component (which means flickering).
Graphics gfx;
JButton button1;
public void draw(Graphics g) {
setLayout(null);
button1.setBounds(225, 400, 250, 50);
button1.setFont(new Font("Courier", Font.PLAIN, 17));
button1.setForeground(Color.WHITE);
button1.setBackground(Color.DARK_GRAY);
add(button1); // is it possible to draw the JButton on the off-screen buffer?
}
How would you add off-screen loading to a JButton component?
Applet (and JApplet) are officially deprecated, they are no longer supported by Java, Oracle, Browsers (or the community generally)
Swing components are, by default, double buffered. If you work with the painting system correctly, you shouldn't experience any flickering, if you do, it's clear sign that you're doing something wrong.
I would recommend having a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the Swing painting system works.
Swing is single threaded AND not thread safe. This means that you should not perform any long running operations within the context of the Event Dispatching Thread and you should not update the UI from outside the context of the EDT.
Have a look at Concurrency in Swing for more details.
A simple solution to these problems is to make use a Swing Timer, which can be used to schedule regular updates which are executed within the context of the EDT.
See How to Use Swing Timers for more details...
As a basic runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int WIDTH = 700, HEIGHT = 500;
public TestPane() {
setLayout(new GridBagLayout());
add(new JButton("Big fat button"));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.WHITE);
g2d.fillRect(50, 50, 100, 100);
g2d.setFont(new Font("Century", Font.BOLD, 30));
g2d.drawString("I feel good sometimes I don't", 200, 200);
g2d.dispose();
}
}
}
Okay, "But I absolutely, must, no questions asked, use Applet ... 😓, then I feel sorry for you, but that doesn't change the fact that Swing is already double buffered. The above example could easily be applied to a J/Applet simply by creating an instance of the JPanel and adding to an Applet container
Swing makes use of "passive rendering" algorithm, if you absolutely must be complete control, then you can have a look at BufferStrategy which hands complete control of the painting system over to you, but you won't be able to use Swing components, as they are updated by the Swing sub-system

Trying to draw lines in java over an image i already drew but i can't get it on top of the image?

I have to draw an archery target with two black lines in the innermost circle that forms a cross, but every time i adjust the lines so that the lines are closer to the centre it goes behind the image instead of appearing on top. How can I stop this? Does it need to have a separate set of instructions entirely?
This is my code:
package sumshapes;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class SumShapes extends JFrame
implements ActionListener {
private JPanel panel;
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.drawLine(250, 200, 250, 200);
g.drawOval(140,90,200,200);
g.setColor(Color.BLACK);
g.fillOval(140,90,200,200);
g.drawOval(162,109,155,155);
g.setColor(Color.BLUE);
g.fillOval(162,109,155,155);
g.drawOval(183,129,112,112);
g.setColor(Color.RED);
g.fillOval(183, 129, 112, 112);
g.drawOval(210,153,60,60);
g.setColor(Color.YELLOW);
g.fillOval(210, 153, 60, 60);
g.setColor(Color.BLACK);
}
public static void main(String[] args) {
SumShapes frame = new SumShapes();
frame.setSize(500,400);
frame.setBackground(Color.yellow);
frame.createGUI();
frame.setVisible(true);
}
private void createGUI(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container window = getContentPane();
window.setLayout (new FlowLayout());
}
public void actionPerformed(ActionEvent event) {
Graphics paper = panel.getGraphics();
paper.drawLine(20,80,120,80);
}
}
All your drawing should go into the paintComponent method of a lightweight component, such as a JPanel.
There should never be a need to call getGraphics. If you wish to change the drawing upon a particular action you should a) program the logic into paintComponent b) alter the logic in the Action c) call repaint on the Component
For example:
private JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);//call parent method first thing
//paint here
}
#Override
public Dimension getPreferredSize(){//provided so you can size this component as necessary
return new Dimension(500,400);
}
};
....
frame.add(panel);
frame.pack();
As an aside, I'd recommend placing all calls to to Swing components on the EDT - this means wrapping your Swing calls in the main method with SwingUtilities. eg
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
SumShapes frame = new SumShapes();
....
}
});
}

Getting paintComponent elements of a JPanel to work with another JPanel inside of it

I have a JPanel with a simple animated snow particle effect inside of a JFrame, and it works from on its own. But when I try and add another panel to it, it makes the effect of the snow stop. Can anyone help me with this? Is it a problem with layering, or something? Or is it an issue with my Layout Managers?
My Code:
package christmasfinal;
import java.awt.*;
import javax.swing.*;
public class ChristmasFinal extends JApplet {
public static void main(String[] args) {
JFrame frame = new JFrame();
TreePanel treePanel = new TreePanel();
frame.setSize(550, 420);
SnowBackgroundPanel snowPanel = new SnowBackgroundPanel(frame.getWidth(), frame.getHeight());
treePanel.setSize(200, 320);
snowPanel.setSize(frame.getWidth(), frame.getHeight());
snowPanel.setLayout(new BorderLayout());
frame.setLayout(new BorderLayout());
frame.add(snowPanel, BorderLayout.CENTER);
snowPanel.add(treePanel, BorderLayout.CENTER);
System.out.println(treePanel.getWidth() + " " + treePanel.getHeight());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setTitle("Christmas Final");
}
}
package christmasfinal;
import javax.swing.*;
import java.awt.*;
public class TreePanel extends JPanel{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int[] treeX = {getWidth()/2, getWidth()/2-20, getWidth()/2-60, getWidth()/2-20,
getWidth()/2-40, getWidth()/2-80, getWidth()/2-20, getWidth()/2-60, getWidth()/2-100,
getWidth()/2+100,getWidth()/2+60, getWidth()/2+20, getWidth()/2+80,
getWidth()/2+40, getWidth()/2+20, getWidth()/2+60, getWidth()/2+20, getWidth()/2};
int[] treeY = {getHeight()/2-120, getHeight()/2-80, getHeight()/2-40, getHeight()/2-40,
getHeight()/2-20, getHeight()/2, getHeight()/2, getHeight()/2+40, getHeight()/2+60,
getHeight()/2+60, getHeight()/2+40, getHeight()/2, getHeight()/2, getHeight()/2-20,
getHeight()/2-40, getHeight()/2-40, getHeight()/2-80, getHeight()/2-120};
g.setColor(Color.GREEN);
g.fillPolygon(treeX, treeY, treeX.length);
g.setColor(new Color(102, 51, 0));
g.fillRect(getWidth()/2-20, getHeight()/2+60, 40, 40);
}
}
package christmasfinal;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import java.math.*;
public class SnowBackgroundPanel extends JPanel{
Random rand = new Random();
//Create ArrayList for SnowParticles
ArrayList<SnowParticle> snow = new ArrayList();
//Create animation timer
Timer timer = new Timer(7, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
public SnowBackgroundPanel(int width, int height){
setSize(width, height);
System.out.println(getHeight() + " " + getWidth());
for(int i=0; i<50; i++){
snow.add(new SnowParticle());
snow.get(i).x = rand.nextInt(getWidth()+1);
snow.get(i).y = 0-rand.nextInt(getHeight()+1);
}
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.WHITE);
//Set, paint, and move snow particles
for(int i=0; i<50; i++){
g.fillOval(snow.get(i).x, snow.get(i).y, 10, 10);
snow.get(i).y += 1;
if(snow.get(i).y > getHeight()){
snow.get(i).x = rand.nextInt(getWidth()+1);
snow.get(i).y = 0-rand.nextInt(getHeight()+1);
}
}
}
}
You're adding your TreePanel to the BorderLayout.CENTER position of the SnowPanel which will effectively cover up the SnowPanel, so it should be expected to cover SnowPanel's graphics and animation. Likely setting TreePanel to be non-opaque by calling setOpaque(false) on it will allow you to see through it:
TreePanel treePanel = new TreePanel();
treePanel.setOpaque(false);
Other issues with your code:
Why have ChristmasFinal extend JApplet when it has no applet code nor behaviors?
Avoid calling setSize(...) on anything as that is a dangerous thing to do.
Better to let the components size themselves based on their layout managers and preferredSizes.
If you absolutely need to set a size, better to override getPreferredSize()
You should avoid having program logic within a paintComponent(...) method since you never have complete control over when or even if paintComponent(...) gets called.
I suggest an entirely different approach:
SnowBackgroundPanel & TreePanel which extend JPanel should instead implement Drawable
The Drawable interface will have a single method draw(Graphics2D g, Dimension sizeOfParent)
In the draw() method, put what was in paintComponent()
In the single area designated for custom painting, (e.g. RenderingSurface extends JPanel) keep a collection of the drawn elements in a list that respects their order. e.g. if the first element is the BG, it is rendered first.
In the paintComponent() of RenderingSurface, iterate the collection of drawable elements and draw each one.

How to properly work with transparent JPanel within a non-opaque JFrame?

I am working on some application designed to be not 100% opaque, so it basically darkens the desktop of the user and my Swing interface is shown on top of this "dark veil".
It seems to me that, when some Swing components are being moved over that veil, my JFrame would need to be repainted for my moving components not to leave a trail behind them. The thing is that repainting the JFrame is too slow and my application wouldn't run smoothly anymore.
For your convenience, I created a SSCCE class that illustrates my issue, here it is:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class TransparentFrameSSCCE extends JFrame {
private static final Dimension SCREEN_DIMENSIONS = Toolkit.getDefaultToolkit().getScreenSize();
private final JPanel movingPanel;
private TransparentFrameSSCCE() {
super();
this.setUndecorated(true);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(TransparentFrameSSCCE.SCREEN_DIMENSIONS);
// This makes my JFrame transparent (its alpha component is set to 0)
this.setBackground(new Color(0, 0, 0, 0));
this.movingPanel = new JPanel();
this.movingPanel.setBounds(0, 0, 50, 50);
this.movingPanel.setBackground(Color.RED);
final JPanel contentPane = new JPanel();
// This makes my panel semi-transparent (its alpha component is set to 128)
contentPane.setBackground(new Color(0, 0, 0, 128));
contentPane.setLayout(null);
contentPane.add(this.movingPanel);
this.setContentPane(contentPane);
}
#Override
public void setVisible(final boolean isVisible) {
super.setVisible(isVisible);
new Thread(new Runnable() {
#Override
public void run() {
int x, y;
for(;;) {
x = TransparentFrameSSCCE.this.movingPanel.getLocation().x;
y = TransparentFrameSSCCE.this.movingPanel.getLocation().y;
TransparentFrameSSCCE.this.movingPanel.setLocation(x + 5, y);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public static void main(final String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TransparentFrameSSCCE().setVisible(true);
}
});
}
}
Would anyone know any other way to do so?
UPDATE: Following #MadProgrammer's directions about Swing components transparency behavior, this is how to deal with my "dark veil". It works perfectly. Many thanks to him :)
final JPanel contentPane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
final Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 0, 0, 128));
g2d.fill(new Area(new Rectangle(new Point(0, 0), getSize())));
g2d.dispose();
}
};
contentPane.setOpaque(false); // Instead of: contentPane.setColor(new Color(0, 0, 0, 128)
Java components don't have a concept of transparency, they are either opaque or fully transparent (alright, the new transparency support for top level windows is an exception ;))
What you need to do is create a custom component that is fully transparent and the override it's paintComponent and fill the area of the component with your translucent color.
Also, don't modify the state of Swing components outside of the context of the Event Dispatching Thread, strange things begin to happen. A better solution might be to use a javax.swing.Timer
For example
Create rectangle with mouse drag, not draw
Java Swing: Transparent PNG permanently captures original background
How to make a transparent JFrame but keep everything else the same?
You may also want to take a look at Concurrency in Swing
Check out Backgrounds With Transparency for a simple explanation of the problem. Basically, you need to make sure your custom component paints the background.
Or instead of doing the custom painting you can take advantage of the AlphaContainer class which will do the painting for you:
//this.setContentPane( contentPane);
this.setContentPane( new AlphaContainer(contentPane) );

Can not draw oval on a JPanel

I have a JFrame created with GUI builder of Netbeans, which contains a JPanel only. I have created a method getPanel for getting a reference to this JPanel:
public class ShowDrawings extends JFrame {
public ShowDrawings() {
initComponents();
}
public JPanel getPanel(){
return panel;
}
private JPanel panel;
}
In my main function I am doing:
public class Main {
public static void main(String[] args){
ShowDrawings sd = new ShowDrawings();
sd.setSize(800, 600);
Graphics g = sd.getPanel().getGraphics();
g.setColor(Color.BLACK);
g.drawOval(400, 300, 50, 50);
sd.getPanel().paint(g);
sd.repaint();
sd.setVisible(true);
}
}
But it does not draw anything. Please help me.
I have looked some related questions but they are all suggesting extending JPanel and overriding its paint method. But I did not want to do that way.
Thanks.
I have looked some related questions but they are all suggesting
extending JPanel and overriding its paint method. But I did not want
to do that way
You should not override JPanel paint() method, rather paintComponent(..). This is best practice and should be done if you want code that will not produce anomalies. Also doing it in your current approach (as you have seen) makes creating persistent drawings a lot harder as they are wiped away on repaint()
Rather extend JPanel and override paintComponent(Graphics g) not forgetting to call super.paintComponent(g) as first call in overridden paintComponent(..) method. Also dont forget to override getPreferredSize() of JPanel so that we can return correct dimensions and pack() may be called on JFrame (+1 to #mKorbels comment):
Here is some example code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
initComponents();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void initComponents() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel testPanel = new JPanel() {
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GREEN);
//g2d.drawOval(10,10,100,100);//I like fill :P
g2d.fillOval(10,10,100,100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
}
};
frame.add(testPanel);
frame.pack();
frame.setVisible(true);
}
}
The first time you repaint() your ShowDrawings sd frame anything you've painted like this (sd.getPanel().getGraphics().drawOval(...)) would be erased by the original JPanel#paintComponent() method.
As Andrew Thompson has written:
Do not use Component.getGraphics(). Instead, subclass and override the paint() (AWT), or paintComponent() (Swing) method.
Component.getGraphics() simply can't work. Java uses a callback mechanism for drawing graphics. You are not supposed to "push" graphics information into a component using getGraphics(). Instead you are supposed to wait until Java calls your paint()/paintComponent() method. At that moment you are supposed to provide the Component with the drawings you would like to do.
If you're just checking/debugging something you could even do something like this:
class Test {
private JPanel panel = new JPanel() {
public void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.drawOval(400, 300, 50, 50);
}
};
}

Categories

Resources