Add new data to Graphics2D panel in a Java Swing application - java

I am writing a Java Swing application for data processing.
one of the functions I need to add is to visualize the data in a graphical way.
For this I want to use the Graphics2D class.
I have a GUI created, integrated my program and also a panel that draws the graphics using the Graphics2D class.
But my problem is that I can't figure out how to call the drawLine method after selecting and loading a file from the GUI
Below is short code example, showing my issue.
It just contains a basic GUI with 2 panels and a menu with load option to explain my problem:
In de MyFrame.java file, I put a comment at line 87 to show exactly where I am stuck.
The appl is based on 3 files:
main: here it creates an instance of MyFrame of the GUI
Myframe: creates the GUI and further process of data
MyPanel: makes a Jpanel of the Graphics2D with a base blue rectangle frame as start view.
If Anyone could give me a hint on how to call this drawLine method from outside the MyFrame() constructor...
I still don't fully understand the whole point on how to interact between classes...
here is a picture of what the GUI looks:
Thank you for helping me on this
public class main {
public static void main(String[] args) {
new MyFrame();
}
}
MyFrame.java:
public class MyFrame extends JFrame {
JTextComponent tc;
String fileName;
MyFrame() {
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.setLayout(null);
this.setBounds(0,0,464,312);
tc = new JTextPane();
tc.setBounds(0,520,450,50);
tc.setPreferredSize(new Dimension(450,50));
JScrollPane sp = new JScrollPane(tc);
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {tc.setText("loading"+"\n");
SDprocess();});
fm.add(loadItem);
mb.add(fm);
MyPanel p1 = new MyPanel();
p1.setBounds(0,0,450,200);
p1.setPreferredSize(new Dimension(450,200));
JPanel p2 = new JPanel();
p2.setBounds(0,200,450,50);
p2.setPreferredSize(new Dimension(450,50));
p2.add(sp);
this.setJMenuBar(mb);
this.add(p1);
this.add(p2);
this.setResizable(false);
this.setVisible(true);
}
public void SDprocess() {
File fr = null;
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
fr = fc.getSelectedFile();
fileName=fr.getName();
System.out.println(fileName);
}
try {
Scanner sc = new Scanner(fr);
tc.setText(fileName +" loading\n");
while (sc.hasNextLine()) {
String line = sc.nextLine();
// ...
// rest of code to get the x and y data for drawing
// lines using drawLine(x1,y1,x2,y2) method.
//
// at this point I need to call this drawLine method but how ???
// i just don't know how to call this method from this point and how to
// and update the graphics panel p1 after adding the data....
}
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyPanel.java:
public class MyPanel extends JPanel {
Graphics2D g2D;
MyPanel() {
this.setPreferredSize(new Dimension (450,200));
}
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
}

Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section.
Here's the revised GUI before "reading the file".
Here's the revised GUI after "reading the file".
I created an application model to hold the line segments. This model is passed to the drawing JPanel so that the line segments can be drawn in the paintComponent method of the drawing JPanel.
I cleaned up your GUI. I used Swing layout managers to create the GUI. I separated the creation of the JPanels from the creation of the JFrame so the code is easier for people to read and understand.
Here's the complete runnable code. I made the additional classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
public class ExampleDrawingGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ExampleDrawingGUI().new MyFrame());
}
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
JTextComponent tc;
MyPanel p1;
public MyFrame() {
super("My Frame");
this.model = new ExampleDrawingModel();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setJMenuBar(createMenuBar());
p1 = new MyPanel(model);
this.add(p1, BorderLayout.CENTER);
this.add(createTextPanel(), BorderLayout.SOUTH);
this.pack();
this.setLocationByPlatform(true);
// this.setResizable(false);
this.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
tc.setText("loading" + "\n");
model.readFile();
p1.repaint();
});
fm.add(loadItem);
mb.add(fm);
return mb;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
tc = new JTextPane();
tc.setPreferredSize(new Dimension(450, 50));
JScrollPane sp = new JScrollPane(tc);
panel.add(sp, BorderLayout.CENTER);
return panel;
}
public void repaint() {
p1.repaint();
}
}
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
public MyPanel(ExampleDrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(450, 200));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
paintBorder(g2D);
for (LineSegment line : model.getLines()) {
Point startPoint = line.getStartPoint();
Point endPoint = line.getEndPoint();
g2D.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
}
private void paintBorder(Graphics2D g2D) {
int margin = 5;
int x1 = margin;
int x2 = getWidth() - margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2D.setStroke(new BasicStroke(3f));
g2D.setPaint(Color.blue);
g2D.drawLine(x1, y1, x1, y2);
g2D.drawLine(x1, y1, x2, y1);
g2D.drawLine(x2, y1, x2, y2);
g2D.drawLine(x1, y2, x2, y2);
}
}
public class ExampleDrawingModel {
private List<LineSegment> lines;
public ExampleDrawingModel() {
this.lines = new ArrayList<>();
}
public void readFile() {
this.lines.clear();
// Here's where you'd read a file and create a list of lines.
lines.add(new LineSegment(new Point(100, 100), new Point(100, 150)));
}
public List<LineSegment> getLines() {
return lines;
}
}
public class LineSegment {
private final Point startPoint, endPoint;
public LineSegment(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}
public Point getStartPoint() {
return startPoint;
}
public Point getEndPoint() {
return endPoint;
}
}
}

