Refreshing picture in drawingPanel extends JPanel - swing

I have to load a small icon on the botton of my software. just to have a loading/ok/error icon. as sudgested on "http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part1/Java/Chapter06/images.html" i create a dwowing panel extending JPanel.
class drawingPanel extends JPanel
{
Image img;
drawingPanel (Image img){
this.img = img;
}
public void paintComponent (Graphics g) {
super.paintComponent (g);
// Use the image width & height to find the starting point
int imgX = getSize ().width/2 - img.getWidth (this);
int imgY = getSize ().height/2 - img.getHeight (this);
//Draw image centered in the middle of the panel
g.drawImage (img, imgX, imgY, this);
} // paintComponent
}
I initialize the component in the following way:
// Grab the image.
Image img = new ImageIcon(iconPath+"ok.png").getImage();
// Create an instance of DrawingPanel
iconPanel = new drawingPanel(img);
all works well but at runtime i want to be able to change the icon within the pannel. i tryed all the fo;llowing but none managed to view the new picture:
Image img = new ImageIcon(iconPath+"loading.gif").getImage();
// Create a new instance of DrawingPanel
this.iconPanel = new drawingPanel(img);
this.iconPanel.repaint();
this.iconPanel.revalidate();
this.iconPanel.repaint();
this.repaint();
this.revalidate();
(i tryed this because the class in whic i am writing the code is another extension of JPanel that contains IconPanel. Any idea about why i do not manage to change the picture?
Thanks,
Stefano

First thing don't start class name with small name. Rename drawingPanel to DrawingPanel.
I have tried making a simple demo based on your description and it works fine. The image in panel is changing perfectly.
public class Demo {
public Demo() {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
// Grab the image.
Image img = new ImageIcon("1.png").getImage();
// Create an instance of DrawingPanel
final DrawingPanel iconPanel = new DrawingPanel(img);
frame.add(iconPanel, BorderLayout.CENTER);
JButton button = new JButton("Change image..");
frame.add(button, BorderLayout.NORTH);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
iconPanel.setImg(new ImageIcon("2.png").getImage());
iconPanel.repaint();
}
});
frame.setVisible(true);
}
public static void main(String[] args){
new Demo();
}
}
class DrawingPanel extends JPanel {
Image img;
DrawingPanel(Image img) {
this.img = img;
}
public void setImg(Image img) {
this.img = img;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Use the image width & height to find the starting point
int imgX = getSize().width / 2 - img.getWidth(this);
int imgY = getSize().height / 2 - img.getHeight(this);
// Draw image centered in the middle of the panel
g.drawImage(img, imgX, imgY, this);
} // paintComponent
}
The changes I have made is add a setter method of img in DrawingPanel class. So instead of creating new DrawingPanel you just have to call setImg() with new Image and then call reapint to paint the new image.

Related

Is there a way to add a JLabel from a class to another CardLayout?

