repaint doesnt clear the panel before repainting - java

I got a JPanel where a triangle is painted.
When someone clicks on a button the triangle should be repainted with new parameters. The problem is the old triangle is still there and the new one is messed up with part of the textfield underneath.
public class Vermessung {
private static void eingabe(){
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(screen.height/2, screen.height/4*3);
JPanel jp = new JPanel();
jp.setLayout(new BoxLayout(jp, BoxLayout.PAGE_AXIS));
//Eingabebereich
JPanel eingabebereich = new JPanel(new GridLayout(3, 1));
JPanel abc = new JPanel(new GridLayout(4, 2));
abc.add(new JLabel("Strecke"));
abc.add(new JLabel("Gemessener Wert in [m]"));
abc.add(new JLabel("a:"));
abc.add(tfa);
abc.add(new JLabel("b:"));
abc.add(tfb);
abc.add(new JLabel("c:"));
abc.add(tfc);
//AusgabeBereich
JPanel ausgabe = new JPanel(new GridLayout(2, 3));
ausgabe.add(new JLabel("p [m]"));
ausgabe.add(new JLabel("q [m]"));
ausgabe.add(new JLabel("h [m]"));
ausgabe.add(P);
ausgabe.add(Q);
ausgabe.add(H);
P.setEditable(false);
Q.setEditable(false);
H.setEditable(false);
//Buttons mit Listenern
JPanel buttons = new JPanel(new FlowLayout());
JButton ok = new JButton("OK");
JButton cancel = new JButton("beenden");
ActionListener al = new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
//Textfelder auslesen
TextfelderAuslesen();
//bei gueltiger Eingabe Höhe Berechnen
if(berechenbar){
berechnungPQ();
berechnungH();
P.setText(String.valueOf(p));
Q.setText(String.valueOf(q));
H.setText(String.valueOf(h));
sketch.update(vec);
sketch.repaint();
}else{
}
}
};
ok.addActionListener(al);
ActionListener beenden = new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
};
cancel.addActionListener(beenden);
buttons.add(ok);
buttons.add(cancel);
//Fensteraufbau
sketch.setPreferredSize(new Dimension(screen.height/2, screen.height/2));
jp.add(sketch);
eingabebereich.add(abc);
eingabebereich.add(ausgabe);
eingabebereich.add(buttons);
eingabebereich.setPreferredSize(new Dimension(screen.height/4, screen.height/4));
jp.add(eingabebereich);
f.add(jp);
f.setVisible(true);
}
}
public class Zeichnung extends JPanel{
public void paint(Graphics g){
zeichneDreieck(g);
}
private void zeichneDreieck(Graphics g){
berechneLaengen();
g.setColor(new Color(255,0,0));
g.drawLine(30, 30, ca, 30);
g.drawString("c", ca/2, 20);
g.drawLine(ca, 30, qa, ha);
g.drawString("a", (ca-pa/2), ha/2);
g.drawLine(qa, ha, 30, 30);
g.drawString("b", (qa/2), ha/2);
g.setColor(new Color(0,0,0));
g.drawLine(qa, ha, qa, 30);
g.drawString("h", qa+5, ha/2);
}
}

public void paintComponent(Graphics g){
super.paintComponent(g);
zeichneDreieck(g);
}
For JPanel, override paintComponent() instead. As the first line, call the super method.

Call
yourFrame.invalidate();
yourFrame.validate();
yourFrame.repaint();
See more information about invalidate() and validate() here.
Per the documentation: The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.

Create a new method clear(Graphics g) in your Zeichnung class and clear all the lines from it by adding the body as:
super.paintComponent(g);
this.removeAll();
this.updateUI();
Call this method in your zeichneDreieck method at the first line.

Related

java- repaint() method is misbehaving - 2?

