Nested panels with background image? - java

I have two panels defined like:
public class JPanel_with_BG extends JPanel
{
private Image bg_image;
public JPanel_with_BG(Image bg_image)
{
this.bg_image = bg_image;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (bg_image != null)
{
g.drawImage(bg_image, 0, 0, this);
}
}
}
and,
public class MPanel extends JPanel
{
public void paint(Graphics g)
{
super.paint(g);
// draw something...
}
}
Then, I want to add the second panel over the bg_pnl. The top panel is smaller like bg_pnl.size = pnl + 2*padding.
MPanel pnl = new MPanel();
JPanel bg_pnl = new JPanel_with_BG(image);
int pad = 50;
bg_pnl.setBorder(new EmptyBorder(pad, pad, pad, pad));
bg_pnl.add(pnl);
The problem is that what I'm drawing on the top panel is not visible. What I can see is only the background image. Any ideas? Thanks.

You Code seems ok. I tried this at my end and I can see a small area on my UI showing MPanel. You need to validate the size of your panel on which you are showing this component.
It might happen that the area is not visible because of the dimension of the window. Also a panel's default layout is flow layout and it arranges the components added on it based on their size and if they are really small, It might not be visible

Related

Java swing setLocation / setSize not working for JPanel

So I have made a chess game where Inside the JFrame there is a boardPanel (chessboard) and a sidePanel (contains buttons like flipBoardButton).
The whole JFrame has a Dimension of 900x640 and the boardPanel therefore is 640x640.
public class BoardPanel extends JPanel{
private static final long serialVersionUID = 1L;
public BoardPanel() {
this.setSize(640, 640);
this.setLocation(0, 0);
}
#Override
public void paint(Graphics g){
boolean white = true;
for(int y = 0; y < 8; y++){
for(int x = 0; x < 8; x++){
if(white){
g.setColor(new Color(235, 235, 235));
}
else{
g.setColor(new Color(166, 123, 90));
}
g.fillRect(x*80, y*80, 80, 80);
white = !white;
}
white = !white;
}
for(Piece p : BoardHandler.piecesOnBoard)
{
Image image;
image = p.getImage();
g.drawImage(image, p.getX(), p.getY(), this);
}
}
}
My SidePanel should be on the right (at x=641 y=0) and have a width of 900-640=260 and a height of 640...
public class SidePanel extends JPanel{
public SidePanel()
{
this.setSize(260,640);
this.setLocation(641, 0);
}
#Override
public void paint(Graphics g) {
g.setColor(new Color(50,50,50));
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
}
This is how I implemented both the panels (usual stuff):
JFrame frame = new JFrame("Chess");
BoardPanel boardPanel = new BoardPanel();
SidePanel sidePanel = new SidePanel();
frame.add(boardPanel);
frame.add(sidePanel);
frame.setVisible(true);
And I thought everything is working because this is what I got:
BUT: when I go into SidePanel calss and change the setSize to 100x100 or when I set the Location to 700,0 , I get the SAME result !
However, when I change Location / Size in the BoardPanel class, it works perfectly fine ?! (the chessboard then gets rearranged/resized)
How is this possible when I used the "same" code for both classes !?
The whole JFrame has a Dimension of 900x640 and the boardPanel therefore is 640x640.
The frame and boardPanel can't possibly have the same height because the frame has a border and a titlebar. Don't attempt to set the size/location of components. That is the job of layout managers.
Many issues:
Swing components are responsible for determining their own size. So when you do custom painting you need to implement the getPreferredSize() method so the layout manager can do its job.
For the boardPanel is would be something like:
#Override
public Dimension getPreferredSize()
{
return new Dimension(640, 640);
}
By default a JFrame uses a BorderLayout. You do NOT specify a constraint when you add your components to the frame, so be default each component is added to the CENTER. However, only the last component is managed by the BorderLayout so it will set the size/location of the sidePanel, which is why your attempt to do so is ignored.
Because the BorderLayout ignores the chessBoard your attempt to set the size/location appears to work.
However you should not attempt to set the size/location. Let the layout manager do its job.
Instead your code should be:
frame.add(boardPanel, BorderLayout.CENTER);
frame.add(sidePanel, BorderLayout.LINE_START);
Custom painting is done by overriding the paintComponent(...) method, not paint() and you always invoke super.paintComponent(...) first to make sure the background of the panel is cleared.
Custom painting is relative to the component, not relative to its location in the frame.
The following code in your sidePanel class is wrong:
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
The getX()/getY() is wrong. If you really need to do custom painting then you should just use (0, 0).
However, there is no need to even use custom painting.
In the constructor of your class you just use:
setBackground( new Color(...) );
and the background will be painted automatically.

drawImage not displaying whole image?

I am trying to draw an image to a JPanel which in turn is added to a JFrame, see here:
JFrame screen;
public void welcome(){
screen = new JFrame("Welcome");
screen.setVisible(true);
screen.pack();
screen.setBackground(Color.darkGray);
screen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// in the original code there is series of methods here that eventually calls the drawBoard() method
public void drawBoard(){
try {
final BufferedImage gboard = ImageIO.read(new File("cutsomGameBoard.jpg"));
final BufferedImage featPanel = ImageIO.read(new File("extraPanel.png"));
board = new JPanel(){
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(gboard, 0, 0, this);
}
};
extra = new JPanel(){
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(featPanel, 0, 0, this);
}
};
board.setSize(480, 480);
extra.setSize(480, 320);
}
catch (IOException e) {
e.printStackTrace();
}
}
gameScreen.add(toolbar, BorderLayout.PAGE_START);
gameScreen.add(board, BorderLayout.EAST);
gameScreen.add(extra, BorderLayout.WEST);
gameScreen.setVisible(true);
screen.add(gameScreen);
}
My problem is that when running the code, only a small corner of the buffered image is visible and I am not sure if it is a problem with the frame layout, the panel size or the drawImage method arguments, ideas?
P.S. The output: screenshot of java window
You didn't override the getPreferredSize() method of your custom component so the default size is basically (10, 10) which is the size of a panel using a FlowLayout with no added components.
Don't use a JPanel to display an image. Or if you do want to use a JPanel then you need to implement the getPreferredSize() method to return the size of your image.
The easiest solution is to just use a JLabel with an ImageIcon.
Read the section from the Swing tutorial on How to Use Icons for more information and working examples.