I'm doing a project for school and my goal here is to design a gallery on Swing. One of the features is the possibility to add new images to my gallery and display the new picture. I created a card where you can add your picture by browsing the file explorer on your computer and then it will copy and add the file to the folder of the app. For the moment this part works fine, but when I go back to my GalleryPage CardLayout the image is not displayed (because I don't know how to add it directly). What I'm trying to do is add this new image to the bottomPanel of my GalleryPage java class from newImg [JLabel] on my NewImage java class. Is there a way to do that? I've been browsing the Internet to find an answer but I can't find a solution.
Here is my code:
Gallery.java
public class Gallery extends JPanel {
private GalleryPage panelGalleryPage;
private NewImage newImage;
protected static CardLayout cardGal;
public Gallery() {
super();
setLayout(new CardLayout());
cardGal = (CardLayout) this.getLayout();
panelGalleryPage = new GalleryPage();
newImage = new NewImage();
add(panelGalleryPage, "GalleryPage");
add(newImage, "NewImage");
}
}
GalleryPage.java
public class GalleryPage extends JPanel {
// preparing my grid of images by sorting the filepath of each image.
File directory = new File("oop_2022/src/main/java/com/phone/common/png/Galery_images");
int fileCount = directory.list().length;
private JButton addImg, delImg;
private JLabel title;
private JLabel[] cache = new JLabel[fileCount];
private ImageIcon[] imgGrid = new ImageIcon[fileCount];
private ImageIcon imgTitle, imgAdd, imgDel;
private JSplitPane splitPanel;
private JPanel bottomPanel, topPanel;
private List<String> results;
public GalleryPage() {
super();
setBackground(PowerOn.userInterface.DARK_GREY);
initComponents();
}
public void initComponents() {
// create a split pane
splitPanel = new JSplitPane();
topPanel = new JPanel(); // top component
bottomPanel = new JPanel(); // bottom component
// configure splitPanel
splitPanel.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitPanel.setDividerLocation(100);
splitPanel.setDividerSize(0);
splitPanel.setTopComponent(topPanel);
splitPanel.setBottomComponent(bottomPanel);
// create title
imgTitle = new ImageIcon("oop_2022/src/main/java/com/phone/common/png/images.png");
imgTitle.setImage(imgTitle.getImage().getScaledInstance(80, 80, Image.SCALE_DEFAULT));
title = new JLabel(imgTitle);
// create add Button
imgAdd = new ImageIcon("oop_2022/src/main/java/com/phone/common/png/add.png");
imgAdd.setImage(imgAdd.getImage().getScaledInstance(40, 40, Image.SCALE_DEFAULT));
addImg = new JButton(imgAdd);
addImg.setPreferredSize(new Dimension(60, 40));
// add Actionlisteneer to addImg Button
addImg.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Gallery.cardGal.show(PowerOn.gallery, "NewImage");
}
});
// create delete Button
imgDel = new ImageIcon("oop_2022/src/main/java/com/phone/common/png/trash.png");
imgDel.setImage(imgDel.getImage().getScaledInstance(40, 40, Image.SCALE_DEFAULT));
delImg = new JButton(imgDel);
delImg.setPreferredSize(new Dimension(60, 40));
// Add title and buttons to the topPanel
topPanel.add(title);
topPanel.add(addImg);
topPanel.add(delImg);
// add the splitPanel to the frame
this.add(splitPanel);
// set top and bottom panel Background
topPanel.setBackground(PowerOn.userInterface.DARK_GREY);
bottomPanel.setBackground(PowerOn.userInterface.DARK_GREY);
// set a GridLayout for the bottomPanel
this.setLayout(new GridLayout());
GridLayout layout = new GridLayout(0, 3, 10, 10);
bottomPanel.setLayout(layout);
// creating an array of file
results = new ArrayList<String>();
File[] files = new File("oop_2022/src/main/java/com/phone/common/png/Galery_images").listFiles();
// retrieving the name of each images
for (File file : files) {
if (file.isFile()) {
results.add(file.getName());
}
}
// create an array of images
for (int i = 0; i < fileCount; i++) {
imgGrid[i] = new ImageIcon(files[i].toString());
}
// loop to sale and add the images to the bottomPanel
for (int i = 0; i < files.length; i++) {
// scaling the images
imgGrid[i].setImage(imgGrid[i].getImage().getScaledInstance(120, 120,
Image.SCALE_SMOOTH));
// add the images to the grid
cache[i] = new JLabel(imgGrid[i]);
bottomPanel.add(cache[i]);
}
}
}
NewImage.java
public class NewImage extends JPanel {
private JLabel title, newImg;
private ImageIcon cacheImg;
private JPanel display;
private JButton choose, back;
private JFileChooser file;
private File selectedFile;
private Gallery addNew;
public NewImage() {
super();
setBackground(PowerOn.userInterface.DARK_GREY);
initComponents();
}
public void initComponents() {
// configure title JLabel
title = new JLabel("Choose a new image to add (jpg or png only)");
title.setHorizontalAlignment(title.CENTER);
// configurin choose JButton + Actionlistener
choose = new JButton("Explore");
file = new JFileChooser();
choose.setPreferredSize(new Dimension(60, 60));
choose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
int result = file.showOpenDialog(display);
if (result == JFileChooser.APPROVE_OPTION) {
selectedFile = file.getSelectedFile();
// check the type of the file only jpg and png accepted
String check = selectedFile.getAbsolutePath()
.substring(selectedFile.getAbsolutePath().length() - 3);
System.out.println(check);
// if the fil is ok add the file to the gallery directory
if (check.equals("png") || check.equals("jpg")) {
// retrieving the name of the file and copy on the galery folder
try {
String[] parts = selectedFile.getAbsolutePath().split(Pattern.quote(File.separator));
String dest = "oop_2022/src/main/java/com/phone/common/png/Galery_images/"
+ parts[parts.length - 1];
copyFile(selectedFile, new File(dest));
cacheImg = new ImageIcon(dest);
cacheImg.setImage(cacheImg.getImage().getScaledInstance(120, 120, Image.SCALE_DEFAULT));
newImg = new JLabel(cacheImg);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
JOptionPane.showMessageDialog(null, "Your image as been succesfully uploaded");
}
// if the file is not a png or a jpg display an error message
else {
JOptionPane.showMessageDialog(null, "You must choose a png or jpg file");
}
}
}
});
// configuring back JButton + Actionlistener
back = new JButton("Return to home page");
back.setPreferredSize(new Dimension(60, 60));
back.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
Gallery.cardGal.show(PowerOn.gallery, "GalleryPage");
}
});
// configure layout of panel
this.setLayout(new GridLayout());
// add panels
display = new JPanel();
display.setLayout(new GridLayout(0, 1));
this.add(display);
display.add(title);
display.add(choose);
display.add(back);
}
public static void copyFile(File from, File to) throws IOException {
Files.copy(from.toPath(), new FileOutputStream(to));
}
}
What I want is that the image I added is directly displayed without running the code again when I go back to my gallery page.
my gallery page