This question is an extension of java- repaint() method is misbehaving?
(Reading it, is optional)
I am working on a Music Player
I am using a JSlider as seek bar and using a JLabel to draw text on screen, such as song name.
I am new to Graphics2D
Here's the minimized code:
public class JSliderDemo extends JFrame
{
JLabel label;
JSlider seek = new JSlider();
int y = 10;
public JSliderDemo()
{
setSize(400, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
createWindow();
setVisible(true);
startThread();
}
public void createWindow()
{
JPanel panel = new JPanel(new BorderLayout());
panel.setOpaque(true);
panel.setBackground(Color.BLUE);
panel.setBorder(new LineBorder(Color.YELLOW));
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
label = new Component();
label.setSize(300, 300);
createSlider();
layeredPane.add(seek, new Integer(50));
layeredPane.add(label, new Integer(100));
panel.add(layeredPane);
add(panel);
}
protected void createSlider()
{
seek.setUI(new SeekBar(seek, 300, 10, new Dimension(20, 20), 5,
Color.DARK_GRAY, Color.RED, Color.RED));
seek.setOrientation(JProgressBar.HORIZONTAL);
seek.setOpaque(false);
seek.setLocation(10, 50);
seek.setSize(300, 20);
seek.setMajorTickSpacing(0);
seek.setMinorTickSpacing(0);
seek.setMinimum(0);
seek.setMaximum(1000);
seek.setBorder(new MatteBorder(5, 5, 5, 5, Color.CYAN));
}
protected void startThread()
{
Thread thread = new Thread(new Runnable(){
#Override
public void run()
{
try
{
while(true)
{
if(y == label.getHeight()){y = 1;}
label.repaint();
y += 1;
Thread.sleep(100);
}
}
catch(Exception ex){}
}
});
thread.start();
}
protected class Component extends JLabel
{
#Override
public void paintComponent(Graphics g)
{
Graphics2D gr = (Graphics2D) g;
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setColor(Color.RED);
gr.setFont(new Font("Calibri", Font.PLAIN, 16));
gr.drawString("Song Name", 50, y);
gr.dispose();
}
}
public static void main(String[] args)
{
new JSliderDemo();
}
}
The problem is, when I call repaint() for JLabel it automatically repaints JSlider with it even though JSlider is not included in JLabel.
Output :
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted
Slider re-painted.........
Now if I remove label.repaint() from the Thread, then the JSlider is not re-painted.
Output:
Slider re-painted
Slider re-painted
Is the repaint() method supposed to work like this?
In my last question, I was told to use Layout Manager and when I did use GridLayout just for checking if it's the solution, then it worked!
Only JLabel was repainted.
But I want to overlap JLabel on JSlider, so I thought of using JLayeredPane. And now, the problem is back.
How can I solve this?
Bottom Line : How can I overlap JLabel on JSlider without leading to repaint() method misbehave ?
OR
Does the repaint() method work like this?
As was already mentioned in the comments, the reason for your JSlider being repainted is that it has overlapping bounds with the JLabel. Even though your label doesn't paint over the area of the slider swing will still mark the overlapping area as dirty (i.e. the overlapping part of the slider will need to be repainted) because swing doesn't know that you are only painting in one part of the component.
To reduce the amount of repaints you will need to make the size of your JLabel smaller. Preferably only as large as it needs to be by invoking its getPreferredSize() method. You'll then be able to move the text by moving the location of the label.
Also you shouldn't be doing updates to the gui in a plain Thread. Use javax.swing.Timer instead. It ensures that all updates to the gui happen on the swing event thread, which is where they should be made.
After making these adjustments to your code the slider is only repainted while the label is actually visually over the slider.
public class JSliderDemo extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(JSliderDemo::new);
}
private final JLabel label = new CustomLabel();
public JSliderDemo() {
setSize(400, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
createWindow();
setVisible(true);
startTimer();
}
public void createWindow() {
JPanel panel = new JPanel(new BorderLayout());
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
label.setLocation(0, 0);
label.setBorder(new LineBorder(Color.RED));
label.setSize(label.getPreferredSize());
layeredPane.add(createSlider(), Integer.valueOf(50));
layeredPane.add(label, Integer.valueOf(100));
panel.add(layeredPane);
setContentPane(panel);
}
protected JSlider createSlider() {
JSlider seek = new CustomSlider();
seek.setOrientation(JProgressBar.HORIZONTAL);
seek.setOpaque(false);
seek.setLocation(10, 50);
seek.setSize(300, 20);
seek.setMajorTickSpacing(0);
seek.setMinorTickSpacing(0);
seek.setMinimum(0);
seek.setMaximum(1000);
seek.setBorder(new LineBorder(Color.BLUE));
return seek;
}
private void startTimer() {
new Timer(100, e -> {
int y = label.getY();
int maxY = label.getParent().getHeight();
if (y == maxY) {
y = -label.getHeight();
}
label.setLocation(label.getX(), y + 1);
label.repaint();
}).start();
}
private static class CustomLabel extends JLabel {
protected CustomLabel() {
setFont(new Font("Calibri", Font.PLAIN, 16));
setText("Song Name");
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Painting Label");
}
}
protected static class CustomSlider extends JSlider {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Painting Slider");
}
}
}

