Understanding painting in swing - java

I'm trying to understand what actually paints components in Swing. I read this article about painting in AWT and Swing and now tried to write the following simple program:
//A simple wrapper to understan how paint() works
public class MyButton extends JButton{
/**
* Default serialVersionUID
*/
private static final long serialVersionUID = 1L;
private final JButton jButton;
public MyButton(JButton jButton) {
this.jButton = jButton;
}
#Override
public void paint(Graphics g){
jButton.paint(g);
}
}
But when I try to add MyButton to frame
JFrame frame = new JFrame("Hello swing");
JPanel panel = new JPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.add(new MyButton(button));
frame.add(panel);
it renders nothing
But after deleting
#Override
public void paint(Graphics g){
jButton.paint(g);
}
it renders the empty button:
QUESTION: Why does it behave that way? Why does the delegating cause rendering to fail?

First of all when you post a question you should post a proper SSCCE that demonstrates the problem. We can't copy/compile random lines of code. Until a problem is solved, you don't know what part of the code is causing the problem.
Why does the delegating cause rendering to fail?
My guess would be that the size of the button is (0, 0) so there is nothing to paint.
When you get rid of the custom paint method, then the real button can be painted because it does have a size because the layout manager has done its job.

public class Demo extends JFrame{
public static void main(String[] args)
{
JPanel panel = new JPanel();
getContentPane().setLayout(new BorderLayout());
panel.add(new JButton("Test"));
this.getContentPane().add(panel, BorderLayout.CENTER);
this.setSize(200,200);
this.setVisible(true);
}
}
If you want to add UI Components do it like that, don't use paint in any way.
If you want to paint for example a rectangle follow this tutorial: https://docs.oracle.com/javase/tutorial/uiswing/painting/

Your paint method does not draw the MyButton object, but instead draws the JButton which is member of your class. The problem now is, that this Button has not been added to the panel and so it's drawn on nothing. By removing your paint method, super.paint(g) is called because your class has no paint method and so your button, but not the member JButton is drawn.
I hope you understand what I am trying to explain to you.

Related

Draw in JPanel with java swing Graphics g