Change at runtime the background of a table added to a stage in the constructor

I need at runtime to reload a background of a table added to a stage (the user can change it at runtime).
I'm doing this but the background will change only when I switch back from another Screen or reopen my game.
MyGame.java:
String backgroundFileName = "background1.jpg";
public class MyGame extends Game {
public void create() {
...
this.setScreen(new MainMenuScreen(this));
}
public void handleChoosenBackground(String _backgroundFileName) {
backgroundFileName = _backgroundFileName;
if(getScreen() instanceof MainMenuScreen) {
((MainMenuScreen)getScreen()).reloadBackground();
}
}
}
MainMenuScreen.java:
public class MainMenuScreen implements Screen, InputProcessor {
Stage stage;
Table table;
TextureRegionDrawable textureRegionDrawableBackground;
public MainMenuScreen(final MyGame game) {
...
stage = new Stage(new FillViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight())));
textureRegionDrawableBackground = new TextureRegionDrawable(new TextureRegion(new Texture(game.backgroundFileName)));
table = new Table();
table.setFillParent(true);
table.setBackground(textureRegionDrawableBackground);
stage.addActor(table);
}
public void reloadBackground() {
textureRegionDrawableBackground = new TextureRegionDrawable(new TextureRegion(new Texture(game.backgroundFileName)));
table.setBackground(textureRegionDrawableBackground);
// log prints here, so this method is reached, with the correct new backgroundFileName
}
}
I also tried this on MyGame class but it seems that does nothing because MainMenuScreen is already the active screen:
setScreen(new MainMenuScreen(this));
If, instead, I set textureRegionDrawableBackground to null, it works showing a white background:
public void reloadBackground() {
textureRegionDrawableBackground = null; // <<<<<
table.setBackground(textureRegionDrawableBackground);
}
Thanks!
I had a similar problem when I didn't run table.pack() once I finished putting all the things into the table - apparently the background size doesn't automatically resize to the table size, and you need to tell it to do so.
Hope that solves your problem :)

Transparent JFXPanel not forwarding events to components under it