JLabel doesnt show on JPanel

I already looked up this problem and found several threads. Tried all the solutions and none of them helped. I am trying to show a cross where my mouse is placed, the x and the y coordinate of my current mouse position is supposed to be shown in the top left and top right corner. In order to achieve this, I used two JLabels.
Maybe I am overlooking something?
I played around with the standard Text I set in the Labels, positioning, different Layouts for my frame and panel - nothing helps.
The following code should be good enough to get an understanding, I dont think it would be helpful if I left out something.
Fensterkreuz(){
jl1 = new JLabel("0");
jl2 = new JLabel("0");
jl1.setSize(new Dimension(100,100));
jl2.setSize(new Dimension(100,100));
jl1.setFont(new Font ("Arial", Font.PLAIN, 15));
jl2.setFont(new Font ("Arial", Font.PLAIN, 15));
cP = new Point();
this.add(jl1);
this.add(jl2);
addMouseMotionListener(this);
}
public void mouseDragged (MouseEvent e){
}
public void mouseMoved (MouseEvent e) {
cP = e.getPoint();
repaint();
}
public void paint (Graphics g){
g.drawLine((cP.x),(cP.y-15), (cP.x),(cP.y+15));
g.drawLine((cP.x-15),(cP.y), (cP.x+15),(cP.y));
jl1.setText(String.valueOf(cP.x));
jl2.setText(String.valueOf(cP.y));
}
public static void main (String [] args) {
JFrame f = new JFrame();
JComponent test = new Fensterkreuz();
test.setOpaque(false);
f.setVisible(true);
f.setSize(1500,1000);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(test);
}
Put repaint() at the bottom of your main method. Repaint calls the Paint method you have but I think you also have to add your own overriding repaint method to stop “flickering”.
You are overriding the paint() method. So, you need add super.paint(g); as the first line in your overridden paint() method.
To show the 2 labels properly, you need to add this.setLayout(new FlowLayout(FlowLayout.LEFT)); line.
I'm adding the full code with above changes here, so that you can run it and see the results yourself.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Fensterkreuz extends JComponent implements MouseMotionListener {
private JLabel jl1;
private JLabel jl2;
private Point cP;
Fensterkreuz(){
jl1 = new JLabel("0");
jl2 = new JLabel("0");
jl1.setSize(new Dimension(100,100));
jl2.setSize(new Dimension(100,100));
jl1.setFont(new Font ("Arial", Font.PLAIN, 15));
jl2.setFont(new Font ("Arial", Font.PLAIN, 15));
cP = new Point();
//this.setLayout(new FlowLayout(FlowLayout.LEFT));
//this.add(jl1);
//this.add(jl2);
this.setLayout(new GridBagLayout());
this.add(jl1, new GridBagConstraints(0, 0, 1, 1, 0.0, 1.0, GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
this.add(jl2, new GridBagConstraints(1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHEAST,
GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
addMouseMotionListener(this);
}
public void mouseDragged (MouseEvent e){
}
public void mouseMoved (MouseEvent e) {
cP = e.getPoint();
repaint();
}
public void paint (Graphics g){
super.paint(g);
g.drawLine((cP.x),(cP.y-15), (cP.x),(cP.y+15));
g.drawLine((cP.x-15),(cP.y), (cP.x+15),(cP.y));
jl1.setText(String.valueOf(cP.x));
jl2.setText(String.valueOf(cP.y));
}
public static void main (String [] args) {
JFrame f = new JFrame();
JComponent test = new Fensterkreuz();
test.setOpaque(false);
f.setVisible(true);
f.setSize(1500,1000);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(test);
}
}

i cant display buttons on top of a graphic

My question is how to display buttons on top of a circle which created with Graphics. My code is :
public class Grafik extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Grafik frame = new Grafik();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Grafik() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 784, 419);
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JButton btnNewButton = new JButton("Click");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fire();
}
});
btnNewButton.setBounds(64, 73, 32, 32);
contentPane.add(btnNewButton);
}
public void fire()
{
JPanel panel = new JPanel(){
#Override
public void paint(Graphics g)
{
g.setColor(Color.black);
g.drawOval(20, 20, 400, 400);
g.fillOval(20,20,400,400);
g.setColor(Color.white);
g.drawOval(60, 60, 300, 300);
g.fillOval(60,60,300,300);
}
};
panel.setBounds(10, 27, 383, 233);
setContentPane(panel);
JButton btn = new JButton();
btn.setIcon(new ImageIcon("C:\\Users\\nuria\\Desktop\\icon1.png"));
btn.setBounds(75, 76, 32, 32);
panel.add(btn);
}
}
This code is a little part of my project. I have a lot of panels in my project and my fire method isn't called firstly. I mean the fire method is called by sixth panel. I have to draw two circles with buttons. These buttons should be between two circles. But i cant display buttons directly. When i hover the buttons, they show. I want to show buttons instantly when the sixth panel opens.
I have two buttons above. When the first button is clicked, another panel should open and show circles(which created with Graphics) and another button(with a icon) between them.
JPanel panel = new JPanel(){
#Override
public void paint(Graphics g)
{
Don't override paint(). Custom painting is done by overriding paintComponent() and don't forget to invoke super.paintComponent() as the first statement.
The problem with your current code is that by overriding paint you have changed the default painting logic of the panel and the child components are never painted.
Take a look at the section from the Swing tutorial on Custom Painting for more information and examples, especially the section on A Closer Look at the Paint Mechanism.

ActionListener to draw shapes on separate panel

I want my GUI to draw circles/rectangles on the exact position I coded in the method paintComponent when I click on the respective buttons.
But I just don't know how to go on. What should I tell actionPerformed to do? Trying for a few hours to figure out a way, but I'm only getting errors.
public class Kreise extends JFrame {
Kreise() {
setLayout(new GridLayout(2, 1));
JLabel label = new JLabel("Draw Circ / Rect here: ");
label.setLayout(new FlowLayout(FlowLayout.CENTER));
JPanel jp1 = new JPanel();
jp1.setBackground(Color.LIGHT_GRAY);;
jp1.add(label);
JPanel jp2 = new JPanel(new FlowLayout());
JButton circ = new JButton("Circle");
JButton rect = new JButton("Rectangle");
circ.addActionListener(new KRListener(true));
rect.addActionListener(new KRListener(false));
jp2.add(circ);
jp2.add(rect);
MyPanel obj = new MyPanel();
jp1.add(obj);
add(jp1);
add(jp2);
setSize(400, 250);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public class MyPanel extends JPanel {
public boolean circleZ = true;
public void paintComponent(Graphics g) {
if (circleZ = true) {
super.paintComponent(g);
g.drawOval(150, 50, 50, 50);
} else if (circleZ = false) {
super.paintComponent(g);
g.drawRect(150, 50, 50, 50);
}
}
}
public class KRListener implements ActionListener {
boolean b;
KRListener(boolean b) {
this.b = b;
}
public void actionPerformed(ActionEvent e) {
?
}
}
public static void main(String[] args) {
new Kreise();
}
}
Presuming I understand the question clearly (you wish to toggle between the a rectangle or circle), in the ActionListener implementation you need to:
Toggle the appropriate boolean value
Call repaint on the JPanel instance that performs the painting
One way to accomplish these steps is to have a single toggle JButton, and pass an instance of the JPanel used for drawing to your ActionListener implementation, which can be used to accomplish both steps above:
public class KRListener implements ActionListener {
private MyPanel panel;
KRListener(MyPanel panel) {
this.panel = panel;
}
#Override
public void actionPerformed(ActionEvent e) {
panel.circleZ = !panel.circleZ;
panel.repaint();
}
}
And when you paint:
if ( circleZ ){
g.drawOval(150, 50, 50, 50);
}else{
g.drawRect(150, 50, 50, 50);
}
I dont know what you are using the global boolean variable b for But I noticed that you have to call the repaint() method when you press the Button.
public class KRListener implements ActionListener {
boolean b;
KRListener(boolean b) {
this.b = b;
}
#Override
public void actionPerformed(ActionEvent e){
//add some code here to change properties of the drawing before calling the repaint method?
repaint();
}
}

Create blocking glass pane show method

I've created a modal dialog inside a JFrame using the glass pane. My display method is quite simple: it creates a JPanel as glass pane with some alpha background and adds the JLabel and an ok and close button. Then the glass pane is set and displayed via frame.getGlassPane().setVisible(true);.
Everything works fine: if I call the method the pane is displayed and I can click ok or cancel and the glass pane hides. But the method returns directly after showing the glass pane. But I want it to behave like the JOptionPane methods: they block until the dialog is closed.
But everytime I'm trying to insert any kind of busy waiting at the end of my show method the GUI is frozen if I click the open button. I've also tried to get the mechanism from JDialog#show() but that's a bit to complicated for me.
So how to block the show method while the glass pane is visible?
Here is a simple example:
public class GlassPaneSSCE extends JPanel {
private JFrame parentFrame;
public GlassPaneSSCE(JFrame parent) {
parentFrame = parent;
addKeyListener(new KeyAdapter() {});
addMouseListener(new MouseAdapter() {});
setBackground(new Color(0, 0, 0, 100));
initGui();
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
private void initGui() {
setLayout(new FlowLayout(FlowLayout.CENTER));
setOpaque(false);
final JPanel content = new JPanel(new BorderLayout(4, 4));
content.setOpaque(true);
content.setBorder(new EmptyBorder(8, 8, 8, 8));
JLabel top = new JLabel("Title of this little modal dialog");
content.add(top, BorderLayout.NORTH);
JPanel inner = new JPanel(new BorderLayout());
content.add(inner, BorderLayout.CENTER);
inner.add(new JScrollPane(new JList(new String[] {
"Item 1 ",
"Item 2", "Item 3"
})));
Box ctrlButtons = Box.createHorizontalBox();
ctrlButtons.setBorder(new EmptyBorder(0, 4, 4, 4));
ctrlButtons.add(Box.createHorizontalGlue());
ctrlButtons.add(new JButton(new AbstractAction("OK") {
#Override
public void actionPerformed(ActionEvent e) {
parentFrame.getGlassPane().setVisible(false);
parentFrame.setGlassPane(new JPanel());
}
}));
content.add(ctrlButtons, BorderLayout.SOUTH);
add(content);
}
public void display() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
parentFrame.setGlassPane(GlassPaneSSCE.this);
parentFrame.getGlassPane().setVisible(true);
// Set the focus on the glass pane
requestFocus();
setFocusCycleRoot(true);
}
});
// The next line should be executed only if
// the ok button is clicked and not before
System.out.println("End of display()");
}
public static void main(String[] args) {
final JFrame f = new JFrame();
f.setLayout(new FlowLayout(FlowLayout.CENTER));
JTextArea tp = new JTextArea(10, 10);
for (int i = 0; i < 20; i++) {
JButton b = new JButton("Open");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GlassPaneSSCE gp = new GlassPaneSSCE(f);
gp.display();
}
});
f.add(b);
tp.append("Item " + (i+1) + "\n");
}
f.add(new JScrollPane(tp));
f.setSize(600, 600);
f.setVisible(true);
}
}

Categories