I am trying to create a program with 2 JPanel using BorderLayout. The center panel is for random drawing of rectangle while the south panel is for the buttons.
I am getting a weird image of the button on the top left corner of the JFrame whenever I hover the mouse cursor over the North or South button. I did some research and found out that this could be the reason for having a transparent background. I tried using super.paintComponent(g) for the panel but the rest of the rectangles drawn earlier disappear. I need the rectangles to stay in the JPanel but not the weird image on the top left.
I don't know what I am doing wrong, hopefully someone can help or give some clue on how to solve this problem.
public class TwoBRandomRec extends JFrame{
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
TwoBRandomRec rec = new TwoBRandomRec();
rec.setSize(500,500);
rec.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rec.setVisible(true);
}
public TwoBRandomRec() {
//Create the buttons
JButton north = new JButton("North");
JButton south = new JButton("South");
DrawPanel drawPanel = new DrawPanel(500,500);
JPanel southP = new JPanel();
southP.add(south);
southP.add(north);
this.add(drawPanel, BorderLayout.CENTER);
this.add(southP, BorderLayout.SOUTH);
this.setTitle("TwoButtonRandomRec");
this.pack();
}
public class DrawPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Random rand = new Random();
private int x,y,xSize,ySize;
private int height,width;
public DrawPanel(int w,int h) {
width = w;
height = h;
}
public void RandomX(){
x=rand.nextInt(width-1);
xSize=rand.nextInt(width-x);
}
public void RandomY(){
y=rand.nextInt(height-1);
ySize=rand.nextInt(height-y);
}
public Color RandomColour(){
rand.nextInt(height);
return new Color(rand.nextInt(255),rand.nextInt(255),rand.nextInt(255));
}
#Override
protected void paintComponent(Graphics g) {
RandomX();
RandomY();
g.setColor(RandomColour());
g.fillRect(x, y, xSize, ySize);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
repaint();
}
}
}
You're not calling super.paintComponent
protected void paintComponent(Graphics g) {
super.paintComponent(g); // <-- Insert me here and watch problem go away
RandomX();
RandomY();
g.setColor(RandomColour());
g.fillRect(x, y, xSize, ySize);
try {
Thread.sleep(50); // <-- This is an INCREDIBLY bad idea, NEVER, EVER do this
} catch (InterruptedException e) {
}
repaint(); // <-- This is a bad idea, watch CPU max out...
}
You are obligated to call super.paintComponent to ensure that the paint chain is upheld correctly and things like opacity and cleaning up of the graphics context takes place.
The Graphics object is shared between components through a single repaint pass, failure to honor the correct paint chain will result in, well, problems like yours
Never update the UI in anyway from any paint method (this includes calling repaint), this is just causing your paint method to be recalled, over and over and over...until you CPU hits 100% and program hangs.
Never, EVER do any time consuming or block operations within the paint methods (or the UI generally), this will make it look like the program as hung and make the users upset (you think zombi hordes are bad :P). Blocking in this way prevents the EDT from responding to paint requests...
I'd recommend having a read through:
Performing Custom Painting
2D Graphics
Painting in AWT and Swing
Concurrency in Swing
Related
first questing for me here. Been searching forever but cant seem to find the answer.
Im working on a school assignment. Got given an ui and are supposed to make the different panels in it do different things in separate threads. Anyway, Im trying to make a triangle rotate inside one of the JPanels. I have managed to draw it and somewhat rotate it, but when I try to make a loop to update it it just blinks and then disappears again. Heres the code Ive written.
StartAssignment, starts the application
public class StartAssignment1 {
public static void main(String[] args) {
Controller controller = new Controller();
}
Controller, recieves calls from the ui and executes various functions in other classes
public class Controller {
private GUIAssignment1 gui = new GUIAssignment1(this);
private RotateShape rotateShape;
private Thread t1;
public Controller() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.Start();
}
});
}
public void startT1(JPanel panel) {
rotateShape = new RotateShape(panel);
t1 = new Thread(rotateShape);
t1.start();
}
public void t1Shutdown() {
rotateShape.shutdown();
}
RotateShape, where Im trying to rotate the damned thing
public class RotateShape implements Runnable {
JPanel panel;
private volatile boolean t1Running = true;
public RotateShape(JPanel panel) {
this.panel = panel;
}
private void draw() {
Graphics2D g2 = (Graphics2D) panel.getGraphics();
g2.rotate(Math.toRadians(10));
g2.drawPolygon(new int[] {50, 100, 150}, new int[] {150, 50, 150}, 3);
}
public void shutdown() {
t1Running = false;
System.out.println("Shutdown");
}
#Override
public void run() {
while (t1Running) {
try {
draw();
Thread.sleep(500);
System.out.println("loop working");
panel.repaint();
panel.revalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I'm not certain because I don't have access to your GUI code and therefore cannot test it myself, but here's an idea:
It looks like you're rotating your Graphics2D object by a fixed amount on every re-render (i.e. every invocation of draw()). It's possible that the internal JPanel code initiates the Graphics2D at a default rotation before every re-render, which may be why your rotation only causes the shape to "somewhat rotate."
Maybe try storing a variable for the radian offset (e.g. double offset;) as a member field of the RotateShapeclass, incrementing it on every re-render, then calling g2.rotate(Math.toRadians(offset));?
Addendum 1:
The reason that it may draw on top of the previous render is because the Graphics2D object is not clearing the canvas between re-renders. One way to fix this is to "clear" the canvas at the beginning of the draw() method by filling a rectangle that covers the whole canvas.
Addendum 2:
When you call panel.repaint(), it triggers the paintComponent() method of the JPanel object. So, by calling repaint() and your own draw() method, you are doing two separate renders, which may cause some graphical errors. If you were able to extends the JPanel class and use that, you should define an override to replace draw():
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add your rendering operations here
}
This way, calls to repaint() should trigger this method.
I have a problem when trying to draw some elements using paint method in Swing.
As title says, my whole frame collapses and does some weird repeating.
I made a separate JPanel so I can manipulate drawn shapes:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.WHITE);
this.setVisible(true);
}
public void paint(Graphics g) {
if(this.listaOblika.isEmpty()) return;
Iterator<Oblik> it = this.listaOblika.iterator();
while(it.hasNext()) {
it.next().crtajUBoji(g);
}
repaint(); // this causes problems!
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}
Here is the frame with this code:
And here it is without repaint method:
No, I know repaint method is essential in order to dynamically add shapes and actually draw, but I can't make this work correctly.
Also, as you can see from the code above, background of panel is set to white, but my frame would'n render it.
Hope there is enough information to solve my problem, if not, I will add code of my JFrame!
Thank you!
You should never override the paint method, as it handles a number of other things behind the scenes. You should override paintComponent instead.
As #Joe C answered, I should have been using paintComponent method, not paint! Working code:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.PINK);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Oblik obl : this.listaOblika) {
obl.crtajUBoji(g);
}
repaint();
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I need to set up a standard smiley face GUI where you can wink, blink, smile, and frown with JButtons. I need to do this with three separate classes: a class that draws the smiley face, a class with all my buttons and actionListeners that control the smiley face, and a class with the applet.
I keep getting NPE's on my buttons in the button class. I can't figure out why. Please go easy on me, I'm new to Java.
Here's my controls class:
public class SmileyControls extends JPanel implements ActionListener {
Smiley smiley;
JPanel controlPanel, eyePanel;
JButton open, wink, shut; // make these an animation???? see loop chapter in text
public SmileyControls(Smiley smileControl) {
smiley = smileControl;
controlLayout();
}
public void controlLayout() {
eyePanel = new JPanel(new FlowLayout());
open = new JButton("Open");
wink = new JButton("Wink");
shut = new JButton("Shut");
open.addActionListener(this);
wink.addActionListener(this);
shut.addActionListener(this);
eyePanel.add(open);
eyePanel.add(wink);
eyePanel.add(shut);
add(eyePanel);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==open){
smiley.setEyeCondition(0); // this calls the method setEyeCondition() from the smiley class that I created. I'm getting my NPE's here
}
if(e.getSource()==wink){
smiley.setEyeCondition(1); // and here
}
if(e.getSource()==shut){
smiley.setEyeCondition(2); // and here
}
}
}
Here's my smiley class:
public class Smiley extends JPanel {
int locX, locY, height, width;
Color moleColor;
int eyeCondition;
Graphics2D g2d;
public Smiley(int x, int y, int w, int h) {
locX = x;
locY = y + 100; // needed to add 100 pixels to make room for hair
height = h;
width = w;
moleColor = new Color(84,60,37);
eyeCondition = 0;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g2d= (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
setHair();
setFace();
setEyes(); // these methods paint the face
setMole();
setMouth();
}
// CONTROL METHODS
public void setEyeCondition(int eye) {
// the int values here are taken from the smileyControls class
// I think they'd repaint my applet if it weren't for the NPE's
if(eye == 0) {
// draw eyes open
g2d.fillOval(locX+width/5, locY+height/5,width/5, height/5); // left eye
g2d.fillOval(locX+3*width/5, locY+height/5, width/5, height/5); // right eye
repaint();
} else if(eye == 1) {
// draw wink
g2d.fillRect(locX+width/5, locY+height/5,width/2, height/20); // left eye winking
g2d.fillOval(locX+3*width/5, locY+height/5, width/5, height/5); // right eye open
repaint();
} else if(eye == 2) {
// draw blink
g2d.fillRect(locX+width/5, locY+height/5,width/2, height/20); // left eye blinking
g2d.fillRect(locX+3*width/5, locY+height/5,width/2, height/20); // right eye blinking
repaint();
}
}
public void setEyes() { // this method paints the original eyes
g2d.setColor(Color.black);
g2d.fillOval(locX+width/5, locY+height/5,width/5, height/5); // left eye
g2d.fillOval(locX+3*width/5, locY+height/5, width/5, height/5); // right eye
}
}
And here's my applet:
public class SmileyApplet extends JApplet {
Smiley smiley1;
SmileyControls control1;
JPanel container, smileyAndControls1, smileyAndControls2, smileyAndControls3;
BorderLayout border;
public void init() {
border = new BorderLayout();
setLayout(border);
setUpContainer();
}
public void setUpContainer() {
container = new JPanel(new FlowLayout());
smileyAndControls1 = new JPanel(new FlowLayout());
setUpControl();
setUpSmiley();
smiley1.setPreferredSize(new Dimension(450, 600));
smileyAndControls1.add(control1);
smileyAndControls1.add(smiley1);
container.add(smileyAndControls1); // add more controls to master container here
add(container, BorderLayout.CENTER);
}
public void setUpSmiley() {
smiley1 = new Smiley(0, 0, 400, 400);
add(smiley1);
}
public void setUpControl() {
control1 = new SmileyControls(smiley1);
}
}
At first you call setUpControl(), in there you create your SmileyControls and pass smiley1 to it (which is null at that time).
After that you call setUpSmiley() which creates the instance of Smiley.
So you probably only have to call setUpSmiley() before you call setUpControl() and your problem should be solved.
Try changing the order of these lines, as you use your smiley1 variable before it gets its value:
setUpControl(); // This uses smiley1
setUpSmiley(); // This instantiates smiley1
EDIT
You should move the drawing to the paint*() methods, or methods called directly from them.
That is, your setEyeCondition() method should set a property on the smiley, and the drawing should go into your setEyes()method.
This question may be a simple matter of me lacking a fundamental understanding of Java Swing or Graphics, and if so, I apologize.
I'm trying to develop a GUI application using Java Swing that can be controlled by an external device that sends pitch, yaw, and roll values via bluetooth to the Application. My idea is to create a cursor (perhaps an empty circle) that moves around when the external device moves around. I have no problems with receiving the data from the device, just the part where I need to actually paint something over all of my components.
I figured that a GlassPane was the easiest way to show a cursor over the entire application, and have it move around when the external device is moved around. I use a Thread to capture the data, and I'm trying to call repaint() afterwards, but it doesn't seem to be triggering.
Here is the relevant code:
JFrame:
public class Frame extends JFrame {
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
//Thread myoHandlerThread = new Thread(myoHandler);
//myoHandlerThread.start();
Frame frame = new Frame();
GlassPane glassPane = new GlassPane();
glassPane.setVisible(true);
frame.setGlassPane(glassPane);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Frame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(50, 50, 1000, 650);
/* Code to add and place components */
}
}
And my GlassPane:
public class GlassPane extends JComponent {
private static double pitch;
private static double yaw;
private static double roll;
Point point;
public void setPoint(Point p) {
this.point = p;
}
public void paintComponent(Graphics g) {
if (point != null) {
System.out.println("Test print statement");
g.setColor(Color.red);
g.fillOval(point.x - 10, point.y - 10, 20, 20);
}
}
public GlassPane() {
Thread handler = new Thread(deviceHandler);
handler.start();
}
private Runnable deviceHandler = new Runnable() {
#Override
public void run() {
Hub hub = new Hub("com.garbage");
System.out.println("Attempting to find device...");
Device externalDevice = hub.waitForDevice(10000);
if (externalDevice == null) {
throw new RuntimeException("Unable to find device!");
}
System.out.println("Connected");
DataCollector dataCollector = new DataCollector();
hub.addListener(dataCollector);
while (true) {
hub.run(1000/20); //gathers data and stores in dataCollector
roll = dataCollector.getRoll();
pitch = dataCollector.getPitch();
yaw = dataCollector.getYaw();
Point p = new Point();
p.setLocation(Math.abs(pitch) * 10, Math.abs(yaw) * 10);
setPoint(p);
repaint();
}
}
};
}
What I would like to happen is for a red circle to be drawn somewhere on the GUI depending on the orientation of the external device. At this point, my "test print statement" doesn't fire even once.
My guess is that I'm lacking some sort of basic understanding of Java's GlassPane or even how paint, paintComponent, and repaint even works. Could anyone point out what I'm doing wrong?
The likely cause of your frustration is trying to set the glass pane visible (Swing components are visible by default), before setting it as the frames GlassPane.
The JFrame is likely resetting the glass pane to be invisible, meaning that it won't be painted (no point painting something that's not visible)
Try setting the glass pane visible AFTER you apply it to the frame
I have a very basic little JFrame with JToggleButtons and subclassed JPanels that know how to draw what I want them to draw. Selecting a button causes an oval to appear in the corresponding panel. Unselecting the buttons makes the drawings disappear.
Unfortunately, minimizing (iconifying) and then restoring (deiconifying) causes any drawn shapes to disappear. So I need to trigger redrawings manually. The problem is that I can only get the redrawing done (that is, I only see it) if I show a message box first.
Here's the deiconify event for the JFrame:
private void formWindowDeiconified(java.awt.event.WindowEvent evt)
{
//having this message makes everything work
JOptionPane.showMessageDialog(null, "Useless message this is.");
//but if I skip it, I'm SOL
//what's going on?
drawAll();
}
This method goes over all of my buttons and asks for the redraws when necessary:
public void drawAll()
{
for (int i=0; i<channels; i++)
{
if (buttons[i].isSelected())
{
lightboxes[i].drawMe();
}
}
}
and here is my subclassed JPanel:
class MyJPanel extends JPanel {
public void drawMe()
{
Graphics myGraphics = this.getGraphics();
myGraphics.fillOval(0, 0, this.getWidth(), this.getHeight());
}
public void unDraw()
{
this.invalidate();
this.repaint();
}
}
The window should automatically be repainted once it is restored by the RepaintManager. The problem is you are not performing custom painting like you should...
This is not how to do custom painting...
public void drawMe()
{
Graphics myGraphics = this.getGraphics();
myGraphics.fillOval(0, 0, this.getWidth(), this.getHeight());
}
getGraphics can return null and is, at best, a snapshot of the graphics state.
Painting in Swing can occur at any time for many different reasons, most of which you don't have control over (nor should you care).
Your job is simply to respond to these repaint requests and update your components state.
Swing has a detailed paint chain which is called automatically and which you can use.
You should be overriding paintComponent and performing all painting within this method
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
Firstly, for speed I would use double buffering. It's best to paint your graphics off screen and display them to the screen when the drawing has completed. The below should sort you out.
public class MyPanel extends JPanel {
private BufferedImage buffer;
private Graphics2D canvas;
#Override
public void paintComponent(Graphics g) {
if(buffer == null) {
buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
canvas = buffer.createGraphics();
}
canvas.fillOval(0, 0, this.getWidth(), this.getHeight());
g.drawImage(buffer, 0, 0, this);
}
}
I'm just providing this answer so people can see what I ended up doing. The major lesson pointed out by everyone was to use the component's paintComponent. See the comments for issues that you might be experiencing yourself.
Edit: Updated to reflect comments from MadProgrammer.
//with help from Slihp and MadProgrammer on StackOverflow
//http://stackoverflow.com/q/17331986/1736461
class MyJPanel extends JPanel {
private boolean buttonSelected = false;
#Override
public void paintComponent(Graphics g) {
//make sure the background gets painted (wacky results otherwise)
super.paintComponent(g);
//now if the corresponding toggle button is on, plop on the circle
if (buttonSelected)
{
g.fillOval(0, 0, this.getWidth(), this.getHeight());
}
}
//an action listener for the button calls this
public void setButtonSelected(boolean buttonStateIn)
{
buttonSelected = buttonStateIn;
}
}
I subclassed the buttons too, so I can get its "ID" off of it from the event handler:
class MyJToggleButton extends JToggleButton
{
private int whoAmI;
public MyJToggleButton(int whoAmIn)
{
//the string given to the constructor becomes the button's label
//("1", "2", "3", etc..)
super(Integer.toString(whoAmIn + 1));
whoAmI = whoAmIn;
}
public int getWho()
{
return whoAmI;
}
}
The JFrame code that makes the buttons:
private void makeButtons(int howMany)
{
buttons = new MyJToggleButton[howMany];
for (int i=0; i<howMany; i++)
{
buttons[i] = new MyJToggleButton(i);
buttons[i].addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//find out which button this is
MyJToggleButton button = (MyJToggleButton) evt.getSource();
int which = button.getWho();
//send the button state to the corresponding lightbox
lightboxes[which].setButtonSelected(button.isSelected());
//trigger its redrawing
lightboxes[which].invalidate();
lightboxes[which].repaint();
}
});
this.add(buttons[i]);
}
}
And that's the only manual redrawing I have to do - resizing and reshowing and all those other fun things eventually hit up the paintComponent, and it just has to know if its button is pushed to know what to do. Super clean and just what I wanted.