I am trying to create a transparent JFXPanel over a swing based UI. The problem is events are not forwarded through the "transparent" sections the way they are for normal swing components. I included a simple example below. Note that there are 3 layers, swing, swing, then the jfxpanel. If you comment out the
addItemToLayeredPanel(panel, createFXOverlay(), 7);
you can click both buttons even though they have jpanel layered above the bottom one. (Just demonstrating the effect works in swing only doesn't work with JFXPanel)
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JLayeredPane panel = new JLayeredPane();
JButton button = new JButton("Push me");
JPanel overlay = createOverlay();
addItemToLayeredPanel(panel, button, 5);
addItemToLayeredPanel(panel, overlay, 6);
addItemToLayeredPanel(panel, createFXOverlay(), 7);
frame.getContentPane().add(panel);
frame.setSize(500, 500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private static JPanel createOverlay() {
JPanel panel = new JPanel();
panel.add(new JButton("on top"));
panel.setOpaque(false);
return panel;
}
private static JFXPanel createFXOverlay() {
JFXPanel panel = new JFXPanel();
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color:transparent;");
root.setCenter(new Button("root"));
Scene scene = new Scene(root);
scene.setFill(javafx.scene.paint.Color.TRANSPARENT);
panel.setScene(scene);
panel.setOpaque(false);
return panel;
}
private static void addItemToLayeredPanel(JLayeredPane panel, JComponent item, int layer) {
item.setBounds(0,0,500,500);
panel.add(item);
panel.setLayer(item, layer);
}
}
Ideas? I tried a couple of ways of forwarding the events (generically) to jcomponents underneath, but I was unable to get it to work. I did find a couple of similar questions, but no solutions here and here
When Swings tries to see if a mouse event goes thru, it uses the JComponent method
public boolean contains(int x, int y) {
return (ui != null) ? ui.contains(this, x, y) : super.contains(x, y);
}
The problem is that JFXPanel, unlike other Swing components, does not define a ui ComponentUI. Therefore, the check falls back to the super.contains(x,y), which returns true if a mouse event happens within the bounds of the object.
The solution is to use a class that extends JFXPanel and overrides the contains method:
public class MyJFXPanel extends JFXPanel{
#Override
public boolean contains(int x, int y) { return false; }
}
Where false can be replaced by a more sophisticated statement, such as a pixel transparency condition.
The mouse events will then be automatically forwarded to the element underneat the MyJFXPanel.

Libgdx ScrollPane adding space at the top

I'm creating a store for my game and I'm having issues with the libgdx ScrollPane. It seems to be moving the table within it down by 155px. The effect it's having is that the table appears roughly 155px down from the top, and the last button is off the screen no matter how far down I scroll.
Here is the window when the "Store" first appears.
As you can see the top of the table is quite a bit below the top of the window. Code below, but the table only has 10px of padding.
Here is the window when I've scrolled down as far as I could go - sorry, the scroll pars disappeared before I could grab the screenshot.
You can see that the "Main Menu" button is about half way off the screen.
Here is the code that lays out the "Store".
table = new Table();
table.setFillParent(true);
table.defaults().expandX().fill().space(5f);
table.pad(10f);
// characters
table.add(new Label("Characters: ", skin, "default")).left().colspan(2);
table.row();
ButtonGroup characterGroup = new ButtonGroup();
characterGroup.setMaxCheckCount(1);
Button boyButton = createImageTextButton("Plain Boy", boyTexture);
Button catButton = createImageTextButton("Cat Girl", catTexture);
Button hornButton = createImageTextButton("Horn Girl", hornTexture);
Button pinkButton = createImageTextButton("Pink Girl", pinkTexture);
Button queenButton = createImageTextButton("Queen", queenTexture);
float minHeight = 130f; // only used to force scrolling.
table.add(boyButton).minHeight(minHeight);
table.add(catButton).minHeight(minHeight).row();
table.add(hornButton).minHeight(minHeight);
table.add(pinkButton).minHeight(minHeight).row();
table.add(queenButton).minHeight(minHeight).colspan(2);
table.row();
characterGroup.add(boyButton, catButton, hornButton, pinkButton, queenButton);
characterGroup.setChecked("Plain Boy");
// drills
ButtonGroup drillGroup = new ButtonGroup();
Button flappyButton = createImageTextButton("Flappy", null);
Button tappyButton = createImageTextButton("Tappy", null);
Button tiltyButton = createImageTextButton("Tilty", null);
table.add(new Label("Drills:", skin, "default")).left().padTop(20).colspan(2);
table.row();
table.add(flappyButton).minHeight(minHeight);
table.add(tappyButton).minHeight(minHeight).row();
table.add(tiltyButton).minHeight(minHeight);
drillGroup.add(flappyButton, tappyButton, tiltyButton);
drillGroup.setChecked("Flappy");
// main menu button
table.row();
Button mainMenuButton = createImageTextButton("Main Menu", null);
table.add(mainMenuButton).minHeight(minHeight).padTop(40).padBottom(10f).colspan(2);
ScrollPane scrollPane = new ScrollPane(table, skin);
scrollPane.setBounds(0, 0, stage.getWidth(), stage.getHeight());
addActor(scrollPane);
I debugged the desktop version and saw that the table's y position was set to -155 before I scroll, and set to 0 after I scroll to the bottom. However, even when it's set to 0, the last button is always partly off the screen.
As requested here is the code where I create the viewport and camera:
package com.example;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.example.stages.GameStage;
public class MainGame extends ApplicationAdapter {
public static final int MIN_WIDTH = 480;
public static final int MIN_HEIGHT = 800;
public static SpriteBatch batch;
public static GameStage game;
public static Skin skin;
public static Viewport viewport;
public static Stage stage;
#Override
public void create() {
viewport = new ExtendViewport(MIN_WIDTH, MIN_HEIGHT);
batch = new SpriteBatch();
initSkin();
game = new GameStage(viewport, batch, skin);
setStage(game);
}
#Override
public void dispose() {
super.dispose();
game.dispose();
skin.dispose();
}
public void initSkin() {
skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
}
#Override
public void render() {
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}
public Skin getSkin() {
return skin;
}
public static void setStage(Stage stage) {
MainGame.stage = stage;
Gdx.input.setInputProcessor(stage);
}
}
Ok, finally figured it out. Turns out the Table within the ScrollPane cannot be set to fill parent.
table.setFillParent(true);
Causes the issue. Not sure why it's doing that, but removing the setFillParent(true) fixes the issue.

Swing JToolbarButton pressing

I use JToolbarButton button, I want to make it be "pressed" when I click on it, just like JButton works.How can I do this?
Please help!Thanks.
As mentioned in Costis' reply, you are probably after a JToggleButton. It might also be necessary to suppress the painting of the border, as in the 2nd tool bar in this example.
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
class ToggleBar {
public static JToggleButton getButton(
Image selected,
Image unselected,
boolean decorated) {
JToggleButton b = new JToggleButton();
b.setSelectedIcon(new ImageIcon(selected));
b.setIcon(new ImageIcon(unselected));
b.setBorderPainted(decorated);
return b;
}
public static Image getCircleImage(Color c) {
BufferedImage bi = new BufferedImage(
32,32,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setColor(c);
g.fillOval(0,0,32,32);
g.dispose();
return bi;
}
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
Image red = getCircleImage(Color.RED);
Image green = getCircleImage(Color.GREEN);
JPanel p = new JPanel(new GridLayout(0,1));
JToolBar tb1 = new JToolBar();
for (int ii=0; ii<5; ii++) {
tb1.add( getButton(red, green, true) );
}
p.add(tb1);
JToolBar tb2 = new JToolBar();
for (int ii=0; ii<5; ii++) {
tb2.add( getButton(red, green, false) );
}
p.add(tb2);
JOptionPane.showMessageDialog(null, p);
}
});
}
}
There is a JToolbarButton in Apache Batik.
It seems to me though that what you are looking for is a JToggleButton. You can adjust it to display a small image and to be small.
jToggleButton1.setIcon(new javax.swing.ImageIcon("image.png"));
jToggleButton1.setSelectedIcon(new javax.swing.ImageIcon("selected_image.png"));
Add another image with setSelectedIcon in order to make you button look pressed.