java 2d puzzle application, cannot draw more then one piece - java

i'm trying to write a jigsaw puzzle application where an image is cut in pieces, scrambled, and the user have to rearrange them with drag&drop to reassemble the original image. (something like this: http://www.jigzone.com/puzzles/74055D549FF0?z=5).
i have to write this in java with Graphics2d.
so, at first i'm trying to make some kind of component which can show a part of the image (a rectangle for now), and can be dragged with mouse.
the code below works well when there is only one one such component. the problem is, when i add the second component, the first one is no longer visible.
i'm really stuck here. i have a feeling i'm missing something really basic. or maybe i'm on a wrong way. any help will be greatly appreciated.
edit: i changed a bit the code according to suggestions, however, still not working as expected.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
public class GraphicDragAndDrop extends JPanel {
Rectangle rect;
Image img;
public GraphicDragAndDrop(String imgFile, int x0, int y0){
rect = new Rectangle(x0, y0, 150, 75);
img = new ImageIcon(imgFile).getImage();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
g2d.drawImage(img, x, y, this);
}
public void setRect(int x, int y) {
rect.setLocation(x, y);
repaint();
}
public static void main(String[] args) {
// first piece
GraphicDragAndDrop piece1 = new GraphicDragAndDrop("a.png", 0, 0);
piece1.setRect(0, 0);
new GraphicDragController(piece1);
// second piece --> only this will be visible
GraphicDragAndDrop piece2 = new GraphicDragAndDrop("a.png", 200, 200);
//GraphicDragAndDrop piece2 = new GraphicDragAndDrop("b.png", 200, 200); // does'n work either
piece2.setRect(150, 150);
new GraphicDragController(piece2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(piece1);
f.add(piece2);
f.setSize(500,500);
f.setLocation(300,100);
f.setVisible(true);
}
}
class GraphicDragController extends MouseInputAdapter {
GraphicDragAndDrop component;
Point offset = new Point();
boolean dragging = false;
public GraphicDragController(GraphicDragAndDrop gdad) {
component = gdad;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Rectangle r = component.rect;
if(r.contains(p)) {
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
}
}
public void mouseReleased(MouseEvent e) {
dragging = false;
}
public void mouseDragged(MouseEvent e) {
if(dragging) {
int x = e.getX() - offset.x;
int y = e.getY() - offset.y;
component.setRect(x, y);
}
}
}

Your code above is written to draw only one image:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
// here
g2d.drawImage(new ImageIcon("a.png").getImage(), x, y, this);
}
If you need to draw more than one image, then consider creating a collection of images and iterating through the collection in paintComponent using a for loop:
also, never read in the image from within paintComponent since this method should be lean, mean and fast, and should concern itself with painting only. Also, there's no need to read the image in each time your program has to draw it as that's very inefficient and will slow the program unnecessarily. Instead read the image in once in the constructor or a similar init method.
For example,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Image img: myImageCollection) {
g2d.drawImage(img, 0, 0, this);
}
}
Edit
You state:
also, my plan was to have more objects of GraphicDragAndDrop class, each of them showing a different piece of the original image. is my approach wrong?
You could use components, but I have a feeling that it would be easy to drag images. I think it would be easier to rotate them for instance if you want your program to have this functionality. If not, though then sure use a component, but if you go this route, I would recommend using a JLabel and simply setting its ImageIcon rather than dragging JPanels.

Related

Java (Graphics2D): triangle drawn by created Graphics2D not visible until second repaint

