This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Java Swing : Obtain Image of JFrame
I am working on a little drag-and-drop Java GUI builder. It works so far, but the widgets I'm dragging and dropping are just rectangles I'm dynamically drawing on a canvas.
If I have a rectangle that represents a widget like a JButton, is there a way for me to create a JButton, set the size and get the image of that JButton if it was drawn on the screen? Then I could paint the image to the screen instead of just my boring rectangle.
For example, I'm currently doing this to draw a (red) rectangle:
public void paint(Graphics graphics) {
int x = 100;
int y = 100;
int height = 100;
int width = 150;
graphics.setColor(Color.red);
graphics.drawRect(x, y, height, width);
}
How can I do something like:
public void paint(Graphics graphics) {
int x = 100;
int y = 100;
int height = 100;
int width = 150;
JButton btn = new JButton();
btn.setLabel("btn1");
btn.setHeight(height); // or minHeight, or maxHeight, or preferredHeight, or whatever; swing is tricky ;)
btn.setWidth(width);
Image image = // get the image of what the button will look like on screen at size of 'height' and 'width'
drawImage(image, x, y, imageObserver);
}
Basically, you'll paint your component to an image, and then paint that image wherever you want. In this case it's okay to call paint directly because you're not painting to the screen (but to a memory location).
If you wanted to optimize your code more than I've done here, you can save the image, and just repaint it in a different location whenever it's moved (instead of calculating the image from the button every time the screen repaints).
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class MainPanel extends Box{
public MainPanel(){
super(BoxLayout.Y_AXIS);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
// Create image to paint button to
BufferedImage buttonImage = new BufferedImage(100, 150, BufferedImage.TYPE_INT_ARGB);
final Graphics g2d = buttonImage.getGraphics();
// Create button and paint it to your image
JButton button = new JButton("Click Me");
button.setSize(button.getPreferredSize());
button.paint(g2d);
// Draw image in desired location
g.drawImage(buttonImage, 100, 100, null);
}
public static void main(String[] args){
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MainPanel());
frame.pack();
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Related
I want to create a grid of squares in my Java Swing GUI. I need to toggle their state so I'm thinking a JToggleButton is appropriate for each of the squares.
The problem that I have is that I want to partially colour each toggle button according to given percentages. e.g. if 50% and 50% I want the left half of the button to be green and the right to be red. If 25%,25%,50% I'd need 3 colours. I also need to use the button Text field so hiding that isn't allowed in the solution.
Is it possible to do something like this with a JToggleButton? Is there a better element to use? Or how might I go about it?
I apologise for not posting my work so far but I can't find anything close to an example of this type of thing.
I want to end up with something like this where each square is a button.
You can construct a button with changeable 2-color background as required by overriding
paintComponent:
import java.awt.*;
import javax.swing.*;
public class TwoColorsButton extends JButton{
private final Color leftColor, rightColor;
private int percentOfLeftColor;
public TwoColorsButton(String text) {
this(text,Color.RED, Color.GREEN, 50);
}
public TwoColorsButton(String text, Color leftColor,Color rightColor, int percentOfLeftColor) {
super(text);
this.leftColor = leftColor;
this.rightColor = rightColor;
this.percentOfLeftColor = percentOfLeftColor;
//make button transparent
setOpaque(false);
setContentAreaFilled(false);
setBorderPainted(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
int leftWidth = getWidth() * percentOfLeftColor/100;
g2.setColor(leftColor);
g2.fillRect(0, 0, leftWidth , getHeight());
g2.setColor(rightColor);
g2.fillRect(leftWidth, 0, getWidth() -leftWidth, getHeight());
g2.setPaint(Color.BLACK);
super.paintComponent(g2); //button is transparent so super paints text only
g2.dispose();
}
public void setPercentOfLeftColor(int percentOfLeftColor) {
this.percentOfLeftColor = percentOfLeftColor;
repaint();
}
public int getPercentOfLeftColor() {
return percentOfLeftColor;
}
public static void main(String[] args) {
//run button test
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
JPanel topPanel = new JPanel();
TwoColorsButton twoColorBtn = new TwoColorsButton("Some Text");
topPanel.add(twoColorBtn);
frame.add(topPanel, BorderLayout.PAGE_START);
JPanel bottomPanel = new JPanel();
JButton runTestBtn = new JButton("Run test");
runTestBtn.addActionListener(e->{
runTestBtn.setEnabled(false);
new Timer(1000, e1 ->{
int percent = twoColorBtn.getPercentOfLeftColor() +25;
percent = percent > 100 ? 0 : percent;
twoColorBtn.setPercentOfLeftColor(percent);
}).start();
});
bottomPanel.add(runTestBtn);
frame.add(bottomPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
}
The code can easily be modified to allow 3 colors, if needed.
(Test it online here)
(See a basic 3 colors toggle button here)
I have png icons and use them as icons in JButton / JLabel.
The problem is that the image displayed at runtime is larger than the original icon, and because of this resizing, it's super ugly.
Here is an example:
Original icon (left) and how it's rendered in the JButton (right)
The source code for this minimal example is simply:
public class Main {
public static void main(String... args) {
JFrame frame = new JFrame("Test");
frame.setBounds(0, 0, 120, 80);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new FlowLayout());
ImageIcon icon = new ImageIcon("icon.png");
frame.getContentPane().add(new JButton("Test", icon));
frame.setVisible(true);
}
}
Is this expected? If not, how can I avoid this? I tried many things around forcing the size of the image, the button, etc. but could not get a proper image displayed.
I have tested with icons of various sizes: 16x16, 17x17, 18x18, 19x19, 20x20, and each time the icon displayed on the JButton is a bit larger than the original which makes it look ugly:
Thank you!
Cheers.
This is because you are using Windows scaling. The entire component is scaled, both the icon and the text.
You could turn the scaling of the Icon off by using a wrapper Icon:
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
public class NoScalingIcon implements Icon
{
private Icon icon;
public NoScalingIcon(Icon icon)
{
this.icon = icon;
}
public int getIconWidth()
{
return icon.getIconWidth();
}
public int getIconHeight()
{
return icon.getIconHeight();
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
Graphics2D g2d = (Graphics2D)g.create();
AffineTransform at = g2d.getTransform();
int scaleX = (int)(x * at.getScaleX());
int scaleY = (int)(y * at.getScaleY());
int offsetX = (int)(icon.getIconWidth() * (at.getScaleX() - 1) / 2);
int offsetY = (int)(icon.getIconHeight() * (at.getScaleY() - 1) / 2);
int locationX = scaleX + offsetX;
int locationY = scaleY + offsetY;
AffineTransform scaled = AffineTransform.getScaleInstance(1.0 / at.getScaleX(), 1.0 / at.getScaleY());
at.concatenate( scaled );
g2d.setTransform( at );
icon.paintIcon(c, g2d, locationX, locationY);
g2d.dispose();
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
JButton button = new JButton( "Button" );
NoScalingIcon icon = new NoScalingIcon( new ImageIcon("box.jpg") );
button.setIcon( icon );
JPanel panel = new JPanel( );
panel.add( button );
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.setSize(200, 200);
f.setLocationRelativeTo( null );
f.setVisible(true);
}
}
The scaling adjustment will position the Icon at the top/left of the button area.
The offset adjustment will then attempt to center the Icon in the scaled icon painting area.
Using the default transform will have a scaling factor of 0 for the Icon.
Thank you all.
The problem was the default scaling factor (which was 1.25).
As I want to be fully in control of the size independently from DPI, I solved my issue by forcing the scaling factor to 1.0.
This answer was helpful
So, either pass to the command line
-Dsun.java2d.uiScale=1.0,
or set it programmatically
System.setProperty("sun.java2d.uiScale", "1.0")
Look at the source code for the constructor of class ImageIcon that takes a string parameter. It uses class java.awt.Toolkit to create the image from the file. This made me think that it must be doing some scaling. So I thought of creating the icon differently. ImageIcon has another constructor that takes an Image parameter. So I created a BufferedImage from the file and then used that image to create an ImageIcon. The BufferedImage is not scaled.
Note that your link to the icon file didn't work for me so I just downloaded a different 16x16 icon.
java.awt.image.BufferedImage img = javax.imageio.ImageIO.read(new java.io.File("cellphon.png"));
javax.swing.Icon ico = new javax.swing.ImageIcon(img);
javax.swing.JButton button = new javax.swing.JButton("Test", ico);
I learned that if you override
protected void paintComponent(Graphics g)
You can get an image painted as the background of a class that extends javax.swing.JPanel.
In my code I have 2 instances of the same Class extending JPanel with almost exactly the same code just with a different position and background image in a second JPanel and while one gets the background the other one does not. Here is my code:
public class CardPanel extends JPanel {
private int x, y, width, height;
private BufferedImage background;
public CardPanel(int x, int y, int width, int height, BufferedImage background) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.background = background;
createCardPanel();
}
private void createCardPanel() {
setPreferredSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setFocusable(false);
setOpaque(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, x, y, null);
}
}
And how I use it:
pCardPanel = new CardPanel();
dCardPanel = new CardPanel();
Declaring the CardPanels
private void createCardPanels(String imgPath) {
BufferedImage background = ImageLoader.loadImage(imgPath);
pCardPanel = new CardPanel(0, (height - Card.CARD_HEIGHT), width, Card.CARD_HEIGHT, background.getSubimage(0, (height - Card.CARD_HEIGHT), width, Card.CARD_HEIGHT));
dCardPanel = new CardPanel(0, 0, width, Card.CARD_HEIGHT, background.getSubimage(0, 0, width, Card.CARD_HEIGHT));
this.add(pCardPanel, BorderLayout.SOUTH);
this.add(dCardPanel, BorderLayout.NORTH);
}
Method for creating and adding the CardPanels
createCardPanels("/textures/background.png");
Using the method
public void addCardImage(BufferedImage img, boolean playerCard) {
JLabel imgLabel = new JLabel();
ImageIcon icon;
icon = new ImageIcon(img);
imgLabel.setIcon(icon);
cardImages.add(imgLabel);
if (playerCard)
pCardPanel.add(imgLabel);
else
dCardPanel.add(imgLabel);
display.pack();
}
This last method is called for adding Card Images to te panel, this part works. Now to my problem:
this is how it looks. (there are some other flaws like the card position but this will be a later issue I can fix myself)
As you can see, the panel on the bottom (pCardPanel) has no background image. Any ideas why it might be this way? Thanks in advance
You can get an image painted as the background of a class that extends javax.swing.JPanel
A background generally implies that the image fills the entire panel and the size of the panel is the same as the size of the image. Therefore when you paint the image the code should be:
g.drawImage(background, 0, 0, null);
So the image is always painted at the top left of the panel.
When the panel is added to the frame, the layout manager will set the location of the panel.
just with a different position
pCardPanel = new CardPanel(0, (height - Card.CARD_HEIGHT),
I would guess the problem is the "y" value is outside the size of the panel, so you don't see the image.
That is your preferred size does not account for the fact that you are attempting to paint the image at some location other than (0, 0) in which case the preferred size should be something like:
setPreferredSize(new Dimension(x + width, y + height));
However, you don't want to do that, since each component should be independent of other components. It should not know or care that you are trying to position two panels above/below one another. It should just worry about painting its own image and let the layout manager worry about setting the location of each panel.
So what you really want to do is just paint the image at (0, 0) and let the layout manager determine the location of the panel.
You are already using the BorderLayout. So it is the job of the layout manager to set the location of the component in the "SOUTH" to some non-zero "y" value.
I am working on a simple object drawing program using Swing in Java.
My program simply should draw shapes according to buttons when clicked, and move any shapes with the mouse. I have four buttons which draw rectangle, circle and square on screen. So far I did managed to draw to shapes when you click on buttons. but i want to move the shapes on screen which it did not work out.
The problem is this: When I click on circle shape to drag it around with mouse, it clears all the screen and noting is on the screen.
And, is there a way to clean all the screen when I click on clear button?
Thank you?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintProject extends JComponent implements ActionListener,
MouseMotionListener {
private int CircleX=0;
private int CircleY=0;
private int RectX=100;
private int RectY=100;
private int SquareX=300;
private int SquareY=200;
public static void main(String[] args) {
JFrame frame = new JFrame("NEW PAINT PROGRAME!");
JButton CircleButton = new JButton("Circle");
CircleButton.setActionCommand("Circle");
JButton RectangleButton = new JButton("Rectangle");
RectangleButton.setActionCommand("Rectangle");
JButton SquareButton = new JButton("Square");
SquareButton.setActionCommand("Square");
PaintProject paint = new PaintProject();
CircleButton.addActionListener(paint);
RectangleButton.addActionListener(paint);
SquareButton.addActionListener(paint);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(paint);
frame.add(CircleButton);
frame.add(RectangleButton);
frame.add(SquareButton);
frame.addMouseMotionListener(paint);
frame.pack();
frame.setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
private void drawCircle() {
Graphics g = this.getGraphics();
g.setColor(Color.red);
g.fillOval(CircleX, CircleY, 100, 100);
}
private void drawRectangle() {
Graphics g = this.getGraphics();
g.setColor(Color.green);
g.fillRect(RectX, RectY, 100, 300);
}
private void drawSquare() {
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(SquareX, SquareY, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("Circle")) {
drawCircle();
}
else if (command.equals("Rectangle")) {
drawRectangle();
}
else if (command.equals("Square")) {
drawSquare();
}
}
#Override
public void mouseDragged(MouseEvent e) {
CircleX=e.getX();
CircleY=e.getY();
repaint();
}
}
As noted here and here, getGraphics() is not how to perform custom painting in Swing. Instead, override paintComponent() to render the desired content. It looks like you want to drag shapes using the mouse. A basic approach to moving a selected object is shown here; substitute your fillXxx() invocation for the drawString() shown there. For multiple shapes, use a List<Shape>; the clear command then becomes simply List::clear. A complete example is cited here; it features moveable, selectable, resizable, colored nodes connected by edges.
I want go full screen and keep everything inside in order.
How should i put the JFrame into full screen AND rescale everything inside: images, generated drawings etc.(sth like zooming it up so the content will fit the screen).
The problem is I am making full screen app, but I don't know on what screen it will be displayed.
This will put the frame into fullscreen, but the content will not be rescaled
frame.dispose();
frame.setUndecorated(true);
frame.setLocation(0, 0);
frame.setSize(java.awt.Toolkit.getDefaultToolkit().getScreenSize());
frame.setVisible(true);
frame.repaint();
Depends on what it is that you want to scale.
If its graphics you draw using Java2D, just figure out how much the stuff needs to be scaled up and use Graphics2D.scale() to scale the gfx appropiately.
If its something with a Swing layout, use a Layout manager to make an adaptive layout.
If its something else, elaborate on your problem
If this really is what you want to do (see warnings from other answers), it's not too hard to do (but takes a little time to figure out). Basically, it involves extending JPanel, and then overwriting the paint method.
Here's a sample that I came up with:
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class CustomPanel extends JPanel{
Component myComponent;
public CustomPanel(){
super();
setLayout(null);
}
/**
* Only allows one component to be added
*/
#Override
public Component add(Component c){
super.add(c);
c.setLocation(0, 0);
c.setSize(c.getPreferredSize());
myComponent = c;
return c;
}
#Override
public void paint(final Graphics g){
Dimension d = this.getSize();
Dimension p = myComponent.getPreferredSize();
// Paints the child component to a image
BufferedImage newImg = new BufferedImage(p.width, p.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = newImg.createGraphics();
super.paint(g2d);
// Resizes the image if necessary
Image img;
if(d.height > p.height && d.width > p.width){
System.out.println("Scaled");
float changePercentage = 0;
if(d.height/p.height > d.width/p.width){
changePercentage = (float)d.width/(float)p.width;
} else{
changePercentage = (float)d.height/(float)p.height;
}
System.out.println(changePercentage);
int newHeight = ((Float)(p.height * changePercentage)).intValue();
int newWidth = ((Float)(p.width * changePercentage)).intValue();
img = newImg.getScaledInstance(newWidth, newHeight, 0);
} else{
System.out.println("Not Scaled");
img = newImg;
}
// Paints the image of the child component to the screen.
g.drawImage(img, 0, 0, null);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SwingUtilities.invokeLater(new Runnable(){public void run(){
JFrame frame = new JFrame("Zoom Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
CustomPanel buffer = new CustomPanel();
JPanel content = new JPanel();
content.add(new JLabel("Bogus"));
content.setBackground(Color.red);
buffer.add(content);
frame.setContentPane(buffer);
frame.setVisible(true);
new CustomPanel();
}});
}
}
few days back I just worked with an java full screen app. Have a look at the following link. if that was your requirement I can help you to some extent.
https://docs.google.com/open?id=0B9U-BwYu62ZaeDM3SWZhaTdSYzQ