I want to make a simple test JComponent so that i can see how things work when calling repaint(), paintComponent() and overriding all of the methods in paint or update. This is for learning only I know that this would not be done in the real world.
To Start with I would like to just to be able to make a component that I can draw a line to x and y points. I want my JComponent class to have every method that should be in a JComponent class even if it is not used. Thus I would like a list of every method that needs to be overridden for creating ones own JComponent. (this is for later)
My main question is why will my component not render on my JPanel that I am using to draw all of my components on.
Question: Why is my component not showing up on my JPanel?
Also I would like to try and make this post a learning option for building a JComponent from scratch. I will add the working code at the bottom as I go and the code that does not work also.
Code:
package testpak;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import java.awt.*;
public class FrameDemo {
JFrame frame = new JFrame("FrameDemo");
JPanel panel = new JPanel();
MyComponent cc = new MyComponent();
FrameDemo() {
setLookFeel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
panel.add(cc);
frame.add(panel);
frame.setSize( new Dimension(200, 200));
frame.setVisible(true);
}
private void setLookFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
}
public static void main(String[] args) {
FrameDemo fd = new FrameDemo();
}
}
JComponent Class:
package testpak;
import java.awt.Graphics;
import javax.swing.JComponent;
public class MyComponent extends JComponent {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(10, 10, 100, 100);
}
public void paint(Graphics g) {
super.paint(g);
paintComponent(g);
paintBorder(g);
paintChildren(g);
}
}
New Code:
package testpak;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
public class MyComponent extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize () {
return new Dimension(100, 25);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(10, 10, 100, 100);
}
public void paintBorder(Graphics g) {
super.paintBorder(g);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 50, 50);
g.setColor(Color.BLACK);
g.drawRect(0, 0, 50, 50);
}
public void paint(Graphics g) {
super.paint(g);
paintComponent(g);
paintBorder(g);
paintChildren(g);
}
}
Here is the new code. I set the preferred size and then am drawing a filled yellow rectangle and the a black border. The border does not appear to be showing on the bottom and right hand size. Can someone try this and see if it works for them.
Code that will throw and error
I tried to include these two methods into my JComponent. If I only have the first setSize(...) then everything works. If I also include the commented out version then I will receive and error.
#Override
public void setSize(Dimension d) {
this.setSize(d);
}
// #Override
// public void setSize(int x, int y) {
// Dimension d = new Dimension(x,y);
// this.setSize(d);
// }
Error:
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
at testpak.MyComponent.setSize(MyComponent.java:15)
JPanel uses FlowLayout which respects preferred sizes of components. You need to override getPreferredSize for MyComponent.
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 400);
}
Also only use frame.pack() instead of setSize before making the frame visible.
You don't need to worry about the extra panel that you have in your code above, you can just do this:
frame.setLayout(new BorderLayout());
frame.add(cc, BorderLayout.CENTER);
This will ensure that your component fills the maximum available space on the frame.
Related
I have drawn a line using the below function:
public void paint(Graphics p) {
super.paint(p);
p.drawLine(600, 200, 580, 250);
}
I am wondering is there a way that I can delete this line?
Then is it possible to call this function in the main() method of a program?
You can use a flag to know whether the line is displaying or not.
As I said before you need to build your GUI towards JPanels and not JFrames. Also overriding paintComponent and not paint method.
For example, the following program displays a line or hides it when you click the JButton, adapt that logic to your own program with your own conditions.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LineDrawer {
private JFrame frame;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new LineDrawer()::createAndShowGui); //Put our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
MyPane pane = new MyPane(); //Create an instance of our custom JPanel class
button = new JButton("Hide/Show");
button.addActionListener(e -> {
pane.setShowLine(!pane.isShowLine()); //Change the state of the flag to its inverse: true -> false / false -> true
});
frame.add(pane);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Our custom class that handles painting.
#SuppressWarnings("serial")
class MyPane extends JPanel {
private boolean showLine;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (showLine) { //If true, show line
g2d.draw(new Line2D.Double(50, 50, 100, 50));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300); //For the size of our JPanel
}
public boolean isShowLine() {
return showLine;
}
public void setShowLine(boolean showLine) {
this.showLine = showLine;
this.repaint(); //Everytime we set a new state to showLine, repaint to make the changes visible
}
}
}
I can't post a GIF right now, but the program itself works. Btw the above code is called a Minimal, Complete and Verifiable Example and on your next questions you're encouraged to post one in order to get specific, faster and better answers to your questions.
Im trying to add a JScrollpane to my JPanel. The problem is that the scrollpane doesn't recognize that my drawing is outside the frame. So how do I add the JScrollpane correctly?
Main class:
public MainFrame() extends JFrame{
public MainFrame() {
Container container = getContentPane();
container(new BorderLayout());
container.add(new JScrollPane(new Drawing()));
setSize(1280,720);
setVisible(true);
}
Drawing class:
public class Drawing() extends JPanel {
#Override
protected void paintComponent(Graphics g) {
g.drawLine(10, 100, 30000, 10);
}
}
There are a couple of errors in your code, let's step through each of them:
You're extending JFrame, and you should avoid it, see: Extends JFrame vs. creating it inside the program for more information about it. You're actually not changing its behavior so it's not needed to extend it.
For your JScrollPane to show the whole line, you need to change your window's size to be the same size of your line (as shown in this answer by #MadProgrammer).
Related to point 2, avoid the use of setSize(...) and instead override getPreferredSize(): See Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? for more information
You forgot to call super.paintComponent(...) method in your paintComponent() method.
Related to points 2, 3, you need to call pack() so Swing calculates the best preferred size for your component.
See this example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LongDraw {
private JFrame frame;
private Drawing drawing;
public static void main(String[] args) {
SwingUtilities.invokeLater(new LongDraw()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
drawing = new Drawing();
JScrollPane scroll = new JScrollPane(drawing);
frame.add(scroll);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class Drawing extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(10, 100, 3000, 10);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(3000, 500);
}
}
}
Which produces something similar to this:
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();
....
}
});
}
I'm from Germany. My English isn't very good but I try to do as less mistakes as possible. In addition it's my first question in this community and I hope I will get a useful and helpful answer (I've asked Google for more than 20 times)
So, I've wrote a program that paints two rectangles in different colors. When I click on a button, the color of one of these rectangles should change but it doesn't do so. I've tried repaint(), validate() and something I don't understand: pack()
Nothing helps. The color changes when I resize the windows but it should changes when I click the button...
Does somebody have an idea or the answer for this problem?
My code:
import java.awt.Graphics2D;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
#SuppressWarnings("serial")
public class Test extends JFrame {
public static boolean buttonClicked = false;
public Test() {
initComponents();
}
private void initComponents() {
zeichne z = new zeichne();
JFrame frame = new JFrame();
frame.setTitle("Grafiktest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
Dimension dm = new Dimension(500,500);
frame.setMinimumSize(dm);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JButton button = new JButton("Push me!");
button.setSize(100, 50);
button.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
buttonActionPerformed(evt);
}
});
panel.add(z);
panel.add(button, BorderLayout.SOUTH);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Test();
}
private void buttonActionPerformed(java.awt.event.ActionEvent evt){
if(buttonClicked == false)
buttonClicked = true;
else
buttonClicked = false;
repaint();
}
public static boolean getClicked() {
return buttonClicked;
}
}
#SuppressWarnings("serial")
class zeichne extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawRect(0, 0, 150, 100);
g2.setColor(Color.black);
g2.fillRect(0, 0, 150, 100);
boolean clicked = Test.getClicked();
if(clicked) {
g2.setColor(Color.red);
}
else {
g2.setColor(Color.gray);
}
g2.fillRect(10,10,130,80);
System.out.println(g2.getColor());
}
}
The properties of a components should be defined within the component. For example when you use a JLabel you can use setText(...), setForeground(...), setFont(...) etc. to change the state of the label and then the label will repaint itself.
So when you do custom painting you should do the same thing. That is in your "Zeichne" class you create a method to change the state of the class like setSelected(...). So your code might be something like:
public void setSelected(boolean selected)
{
this.selected = selected;
repaint();
}
You would also need to create the isSelected() method so you can access the state of the property. Then in the paintComponent(...) method you can do:
g2.setColor( selected ? Color.RED : Color.GRAY );
g2.fillRect(10,10,130,80);
Finally in the ActionListener the code would be:
z.setSelected( !z.isSelected() );
I get it!
The extension of JFrame and the additional declaration of a JFrame was the problem. I've deleted the declaration, put the super() into the method Test() and modulated the used methods of JFrame. Now it's working.
Thanks for your help!
I have a JFrame. Within that JFrame I have a JLayeredPane layed out with an OverlayLayout that contains multiple Jpanels. in one of those JPanels I have a BufferedImage. When the JFrame is resized, the image quickly dissapears and dislocates, then it jumps back again, dislocates again, back again, and so on.
I have tried a lot of things to stop the image from flickering, but I don't know what the cause of the problem exactly is.
The Jpanel that holds the image contains the following code to render the image:
protected void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawImage(myBufferedImage, 0, 0, 200, 200, null);
}
In trying to reconstruct and simplify the problem, I got a working version of what I wanted. I still don't know what the problem is with my other code though.
This is the code that works:
import java.awt.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Main {
public Main() {
// Create the JFrame:
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(600, 400);
window.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
// Create the pane to hold layers:
JLayeredPane layers = new JLayeredPane();
layers.setLayout(new OverlayLayout(layers));
// Add two layers:
layers.add(new MyGraphics());
layers.add(new MyImage());
//
window.add(layers);
window.setVisible(true);
}
public static void main(String[] args) {
Main app = new Main();
}
public class MyImage extends JPanel {
public BufferedImage source;
public MyImage () {
this.setPreferredSize(new Dimension(180,180));
this.setLocation(0,0);
try {
this.source = ImageIO.read(new File("image.jpg"));
} catch (IOException ie) {
ie.printStackTrace();
}
}
protected void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawImage(this.source, 0, 0, 180, 180, null);
}
}
public class MyGraphics extends JPanel {
public MyGraphics () {
this.setOpaque(false);
this.setPreferredSize(new Dimension(180,180));
this.setLocation(0,0);
}
protected void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawLine(0, 0, 180, 180);
}
}
}
Try adding below line of code in constructor:
public FlickerDemo()
{
// No flickering during resize
System.setProperty("sun.awt.noerasebackground", "true");
}