I am printing various elements such as text and images using the Panels and components it is generating pages for printing and print is also coming but the hard print also has the print button on physical paper. I to remove the print button from the page. here is the code
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.print.*;
public class Printing extends JFrame
implements ActionListener {
public static void main(String[] args)
{
// intialise
}
public Printing(String Firstname,String LastName,String contactid)
{
super("Print badge");
WindowUtilities.setNativeLookAndFeel();
addWindowListener(new ExitListener());
Container content = getContentPane();
JButton printButton = new JButton("Print");
printButton.addActionListener(this);
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(Color.BLACK);
buttonPanel.add(printButton);
content.add(buttonPanel, BorderLayout.SOUTH);
DrawingPanel drawingPanel = new DrawingPanel(Firstname,LastName,contactid);
content.add(drawingPanel, BorderLayout.CENTER);
pack();
setVisible(true);
}
public void actionPerformed(ActionEvent event)
{
//call for printing
}
}
and code for actual printing panel
public class DrawingPanel extends JPanel
{
private int fontSize1 = 32;
private Image img1=null;
public DrawingPanel(String n1,String n2,String n3)
{
String path="D:"+"\\25175.jpg";
setBackground(Color.white);
Font font = new Font("Serif", Font.PLAIN, 32);
setFont(font);
img1=new ImageIcon(path).getImage();
setPreferredSize(new Dimension(400, 400));
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.translate(x, y);
g2d.setPaint(Color.lightGray);
AffineTransform origTransform = g2d.getTransform();
g2d.shear(-0.95, 0);
g2d.scale(1, 3);
g2d.setTransform(origTransform);
g2d.setPaint(Color.BLUE);
g2d.drawString(string,25 , 50);
g2d.drawString(string, 125,100);
g.drawImage(img1, 280, 190, null);
}
}
The print method setup is here
import java.awt.*;
import javax.swing.*;
import java.awt.print.*;
public class PrintUtilities implements Printable {
private Component componentToBePrinted;
public static void printComponent(Component c) {
new PrintUtilities(c).print();
}
public PrintUtilities(Component componentToBePrinted) {
this.componentToBePrinted = componentToBePrinted;
}
public void print() {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(this);
if (printJob.printDialog())
try {
printJob.print();
} catch(PrinterException e) {
System.out.println("Error printing: " + e);
}
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
disableDoubleBuffering(componentToBePrinted);
componentToBePrinted.paint(g2d);
enableDoubleBuffering(componentToBePrinted);
return(PAGE_EXISTS);
}
}
public static void disableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(false);
}
public static void enableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(true);
}
}
by this call PrintUtilities.printComponent(this) in button action listener
This would seem to be your problem.
This will, technically, print the contents of the entire frame, you only need to print the DrawingPanel.
This is going to require you to make some minor changes to your code so you access the instance of the DrawingPanel from your actionPerformed method...
private DrawingPanel drawingPanel;
public Printing(String Firstname,String LastName,String contactid)
{
super("Print badge");
//...
drawingPanel = new DrawingPanel(Firstname,LastName,contactid);
//...
}
This should no allow you to do something like...
PrintUtilities.printComponent(drawingPanel);
Instead...
You can
define custom clip for the print Graphics to exclude the print button area
OR 2. just hide the button
OR 3. override the button's paintComponent() method where check isPrint flag (if it's false call super it true do nothing).
Related
I'm experimenting with 2D painting in Swing, and I'd like to paint a JLabel in the middle of an empty JList.
Thus I came up with:
public class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Whatever");
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0 && !emptyLabel.getText().isBlank()) {
final var preferredSize = emptyLabel.getPreferredSize();
final var x = (getWidth() - preferredSize.width) / 2;
final var y = (getHeight() - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
However when the text reaches the bounds of the list, it seems to get truncated:
If I increase the clipping rectangle width, the label is fully painted.
What am I doing wrong? Is this a good way to paint?
Complete minimal example:
class Main {
public static void main(final String[] args) {
final var myFrame = new MyFrame();
myFrame.setVisible(true);
}
public static class MyFrame extends JFrame {
public MyFrame() {
super("Example");
final var list = new MyJList<String>();
final var contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(list, BorderLayout.CENTER);
pack();
setMinimumSize(new Dimension(160, 200));
setLocationRelativeTo(null);
}
}
public static class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Selezionare un oggetto");
{
emptyLabel.setEnabled(false);
}
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0) {
final var preferredSize = emptyLabel.getPreferredSize();
final var listBounds = getBounds();
final var x = (listBounds.width - preferredSize.width) / 2;
final var y = (listBounds.height - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
}
Text layout is tricky at the best of times, and the more you can avoid it, the better (IMHO).
Personally, I avoid the custom paint route, but this is me, and try something a little more simpler.
With the use of a PropertyChangeListener, to monitor when the ListModel changes, and a custom ListDataListener, to monitor when the ListModel itself changes, you can create a similar result.
One addition here is the fact that I've wrapped the text in <html> tags, this will trigger the "word" wrapping.
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultListModel<String> model = new DefaultListModel<String>();
public TestPane() {
setLayout(new BorderLayout());
JPanel panel = new JPanel(new GridBagLayout());
JButton add = new JButton("Add");
JButton remove = new JButton("Remove");
panel.add(add);
panel.add(remove);
MyList<String> myList = new MyList<>();
myList.setModel(model);
add(new JScrollPane(myList));
add(panel, BorderLayout.SOUTH);
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
model.addElement("Hello");
}
});
remove.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (model.size() > 0) {
model.remove(0);
}
}
});
}
}
public class MyList<T> extends JList<T> {
final private ModelHandler modelHandler = new ModelHandler();
final private JLabel emptyLabel = new JLabel("<html>Selezionare un oggetto</html>");
public MyList() {
emptyLabel.setHorizontalAlignment(JLabel.CENTER);
setLayout(new BorderLayout());
add(emptyLabel);
emptyLabel.setVisible(false);
addPropertyChangeListener("model", new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ((evt.getOldValue() instanceof ListModel)) {
ListModel model = (ListModel) evt.getOldValue();
model.removeListDataListener(modelHandler);
}
if ((evt.getNewValue() instanceof ListModel)) {
ListModel model = (ListModel) evt.getNewValue();
model.addListDataListener(modelHandler);
}
updateEmptyLabel();
}
});
}
protected void updateEmptyLabel() {
emptyLabel.setVisible(getModel().getSize() == 0);
}
protected class ModelHandler implements ListDataListener {
#Override
public void intervalAdded(ListDataEvent evt) {
updateEmptyLabel();
}
#Override
public void intervalRemoved(ListDataEvent evt) {
updateEmptyLabel();
}
#Override
public void contentsChanged(ListDataEvent evt) {
updateEmptyLabel();
}
}
}
}
Calculating text size is difficult and has lots of gotchas. There is a reason why JList, JTable etc. make use of a renderer pane and not call paint on the cell renderer directly.
It is only when a component is part of a frame hierarchy it can correctly determine the size of text. That is because only then the component uses the correct GraphicsEnvironment for the current display device. The GraphicsEnvironment contains information that is necessary for laying out text as antialiasing support, kerning, fractional font support etc.
So the correct way do what you are doing is to add the label to the JList :
public static class MyJList<T> extends JList<T> {
private final JLabel emptyLabel = new JLabel("Selezionare un oggetto");
{
add(emptyLabel); // <---- add label
emptyLabel.setEnabled(false);
}
#Override
public void paint(final Graphics g) {
super.paint(g);
if (getModel().getSize() == 0) {
final var preferredSize = emptyLabel.getPreferredSize();
final var listBounds = getBounds();
final var x = (listBounds.width - preferredSize.width) / 2;
final var y = (listBounds.height - preferredSize.height) / 2;
final var g2 = (Graphics2D) g.create(x, y, preferredSize.width, preferredSize.height);
try {
emptyLabel.setBounds(0, 0, preferredSize.width, preferredSize.height);
emptyLabel.paint(g2);
} finally {
g2.dispose();
}
}
}
}
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I am currently trying to create a Java GUI program that generates images based on the arguments given in the terminal. If I get the argument in the command line java Draw Image1 for example, I want to draw my image 1 and etc. for the others. How can one take a command argument and use it in paintComponent? Here's a sample of what I am trying to do below:
import javax.swing.*;
import java.awt.*;
public class Draw extends JPanel
{
public Draw()
{
this.setSize(800,800);
JPanel drawing = new JPanel();
this.add(drawing);
this.setVisible(true);
}
protected void paintComponent(Graphics g)
{
if (args[0].equals("Image1")) // won't work
{
super.paintComponent(g);
Image myImage = Toolkit.getDefaultToolkit().getImage("image/myimage.jpg");
g.drawImage(myImage, 0, 0, this);
}
else
{
// draw image 2
}
}
public static void main(String[] args)
{
// create new Jframe
JFrame frame = new JFrame("Draw");
frame.add(new Draw());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
}
Change this:
frame.add(new Draw());
to this:
frame.add(new Draw(args));
And then have your constructor accept a String array parameter and use it to set a class field.
public class Draw extends JPanel
{
private String[] params
public Draw(String params)
{
this.params = params;
this.setSize(800,800); // this generally should be avoided
JPanel drawing = new JPanel();
this.add(drawing);
this.setVisible(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // this should be out of the if block
if (params != null && params.length > 0 && params[0].equals("Image1")) {
// ..... etc
Edit: Andrew is right and I did not read your code carefully. Read your image in the constructor and use it in the image in the paintCompnent method
e.g.,
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
#SuppressWarnings("serial")
public class Draw extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private BufferedImage image;
public Draw(String[] params) throws IOException {
if (params != null && params.length > 0) {
image = ImageIO.read(new File(params[0]));
}
// this.setSize(800,800);
JPanel drawing = new JPanel();
drawing.setOpaque(false); // may need this
this.add(drawing);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
} else {
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// not sure if you want to size it to the image or not
if (image != null) {
int w = image.getWidth();
int h = image.getHeight();
return new Dimension(w, h);
} else {
return new Dimension(PREF_W, PREF_H);
}
}
public static void main(String[] args) {
// create new Jframe
JFrame frame = new JFrame("Draw");
try {
frame.add(new Draw(args));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setSize(500,500);
frame.pack();
frame.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
So I want to set my Rectangle's color to whatever the JColorChooser picks, but unfortunately, I don't think that it recognises it as it just stays black, unless I assign it a real color like Color.BLUE.
This is the rectangle which should get the color from the other class:
TestProjectJPanel jpp = new TestProjectJPanel();
public void paintComponent(Graphics g){
super.paintComponent(g);
Rectangle r = new Rectangle(500,300,250,400);
g.setColor(jpp.bodyColour);
g.fillRect((int)r.getX(),(int)r.getY(),(int)r.getHeight(),(int)r.getWidth());
g.setColor(Color.BLUE);
g.drawString("banana", 50, 60);
}
and this is the class which has the color itself
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestProjectJPanel extends JFrame {
public JButton b;
public JButton u;
public JButton l;
public JButton r;
public String s;
public Color color = (Color.WHITE);
public JPanel panel;
public Color bodyColour;
public Color doorColour;
public Color wheelColour;
public TestProjectJPanel(){
JFrame f = new JFrame();
panel = new JPanel();
panel.setBackground(color);
// bodyColour button
b = new JButton("Body Colour");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
bodyColour = JColorChooser.showDialog(null, "Pick the colour", bodyColour);
if (bodyColour == null)
bodyColour = Color.RED;
}
});
u = new JButton("Wheel Colour");
u.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
wheelColour = JColorChooser.showDialog(null, "Pick the colour", wheelColour);
if (wheelColour == null)
wheelColour = (Color.BLACK);
}
});
l = new JButton("Door Colour");
l.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
doorColour = JColorChooser.showDialog(null, "Pick the colour", doorColour);
if(doorColour==null)
doorColour = (Color.RED);
}
});
r = new JButton("Change Name");
r.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
s = JOptionPane.showInputDialog("What name do you want to change it to?");
}
});
}
}
This is the whole code, which the Rectangle is included in.
import javax.swing.*;
import java.util.*;
import java.awt.*;
public class TestProjectDialog extends JPanel {
TestProjectJPanel jpp = new TestProjectJPanel();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle r = new Rectangle(500, 300, 250, 400);
g.setColor(jpp.bodyColour);
g.fillRect((int) r.getX(), (int) r.getY(), (int) r.getHeight(), (int) r.getWidth());
g.setColor(Color.BLUE);
g.drawString("banana", 50, 60);
}
public static void main(String[] args){
TestProjectJPanel jpp = new TestProjectJPanel();
/* JOptionPane.showMessageDialog(null, "Just about to draw a REALLY GOOD 2D car \n just need input please.");
jpp.s = JOptionPane.showInputDialog("Imagine a car, what is it's name?");
if(jpp.s == null || (jpp.s != null && ("".equals(jpp.s))))
{
JOptionPane.showMessageDialog(null, "Invalid input/pressed cancel, closing program.");
System.exit(0);
}
JOptionPane.showMessageDialog(null, "Ah okay, so it's name is " + jpp.s); */
JFrame f = new JFrame("My 2D Car Drawing");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jpp.setBackground(Color.WHITE);
f.setSize(1440,900);
f.add(new TestProjectDialog(), BorderLayout.CENTER);
f.add(jpp.b, SpringLayout.SOUTH); // bodyColour
// f.add(jpp.u, SpringLayout. NORTH); // wheelColour
// f.add(jpp.l, SpringLayout.WEST); // doorColour
// f.add(jpp.r, SpringLayout.EAST); // changeName
f.setVisible(true);
}
}
Could anyone help me and give me a solution for why it's not recognising the color?
Without a runnable example that demonstrates your problem, it is impossible to 100% sure, but from the sounds of it, you have created two more instances of TestProjectJPanel and/or paint panel, so what you think you're drawing to isn't actually on the screen.
However, it also sounds like you're not adhering to basic OO design, ensuring that the object is self managed, but instead, allowing the object to ascertain it's state from the properties of another object.
Instead, your paint/rectangle pane should be self contained, that is, all the properties it needs should be managed by it (this could also include a model, but that's going beyond the problem at hand).
For example, the ControlPane manages the interactions between the user (choosing a color) and the setting of that color on the PaintPane, which deals only with painting the results...
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class ColorTest {
public static void main(String[] args) {
new ColorTest();
}
public ColorTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
PaintPane paintPane = new PaintPane();
ControlPane controlPane = new ControlPane(paintPane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(paintPane);
frame.add(controlPane, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
public ControlPane(PaintPane paintPane) {
JButton colorButton = new JButton("Color");
add(colorButton);
colorButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Color color = JColorChooser.showDialog(ControlPane.this, "Background", paintPane.getFillColor());
if (color != null)
paintPane.setFillColor(color);
}
});
}
}
public class PaintPane extends JPanel {
private Color fillColor = Color.RED;
public PaintPane() {}
public Color getFillColor() {
return fillColor;
}
public void setFillColor(Color fillColor) {
this.fillColor = fillColor;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(50, 50, 100, 100);
g2d.dispose();
}
}
}
I'm trying to create a simple game using AWT but I want to have some JButtons aswell to exit/reset the game. The problem is, I want the BufferedImage to be drawn inside the visible frame like so in my container I have this at the end:
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
My problem is, when I add a JButton to that frame, it only detects rollover in space that doesn't take into account the offsetting, but is drawn in a space that does. This is the relevant code (con is the container).
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
The paint method in the Container
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
How can I make the button be drawn and detected in the same spot?
here is a screenshot of the problem
As requested:
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class Draw implements ActionListener{
private SnakeFrame frame;
private SnakeCon con;
JButton reset, exit;
private boolean res;
public Draw()
{
frame = new SnakeFrame("Snake");
frame.setResizable(false);
frame.setLayout(null);
frame.setSize(600,600);
frame.setVisible(true);
con = new SnakeCon();
con.setBounds(0,0,600,600);
frame.add(con);
}
private void addButtons()
{
reset = new JButton("reset");
reset.setBounds(180,460, 75,30);
reset.addActionListener( this );
con.add(reset);
exit = new JButton("exit");
exit.setBounds(290,460, 60,30);
exit.addActionListener( this );
con.add(exit);
con.repaint();
}
public void run()
{
addButtons();
res = false;
boolean dead = false;
while(!dead)
{
if( (res) )
dead = true;
if (!dead)
{
try{
Thread.sleep(100);
}
catch (Exception e)
{}
frame.repaint();
}
}
con.removeAll();
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == reset)
res = true;
else if (e.getSource() == exit)
System.exit(0);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeCon extends Container{
private BufferedImage bf;
public SnakeCon()
{
super();
setBounds(0,0,600,600);
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
b.setColor(Color.GRAY);
b.fillRect(0, 0, this.getWidth(), this.getHeight());
b.setColor(Color.BLACK);
b.drawRect(0,0,420,420);
super.paint(b);
g.drawImage(bf,getParent().getInsets().left,getParent().getInsets().top,null);
}
public void update(Graphics g)
{
paint(g);
}
}
--
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.Color;
public class SnakeFrame extends Frame implements WindowListener{
private BufferedImage bf;
public SnakeFrame(String s)
{
super(s);
addWindowListener( this );
}
public void paint(Graphics g)
{
bf = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics b = bf.getGraphics();
super.paint(b);
g.drawImage(bf,0,0,null);
}
public void update(Graphics g)
{
paint(g);
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
public void windowClosed(WindowEvent e) { }
public void windowOpened(WindowEvent e) { }
public void windowIconified(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowActivated(WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
}
--
public class Main {
public static void main(String[] args)
{
boolean never = false;
Draw d = new Draw();
while(!never)
{
d.run();
}
System.exit(0);
}
}
Im not sure exactly what is wrong/what you want (why do you have a loop to constantly drawing buttons and invoking removeAll()? etc but i cant shake the feeling it might be implemented in a more readable/efficient way)...
But here are some suggestions that can only help your code get better:
Dont use null/Absolute Layout choose an appropriate LayoutManager.
Do not override JFrame paint(..), rather add JPanel to JFrame and override paintComponent(Graphics g) of JPanel and do drawing there.(Do not forget to have super.paintComponent(..) as 1st call in overriden paintComponent method. See here for more: Performing Custom Painting
Do not set JFrame visible before adding all components to JFrame
Always create and manipulate Swing components on Event Dispatch Thread like so:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create Swing components
}
});
Do not do long running tasks on Event Dispatch Thread rather use Swing Timer/Swing Worker
Do not call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return Dimensions which fit all components (see here for reasoning), than call pack() on JFrame before setting it visible
Dont extend JFrame unnecessarily or Container!
Adding WindowListener for detecting JFrame exit is not worth the lines rather use:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
to exit Swing application when X is pressed.