First off, some problems with your code:
class MyFrame extends JFrame {
//....
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null); // !! Good God, no don't do this!
this.setBounds(0, 0, 464, 312); // and don't do this
Avoid null layouts and setBounds like the plague as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Also, don't forget to call pack() on your JFrame after adding components and before setting visible, in order for the layout managers to do their things.
And then:
class MyPanel extends JPanel {
// ...
Graphics2D g2D; //!! -- no, don't do this
If you create a Graphics or Graphics2D field, you are tempted to use it outside of a painting method, and this is a recipe for disaster since any Graphics obtained from a component is short lived and doing this risks creating a brittle graphic or throwing a NullPointerException
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
Don't override paint but rather paintComponent since this is less risky (paint has greater responsibilities that you don't want to mess with) and smoother animations if needed since paintComponent uses double buffering by default.
Also, you almost always should call the super's painting method in your own override, and so instead do:
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
Now as for your actual problem, creating images and drawing after the GUI has been rendered, probably the easiest way to do this is to create a BufferedImage and draw with it in your GUI. You can do this easily by calling Graphics method drawImage(...). And you can pass a BufferedImage into your drawing JPanel any time it is needed. So for instance, your code could look something like...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
MyMenu myMenu = new MyMenu();
myMenu.setMyPanel(mainPanel.getMyPanel());
myMenu.setMainPanel(mainPanel);
frame.add(mainPanel);
frame.setJMenuBar(myMenu);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class MainPanel extends JPanel {
private static final int GAP = 5;
private JTextArea textArea = new JTextArea(4, 40);
private MyPanel myPanel = new MyPanel();
public MainPanel() {
textArea.setFocusable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(myPanel);
add(scrollPane, BorderLayout.PAGE_END);
}
public MyPanel getMyPanel() {
return myPanel;
}
public void appendTextAreaText(String text) {
textArea.append(text);
}
public void setBuffImg(BufferedImage bImage) {
myPanel.setBuffImg(bImage);
}
}
class MyMenu extends JMenuBar {
private MainPanel mainPanel;
private MyPanel myPanel;
public MyMenu() {
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
// Emulate reading file here in a background thread
if (myPanel != null) {
int width = MyPanel.MY_WIDTH;
int height = MyPanel.MY_HEIGHT;
BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bImg.createGraphics();
// draw with g2 here using data from file
// emulating this:
g2.setColor(Color.RED);
float strokeWidth = (float) (2 + 6 * Math.random());
g2.setStroke(new BasicStroke(strokeWidth));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x2 = (int) (MyPanel.MY_WIDTH * (1 + Math.random()) / 2);
int y2 = (int) (MyPanel.MY_HEIGHT * (1 + Math.random()) / 2);
g2.drawLine(5, 5, x2, y2);
g2.dispose();
myPanel.setBuffImg(bImg);
if (mainPanel != null) {
mainPanel.appendTextAreaText("adding image \n");
}
}
});
fm.add(loadItem);
add(fm);
}
public void setMyPanel(MyPanel myPanel) {
this.myPanel = myPanel;
}
public void setMainPanel(MainPanel mainPanel) {
this.mainPanel = mainPanel;
}
}
class MyPanel extends JPanel {
private static final int GAP = 5;
public static final int MY_WIDTH = 450;
public static final int MY_HEIGHT = 200;
private BufferedImage bImg = null;
// Graphics2D g2D; //!! -- no, never do this!!
MyPanel() {
// this.setPreferredSize(new Dimension(450, 200));
setBackground(Color.WHITE);
}
// better to override getPreferredSize
#Override
public Dimension getPreferredSize() {
return new Dimension(MY_WIDTH + 2 * GAP, MY_HEIGHT + 2 * GAP);
}
public void setBuffImg(BufferedImage bImg) {
this.bImg = bImg;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
if (bImg != null) {
g.drawImage(bImg, GAP, GAP, null);
}
Graphics2D g2D = (Graphics2D) g.create();
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
Rectangle rect = new Rectangle(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
g2D.draw(rect);
}
// Don't override paint but rather paintComponent
// public void paint (Graphics g) {
}

Related

Painting method paints things from other components

I am trying to make a simple Java program with GUI using Java Swing.
I have painting panel (gPanel) in the center of the screen, panel with buttons (buttonSet) in the west and panel with labels (labelPanel) in the east. To paint over gPanel I use paintComponent method and since I have two buttons, which are supposed to draw different things (and change label on the right of the screen), I decided to put switch case in paintComponent method for it to choose the correct actual painting method.
When I run the program everything looks fine - program uses the first method to paint and there is a sampletext.png image shown in the middle of the screen with yellow background, as it should be. Button number 1 also uses this method to draw over gPanel, so pressing it draws the same thing.
Now Button number 2 uses the second painting method and this is where things go wrong. It draws sampleimage.png over the gPanel, but also parts of left and right panels (i.e. buttons from left buttonSet panel and orange colour that is background colour of side panels) are drawn, though it shouldn't happen. Also the whole gPanel becomes gray (I think it happens because of label on the right that becomes very long after pressing Button number 2, because when the label was shorter gPanel didn't turn gray and left the previously drawn things instead).
Pressing Button number 1 paints things from first method properly, so pressing it after pressing Button number 2 "reverts" the changes.
What do I have to do to make my second painting method work properly?
Also why adding border to buttonSet and labelPanel works but adding it to gPanel doesn't?
package com.inferjus.drawingproject;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.border.*;
/**
*
* #author inferjus
*/
public class DrawingProject
{
private JFrame frame;
private graphicPanel gPanel;
private JPanel buttonSet;
private JPanel labelPanel;
private JLabel label;
private int painter=0;
public static void main(String[] args)
{
DrawingProject program=new DrawingProject();
program.prepareGUI();
}
public int getPainter()
{
return painter;
}
public void setPainter(int x)
{
painter=x;
}
public void prepareGUI()
{
//setting JFrame and icon
frame=new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
try { frame.setIconImage(ImageIO.read(getClass().getResource("/resources/sampleicon.png")));}
catch (IOException e) { e.printStackTrace(); }
//border for components
Border bigBlackBorder=new LineBorder(Color.black, 3);
//setting JPanel (graphicPanel) for drawing images
gPanel=new graphicPanel();
gPanel.setBorder(bigBlackBorder); // <--- why it does not work?
//setting JPanel for buttons on the left of the screen
buttonSet=new JPanel();
buttonSet.setLayout(new BoxLayout(buttonSet, BoxLayout.Y_AXIS));
buttonSet.setBorder(bigBlackBorder);
//setting JButtons
JButton buttonOne=new JButton("Button number 1");
buttonOne.addActionListener(new buttonOneListener());
buttonSet.add(buttonOne);
buttonSet.setBackground(Color.orange);
JButton buttonTwo=new JButton("Button number 2");
buttonTwo.addActionListener(new buttonTwoListener());
buttonSet.add(buttonTwo);
//setting JLabels on the right of the screen
label=new JLabel("Default label");
label.setFont(new Font("Consolas", Font.PLAIN, 20));
labelPanel=new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.Y_AXIS));
labelPanel.setBackground(Color.orange);
labelPanel.setBorder(bigBlackBorder);
JLabel popeLabelTitle=new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font("Consolas", Font.BOLD, 24));
//adding JLabels to labelPanel
labelPanel.add(BorderLayout.NORTH, popeLabelTitle);
labelPanel.add(BorderLayout.CENTER, label);
//adding components to JFrame
frame.getContentPane().add(BorderLayout.CENTER, gPanel);
frame.getContentPane().add(BorderLayout.EAST, labelPanel);
frame.getContentPane().add(BorderLayout.WEST, buttonSet);
frame.setVisible(true);
}
class graphicPanel extends JPanel
{
private BufferedImage sampletext=null;
private BufferedImage sampleimage=null;
#Override
public void paintComponent(Graphics g)
{
//for Button One paint sampletext.png, for Button Two paint sampleimage.png
switch (painter)
{
case 0:
paintSampletext(g);
break;
case 1:
paintSampleimage(g);
break;
}
}
//paint yellow background and put sampletext.png in the middle
private void paintSampletext(Graphics g)
{
if (sampletext==null)
{
gPanel.setSampletextPNG();
}
g.setColor(Color.yellow);
g.fillRect(0,0, gPanel.getWidth(), gPanel.getHeight());
g.drawImage(sampletext, gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, this);
g.setColor(Color.black);
g.drawRect(gPanel.getWidth()/2-sampletext.getWidth()/2, gPanel.getHeight()/2-sampletext.getHeight()/2, sampletext.getWidth(), sampletext.getHeight());
g.dispose();
}
//paint sampleimage.png over what is already displayed
private void paintSampleimage(Graphics g)
{
if (sampleimage==null)
{
gPanel.setSampleimagePNG();
}
int x=(int)((Math.random()*gPanel.getWidth())-sampleimage.getWidth());
int y=(int)((Math.random()*gPanel.getHeight())-sampleimage.getHeight());
g.drawImage(sampleimage, x, y, gPanel);
g.dispose();
}
public void setSampletextPNG()
{
try { sampletext=ImageIO.read(getClass().getResource("/resources/sampletext.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
public void setSampleimagePNG()
{
try { sampleimage=ImageIO.read(getClass().getResource("/resources/sampleimage.png")); }
catch (IOException ex) { System.out.println("Image error"); }
}
}
class buttonOneListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 1: change of label.");
setPainter(0);
gPanel.repaint();
}
}
class buttonTwoListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
label.setText("Reaction to button number 2: change of label + drawing images over gPanel.");
setPainter(1);
gPanel.repaint();
}
}
}
Tree of my project:
DrawingProject
-JRE System Library
-src
--com.inferjus.drawingproject
---DrawingProject.java
--resources
---sampleicon.png
---sampleimage.png
---sampletext.png
what shows after running the program by default or after pressing Button One
what shows after pressing Button Two one time
what shows after pressing Button Two a few times
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
I went ahead and created the following GUI. I created two BufferedImages for the text image and the plain image so I wouldn't have to read any external files.
Explanation
When I create a Swing GUI, I use the model-view-controller pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
Model
I created a model class to hold the button flag and the two BufferedImages. This is the class where you would read the resources.
You can add the JFrame icon back to this class.
Model classes are plain Java getter/setter classes.
View
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
Class names are written in camel case and start with an upper case character. Method names are written in camel case and start with a lower case character. Field names follow the same rules as method names.
I separated the creation of the JFrame from the creation of the JPanels. This helps me to separate my concerns and makes it much easier to visually verify whether or not the code is correct. Aim to write short methods that do one thing and do it well.
You have to manually draw a border on a graphic JPanel. I added the code to your paintComponent method to paint a partial border.
Your paintComponent method should paint. Period. Nothing else. It must also start with a call to the super.paintComponent method to maintain the Swing paint chain.
I changed your JLabel in the right JPanel to a JTextArea. A JTextArea allows for longer messages to word wrap on multiple lines and not make your JFrame change size.
Controller
Your JButton controller classes were fine, except for the class names.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post the code in one block.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint border
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
switch (model.getPainter()) {
case 0:
paintSampleText(g);
break;
case 1:
paintSampleImage(g);
break;
}
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void paintSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) ((Math.random() * getWidth()) - image.getWidth());
int y = (int) ((Math.random() * getHeight()) - image.getHeight());
g.drawImage(image, x, y, this);
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
}
}
Update
In order to paint multiple images, you have to save the origin of the images in a List. I've modified the application model to hold a List of origin Point instances. I also corrected the code to create a random point.
Here's the GUI with multiple images.
Here's the modified code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class DrawingProject implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawingProject());
}
private final DrawingModel model;
private GraphicPanel graphicPanel;
private JTextArea textArea;
public DrawingProject() {
this.model = new DrawingModel();
}
#Override
public void run() {
JFrame frame = new JFrame("Drawing Project");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
graphicPanel = new GraphicPanel(model);
frame.add(createButtonPanel(), BorderLayout.WEST);
frame.add(graphicPanel, BorderLayout.CENTER);
frame.add(createTextPanel(), BorderLayout.EAST);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.orange);
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JButton buttonOne = new JButton("Button number 1");
buttonOne.addActionListener(new ButtonOneListener());
panel.add(buttonOne);
JButton buttonTwo = new JButton("Button number 2");
buttonTwo.addActionListener(new ButtonTwoListener());
panel.add(buttonTwo);
return panel;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
JLabel popeLabelTitle = new JLabel("What does the label say?");
popeLabelTitle.setFont(new Font(Font.MONOSPACED, Font.BOLD, 24));
panel.add(popeLabelTitle, BorderLayout.NORTH);
textArea = new JTextArea(4, 30);
textArea.setEditable(false);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
textArea.setText("Default label");
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
panel.add(textArea, BorderLayout.CENTER);
return panel;
}
public class GraphicPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DrawingModel model;
public GraphicPanel(DrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintMyBorder(g);
if (model.getPainter() == 1) {
createSampleImage(g);
}
paintSampleText(g);
BufferedImage image = model.getSampleImage();
List<Point> origin = model.getImageOrigin();
for (Point point : origin) {
g.drawImage(image, point.x, point.y, this);
}
}
private void paintMyBorder(Graphics g) {
int width = getWidth();
int height = getHeight();
int lineThickness = 3;
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.YELLOW);
g.fillRect(0, lineThickness, width, height - 2 * lineThickness);
}
private void paintSampleText(Graphics g) {
BufferedImage image = model.getSampleText();
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
g.drawImage(image, x, y, this);
}
private void createSampleImage(Graphics g) {
BufferedImage image = model.getSampleImage();
int x = (int) (Math.random() * (getWidth() - image.getWidth()));
int y = (int) (Math.random() * (getHeight() - image.getHeight()));
model.addNewImageOrigin(new Point(x, y));
}
}
public class ButtonOneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 1: change of label.");
model.setPainter(0);
graphicPanel.repaint();
}
}
public class ButtonTwoListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
textArea.setText("Reaction to button number 2: change of label + "
+ "drawing images over gPanel.");
model.setPainter(1);
graphicPanel.repaint();
}
}
public class DrawingModel {
private int painter;
private final BufferedImage sampleText;
private final BufferedImage sampleImage;
private final List<Point> imageOrigin;
public DrawingModel() {
this.painter = 0;
this.sampleText = createBufferedImage(Color.BLUE);
this.sampleImage = createBufferedImage(Color.MAGENTA);
this.imageOrigin = new ArrayList<>();
}
private BufferedImage createBufferedImage(Color color) {
BufferedImage image = new BufferedImage(64, 64,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(color);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.dispose();
return image;
}
public void addNewImageOrigin(Point point) {
this.imageOrigin.add(point);
}
public int getPainter() {
return painter;
}
public void setPainter(int painter) {
this.painter = painter;
}
public BufferedImage getSampleText() {
return sampleText;
}
public BufferedImage getSampleImage() {
return sampleImage;
}
public List<Point> getImageOrigin() {
return imageOrigin;
}
}
}