Java repaint() doesn't call paintComponent() for drawing image

I have read a lot of answers about this problem but I can't manage to find my error even on a simple code. Here is the problem : I'd like to draw an Image in a JLabel which is in a JPanel, but the paintComponent() method of the JLabel isn't called.
Here is the code :
The ImagePainter class should draw an image
public class ImagePainter extends JLabel{
private Image image;
public ImagePainter(){
try {
image = ImageIO.read(new File("src/testgui/image.png"));
} catch (IOException exception) {
exception.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
System.out.println("in paintComponent");
}
}
Here is a standard JFrame. I took care to add the JPanel to the contentPane
public class Display extends JFrame{
public Display(){
JPanel jp = new JPanel();
ImagePainter i = new ImagePainter();
getContentPane().add(jp);
jp.add(i);
jp.repaint();
setSize(800, 800);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
And finally the main. I instanciate Display on the EDT like everyone tell to do :
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
Display d = new Display();
}
});
}
}
Finally, I observed that if I directly add the ImagePainter to the contentPane, the image is drawn correctly. It's probably a stupid error but I spend literally hours trying to find it and really can't see it. Thank you in advance !
The label does not account for the preferred size of the image when the image is custom painted! The panel by default has a flow layout. A flow layout does not stretch components to fit. So that label would have size of 0 x 0 pixels. You can confirm that by adding a visible border to the label.
But given the image is displayed in a label, why not just set the image as the icon of the label?
Also, the jp.repaint() statement in the Display constructor is useless, since you have not yet set the frame visible.

Panel in a panel display in java