I have the following minimal code to draw a line with an arrow head:
package gui;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import javax.swing.JPanel;
public class StateBtn extends JPanel {
private static final long serialVersionUID = -431114028667352251L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// enable antialiasing
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// draw the arrow
Line2D.Double line = new Line2D.Double(0, getHeight()/2, 20, getHeight()/2);
drawArrowHead(g2, line);
g2.draw(line);
// If I call repaint() here (like in my answer below), it works
}
private void drawArrowHead(Graphics2D g2d, Line2D.Double line) {
AffineTransform tx = new AffineTransform();
tx.setToIdentity();
double angle = Math.atan2(line.y2-line.y1, line.x2-line.x1);
tx.translate(line.x2, line.y2);
tx.rotate((angle-Math.PI/2d));
Polygon arrowHead = new Polygon();
arrowHead.addPoint(0,5);
arrowHead.addPoint(-5,-5);
arrowHead.addPoint(5,-5);
Graphics2D g = (Graphics2D) g2d.create();
g.setTransform(tx);
g.fill(arrowHead);
g.dispose();
}
}
It is created like this:
package gui;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Main extends JFrame {
private static final long serialVersionUID = 4085389089535850911L;
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setLocation(0, 0);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
StateBtn stateBtn = new StateBtn();
stateBtn.setBounds(200,200,35,35);
contentPane.add(stateBtn);
}
}
The line is drawn properly but the arrow head is invisible until I call repaint(). The problem is that the element is a draggable one, so I would have to call repaint() twice every time the position is changed. This would make the code more complex and the GUI would be laggy.
Why can't the arrow head just be drawn together with the line? Is there really no one who can help me?
You've not posted a true MCVE, so it's impossible to know what you could be doing wrong, but there's no need for the kludge you've used in your answer, where you re-call repaint() within paintComponent. If you still need help with your own code, then please post a valid MCVE, code we can compile and run without modification. For an example of what I mean by MCVE, please read the MCVE link and look at the example MCVE that I've posted in my answer below.
Having said this, understand that generally Swing graphics are passive, meaning that you would have your program change its state based on an event, then call repaint() and this suggests to the Swing repaint manager to call paint. There is no guarantee that painting will occur, since repaint requests that have "stacked", that are backing up due to many being called in a short time, may be ignored.
So in your case, we can use your code and modify it to see how this works. Say I give my JPanel a MouseAdapter -- a class that is both a MouseListener and MouseMotionListener, and in this adapter I simply set two Point instance fields, p0 -- for where the mouse was initially pressed, and p1 -- for where the mouse drags or releases. I can set these fields and then call repaint, and let my painting methods use p0 and p1 to draw my arrow. So the mouse adapater could look like so:
private class MyMouse extends MouseAdapter {
private boolean settingMouse = false;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
p0 = e.getPoint();
p1 = null;
settingMouse = true; // drawing a new arrow
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
setP1(e);
settingMouse = false; // no longer drawing the new arrow
}
#Override
public void mouseDragged(MouseEvent e) {
setP1(e);
}
private void setP1(MouseEvent e) {
if (settingMouse) {
p1 = e.getPoint();
repaint();
}
}
}
And then within my painting code, I'd use your code, modified to make it use my p0 and p1 points:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
if (p0 != null && p1 != null) {
Line2D.Double line = new Line2D.Double(p0.x, p0.y, p1.x, p1.y);
drawArrowHead(g2, line);
g2.draw(line);
}
}
The whole shebang would look like so:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class StateBtn extends JPanel {
// constants to size the JPanel
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final int AH_SIZE = 5; // size of arrow head -- avoid "magic"
// numbers!
// our start and end Points for the arrow
private Point p0 = null;
private Point p1 = null;
public StateBtn() {
// create and add a label to tell the user what to do
JLabel label = new JLabel("Click Mouse and Drag");
label.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 42));
label.setForeground(new Color(0, 0, 0, 50));
setLayout(new GridBagLayout());
add(label); // add it to the center
// create our MouseAdapater and use it as both MouseListener and
// MouseMotionListener
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// only do this if there are points to draw!
if (p0 != null && p1 != null) {
Line2D.Double line = new Line2D.Double(p0.x, p0.y, p1.x, p1.y);
drawArrowHead(g2, line);
g2.draw(line);
}
}
private void drawArrowHead(Graphics2D g2d, Line2D.Double line) {
AffineTransform tx = new AffineTransform();
tx.setToIdentity();
double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
tx.translate(line.x2, line.y2);
tx.rotate((angle - Math.PI / 2d));
Polygon arrowHead = new Polygon();
arrowHead.addPoint(0, AH_SIZE); // again avoid "magic" numbers
arrowHead.addPoint(-AH_SIZE, -AH_SIZE);
arrowHead.addPoint(AH_SIZE, -AH_SIZE);
Graphics2D g = (Graphics2D) g2d.create();
g.setTransform(tx);
g.fill(arrowHead);
g.dispose(); // we created this, so we can dispose of it
// we should **NOT** dispose of g2d since the JVM gave us that
}
#Override
public Dimension getPreferredSize() {
// size our JPanel
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
private boolean settingMouse = false;
#Override
public void mousePressed(MouseEvent e) {
// if we press the wrong mouse button, exit
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
p0 = e.getPoint(); // set the start point
p1 = null; // clear the end point
settingMouse = true; // tell mouse listener we're creating a new arrow
repaint(); // suggest a repaint
}
#Override
public void mouseReleased(MouseEvent e) {
setP1(e);
settingMouse = false; // no longer drawing the new arrow
}
#Override
public void mouseDragged(MouseEvent e) {
setP1(e);
}
private void setP1(MouseEvent e) {
if (settingMouse) {
p1 = e.getPoint(); // set the end point
repaint(); // and paint!
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
StateBtn mainPanel = new StateBtn();
JFrame frame = new JFrame("StateBtn");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
This code is what I meant by my example of a MCVE. Actually it's a little large for a decent MCVE, but it will do. Please compile and run the code to see that it works. If this doesn't help you, if you still must use a kludge with your repaint calls, then I urge you to create your own MCVE and post it with your question, and then comment to me so I can see it.
An aside, someone questioned if it was OK to create a new Graphics object as you're doing within drawArrowHead(...) method, and yes this not only OK, it's the preferred thing to do when dealing with AffineTransforms, since this way you don't have to worry about down-stream effects that the transform might have on border and child components that might share the original Graphics object. Again this is OK, as long as you follow the rule of disposing Graphics objects that you yourself create, and not disposing Graphics objects given to you by the JVM.
OK, it seems, there is no other way than to call repaint again. I did like this at the end of the paintComponent-method:
if (repaint == false) {
repaint = true;
} else {
repaint = false;
repaint();
}
Hence, it repaints exactly one time. But is there no cleaner solution?

Hide/Show an image drawn with Graphics

I have multiple images drawn with Graphics. How can I make them appear and disappear using a JCheckBox ?
private void drawImages(int index) {
Graphics g = mNew.getGraphics();
int x = index % this.width;
int y = index / this.width;
g.drawImage(imageLabelPixel.get(idImage-1), x, y, 100, 100, null);
}
You wouldn't use graphics to draw something on the screen you want to remove. Graphics just renders it on the screen along with all the other graphics you have drawn, It doesn't keep track of components
Your options are to add an action event to your checkbox and repaint the screen from scratch not drawing the image, or to just use a Label to draw the image and set it to invisible when the box is checked
I would do it like that:
JCheckBox cb = new JCheckBox();
ImgPanel p = new ImgPanel();
cb.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
if(cb.isSelected){
p.set(0);
} else {
p.set(-1);
}
}
});
.
public class ImgPanel extends JPanel {
private int i = 0;
private List<BufferedImage> imgs;
public ImgPanel(){
//init imgs
}
public void set(){
i = 0;
repaint();
}
#Override
public void paintComponent (Graphics g){
super.paintComponent(g);
if(i >= 0){
Image img = imgs.get(i-1);
Image img1 = img.getScaledInstance(100, 100, null);
}
g.drawImage(img1, 0, 0, null);
}
}
You can't simply draw on a graphic and then hand it to a compoennt or so (I don't really understand what your given code should have done). Instead you have to overwrite the paintComponent method of a Component and put your custom drawing code in there.