How to Overlap Panels in Swing?

I am attempting to have multiple JPanels that can "overlap", also allowing me to perform custom painting.
For this I am using a MainPanel, which extends JLayeredPane, and from what I can see, I have set bounds and index correctly.
The expected result, would be two rectangles painting at the same time to the screen.
The result I get, is flickering on one of the two OverlappingPanels, which I assume is from the RepaintManager fighting on which panel to draw (Found this here).
My question is, How can I properly overlap panels and retain painting capabilties, using Swing?
EDIT:
Code in question:
import javax.swing.*;
import java.awt.*;
public class Example extends JFrame {
public static class MainPanel extends JLayeredPane implements Runnable {
public OverlappingPanel1 overlappingPanel1;
public OverlappingPanel2 overlappingPanel2;
Thread mainThread;
public void startMainThread() {
mainThread = new Thread(this);
mainThread.start();
}
public MainPanel() {
this.setPreferredSize(new Dimension(1920,720));
this.setBackground(Color.BLACK);
this.setDoubleBuffered(true);
overlappingPanel1 = new OverlappingPanel1();
overlappingPanel2 = new OverlappingPanel2();
overlappingPanel1.setBounds(0,0,1920,720);
overlappingPanel2.setBounds(0,720/2,1920,720);
add(overlappingPanel1,1);
add(overlappingPanel2,2);
}
#Override
public void run() {
while(mainThread != null) {
overlappingPanel1.repaint();
overlappingPanel2.repaint();
}
}
}
public static class OverlappingPanel1 extends JPanel {
public OverlappingPanel1() {
setDoubleBuffered(true);
setPreferredSize(new Dimension(1920,720));
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D graphics2D = (Graphics2D) g;
graphics2D.fillRect(0,0,200,200);
}
}
public static class OverlappingPanel2 extends JPanel {
public OverlappingPanel2() {
setDoubleBuffered(true);
setPreferredSize(new Dimension(1920,720));
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D graphics2D = (Graphics2D) g;
graphics2D.fillRect(0,80,200,200);
}
}
public static void main(String[] args) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
MainPanel mainPanel = new MainPanel();
window.add(mainPanel);
window.setBackground(Color.BLACK);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
mainPanel.startMainThread();
}
}
So yes, a JLayeredPane would allow easy overlap of Swing components such as JPanels, and there are also layouts others have created that allow this, one called "overlay layout", but that's not what you want to for your currently stated problem.
Yours is an XY Problem type question where you ask "how do I solve X problem" when the best solution is not to solve it in this way, but rather to do Y, something completely different. Here, to paint multiple different images, your best solution is not to create and overlap heavier-weight Swing components such as JPanels, but rather to draw in one single JPanel and overlap sprite images. Otherwise you're just making things unnecessarily harder for yourself and your code than is needed.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Example2 extends JPanel {
private static final int MY_WIDTH = 1600;
private static final int MY_HEIGHT = 720;
List<Rectangle> rectangles = new ArrayList<>();
public Example2() {
setPreferredSize(new Dimension(MY_WIDTH, MY_HEIGHT));
setBackground(Color.WHITE);
rectangles.add(new Rectangle(0, 0, 200, 200));
rectangles.add(new Rectangle(0, 80 + MY_HEIGHT / 2, 200, 200));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Rectangle rectangle : rectangles) {
g2.fill(rectangle);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Example2 example = new Example2();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(example);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
And yes, as suggested in comments, override paintComponent, not paint. This reduces the risk of unwanted side effects that might come from painting child components or borders, and also allows for automatic double-buffering for when you do animation.
Also, a while (true) loop is not a healthy construct within an event-driven GUI program, not as you've written it. If you need repeated actions in a Swing program (which you don't in your example, not yet), use a Swing Timer instead.
So doing this this way gives you good flexibility. For instance, if you wanted to modify the above program to allow addition of shapes on mouse click, it would be easy to do so:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class Example3 extends JPanel {
private static final int MY_WIDTH = 1600;
private static final int MY_HEIGHT = 720;
List<ColorShape> colorShapes = new ArrayList<>();
public Example3() {
setPreferredSize(new Dimension(MY_WIDTH, MY_HEIGHT));
setBackground(Color.WHITE);
addMouseListener(new MyMouse());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (ColorShape colorShape : colorShapes) {
colorShape.draw(g2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
// create a random color
float hue = (float) Math.random();
float saturation = 1f;
float brightness = (float) (0.5 * Math.random() + 0.5);
Color color = Color.getHSBColor(hue, saturation, brightness);
// create a new ColorShape, add to list, and repaint:
colorShapes.add(new ColorShape(e.getPoint(), color));
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Example3 example = new Example3();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(example);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class ColorShape {
private int width = 80;
private Point location;
private Color color;
private Shape shape;
public ColorShape(Point location, Color color) {
this.location = location;
this.color = color;
int x = location.x - width / 2;
int y = location.y - width / 2;
shape = new Ellipse2D.Double(x, y, width, width);
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.fill(shape);
}
public Point getLocation() {
return location;
}
}
The last two parameteres in setBounds(int x,int y, int width, int height) are the width and height of your panel. In your case, these are the dimensions of your rectangle , thus you should set them to 200, as below:
overlappingPanel1.setBounds(0,0,200,200);
overlappingPanel2.setBounds(0,720/2,200,200);
Also, remove setPreferredSize(new Dimension(1920,720)); in the OverlappingPanel1 and OverlappingPanel2 classes, as they are not needed.

JComponents with Graphics.draw

I am trying to make a simple game using JPanel.
I am using Graphics Draw to display all information, including text, but I need to add user input.
I was thinking about using JTextField with absolute positioning to make it work with what is being drawn, but I have heard that absolute positioning is not a good way set up a JPanel.
Is there a better way to use both graphics paint and JComponents in the same panel?
Solution: use layout managers
Why not simply have the drawing JPanel held by another JPanel, one that uses BorderLayout and held in the BorderLayout.CENTER position. You can then place JTextFields or other control components in the outer JPanel in other positions.
You can also add a layout manager to the drawing JPanel and then add components onto this using the layout. Just remember that if you add any JPanels on top of the drawing JPanel, the added JPanels should be transparent, that is myPanel.setOpaque(false) should be called on them so that the drawing underneath shows through.
For example -- run this program to see what I mean:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
private JSlider hue1Slider = new JSlider(0, 100, 0);
private JSlider hue2Slider = new JSlider(0, 100, 0);
public GradientPaintEg() {
Color color = drawingPanel.getColor1();
float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
int value = (int) (hsb[0] * 100);
hue1Slider.setValue(value);
color = drawingPanel.getColor2();
hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
value = (int) (hsb[0] * 100);
hue2Slider.setValue(value);
hue1Slider.setMajorTickSpacing(20);
hue1Slider.setMinorTickSpacing(5);
hue1Slider.setPaintTicks(true);
hue1Slider.setPaintLabels(true);
hue1Slider.setPaintTrack(true);
hue1Slider.addChangeListener(new SliderListener(hue1Slider, drawingPanel, true));
hue1Slider.setOpaque(false);
hue2Slider.setMajorTickSpacing(20);
hue2Slider.setMinorTickSpacing(5);
hue2Slider.setPaintTicks(true);
hue2Slider.setPaintLabels(true);
hue2Slider.setPaintTrack(true);
hue2Slider.addChangeListener(new SliderListener(hue2Slider, drawingPanel, false));
hue2Slider.setOpaque(false);
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
setLayout(new BorderLayout());
// if you want to add the slider panel to the main JPanel:
// add(sliderPanel, BorderLayout.PAGE_START);
add(drawingPanel);
// if you want to add the sliderPanel to the drawing JPanel
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
}
private class SliderListener implements ChangeListener {
private JSlider slider;
private DrawingPanel drawingPanel;
private boolean color1Listener;
public SliderListener(JSlider slider, DrawingPanel drawingPanel, boolean color1Listener) {
this.slider = slider;
this.drawingPanel = drawingPanel;
this.color1Listener = color1Listener;
}
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
float hue = value / 100f;
Color c = Color.getHSBColor(hue, 1f, 1f);
if (color1Listener) {
drawingPanel.setColor1(c);
} else {
drawingPanel.setColor2(c);
}
}
}
private static void createAndShowGui() {
GradientPaintEg mainPanel = new GradientPaintEg();
JFrame frame = new JFrame("GradientPaintEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final float X2 = 20;
private static final float Y2 = X2;
private Color color1 = Color.RED;
private Color color2 = Color.BLUE;
public Color getColor1() {
return color1;
}
public void setColor1(Color color1) {
this.color1 = color1;
repaint();
}
public Color getColor2() {
return color2;
}
public void setColor2(Color color2) {
this.color2 = color2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(new GradientPaint(0, 0, color1, X2, Y2, color2, true));
g2.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
In this code example, I have a JPanel that draws, called class DrawingPanel and use within another main JPanel, the GradientPaintEg class:
public class GradientPaintEg extends JPanel {
private DrawingPanel drawingPanel = new DrawingPanel();
If I want to add components to the DrawingPanel, I first give it a layout, and then add the component(s). For instance, there is a JPanel that holds JSliders called sliderPanel that I add to the DrawingPanel instance using BorderLayout:
drawingPanel.setLayout(new BorderLayout());
drawingPanel.add(sliderPanel, BorderLayout.PAGE_START);
This adds the sliderPanel to the top of the drawingPanel.
But also note that I have to make sliderPanel non-opaque first:
JPanel sliderPanel = new JPanel(new GridLayout(0, 1, 4, 4));
sliderPanel.add(hue1Slider);
sliderPanel.add(hue2Slider);
sliderPanel.setOpaque(false);
I've also made the JSliders themselves non-opaque so that the underlying drawing shows through:
// ......
hue1Slider.setOpaque(false);
// ......
hue2Slider.setOpaque(false);
Here is a basic mcve of combining user input in JTextfield and painting, on a panel using a layout manager:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
class DrawingPanel extends JPanel {
private final JButton update;
private final JTextField input;
private final static int W = 300, H = 350, RADIUS = 100, GAP = 50;
private String text;
public DrawingPanel() {
setPreferredSize(new Dimension(W, H));
setOpaque(false);
setLayout(new BorderLayout());
update = new JButton("Update");
update.addActionListener(e->update());
add(update, BorderLayout.PAGE_START);
input = new JTextField();
add(input, BorderLayout.PAGE_END);
text = "Enter text and press button";
}
private void update() {
text = input.getText();
input.setText("");
repaint();
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponents(g);
final int width = getWidth();
final int height = getHeight();
g.setColor(Color.RED);
g.fillOval(width/2 - RADIUS, height/2 - RADIUS, RADIUS*2, RADIUS*2);
g.setColor(Color.BLUE);
g.drawString(text, height/2 - RADIUS - GAP, GAP);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(()->makeGui());
}
private static void makeGui() {
JFrame frame = new JFrame();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new DrawingPanel());
frame.pack();
frame.setVisible(true);
}
}

How do I draw on a JPanel from multiple outside classes?

I am currently making a game with a main menu and a world where you actually play.
I have a class called Game, which inherits from JPanel and implements the Runnable, MouseListener, KeyListener and ActionListener interfaces
(only important parts included)
I also have two classes InWorldHandler and OutWorldHandler for handling the mechanics in the world and outside of it respectively.
The Game class:
public class Game extends JPanel implements Runnable, KeyListener, MouseListener, ActionListener
{
protected JFrame frame;
private Timer timer = new Timer(25, this);
private World world;
private Player player = new Player();
private boolean draw;
Game(World world)
{
frame = new JFrame("Minecraft 2D");
this.world = world;
player.enterWorld(world, this);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
if(draw)
{
g2d.setColor(new Color(255, 255, 255));
g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());
draw = false;
//Here, the in-game mechanics should be handled
if(player.inWorld())
{
Chunk chunk = player.getLoadedChunk();
for(int x = 0; x < 16; x++)
{
for(int y = 0; y < 16; y++)
{
Block block = chunk.getBlockAt(x, y);
BufferedImage texture = block.getTexture();
g2d.drawImage(block.getTexture(), x*32, y*32, texture.getWidth()*2, texture.getHeight()*2, null);
}
}
}
//Here, the out-game mechanics should be handled
else
{
}
}
}
#Override
public void actionPerformed(ActionEvent e)
{
this.repaint();
draw = true;
}
#Override
public void run()
{
draw = true;
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(this);
frame.setMinimumSize(new Dimension(518, 540));
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.addKeyListener(this);
frame.addMouseListener(this);
frame.setFocusable(true);
frame.setVisible(true);
frame.pack();
timer.start();
}
}
Both the other classes have empty bodies currently. I just have no idea how to do that.
I want the InWorldHandler class to draw on the panel when the player is in game, and the OutWorldHandler class when the player is in the main menu, both called in the Game class. How do I do that?
Instead of having a single JPanel, why don't you try with a CardLayout and switch whether to show the InnerWorld or the OuterWorld according to a flag that determines where in the program you're at.
As you're implementing KeyListener, I think that's for you to be able to move your character, please take a look at the accepted answer on this question: Keylistener not working for JPanel and use KeyBindings instead.
Also avoid the use of setMimimum/Maximum/PreferredSize() and override those methods instead: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Take this code as an example:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Game {
private JFrame frame;
private JButton button;
private boolean status;
private JPanel[] cards;
private CardLayout cl;
private JPanel gamePane;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Game().createAndShowGUI());
}
private void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
button = new JButton("Switch Worlds");
status = true; //True = Inner / False = Outer
cl = new CardLayout();
gamePane = new JPanel(cl);
cards = new JPanel[2];
cards[0] = new InnerWorld();
cards[1] = new OuterWorld();
gamePane.add(cards[0], "innerWorld");
gamePane.add(cards[1], "outerWorld");
button.addActionListener(e -> {
status = !status;
if (status) {
cl.show(gamePane, "innerWorld");
} else {
cl.show(gamePane, "outerWorld");
}
});
frame.add(gamePane);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
#SuppressWarnings("serial")
class InnerWorld extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawString("Inner World", 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
#SuppressWarnings("serial")
class OuterWorld extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawString("Outer World", 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}

What will be the Main of this program in java |How do i call the line method in main?

package drawinglinebymethods;
import java.awt.*;
import javax.swing.JFrame;
public class DrawingLineByMethods extends Frame {
public JFrame f=new JFrame();
void fra_size()
{
f.setSize(450, 300);
}
void fra_visible()
{
f.setVisible(true);
}
void fra_title()
{
f.setTitle(" java frame");
}
void exit()
{
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void line(Graphics g) {
g.drawLine(10, 10, 20, 300);
}
public static void main(String[] args) {
DrawingLineByMethods obj = new DrawingLineByMethods();
obj.fra_size();
obj.fra_title();
obj.fra_visible();
obj.fra_exit();
obj.line(g); // <-- error here
}
}
Your question suggests that you are not yet clear on how graphics and drawing works in Swing GUI's. Some suggestions for you:
Don't have your class extend java.awt.Frame as this makes no sense. You're not creating an AWT Frame window and have no need of this.
Create a class that extends JPanel and draw in its paintComponent method as the Swing painting tutorials (link now added) show you.
You never call drawing code directly but rather it is indirectly called by the JVM.
If you want to draw from outside of the GUI itself, then draw to a BufferedImage, one that is then displayed within the GUI.
Don't use a Graphics object obtained by calling getGraphics() on a GUI component as the object thus obtained will not persist, and this risks you creating unstable graphics or worse -- throwing a NullPointerException.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
public LineDraw() {
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
If you want to add lines as the program runs, again, use a BufferedImage that is drawn within the JPanel's paintComponent method. When you need to add a new line to the GUI, extract the Graphics or Graphics2D object from the BufferedImage using getGraphics() or createGraphics() respectively (it's OK to do this), draw with this Graphics object, dispose of the Graphics object, and repaint the GUI. For example in the code below, I draw a new line when the button is pressed by adding code within the JButton's ActionListener (actually its AbstractAction which is similar to an ActionListener but more powerful):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
private BufferedImage img;
private int yDistance = 20;
private int deltaY = 10;
public LineDraw() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
add(new JButton(new DrawLineAction("Draw Line", KeyEvent.VK_D)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// draw the buffered image here
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
public void drawNewLines() {
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
yDistance += deltaY;
g2.drawLine(10, 10, yDistance, PREF_H);
g2.dispose();
repaint();
}
private class DrawLineAction extends AbstractAction {
public DrawLineAction(String name, int mnemonic) {
super(name); // give button its text
putValue(MNEMONIC_KEY, mnemonic); // alt-hot key for button
}
#Override
public void actionPerformed(ActionEvent e) {
drawNewLines();
}
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Line is a non-static method, the only way to call it from a static method (main here) is to have an instance of the class.

Categories