MASSIVE EDIT: I added more descritpion and code.
I recently encounter a problem where a panel that I added to another panel won't display properly (only display a black dot instead of the image). The flow of the code is: the Menu class has a button panel. When the button start is press, the Menu remove the button panel, create a Board object(that implement panel) and add it to Menu. In Board constructor, an image is loaded (a .png), then a PlayerPanel (that implement panel) is added to the board panel. In PlayerPanl constructor, an image is loaded.
The plan is to make the menu repaint() method able to call Board paintcomponent. Board will then ask PlayerPanel to paintComponent. PlayerPanel paint his image, Board paint his image and that's it, both image should display.
public class Menu extends JFrame implements ActionListener
{
Board theBoard;
JPanel pnlButton = new JPanel();
JButton btnStart = new Jbutton("Start");
public Menu(String s)
{
pnlButton.add(btnStart);
super.add(pnlButton);
super.setLocation(0,0);
super.setSize(600, 500);
super.setResizable(false);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource() == btnStart)
{
theBoard = new Board ("TestBoard");
super.remove(pnlButton);
super.add(theBoard);
super.repaint();
}
}
}
public class Board extends JPanel
{
BufferedImage boardImage;
PlayerPanel playerPanel;
public Board (String boardName)
{
boardImage = Tools.loadImage(boardName);
playerPanel = new PlayerPanel();
this.add(playerPanel);
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(boardImage, 0, 200, null);
}
}
public class PlayerPanel extends JPanel
{
BufferedImage playerImage;
public PlayerPanel()
{
playerImage = Tools.loadImage("TestPlayer");
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(playerImage, 0, 0, null);
}
}
Both image load successfully as tested, but when the repaint() is call from the JFrame holding the Board panel, only the image in Board is painted, and the image in PlayerPanel is replace with a black dot.
Any help? Thanks!
Your problem seems to be due to the size of your PlayerPanel JPanel. Sometimes it helps to debug things by testing to see what size things are when they're rendered. For instance, if you change your Board paintComponent method to this:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (boardImage != null) {
g.drawImage(boardImage, 0, 200, null);
}
System.out.printf("Board size: [%d, %d]%n", getWidth(), getHeight());
System.out.printf("Player size: [%d, %d]%n", playerPanel.getWidth(), playerPanel.getHeight());
}
it will tell you exactly what the sizes are of itself and its constituent playerPanel.
To solve the issue, you could give Board a layout manager that expands its constituent components such as a BorderLayout. Another possible solution is to give PlayerPanel its own getPreferredSize() method override, especially if you want it to size itself to its image. e.g.,
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (playerImage != null) {
g.drawImage(playerImage, 0, 0, null);
}
}
Alternatively, you could use a JLabel that holds an ImageIcon rather than draw in the JPanel.
Note, you should swap views with a CardLayout rather than calling remove(...), add(...), revalidate(), repaint()

Background image JFrame with content

I have a JFrame with BorderLayout, there are panels on all sides (North, East ,...). In the panels there are labels and buttons mostly.
Now I want the frame to have a background image, some research told me that i had to change the content pane of my frame.
When I try this however, the content gets put in the background and isn't visible. Also, I don't know how to resize the image if the frame is resized.
Is there an easy fix for this or will I have to rework most of my code?
put JPanel (or JComponent) with background Image to the BorderLayout.CENTER, then this JPanel fills whole JFrame area, rest of yout JComponents put to this JPanel
there are Jpanels on all sides (North, East ,...). In the Jpanels there are Jlabels and Jbuttons mostly.
these JComponents covered all available Rectangle for JFrame, then Background Image (from my 1st point) never will be dispalyed, because these JComponents are on_top JFrame and could be hide this Image as well,
add JPanel with Background Image (from my 1st point), then put there another JPanel(s) with JPanel#setOpaque(false);, then this JPanel will be transparent, notice JPanel has implemented by default FlowLayout
frame.getContentPane().add(new JPanel() {
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight());
}
});
This example will get you started. Use it like any JPanel.
public class JPanelWithBackground extends JPanel {
Image imageOrg = null;
Image image = null;
{
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
int w = JPanelWithBackground.this.getWidth();
int h = JPanelWithBackground.this.getHeight();
image = w>0&&h>0?imageOrg.getScaledInstance(w,h,
java.awt.Image.SCALE_SMOOTH):imageOrg;
JPanelWithBackground.this.repaint();
}
});
}
public JPanelWithBackground(Image i) {
imageOrg=i;
image=i;
setOpaque(false);
}
public void paint(Graphics g) {
if (image!=null) g.drawImage(image, 0, 0, null);
super.paint(g);
}
}
Usage Example:
Image image = your image
JFrame f = new JFrame("");
JPanel j = new JPanelWithBackground(image);
j.setLayout(new FlowLayout());
j.add(new JButton("YoYo"));
j.add(new JButton("MaMa"));
f.add(j);
f.setVisible(true);

Categories