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.
Related
I have a method that creates a buffered image and I want to be able to create a progress bar and then paint that onto the buffered image. Any idea how I can achieve this please?
public void paint() {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
JProgressBar pb = new JProgressBar();
//Draw the progress bar on Graphics g ???
}
Yours appears to be an xy problem in that you really don't want to add or remove components inside of any painting method. I'm going to assume that you want to place a JProgressBar onto a component that shows an image, and to do this best, create a class that extends JPanel, override its paintComponent method, draw your image in that method override, and add your JProgressBar to that JPanel (but not within its paint or paintComponent method).
e.g.
public class MyPanel extends JPanel {
private BufferedImage image;
private JProgressBar progressBar = new JProgressBar();
public MyPanel() {
// get your image here
add(progressBar);
}
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();;
if (image == null) {
return size;
} else {
int w = Math.max(image.getWidth(), size.width);
int h = Math.max(image.getHeight(), size.height);
return new Dimension(w, h);
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
}
}
}
I was trying to extend the java ScrollDemo2 to report if a shape on the canvas had been clicked. I started with a simple rectangle believing that it should be no problem to simply loop over rectangle in the canvas checking if the click point was contained within them. But then something peculiar happened, the contains methods only seems to care if the point is in a rectangle anchored at (0,0), and doesn't seem to care that my component is at y=20. Therefore i get a hit if I click withing the Jpanel from x:[0,20] y:[0,20] when I should only get a hit if I click on x:[0,20] y[20,40]. Is this a bug, or am I doing something incorrectly?
public class CachedDrawableComponent extends JComponent
{
//this will do more later
CachedDrawableComponent(Rectangle bounds)
{
this.setBounds(bounds);
}
protected void paintComponent(Graphics g)
{
g.setColor(Color.magenta);
Rectangle r = this.getBounds();
g.fillRect(r.x, r.y, r.width, r.height);
}
}
public class ScrollDemo2 extends JPanel
implements MouseListener {
private Dimension area; //indicates area taken up by graphics
private Vector<Rectangle> circles; //coordinates used to draw graphics
private Vector<CachedDrawableComponent> otherDrawables;
private JPanel drawingPane;
private final Color colors[] = {
Color.red, Color.blue, Color.green, Color.orange,
Color.cyan, Color.magenta, Color.darkGray, Color.yellow};
private final int color_n = colors.length;
public ScrollDemo2() {
super(new BorderLayout());
area = new Dimension(0,0);
circles = new Vector<Rectangle>();
this.otherDrawables = new Vector<CachedDrawableComponent>();
//Set up the instructions.
JLabel instructionsLeft = new JLabel(
"Click left mouse button to place a circle.");
JLabel instructionsRight = new JLabel(
"Click right mouse button to clear drawing area.");
JPanel instructionPanel = new JPanel(new GridLayout(0,1));
instructionPanel.setFocusable(true);
instructionPanel.add(instructionsLeft);
instructionPanel.add(instructionsRight);
//Set up the drawing area.
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.addMouseListener(this);
TestRect t = new TestRect(new Rectangle(0,20,20,20));
this.otherDrawables.add(t);
//Put the drawing area in a scroll pane.
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(200,200));
//Lay out this demo.
add(instructionPanel, BorderLayout.PAGE_START);
add(scroller, BorderLayout.CENTER);
}
/** The component inside the scroll pane. */
public class DrawingPane extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect;
for (int i = 0; i < circles.size(); i++) {
rect = circles.elementAt(i);
g.setColor(colors[(i % color_n)]);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
}
for (int i = 0; i < otherDrawables.size(); i++) {
CachedDrawableComponent drawMe = otherDrawables.elementAt(i);;
g.setColor(colors[(i % color_n)]);
drawMe.paint(g);
}
}
}
//Handle mouse events.
public void mouseReleased(MouseEvent e) {
final int W = 100;
final int H = 100;
boolean changed = false;
if (SwingUtilities.isRightMouseButton(e)) {
//This will clear the graphic objects.
circles.removeAllElements();
area.width=0;
area.height=0;
changed = true;
} else {
int x = e.getX() - W/2;
int y = e.getY() - H/2;
if (x < 0) x = 0;
if (y < 0) y = 0;
Rectangle rect = new Rectangle(x, y, W, H);
circles.addElement(rect);
drawingPane.scrollRectToVisible(rect);
int this_width = (x + W + 2);
if (this_width > area.width) {
area.width = this_width; changed=true;
}
int this_height = (y + H + 2);
if (this_height > area.height) {
area.height = this_height; changed=true;
}
}
if (changed) {
//Update client's preferred size because
//the area taken up by the graphics has
//gotten larger or smaller (if cleared).
drawingPane.setPreferredSize(area);
//Let the scroll pane know to update itself
//and its scrollbars.
drawingPane.revalidate();
}
drawingPane.repaint();
}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
System.out.println("Did press:"+e.getPoint());
System.out.println(otherDrawables.get(0).getBounds());
if(otherDrawables.get(0).contains(e.getPoint()))
{
System.out.println("Did Hit");
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new ScrollDemo2();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Output:
Did press:java.awt.Point[x=8,y=7]
java.awt.Rectangle[x=0,y=20,width=20,height=20]
Did Hit
Did press:java.awt.Point[x=14,y=89]
java.awt.Rectangle[x=0,y=20,width=20,height=20]
The face problem is that you are mixing coordinate systems:
the coordinates in the mouseListener are coordinates of the drawingPane
the coordinates expected in somecomponent.contains are coordinates of the component
From the api doc of contains:
Checks whether this component "contains" the specified point,
where x and y are defined to be
relative to the coordinate system of this component.
It's unusual (don't do without a reeeaal good reason :-) to have those components without actually adding them to the drawingPane.
Check out Playing With Shapes. It may give you some ideas for a slightly different approach.
For example by creating Shapes you don't need to keep track of Rectangle and others since all shapes will be treated the same.
You can also use the Shapes as components is you wish.
I'm making a program that has an image that you scroll around on, and I can't figure out how to update the image if a button is pressed (For example: Adds a Green Ellipse to the image.) It already draws the image into the JScrollPane and you can scroll around, but when you click a button it doesn't refresh the image. (more details in code)
Here is the code:
public class PegMaster extends JPanel implements ActionListener {
//Note: not complete code
public PegBox[] pegbox = new PegBox[9];
public static Dimension size = new Dimension(520, 500);
public BufferedImage canvas;
public Graphics2D g2d;
public JScrollPane scroller;
JPanel panel;
private Canvas window;
JScrollPane pictureScrollPane;
public PegMaster() {
JButton button = new JButton("test");
button.addActionListener(this);
add(button);
canvas = new BufferedImage((int)size.getWidth()-30, 75 * GUESSES, BufferedImage.TYPE_INT_RGB);
g2d = canvas.createGraphics();
for(int i = 0;i<=pegbox.length-1;i++) {
pegbox[i] = new PegBox(i, g2d);
}
window = new Canvas(new ImageIcon(toImage(canvas)), 1);
//Class Canvas is a Scrollable JLabel to draw to (the image)
pictureScrollPane = new JScrollPane(window);
pictureScrollPane.setPreferredSize(new Dimension((int)size.getWidth()-10, (int)size.getHeight()-20));
pictureScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
add(pictureScrollPane);
//adds the scrollpane, but can't update the image in it
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
//just adds the scrollpane
}
});
}
public void paint(Graphics g) {
super.paint(g);
for(int i = 0;i<=pegbox.length-1;i++) {
//pegbox[i] = new PegBox(i);
pegbox[i].draw(g2d);
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//tried re-making the scrollpane, didn't work.
//window = new Canvas(new ImageIcon(toImage(canvas)), 1);
//pictureScrollPane = new JScrollPane(window);
//pictureScrollPane.setPreferredSize(new Dimension((int)size.getWidth()-10 (int)size.getHeight()-20));
//pictureScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
//tried imageupdate: pictureScrollPane.imageUpdate(canvas, 0, 0, 0 (int)size.getWidth()-10, (int)size.getHeight()-20);
//remove(pictureScrollPane);
//tried this: pictureScrollPane.revalidate();
repaint();
}
}
Firstly, don't use Canvas it's a heavy weight component, it will only cause you issues in the long run, use either JComponent or JPanel
Secondly, don't override paint, use paintComponent instead. paint does a lot of work, including painting things like the border and child components. It's better if you use paintComponent as it's at the right layer within the paint hierarchy for what you want do to.
Thirdly, NEVER call something like Thread.sleep while in the Event Dispatching Thread. This will cause the event queue to pause and stop responding to events, making you program look like it's stalled.
Fourthly, NEVER call repaint (invalidate, revalidate or any method that might cause a repaint request to occur) within a paint method. You will simply end up maxing out your CPU and you will be forced to kill the process.
Fifthly, you didn't provide the actionPerformed method, which is probably where all the action (and problems) are. I'd imagin you need to call window.repaint() and possibly window.invalidate() (in reverse order), but since you didn't provided use with this code, that's simply speculation...
Try this class which displays an Image. This can be added to a JScrollPane
public class ImagePanel extends JPanel {
public Image img;
public ImagePanel(Image img){
this.img = img;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img, 0, 0, this);
}
}
Now add this class to the JScrollPane. To update it, just change the image reference and call the repaint() method on the component
The above solution didn't solved my purpose so I researched and found this.
Please follow the link for whole example. I have added the code to refer in case the link changes.
public class ScrollablePicture extends JLabel
implements Scrollable,
MouseMotionListener {
private int maxUnitIncrement = 1;
private boolean missingPicture = false;
public ScrollablePicture(ImageIcon i, int m) {
super(i);
if (i == null) {
missingPicture = true;
setText("No picture found.");
setHorizontalAlignment(CENTER);
setOpaque(true);
setBackground(Color.white);
}
maxUnitIncrement = m;
//Let the user scroll by dragging to outside the window.
setAutoscrolls(true); //enable synthetic drag events
addMouseMotionListener(this); //handle mouse drags
}
//Methods required by the MouseMotionListener interface:
public void mouseMoved(MouseEvent e) { }
public void mouseDragged(MouseEvent e) {
//The user is dragging us, so scroll!
Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
scrollRectToVisible(r);
}
public Dimension getPreferredSize() {
if (missingPicture) {
return new Dimension(320, 480);
} else {
return super.getPreferredSize();
}
}
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxUnitIncrement;
} else {
return visibleRect.height - maxUnitIncrement;
}
}
public boolean getScrollableTracksViewportWidth() {
return false;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
public void setMaxUnitIncrement(int pixels) {
maxUnitIncrement = pixels;
}
I want to make a chess type board using a custom subclass of JButton. My problem is that my images of the chess pieces are a bit too small. Is there a way I can get the image to scale to exactly the size of each grid in my gridlayout? If I resize the Jframe, the grids will change size as well. Is there a way to get the image to resize dynamically upon resizing of the whole frame?
You have 3 option for this
1) Resize the images themselves using Gimp, Photoshop, etc.
2) Create an icon dynamically
Image i = icon.getImage();
if(i != null){
int width = (int)(size * fraction);
int height =(int)(size*icon.getIconHeight()/icon.getIconWidth()*fraction);
miniature = new ImageIcon(i.getScaledInstance(width, height, Image.SCALE_SMOOTH));
}
3) on the paint of your frame you can use scale
private void scaledDrawing(Graphics g, float scale){
Graphics2D g2 = (Graphics2D) g;
AffineTransform at = new AffineTransform();
AffineTransform save = g2.getTransform();
at.setToIdentity();
at.scale(goa.getScale().x, goa.getScale().y);
g2.transform(at);
image.paintIcon(c, g2);
g2.setTransform(save);
}
You could apply some transformation to the images but it might look a little ugly. If the images are small enough, maybe you can just force a minimum size of the button so that a scrollbar will appear if the frame is sized really small. Another option might be to have two or three different sets of the images at nicely scaled sizes, and swap them out for different board sizes.
Another alternative will be to override the paint function to fill all the available place:
#Override
public final void paint(final Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
Here is an example:
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
public final class Tileset extends Component {
private Image image;
public Tileset(final Image image) {
this.image = image;
}
#Override
public final void paint(final Graphics g) {
super.paint(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
public final Image getImage() {
return (image);
}
public final void setImage(final Image image) {
this.image = image;
}
}
With:
import javax.swing.JPanel;
import java.awt.GridLayout;
public final class Map extends JPanel {
public Map(final GridLayout layout) {
setLayout(layout);
}
public Map(final Integer width, final Integer height) {
this(new GridLayout(width, height));
}
}
And:
final Map map = new Map(13, 17);
final Image grass = new ImageIcon("src/main/res/tilesets/grass1.png").getImage();
final Image wood = new ImageIcon("src/main/res/tilesets/wood1.png").getImage();
final Image rock = new ImageIcon("src/main/res/tilesets/rock1.png").getImage();
for (int i = 0; i != 13; ++i) {
for (int j = 0; j != 17; ++j) {
if (i % 2 == 0) {
if (j % 2 == 0)
map.add(new Tileset(grass), i, j);
else
map.add(new Tileset(rock), i, j);
}
else
map.add(new Tileset(wood), i, j);
}
}
That will give you:
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));