Java robot class - getting Pixel color after they're painted

I need to create a colorPicker tool for my little ms paint app.
I originally asked how to switch from my Graphics 2D implementation to a Graphics2D-->BufferedImage one, (and then it would be easy to get the pixels) but I have instead been suggested to get the pixel colors thought the robot class.
First of all, here's my MCEVE: NB. It cannot be a single class, it won't save.
import java.awt.*;
import javax.swing.*;
public class Runner {
public static void main(String[] args){
JFrame Maiframe = new JFrame("Paint");
Canvas DrawingBoard = new Canvas();
Maiframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Maiframe.setSize(700, 500);
Maiframe.add(DrawingBoard);
Maiframe.setVisible(true);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
public class Canvas extends JPanel {
public int xp; //present x
public int yp; //present y
public int xo; //old x
public int yo; //old y (Drawing from starting to new point as mouse drags)
public Canvas(){
super();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println("RGB : Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
addMouseMotionListener(new MouseMotionAdapter() { //get coords as mouse drags
#Override
public void mouseDragged(MouseEvent e) {
xp = e.getX();
yp = e.getY();
if(SwingUtilities.isLeftMouseButton(e))
repaint(); //call paintcomponent
}
public void mouseMoved(MouseEvent e1){ //keep trak of coords when mouse is not dragging
xo = e1.getX();
yo = e1.getY();
}
});
}
public void draw(int x, int y, Graphics g){ //draw the line
if(xo != 0)
g.drawLine(xo, yo, x, y);
xo = x; //old x is now present x and so on
yo = y;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(xp, yp, g);
}
}
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
And it works just fine.
I needed to implement something to get the color from any pixel, when the mouse clicks.
I did this: (adding this method to Canvas, and calling it from the mouse clicker listener)
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
Example of call:
//replace old mouseListener with this
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println(" da tela Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
Unfortunately, from this implementation I get undefined behaviour. The color read is always 255, 255, 255. Exept if I color the hole panel, then it gets it right 9 times out of 10, but in some areas it's still missing it.
I have also tried wrapping the whole thing into a bufferedImage with robot#screenCap but that doesn't even remotely work.
What am I doing wrong here?
Thank you very much.
EDIT1:
There are doubts as to how a line can remain on screen after a second one has been drawn. I'll provide a screenshot:
NBB. This works because an instance of Canvas is create inside of a JFrame into Runnable, so the changes are saved, avoiding the need for shapes and arrayLists.
I'll also add a full version of the code which prints wrong RGB results, remember that this does not save the lines as it stands. Please refer to the two separate classes above for testing.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
public class Canvas extends JPanel{
public int xp; //present x
public int yp; //present y
public int xo; //old x
public int yo; //old y (Drawing from starting to new point as mouse drags)
public Robot robot;
public static void main(String[] args){
JFrame Maiframe = new JFrame("Paint");
Canvas DrawingBoard = new Canvas();
Maiframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Maiframe.setSize(700, 500);
Maiframe.add(DrawingBoard);
Maiframe.setVisible(true);
}
public Canvas(){
super();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
xo = e.getX();
yo = e.getY();
Color col = RGBFinder();
System.out.println("RGB --> Red =" + col.getRed() + "Green" + col.getGreen() + "Blue" + col.getRed());
}
});
addMouseMotionListener(new MouseMotionAdapter() { //get coords as mouse drags
#Override
public void mouseDragged(MouseEvent e) {
xp = e.getX();
yp = e.getY();
if(SwingUtilities.isLeftMouseButton(e))
repaint(); //call paintcomponent
}
public void mouseMoved(MouseEvent e1){ //keep trak of coords when mouse is not dragging
xo = e1.getX();
yo = e1.getY();
}
});
}
public void draw(int x, int y, Graphics g){ //draw the line
if(xo != 0)
g.drawLine(xo, yo, x, y);
xo = x; //old x is now present x and so on
yo = y;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
draw(xp, yp, g);
}
public Color RGBFinder(){
try{
robot = new Robot();
}
catch(AWTException e){
System.out.println("Could not create color picker robot");
}
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
//also tried robot.getPixelColor(p.getX(), p.getY());
//also tried to pass coordinates from listener to RGBFinder, and use those, no luck. (event.getX() ...)
return pixelColor;
}
}
my little ms paint app
Well its not much of a paint app. All it does is draw a single line. Whenever you attempt to draw the second line the first will be removed.
So the first step you need to do is decide how you want the painting to work. There are two common approaches:
Store Objects you want to paint in an ArrayList and then the paintComponent(...) method will paint each Object in the List.
Paint directly to a BufferedImage and then the paintComponent(...) method can just paint the BufferedImage.
Check out Custom Painting Approaches for working examples of both of these approaches and give the pros/cons of using each approach.
I have instead been suggested to get the pixel colors thought the robot class
It depends which painting approach you want to use.
If you use the Draw on Component approach then you would use the MouseInfo and Robot to get the pixel color:
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
System.out.println( robot.getPixelColor(p.x, p.y) );
If you use the Draw on Image approach then you would get the pixel color from the BufferedImage:
int rgb = bufferedImage.getRGB(mouseEvent.getX(), mouseEvent.getY());
Color color = new Color( rgb );
System.out.println( color );
Final Update
You still have not posted a SSCCE. The code you posted does NOT draw a line. Even if it did draw a line, how do you expect us to click (with accuracy) a single pixel line.
The point of a SSCCE is to demonstrate the concept you are asking about. You are asking how to get the Color of a pixel on your panel. How the drawing gets on the panel is irrelevant to the question so the painting code should be as simple as possible.
Following is a proper SSCCE. Note:
The createAndShowGUI() and main() methods will be the same for all SSCCE. The difference is the `DrawingPanel code/class will change to demonstrate your problem.
The custom painting is hardcoded. There is no need for mouseMoved/mouseDragged logic (unless that is the problem) you are trying to solve. All you care about is having different colors to click on.
Just use a simple System.out.println(...) to display the value of the Color object.
All I did was copy the relevant pieces of code from you class and remove the irrelevant code to keep the code simple and straight forward. Anybody can do that.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DrawingCanvas extends JPanel
{
private Robot robot;
public DrawingCanvas()
{
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
try
{
robot = new Robot();
}
catch(Exception re) { System.out.println(re); }
PointerInfo pi = MouseInfo.getPointerInfo();
Point p = pi.getLocation();
Color pixelColor = robot.getPixelColor(p.x, p.y);
System.out.println(pixelColor);
}
});
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor( Color.RED );
g.fillRect(0, 0, 40, 40);
g.setColor( Color.GREEN );
g.fillRect(40, 40, 40, 40);
g.setColor( Color.BLUE );
g.fillRect(80, 80, 40, 40);
}
private static void createAndShowGUI()
{
JPanel panel = new DrawingCanvas();
JFrame frame = new JFrame("DrawingCanvas");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() ); // Java 8 only
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Just copy/paste/compile/execute to see that the code works.
I'll let you figure out why your code doesn't appear to work.
NBB. This works because an instance of Canvas is create inside of a JFrame into Runnable, so the changes are saved, avoiding the need for shapes and arrayLists.
That statement is completely wrong. Painting done with the Graphics Object in the paintComponent() method is only temporary until the next time the paintComponent() method is invoked.
For example add the following code after the frame.setVisible() statement:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Graphics g = panel.getGraphics();
g.setColor( Color.YELLOW );
g.fillRect(120, 120, 40, 40);
}
});
The yellow square will disappear when the frame is resized. For permanent painting you need to use one of the two approaches I suggested in my original answer.
Edit 2:
This is the output I get when I click on the red, green, blue, background respectively:
C:\Java>java DrawingCanvas
java.awt.Color[r=255,g=0,b=0]
java.awt.Color[r=0,g=255,b=0]
java.awt.Color[r=0,g=0,b=255]
java.awt.Color[r=238,g=238,b=238]

Image as background and paintComponent

I am simply trying make a game for college that is a ball that player navigates with keyboard to get through maze. The only thing I am stuck on is my background is an image of a maze, when I add the "ball" it repaints the background (defualt color, no image) when I comment out the paint and paint component, the maze background is back, but no ball of course.
I am a new to java and I have searched this and can't see a good soulition for the code I already have. is it setOpaque? isOpaque?.... is opaque even the right direction?
Once I get this... i totally understand using collision detection to detect collisions with the maze "wall"
Trying to figure this out, being new to java, can make a grown man cry.
main.java
public class Main{
public static void main (String[] args){
//System.out.println("Start of the Game");
GameFrame game1 = new GameFrame();
}
JavaGame.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import java.io.File;
import java.io.IOException;
public class GameFrame extends JFrame{
private int frameWidth = 240, frameHeight =315;
int x, y;
private Image dbImage;
private Graphics dbg;
//Below is the constructor
public GameFrame(){
super("OperationMaze!");
addKeyListener(new AL());
setResizable(false);
//setTitle("OperationMaze!2");
//setSize(250, 302);
setSize(frameWidth, frameHeight); //Set height and width
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Because of opening file, there needs to be a try catch just incase there is an error opening.
//Plus java won't let you unless you try catch.
try{
setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("C:/Documents and Settings/brando/My Documents/NetBeansProjects/GameFrame/src/maze.jpg")))));
} catch (IOException e) {
e.printStackTrace();
}
//Show the frame
setVisible(true);
//setOpaque(false);
y = 35;
x = 15;
}
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
g.fillOval(x, y, 15, 15);
//g.setOpaque(false);
repaint();
}
// private void setOpaque(boolean b) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
// }
public class AL extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == e.VK_LEFT) {
if(x <= 5)
x = 5;
else
x += -5;
}
if(keyCode == e.VK_RIGHT) {
if(x >= 230)
x = 230;
else
x += +5;
}
if(keyCode == e.VK_UP) {
if(y <= 23)
y=23;
else
y += -5;
}
if(keyCode == e.VK_DOWN) {
if(y >= 277)
y = 277;
else
y += +5;
}
}
}
}
Ok, lets's see what's going wrong here:
when I add the "ball" it repaints the background (default color, no image)
In this scenario, when you are using your custom paint method, you don't call super.paint(g), so the JLabel with the Image doesnt get drawn.
The Image that you do create is blank:
dbImage = createImage(getWidth(), getHeight());
(Graphics offers a convenient overloaded method of drawImage that allows you to specify width & height, allowing you to avoid doing this.)
The result: no background image.
when I comment out the paint and paint component, the maze background is back, but no ball of course.
Now Swing is calling the default paint implementation which ensures that all child components are drawn but now you're not calling your fillOval method, net result: background Image, no ball.
Where next:
To allow Swing do the work of custom painting you need to override paintComponent rather than calling it directly. However this cannot be done in a top level container like JFrame. Sub-classing a JComponent and overriding paintComponent in that new component will allow Swing to call that method without you having to do it directly. Adding the #Override will allow the compiler to check that you are indeed overriding the method. As you're intending to paint the Image, you could simply load the Image and save the reference for use when you need to paint it:
dbImage = ImageIO.read(new File("MyImage.jpg"));
Then in your new JComponent-based class:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(dbImage, 0, 0, getWidth(), getHeight(), this);
g.fillOval(x, y, 15, 15);
}

Rounded Swing JButton using Java

Well, I have an image that I would like to put as a background to a button (or something clicable). The problem is that this image is round, so I need to show this image, without any borders, etc.
The JComponent that holds this button has a custom background, so the button really needs to only show the image.
After searching Google, I couldn't manage to do so. I have tried all the following, but with no luck:
button.setBorderPainted(false);
button.setContentAreaFilled(false);
button.setOpaque(true);
And after I paint the icon at the background, the button paints it, but holds an ugly gray background with borders, etc. I have also tried to use a JLabel and a JButton. And to paint an ImageIcon at it, but if the user resizes or minimizes the window, the icons disappear!
How can I fix this?
I just need to paint and round an image to a JComponent and listen for clicks at it...
Create a new Jbutton:
JButton addBtn = new JButton("+");
addBtn.setBounds(x_pos, y_pos, 30, 25);
addBtn.setBorder(new RoundedBorder(10)); //10 is the radius
addBtn.setForeground(Color.BLUE);
while setting the border for a JButton, call the overridden javax.swing.border.Border class.
addBtn.setBorder(new RoundedBorder(10));
Here is the class
private static class RoundedBorder implements Border {
private int radius;
RoundedBorder(int radius) {
this.radius = radius;
}
public Insets getBorderInsets(Component c) {
return new Insets(this.radius+1, this.radius+1, this.radius+2, this.radius);
}
public boolean isBorderOpaque() {
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
g.drawRoundRect(x, y, width-1, height-1, radius, radius);
}
}
Did you try the following?
button.setOpaque(false);
button.setFocusPainted(false);
button.setBorderPainted(false);
button.setContentAreaFilled(false);
setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); // Especially important
setBorder(null) might work, but there is a bug described at Sun explaining it is by design that the UI sets a border on a component unless the client sets a non-null border which does not implement the UIResource interface.
Rather than the JDK itself setting the border to an EmptyBorder when null is passed in, the clients should set an EmptyBorder themselves (a very easy workaround). That way there is no confusion about who's doing what in the code.
I wrote an OvalButton class that can handle oval, circular and capsule-like shaped JButtons.
In your case, extend the OvalButton class and override getBackgroundImage() method to return the image you want to set as the background.
Then add listeners and text as usually. Only a click on the oval/circular area triggers the action.
Example of your button class:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageButton extends OvalButton {
private BufferedImage image;
public ImageButton() {
super(); // Default is oval/circle shape.
setBorderThickness(0); // Oval buttons have some border by default.
try {
image = ImageIO.read(new File("your_image.jpg")); // Replace with the path to your image.
}
catch (IOException e) {
e.printStackTrace();
image = null;
}
}
#Override
protected BufferedImage getBackgroundImage() {
return image;
}
}
I would recommend overriding paint(Graphics g) method as so:
class JImageButton extends JComponent implements MouseListener {
private BufferedImage img = null;
public JImageButton(BufferedImage img) {
this.img = img;
setMinimumSize(new Dimension(img.getWidth(), img.getHeight()));
setOpaque(false);
addMouseListener(this);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
}
You can try the following. It works fine for me, and I also faced the same issue with the button.
// Jbutton
JButton imageButton = new JButton();
// Buffered Icon
BufferedImage buttonIcon = null;
try {
// Get the image and set it to the imageicon
buttonIcon = ImageIO.read(getClass().getClassLoader().getResource("images/login.png"));
}
catch(Exception ex) {
}
// Set the image icon here
imageButton = new JButton(new ImageIcon(buttonIcon));
imageButton.setBorderPainted(false);
imageButton.setContentAreaFilled(false);
imageButton.setFocusPainted(false);
imageButton.setOpaque(false);
Drag a normal button to your panel
Right click your button and go to properties:
border = no border
border painted = false
contentAreaFilled = false
focusPainted = false
opaque = false
Set an (icon) and a (rolloverIcon) by importing to project.
Opacity should be set to false, so
button.setOpaque(false);
could already be what you want.
I just had the same problem and answer of #Lalchand inspired me. I created custom class for rounded borders, that is actually a modified version of LineBorder class. It draws two rectangles: inner and outer. For my case it is ok if border is colored like a background, but if you need something else, you should tinker with detentions of inner and outer. And as creative liberty I used subpixel rendering for smoother borders.
import javax.swing.border.LineBorder;
import java.awt.*;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
class RoundedBorder extends LineBorder {
private int radius;
RoundedBorder(Color c, int thickness, int radius) {
super(c, thickness, true);
this.radius = radius;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
// adapted code of LineBorder class
if ((this.thickness > 0) && (g instanceof Graphics2D)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
Color oldColor = g2d.getColor();
g2d.setColor(this.lineColor);
Shape outer;
Shape inner;
int offs = this.thickness;
int size = offs + offs;
outer = new RoundRectangle2D.Float(x, y, width, height, 0, 0);
inner = new RoundRectangle2D.Float(x + offs, y + offs, width - size, height - size, radius, radius);
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
path.append(outer, false);
path.append(inner, false);
g2d.fill(path);
g2d.setColor(oldColor);
}
}
}
You can create an empty border to the button like this:
button.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));

Categories