This is my first java project and I am trying to draw a simple rectangle on my JPanel inside my JFrame. Been trying to solve this issue with the help of the same topics on stackoverflow but still no success.
The exception I get when I run the program is java.lang.NullPointerException. From my understanding I can not draw on the JPanel itself? which is created in mainWindow.
Main:
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GameBoard game = new GameBoard();
mainWindow view = new mainWindow(game);
mainModel model = new mainModel();
mainController cont = new mainController(model, view, game);
cont.controllerInit();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
View:
public class mainWindow{
public JFrame frame;
public JPanel panel;
GameBoard game = new GameBoard();
frame = new JFrame();
frame.getContentPane().setBackground(SystemColor.control);
frame.setBounds(100, 100, 728, 435);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.getContentPane().setLayout(null);
panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
panel.setBounds(166, 44, 550, 349);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
Game:
public class GameBoard extends JPanel{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(200, 200, 200, 200);
}
}
Never, ever call paintComponent directly, no external source has any reason to do so. Also, what do you thing would happen if you passed it null?
You should start by having a look at Performing Custom Painting and Painting in AWT and Swing to get a better understand of how paint in Swing works.
The Swing API basically uses a delegate model, where the system delegates responsibility of the paint of each component to the component. This is achieved by the system calling the components paint method, which in-turn calls (among a few others) paintComponent.
Swing also uses a passive rendering approaching, meaning that painting occurs at the discretion of the paint system. You component is notified of the need when its paint method is called. This may occur at any time.
In order for a component to be painted, it must first be added to container which is realised on the screen (has a native peer), in most cases, this means that the component hierarchy needs to resolve to some kind of window based class, like JFrame.
So, the answer to your question is:
Read the above documentation (and get a better understanding of how the API works)
Add your GameBoard to a container which can be resolved to a window based class
Never call paint or paintComponent directly
Reflection....
private mainWindow view;
private mainModel model;
public GameBoard(mainModel m, mainWindow v)
{
view = v;
model = m;
}
To me, this makes no sense. There is no reasonable reason why GameBoard needs a reference to mainWindow. GameBoard is, in of itself, a "view". If anything, the only thing you "should" be passing to GameBoard (assuming you're trying to use a MVC) is a controller

How to paint in a split panel in Java swing

Recently, I've met an issue that the paintComponent function is not invoked in the function, and I found that when I use splitpane function, it will disable the paint function, and gives error:
cannot add to layout: unknown constraint: null
I think the paint function may not be added to the right way, below is my code(partly):
Class: test
public class Test extends JFrame{
public Test() throws IOException{
//JFrame jf = new JFrame("my frame");
this.add(new NewPanel(this));
this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
this.setBounds(300,200,1050,600);
this.setVisible (true);
}
public static void main (String[] args) throws IOException{
Test test = new Test ();
test.setTitle("Hello");
//frame.pack ();
}
}
Class: NewPanel
public class NewPanel extends JPanel{
public NewPanel(JFrame frame) throws IOException{
JTabbedPane jTabbedpane = new JTabbedPane();
JSplitPane splitPane = new JSplitPane();
JPanel p1 = new JPanel();
p1.setLayout(null);
p2.setLayout(new FlowLayout());
splitPane.setOneTouchExpandable(true);
splitPane.setContinuousLayout(true);
//splitPane.setPreferredSize(new Dimension (250,500));
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitPane.setLeftComponent(p1);
splitPane.setRightComponent (p2);
splitPane.setDividerSize(3);
splitPane.setDividerLocation(250); //balance two panels width
jTabbedpane.addTab("ABC",p2);
jTabbedpane.addTab("AB",p3);
jTabbedpane.addTab("AC",p4);
jTabbedpane.addTab("BC",p5);
frame.setContentPane(splitPane);
frame.add(jTabbedpane);
}
}
public void paintComponent(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.drawLine(303, 90, 303, 200);
g.drawLine(583, 90, 583, 200);
g.drawLine(863, 90, 863, 200);
}
}
When I comment frame.add(jTabbedpane),the line could be drawn in the panel, BUT it is only available in one panel, I cannot draw it into another split panel, I don't know why.. And when I uncomment frame.add(jTabbedpane), it pops up the above mentioned error.
Your UI assembly doesn't make sense. You're calling 'setContentPane' to the splitpane, which is sort-of OK (but unusual), but then you're calling add() to the frame, which tries to then add something else to the contentPane (the JSplitPane). You should either add the JTabbedPane to the SplitPane before adding the splitPane to the JPanel, or set up your layout differently.
//These don't make sense together.
frame.setContentPane(splitPane);
frame.add(jTabbedpane);
Your second question about drawing the blue line is more complicated.
You're doing a bunch of crazy stuff - you're creating a NewPanel and trying to add it to the JFrame, but then you're setting the contentPane of the JFrame to a different component later. You need to go through the Swing Tutorial and lay out your UI better.
I think the paint function may not be added to the right way,
public void paintComponent(Graphics g){
super.paint(g);
You are overriding paintComponent(...), so why are you calling super.paint(...)?
Start by reading the Swing Tutorial for Swing basics. All sections in the tutorial have working examples you can download and test.
So you might start with:
How to Use Split Panes - it will show you how to add a split pane to a frame
Performing Custom Painting - it will explain how painting works and show how to override the paintComponent(...) method.

Weird Swing Glitch?

I don't understand this, I'm currently making a main menu for a game i'm making but for some reason when I hover over a JButton, it flashes up on the top left side of the JFrame. I don't have any mouseAction methods or anything, is it the gif i'm using? I'm not sure...
Here is a screengrab of the error
Here is my code :
import javax.swing.*;
import java.awt.*;
public class MainMenu {
JFrame frame = new JFrame("Frasergatchi");
public void display(){
frame.setSize(400,400);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
public void addComponents(){
JButton play = new JButton("Play");
JButton instructions = new JButton("Instructions");
JButton exit = new JButton("Exit");
JPanel panel = new JPanel();
paintMenu paintMenu = new paintMenu();
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
frame.add(panel);
play.setAlignmentX(Component.CENTER_ALIGNMENT);
instructions.setAlignmentX(Component.CENTER_ALIGNMENT);
exit.setAlignmentX(Component.CENTER_ALIGNMENT);
panel.add(paintMenu);
panel.add(play);
panel.add(instructions);
panel.add(exit);
}
public class paintMenu extends JPanel{
public void paintComponent(Graphics graphics){
Image dog = new ImageIcon(getClass().getResource("DogMenuImage.gif")).getImage();
graphics.drawImage(dog,170,240,this);
}
}
}
The first statement in the paintComponent() method should always be:
super.paintComponent(g);
to make sure the background gets cleared.
Also:
Don't do I/O in a painting method. Painting methods are for painting only. Read the image in the constructor of your class and same the image in an instance variable.
Class names SHOULD start with an upper case character. Think of all the classes in the JDK and follow the standards.
There is no need to create a custom class to paint the image. Just use a JLabel with an Icon.

Drawing a line on another JFrame in a class that extends JFrame

I have a class with two JFrames and am trying to draw a line on a particular frame .
I tried the code below but it only appears in the first frame which is the success frame.
It also appears above all the other components of the success frame thus making all other
components invisible. It does not appear in the comp Frame.
How do I correct this.
Here is the code I have so far :
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
public class lineGUI{
public static void main(String []args){
Success s=new Success();
s.setVisible(true);
}
}
class Success extends JFrame{
JPanel alas =new JPanel();
JFrame comp =new JFrame();
public Success(){
JPanel panel=new JPanel();
getContentPane().add(panel);
setSize(450,450);
JButton button =new JButton("press");
panel.add(button);
comp.setSize(650,500);
comp.setTitle("View Report");
JRootPane compPane=comp.getRootPane();
Container contePane=compPane.getContentPane();
contePane.add(alas);
ActionListener action =new ActionListener(){
public void actionPerformed(ActionEvent e){
if (e.getSource()==button){
comp.setVisible(true);
}
}
};
button.addActionListener(action);
JButton button2=new JButton("access");
alas.add(button2);
}
public void paint(Graphics g) {
comp.paint(g);
Graphics2D g2 = (Graphics2D) g;
Line2D lin = new Line2D.Float(100, 100, 250, 260);
g2.draw(lin);
}
}
You've got some crazy code up there. Suggestions:
Don't draw directly in a JFrame, but in a the paintComponent method of an object derived from JComponent such as JPanel or JComponent itself.
Your drawing directly in another component's paint(...) method is not kosher at all. Why not simply share the data between classes, and use the data (the ints) to draw where desired.
You would rarely want to have a GUI display more than one JFrame at a time. Usually one window is the main window (the JFrame), and it often owns any other windows which would be dialog windows such as JDialogs.
Read the graphics tutorials to learn the correct way to do Swing Graphics
Two things:
If you want to draw in the "comp" frame, then you should extend that frame explicitly to overload its paint method. Right now you're overloading the paint method of "Success" frame. The line comp.paint(g) is using the paint method of comp (a standard JFrame) to draw on the Graphics object of the "Success" frame. You probably want to make that into super.paint(g) instead, then put this paint function into it's own JFrame and create comp from that.
http://pastebin.com/ZLYBHpmj
(Sorry, first post, couldn't figure out how to get Stackoverflow to quit complaining about format)

How do you add a JPanel onto another JPanel that has graphics?

Basically what im trying to do is to add a JPanel onto another JPanel that has painted graphics on it using the paintComponent() method. But the JPanel I am trying to add is not shown because it is covered up by the JPanel w/ Graphics on it.
How can I make it so that when I add the JPanel to the one w/ graphics it will show the JPanel in the front instead of being covered up by the Graphics?
All answers are appreciated! :)
If you need the code just tell me and I will gladly put it on here.
Alright here is the code:
package javavideogame;
public class Game extends JPanel implements ActionListener, Runnable
{
public Game(MainCharacter character)
{
setLayout(null);
setFocusable(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(ground, 0, 0, this);
g.drawImage(character.getImage(), character.getX(), character.getY(), this);
g.setColor(Color.RED);
g.drawRect(10, 10, character.getMaxHealth(), 10);
g.fillRect(10, 10, character.getHealth(), 10);
g.dispose();
}
public void getInventoryScreen()
{
Main.inv = new Inventory();
Main.game.add(Main.inv);
}
}
And here is the code for the JPanel that I'm adding to the Game JPanel
public class Inventory extends JPanel
{
public Inventory()
{
setLayout(null);
setSize(400, 300);
setBackground(Color.BLACK);
addKeyListener(this);
setFocusable(true);
}
}
Custom painting is done by overriding the paintComponent(...) method. My guess is that you are overriding the paint() method.
Read this section from the Swing tutorial on Custom Painting. Because you are overriding the wrong method, you end up painting the children first and then the custom painting is done on top.
If you need more help than post your SSCCE demonstrating the problem.
Just a reminder: java.awt and javax.swing don't mix well. When you're painting stuff on one panel AND trying to add a JPanel to it, you are bound to run into problems somewhere. A far better solution is to have 2 different JPanels, one for your graphics, and one for your inventory.
Something like this:
JPanel mainPanel = new JPanel(); //will hold BOTH panels
JPanel gamePanel = new Game(myCharacter); //declare game panel
JPanel inventoryPanel = new Inventory(); //declare inventory panel
//set up some layout
mainPanel.setLayout(new GridLayout(2, 1));
//add the graphics panel, then add the inventory
mainPanel.add(gamePanel);
mainPanel.add(inventoryPanel);
This will keep your swing and awt components from mixing and save you a lot of headaches.

Categories