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.
Related
I'm making simple game in Java and I'm using Swing. I have JFrame and inside it I want to have two JPanels - one for score and so on and second below, for actual game. I read that every JPanel has its own coordinates, so point (0, 0) is on the upper-left corner of that panel.
I override method paintComponent() im my class GameView which displays the game (so it's the second JPanel from these I mentioned). But when I want to draw something in upper-left corner of gameView and set coordinates of that image to (0,0) it draws on BarView.
I read many tutorials and posts about drawing and I don't see what am I doing wrong. So my question is, how to draw something using JPanel coordinates, not JFrame ones? Here's some code:
Adding objects extending JPanel to JFrame:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv);
frame.getContentPane().add(v);
frame.setVisible(true);
v.requestFocus();
v.repaint();
bv.repaint();
Drawing in JPanel:
public class GameView extends JPanel implements View, Commons{
public static final int WIDTH=WINDOW_WIDTH, HEIGHT=ARENA_HEIGHT;
private GameScene gameScene;
private TexturePaint paint;
private BufferedImage bi;
public GameView(){
addKeyListener(new CustomKeyListener());
addMouseMotionListener(new CustomMouseListener());
setSize(WINDOW_WIDTH, ARENA_HEIGHT);
setFocusable(true);
try {
bi = ImageIO.read(new File("src/res/texture.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.paint = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
}
#Override
public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D) g1;
g.setPaint(paint);
g.fillRect(0, 0, WINDOW_WIDTH, ARENA_HEIGHT);
for(Iterator<Drawable> it = gameScene.models.iterator(); it.hasNext();)
{
Drawable d = it.next();
d.draw(g1);
}
Toolkit.getDefaultToolkit().sync();
}
and method draw of model in gameScene usually looks like this:
public void draw(Graphics g1){
Graphics2D g = (Graphics2D) g1.create();
int cx = image.getWidth(null) / 2;
int cy = image.getHeight(null) / 2;
g.rotate(rotation, cx+x, cy+y);
g.drawImage(image, x, y, null);
}
It looks like you haven't specifed a LayoutManager for your frame, so it will default to BorderLayout.
When you subsequently call frame.getContentPane().add(component) without passing in a position constant, the position BorderLayout.CENTER will be defaulted.
The result is that your GameView and BarView components will be rendered on top of each other.
As a quick test, try specifying the component position as follows:
GameView v = new GameView();
BarView bv = new BarView();
frame.getContentPane().add(bv, BorderLayout.LINE_START);
frame.getContentPane().add(v, BorderLayout.LINE_END);
Unless your UI is really simple, you'll probably find that you need to use some other layout manager. Refer to 'How to Use Various Layout Managers' for more on this subject.
I am trying to display two different images on my screen. one of which is a banner that goes at the top of my JFrame, and another that I just placed randomly below the banner for testing purposes. The issue I am having is that while I can display a single image on the screen by adding an object of class WindowStructure to my window, I am not able to display more than one image at a time. Only the last image added to the window is displayed on the screen:
Here is the window class:
import javax.swing.JFrame;
public class Window extends JFrame {
public Window(String name) {
super(name);
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
WindowStructure banner = new WindowStructure("Beatles Logo.jpg", 0, 0, getWidth(), 75);
WindowStructure fireball = new WindowStructure("fireball.png", 100, 100, 100, 100);
add(banner); //banner
add(fireball);
setVisible(true);
while(true){
repaint();
}
}
public void paint(Graphics g
) {
super.paintComponents(g);
}
}
Here's the actual class that creates the image:
public class WindowStructure extends JPanel {
ImageIcon imageIcon;
int xLoc, yLoc, xSize, ySize;
public WindowStructure(String bannerImg, int xLoc, int yLoc, int xSize, int ySize){
URL bannerImgURL = getClass().getResource(bannerImg);
imageIcon = new ImageIcon(bannerImgURL);
this.xLoc = xLoc;
this.yLoc = yLoc;
this.xSize = xSize;
this.ySize = ySize;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(imageIcon.getImage(), xLoc, yLoc, xSize, ySize, null);
}
}
The default layout manager for JFrame is BorderLayout.
As the documentation says: "BorderLayout interprets the absence of a string specification the same as the constant CENTER". For instance:
add(banner); // Same as p.add(banner, BorderLayout.CENTER);
add(fireball); // Same as p.add(fireball, BorderLayout.CENTER);
You can fix this if you specify the location as a second argument to add():
add(banner, BorderLayout.NORTH);
add(fireball, BorderLayout.CENTER);
Or you can use another layout manager for the JFrame by invoking setLayout(LayoutManager) in your Window class constructor.
public class Window extends JFrame {
public Window(String name) {
super(name);
setLayout(new FlowLayout()); // or another the layout that best fit your needs...
...
Guide about layout managers: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
The JFrame javadocs state that the default layout manager used is BorderLayout. To add multiple components, you have to specify a different place in the layout to put each one (NORTH, SOUTH, EAST, WEST, CENTER). By default it's BorderLayout.CENTER if not specified, which is why you only see the last one added.
I have a class, SheetGood, which extends Rectangle. At the moment I place these SheetGoods onscreen using absolute positions based off of the users resolution, but I'd like to let a layoutmanager take over this aspect.
To do so I'd like to add a SheetGood object to a JPanel, but can't as SheetGood does not extend JComponent.
Any ideas as to how I can get around this?
//edit//
Will I run into issues if I force my program to run at a certain size and remove resizing options?
Ie, a fixed size of 1280x1024 so I can continue placing SheetGoods how I have been and not have to worry about the other controls clipping them when their layout manager moves them around.
To use absolute positioning, dont use a layout manager. You should set layout to null.
I suggest that: extends JPanel as rectangle and set a background color, and set bounds to the positions you want to place.
static class MyRectangle extends JPanel {
int x,
y,
width,
height;
Color bg;
public MyRectangle(int x, int y, int width, int height, Color bg) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.bg = bg;
setBounds(x, y, width, height);
setBackground(bg);
}
}
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("Test rectangle");
MyRectangle rect1 = new MyRectangle(10, 10, 90, 90, Color.red),
rect2 = new MyRectangle(110, 110, 90, 90, Color.yellow);
JPanel contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(null); //to make things absolute positioning
contentPane.add(rect1);
contentPane.add(rect2);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
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);
}
}
Can JPanels background be set to transparent?
My frame is has two JPanels:
Image Panel and
Feature Panel.
Feature Panel is overlapping Image Panel.
The Image Panel is working as a background and it loads image from a remote URL.
On Feature Panel I want to draw shapes. Now Image Panel cannot be seen due to Feature Panel's background color.
I need to make Feature Panel background transparent while still drawing its shapes and I want Image Panel to be visible (since it is doing tiling and cache function of images).
I'm using two JPanel's, because I need to seperate the image and shape drawing .
Is there a way the overlapping Jpanel have a transparent background?
Calling setOpaque(false) on the upper JPanel should work.
From your comment, it sounds like Swing painting may be broken somewhere -
First - you probably wanted to override paintComponent() rather than paint() in whatever component you have paint() overridden in.
Second - when you do override paintComponent(), you'll first want to call super.paintComponent() first to do all the default Swing painting stuff (of which honoring setOpaque() is one).
Example -
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(null);
CirclePanel topPanel = new CirclePanel();
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel();
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.drawArc(x, y, width, height, 0, 360);
}
}
}
Alternatively, consider The Glass Pane, discussed in the article How to Use Root Panes. You could draw your "Feature" content in the glass pane's paintComponent() method.
Addendum: Working with the GlassPaneDemo, I added an image:
//Set up the content pane, where the "main GUI" lives.
frame.add(changeButton, BorderLayout.SOUTH);
frame.add(new JLabel(new ImageIcon("img.jpg")), BorderLayout.CENTER);
and altered the glass pane's paintComponent() method:
protected void paintComponent(Graphics g) {
if (point != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.3f));
g2d.setColor(Color.yellow);
g2d.fillOval(point.x, point.y, 120, 60);
}
}
As noted here, Swing components must honor the opaque property; in this variation, the ImageIcon completely fills the BorderLayout.CENTER of the frame's default layout.
In my particular case it was easier to do this:
panel.setOpaque(true);
panel.setBackground(new Color(0,0,0,0,)): // any color with alpha 0 (in this case the color is black
(Feature Panel).setOpaque(false);
Hope this helps.
To set transparent you can set opaque of panel to false like
JPanel panel = new JPanel();
panel.setOpaque(false);
But to make it transculent use alpha property of color attribute like
JPanel panel = new JPanel();
panel.setBackground(new Color(0,0,0,125));
where last parameter of Color is for alpha and alpha value ranges between 0 and 255 where 0 is full transparent and 255 is fully opaque
public void paintComponent (Graphics g)
{
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.0f)); // draw transparent background
super.paintComponent(g);
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f)); // turn on opacity
g.setColor(Color.RED);
g.fillRect(20, 20, 500, 300);
}
I have tried to do it this way, but it is very flickery
As Thrasgod correctly showed in his answer, the best way is to use the paintComponent, but also if the case is to have a semi transparent JPanel (or any other component, really) and have something not transparent inside. You have to also override the paintChildren method and set the alfa value to 1.
In my case I extended the JPanel like that:
public class TransparentJPanel extends JPanel {
private float panelAlfa;
private float childrenAlfa;
public TransparentJPanel(float panelAlfa, float childrenAlfa) {
this.panelAlfa = panelAlfa;
this.childrenAlfa = childrenAlfa;
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, panelAlfa));
super.paintComponent(g2d);
}
#Override
protected void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getBackground());
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_ATOP, childrenAlfa));
super.paintChildren(g);
}
//getter and setter
}
And in my project I only need to instantiate Jpanel jp = new TransparentJPanel(0.3f, 1.0f);, if I want only the Jpanel transparent.
You could, also, mess with the JPanel shape using g2d.fillRoundRect and g2d.drawRoundRect, but it's not in the scope of this question.