Hi I have made an actionlistener and I want to call a paintComponent method when you click the button?
I have googled it but with no luck.
Here is the actionlisetener,
graf.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
graf();
}
And here is the method,
public static void paintComponent (Graphics g) {
int width = Integer.parseInt(xinwindow.getText());
int hight = Integer.parseInt(yinwindow.getText());
g.setColor(Color.black);
g.drawLine((width/2)- 1, 0, (width/2) +1 , hight);
}
How to call it?
Any help would be appreciated.
Override the paintComponent method of a JComponent object you want to paint.
JComponent c = new JComponent() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = Integer.parseInt(xinwindow.getText());
int hight = Integer.parseInt(yinwindow.getText());
g.setColor(Color.black);
g.drawLine((width/2)- 1, 0, (width/2) +1 , hight);
}
}
And add
c.revalidate();
c.repaint();
after handling the click in actionPerformed.
You should start with some examples to construct a GUI.
static is for a global, a once occuring instance; one per class. Try to remove all, just the program's entry point:
public static void main(String args) {
JFrame appWindow = new MyFrame();
SwingUtilities.invokeLater(() -> appWindow->setVisible(true));
}
public class MyFrame extends JFrame {
private MyPanel panel;
public MyFrame() {
panel = new MyPanel();
add(panel);
panel.addClickListener(evt -> panel.repaint(50L));
}
}
public class MyPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
g2.drawRectangle(40, 40, getWidth() - 80, getHeight() - 80);
}
}
The mechanism is as follows:
A click is handled on the event dispatching thread of swing.
Here it says to repaint the panel in 50 ms.
A bit later repaintComponent is called from the swing framework in the conjunction of erasing the background and painting the child components.
In paintComponent the Graphics parameter for all newer java versions actually is a Graphics2D which has many nice methods.
Related
I'm having trouble getting smooth and fluid motion with java Swing. Using this code, which involves using a BufferedImage to draw graphics to before applying it to the panel, gives me results with shaky motion and stuttering. How can I fix this?
public class SmoothMotion extends JPanel {
public static final int size = 800;
public static int pos = 0;
public static void main(String[] args) {
JPanel panel = new SmoothMotion();
JFrame frame = new JFrame("Smooth As");
frame.setSize(size, size);
frame.add(panel);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Timer timer = new Timer(17, new ActionListener() {
public void actionPerformed(ActionEvent e) {
SmoothMotion.updateItems();
panel.repaint();
}
});
timer.start();
}
public static void updateItems() {
pos += 4;
pos = pos > 750 ? 0 : pos;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, size, size);
g.setColor(Color.RED);
g.fillRect(pos > 375 ? 750 - pos : pos, 100, 50, 50);
}
}
don't override paint(). Custom painting is done by overriding paintComponent(...)
there is no need for the BufferedImage. Swing is double buffered by default. Just invoke super.paintComponent(graphics) first to clear the background and then do your custom painting.
the painting method should NOT change properties of the class. You should have a method like updatePos(…) to update the position.
The ActionLIstener would invoke the updatePos() which would then invoke repaint().
See: get width and height of JPanel outside of the class for an example that demonstrates many of the above suggestions.
I ran into a problem where no matter what I do my JPanel won't repaint. I was using the following method to create a connect four game board and dynamically change the color of the circles as the play progresses, but I've simplified it into a test class which has the same issue.
I decided to use a state pattern design for each of the circles. The following is the code for the classes so it knows which color to print which are JPanel children classes.
public class GridCircle extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, 100, 100);
}
}
public class WhiteGridCircle extends GridCircle
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillOval(5, 5, 80, 80);
}
}
public class RedGridCircle extends GridCircle
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(5, 5, 80, 80);
}
}
The following is a test class where I attempt to change the class of a JPanel in the main method to see if it would change the color that is painted (which fails).
public class test extends JFrame
{
static JPanel testPanel = new WhiteGridCircle();
public static void main(String[] args)
{
new test();
testPanel = new RedGridCircle();
testPanel.revalidate();
testPanel.repaint();
}
test()
{
this.add(testPanel);
this.setSize(150,150);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
}
I can't figure out why the panel wouldn't repaint no matter what I've tried. From what I understand repaint() has no guarantee to call a the paint method but I see no reason it shouldn't when theres nothing else going on.
Because instance you created and added this.add(testPanel); is still new WhiteGridCircle().
You changed instance but the original one added to JFrame remains in the frame.
To change call this.getContentPane().add(testPanel); after instantiating RedGridCircle and before revalidate() call.
I have a very simple java swing application, I have a canvas class extended from JPanel
public class Canvas extends JPanel
{
private void doDrawing(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.drawString("Java 2D", 50, 50);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
doDrawing(g);
}
}
And then I have my main class
public class SwingCounter extends JFrame {
private JTextField tfCount; // Use Swing's JTextField instead of AWT's TextField
private int count = 0;
public SwingCounter () {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
cp.add(tfCount);
JButton btnCount = new JButton("Count");
cp.add(btnCount);
Canvas canvas = new Canvas();
canvas.setSize(150, 150);
cp.add(canvas);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if close-window button clicked
setTitle("Swing Counter"); // "this" JFrame sets title
setSize(300, 100); // "this" JFrame sets initial size
setVisible(true); // "this" JFrame shows
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SwingCounter(); // Let the constructor do the job
}
});
}
}
It is basically code from a tutorial, apart from the JPanel. Everything shows fine, the JPanel/Canvas doesn't. What is missing?
You are adding your Canvas class to a panel which uses a FlowLayout. The FlowLayout respects the preferred size of all components. Your component has a preferred size of (0, 0) so there is nothing to paint.
You need to override the getPreferredSize() method of your Canvas class to return an appropriate Dimension for your panel.
Read the section from the Swing tutorial on Custom Painting for more information and a working example that does implement the getPreferredSize() method.
Also, don't call your class Canvas, since that is already an AWT component and is confusing. Use a more descriptive name.
I want do draw a rectangle in my JFrame window, but I'am always getting a nullpointer error..
Why is it happening? what is the best (correct) way to draw graphics like rectangles, gradients, etc or something like falling snow in swing?
This is the Exception:
Exception in thread "Thread-0" java.lang.NullPointerException
at gui.Window.run(Window.java:24)
at gui.Window$1.run(Window.java:34)
at java.lang.Thread.run(Unknown Source)
And source:
public class Window extends JFrame implements Runnable {
private boolean run = true;
public Window() {
super.setSize(500, 500);
super.setTitle("MY GUI");
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
super.setContentPane(new Container());
}
#Override
public void run() {
Graphics g = super.getContentPane().getGraphics();
while (this.run) {
g.setColor(new Color(0, 0, 0, 255));
g.fillRect(0, 0, 200, 200);
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
#Override
public void run() {
Window window = new Window();
window.run();
}
}).start();
}
}
Error line 24: g.setColor(new Color(0, 0, 0, 255));
Why is it doing that?
The code you posted makes no sense.
First of all, every interaction with Swing components (except calls to repaint()) must be done in the event dispatch thread.
Second, there is no point in running an infinite loop that would constantly paint the same thing on a Graphics.
Third, that's not how it works. You can't get the Graphics associated to a component and paint on it. Instead, you must override the paintComponent(Graphics) method of a Swing component, wait for swing to call this method, and use the provided Graphics argument to paint whatever you want. If you want to change what is being painted, then you need to call repaint() on this element. Don't do that with JFrame. Create a subclass of JComponent or JPanel, and add an instance of the subclass to a JFrame, and then make this JFrame visible:
public class CustomComponent extends JComponent {
#Override
public void paintComponent(Graphics g) {
// paint here
}
#Override
public Dimension getPreferredSize() {
// return preferred size here
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.add(new CustomComponent());
f.pack();
f.setVisible(true);
}
});
}
}
getGraphics will return null if the component is not visible.
To make your Window visible you have to call setVisible(bool).
You also have to be careful using threads with Swing.
I have the folowing custom JPanel and I have aded it to my frame using Netbeans GUI builder but the background won't change! I can see the circle, drawing with g.fillOval(). What's wrong?
public class Board extends JPanel{
private Player player;
public Board(){
setOpaque(false);
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getxCenter(), player.getyCenter(), player.getRadius(), player.getRadius());
}
public void updatePlayer(Player player){
this.player=player;
}
}
If your panel is 'not opaque' (transparent) you wont see your background color.
You have to call the super.paintComponent(); as well, to allow the Java API draw the original background. The super refers to the original JPanel code.
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getxCenter(), player.getyCenter(), player.getRadius(), player.getRadius());
}
You need to create a new Jpanel object in the Board constructor.
for example
public Board(){
JPanel pane = new JPanel();
pane.setBackground(Color.ORANGE);// sets the background to orange
}
setOpaque(false);
CHANGED to
setOpaque(true);
I just tried a bare-bones implementation and it just works:
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame("Hello");
frame.setPreferredSize(new Dimension(200, 200));
frame.add(new Board());
frame.pack();
frame.setVisible(true);
}
}
public class Board extends JPanel {
private Player player = new Player();
public Board(){
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(player.getCenter().x, player.getCenter().y,
player.getRadius(), player.getRadius());
}
}
public class Player {
private Point center = new Point(50, 50);
public Point getCenter() {
return center;
}
private int radius = 10;
public int getRadius() {
return radius;
}
}
In order to completely set the background to a given color :
1) set first the background color
2) call method "Clear(0,0,this.getWidth(),this.getHeight())" (width and height of the component paint area)
I think it is the basic procedure to set the background...
I've had the same problem.
Another usefull hint : if you want to draw BUT NOT in a specific zone (something like a mask or a "hole"), call the setClip() method of the graphics with the "hole" shape (any shape) and then call the Clear() method (background should previously be set to the "hole" color).
You can make more complicated clip zones by calling method clip() (any times you want) AFTER calling method setClip() to have intersections of clipping shapes.
I didn't find any method for unions or inversions of clip zones, only intersections, too bad...
Hope it helps