I'm making an app where a user would be able to add a button to the screen or remove it (I don't have those options implemented yet). So, for now I'm manually populating it with a for() loop and manually removing one of the buttons. My problem is that after the button has been removed (the removal action in the main()), there's just a blank spot. I want to be able to repaint the screen after I remove one of those buttons. In this example, index 2 (block #3) has been removed, leaving an empty space, where it was before... and I have no idea how to repaint it. I've tried validating or repainting from different places in the program with no success.
Here's the code (P.S. I'm sure my code is not the most efficient way to accomplish what I'm trying to and I'm using setLayout(null), which is not a preferred method, but for now I'm just trying to learn certain things and then expand on that to better myself and my code):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
class TestApp extends JFrame{
JFrame frame = new JFrame("Test Program");
ArrayList<JButton> grid = new ArrayList<JButton>();
private int w = 14;
private static int amount = 102;
private static int counter = 0;
//Default Constructor (sets up JFrame)
TestApp(){
frame.setLayout(null);
frame.setPreferredSize(new Dimension(1186, 880));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setResizable(false);
paintGrid();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void newWindow()
{
JFrame select_win = new JFrame("Selected Frame");
JPanel select_panel = new JPanel();
select_panel.setPreferredSize(new Dimension(600, 800));
select_panel.setBackground(Color.ORANGE);
select_win.add(select_panel);
select_win.pack();
select_win.setResizable(false);
select_win.setVisible(true);
select_win.setLocationRelativeTo(null);
}
private void paintGrid()
{
for(int i = 0, y = 4; i < ((amount / w) + (amount % w)); i++, y += 104)
{
for(int j = 0, x = 4; j < w && (counter < amount); j++, x += 84)
{
addBlock(counter, x, y);
counter++;
}
}
}
//Adds a block
private void addBlock(int index, int x, int y){
int height = 100;
int width = 80;
grid.add(new JButton("counter: " + (counter + 1)));
(grid.get(index)).addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
newWindow();
}
});
}
});
(grid.get(index)).setBorder(new LineBorder(Color.BLACK));
(grid.get(index)).setBackground(Color.YELLOW);
(grid.get(index)).setVisible(true);
(grid.get(index)).setBounds(x, y, width, height);
frame.add(grid.get(index));
}
//Removes a block
private void removeBlock(int index){
frame.remove(grid.get(index));
grid.remove(index);
amount--;
counter--;
}
public static void main(String [] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestApp app = new TestApp();
//testing block removal
app.removeBlock(2);
}
});
}
}
The proper way would be: revalidate()
revalidate() method informs the layout manager that this component and all parents above it are marked as needing to be laid out. This means the Layout Manager will try to realign the components. Often used after removing components.
I would think that you will only know this if you are actually using Swing
As you said, is not good to use NullLayout. To fix your problem you only need to do two changes:
Change the layout to FlowLayout on the constructor, like this:
frame.setLayout(new FlowLayout());
Change the setBounds call to a setPreferredSize:
(grid.get(index)).setPreferredSize(new Dimension(width, height));
Now the FlowLayout will automatically align your items and you don't need to worry about it anymore.
Related
Hello first of all when I run the program a button appear , when I press the button the image will go from top to down.
I try the code when the image go from top to down , it work very well
BUT when I put all the codes together there is an error in ( frame.add(new AnimationPane() ); )
Question : How to add AnimationPane() to the frame ???
because this is my problem.
The idea that I want to make two scenes , the first one have a button to make go to the second scene which will have an image (it must be pushed from top until reach down ).
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package maincontentpaneswitching;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching {
private static class ChangeContentPaneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// I want to put the image here
JPanel newFrameContents = new JPanel(); //Uses FlowLayout by default.
newFrameContents.add(new JLabel("You have successfully changed the content pane of the frame!", JLabel.CENTER));
/*We assume that the source is a JButton and that the Window is of type JFrame, hence
the following utility method call is possible without letting any errors appear:*/
JFrame frame = (JFrame) SwingUtilities.getWindowAncestor((JButton) e.getSource());
frame.setSize(600, 300);
frame.setContentPane(newFrameContents); //Change the content pane of the frame.
frame.revalidate(); //Notify the frame that the component hierarchy has changed.
frame.add(new AnimationPane() );
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.repaint(); //Repaint frame with all its contents.
}
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int yPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new URL("https://i.stack.imgur.com/memI0.png"));
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += direction;
if (yPos + boat.getHeight() > getHeight()) {
yPos = getHeight() - boat.getHeight();
direction *= +1;
} else if (yPos < 0) {
yPos = 0;
direction *= +1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getHeight()*2 , boat.getWidth() *2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() - boat.getWidth();
g.drawImage(boat, x, yPos, this);
}
}
private static class MainRunnable implements Runnable {
#Override
public void run() {
JButton changeContentPaneButton = new JButton("Click to go to the next image!");
changeContentPaneButton.addActionListener(new ChangeContentPaneListener());
JPanel frameContents = new JPanel(); //Uses FlowLayout by default.
frameContents.add(changeContentPaneButton);
JFrame frame = new JFrame("My application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Tells the frame that when the user closes it, it must terminate the application.
frame.setContentPane(frameContents); //Add contents to the frame.
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.setVisible(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainRunnable()); //Swing code must always be used in the Event Dispatch Thread.
}
}
Introduction
As I said in my comment, I couldn't get the image animation to work properly. At least this code would give you a solid foundation to start with.
Here's the GUI I came up with.
Here's the GUI after you left-click on the button.
If you're going to add comments to your code, put the comments on separate lines from the code. Not everyone has a large monitor and can read 200+ character lines of code.
Explanation
Oracle has a rad tutorial, Creating a GUI With Swing. Skip the Netbeans section.
When I create a Swing GUI, I use the model/view/controller (MVC) pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
In Swing, the MVC pattern means:
The view reads information from the model
The view may not update the model
The controller updates the model and repaints/revalidates the view.
There's usually not one controller to "rule them all". Each listener controls its portion of the model and the view.
When I put together an application, I code one tiny tiny piece of it, then run tests. I probably ran two to three dozen tests, and this was mostly coded by you.
Model
I created a BoatImage class to read the boat image. It's a separate class, so I can read the image before I start to construct the GUI.
View
I created a JFrame. I created a main JPanel with a CardLayout.
I use a CardLayout to layout the button JPanel and the image JPanel. This way, the JFrame is not constantly changing size.
I create the JFrame and JPanels as separate methods/classes. This makes it much easier for people, including yourself, to read and understand the view code.
Controller
I coded the ChangeContentPaneListener to change from the button JPanel to the image JPanel. This is where you would put your image animation code.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching implements Runnable {
public static void main(String[] args) {
// Swing code must always be used in the Event Dispatch Thread.
SwingUtilities.invokeLater(new MainContentPaneSwitching());
}
private AnimationPane animationPane;
private BoatImage boatImage;
private CardLayout cardLayout;
private JPanel mainPanel;
public MainContentPaneSwitching() {
this.boatImage = new BoatImage();
}
#Override
public void run() {
JFrame frame = new JFrame("My application");
// Tells the frame that when the user closes it, it
// must terminate the application.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.mainPanel = createMainPanel();
frame.add(mainPanel, BorderLayout.CENTER);
// Resize the frame as necessary in order to fit as many contents
// as possible in the screen.
frame.pack();
// Place the frame in the center of the screen. As you can tell, this
// needs its size to calculate the location, so we made sure in the
// previous line of code that it is set.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
cardLayout = new CardLayout();
JPanel panel = new JPanel(cardLayout);
panel.add(createButtonPanel(), "button");
animationPane = new AnimationPane(boatImage);
panel.add(animationPane, "image");
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton changeContentPaneButton = new JButton(
"Click to go to the next image!");
changeContentPaneButton.addActionListener(
new ChangeContentPaneListener(this, boatImage));
panel.add(changeContentPaneButton);
return panel;
}
public JPanel getAnimationPane() {
return animationPane;
}
public void repaint() {
animationPane.repaint();
}
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private BoatImage boat;
public AnimationPane(BoatImage boat) {
this.boat = boat;
BufferedImage image = boat.getBoat();
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = boat.getBoat();
int x = getWidth() - image.getWidth();
g.drawImage(image, x, boat.getyPos(), this);
}
}
private class ChangeContentPaneListener implements ActionListener {
private int direction, yPos;
private final MainContentPaneSwitching view;
private final BoatImage model;
public ChangeContentPaneListener(MainContentPaneSwitching view,
BoatImage model) {
this.view = view;
this.model = model;
this.direction = 1;
this.yPos = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(mainPanel, "image");
}
}
public class BoatImage {
private int yPos;
private BufferedImage boat;
public BoatImage() {
try {
URL url = new URL("https://i.stack.imgur.com/memI0.png");
boat = ImageIO.read(url); // boat.jpg
} catch (MalformedURLException e) {
e.printStackTrace();
boat = null;
} catch (IOException e) {
e.printStackTrace();
boat = null;
}
this.yPos = 0;
}
public BufferedImage getBoat() {
return boat;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public int getyPos() {
return yPos;
}
}
}
TL;DR
On different DPI settings, components of my application can overlap. To get rid of that I want to check the components width, which always equals 0 after updating the UI. Simplyfied code at the end of the question.
The base problem is that on different DPI settings different parts of my program is the one that reaches farthest to the right. On higher DPI its a dynamically generated label and on lower DPI a fixed width textfield.
I am working with a SpringLayout to deal with possible changing DPI settings. With the above described I have the problem that on specific settings components overlap.
To overcome that, I wanted to check whether the left edge of the one component is further left than the right edge of the other component +10 pixels for more space.
if (labelRight.getX() < (labellist.get(0).getX() + labellist.get(0).getWidth+10)){
//Use one SpringLayout setting for labelRight
} else //Use another SpringLayout setting for labelRight
I assume the problem is that I check the width of the components after they were generated. The user may choose from different settings which will change parts of the panels content. labellist contains the rightmost labels from that area.
I made up a small testprogram to show the problem width the width check:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.UIManager;
public class testsforSO extends JFrame {
private static final long serialVersionUID = -857761460788731909L;
ArrayList<JLabel> lbls = new ArrayList<>();
int lblCount = 6;
JPanel panel = new JPanel();
SpringLayout sl_panel = new SpringLayout();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testsforSO frame = new testsforSO();
frame.pack();
frame.setVisible(true);
frame.setSize(500, 500);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public testsforSO() {
getContentPane().add(panel);
updateUI();
JButton incr = new JButton("Increment here!");
incr.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
for (int i=0; i<=lbls.size()-1; i++){
panel.remove(lbls.get(i));
}
lblCount++;
lbls.clear();
updateUI();
panel.repaint();
panel.revalidate();
System.out.println("Width of slimest label: " + lbls.get(0).getWidth());
System.out.println("Width of widest label : " + lbls.get(lbls.size()-1).getWidth());
}
});
panel.add(incr);
sl_panel.putConstraint(SpringLayout.NORTH, incr, 10, SpringLayout.NORTH, panel);
sl_panel.putConstraint(SpringLayout.EAST, incr, -10, SpringLayout.EAST, panel);
panel.setLayout(sl_panel);
}
private void updateUI(){
for (int i = 0; i <= lblCount; i++) {
String abc = new String();
for (int j = 0; j <= i; j++) {
abc += "xx";
}
JLabel bfrLbl = new JLabel(abc);
lbls.add(bfrLbl);
panel.add(bfrLbl);
}
for (int i = 0; i <= lbls.size() - 1; i++) {
if (i > 0)
sl_panel.putConstraint(SpringLayout.NORTH, lbls.get(i), 8, SpringLayout.SOUTH, lbls.get(i - 1));
else
sl_panel.putConstraint(SpringLayout.NORTH, lbls.get(i), 8, SpringLayout.NORTH, panel);
}
}
}
When pressing the button, the UI gets updated with one more label at the bottom. The SpringLayout is fitted accordingly and the panel gets revalidated after. However the output of the width is 0. Why is it and what can I do against that?
After further investigation I found a way to correctly read the width of the component:
sl_panel.getConstraint(SpringLayout.WIDTH, lbls.get(0)).getValue();
Does the trick. Seems to be a very massive line compared to
lbls.get(0).getWidth();
though.
(Beginner)
Hi, sorry for the specific question, but I'm having errors constantly ambush me out of nowhere with a program that I would expect to be quite simple.
I was planning on creating a program that would allow the user to click on JPanels with in a GridLayout in order to change their colours. Imagine a poor man's pixel art program, like the old MS Paint.
The plan was to create a JFrame set to GridLayout, of an integer width and height, and fill the grids with JPanels with a 2d array and a for loop. I would then put a MouseListener into each individual JPanel to listen for a mouseClicked, which would change the background colour of the panel clicked.
package pixelpainter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PixelPainter extends JPanel {
int width = 20;
int height = 20;
int pixSize = 10;
Color bGColor = Color.WHITE;
Dimension pixDim = new Dimension(pixSize,pixSize);
private JPanel panelClicked = null;
JFrame frame= new JFrame();
/**
* #param args the command line arguments
*/
public PixelPainter()
{
initGUI();
}
public void initGUI() {
frame.setLayout(new GridLayout(height, width, 0, 0));
frame.setSize((height * pixSize), (width * pixSize));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
int[][] pixGrid = new int [width][height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
JPanel pixel[][] = new JPanel[width][height];
frame.add(pixel[row][col]);
pixel[row][col].setBackground(bGColor);
pixel[row][col].setPreferredSize(pixDim);
pixel[row][col].setBorder(BorderFactory.createLineBorder(Color.BLACK));
pixel[row][col].addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent click)
{
JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
if (selectedPixel == null || selectedPixel == PixelPainter.this)
{
return;
}
if (selectedPixel != null)
{
selectedPixel.setBackground(Color.BLACK);
}
}
#Override
public void mousePressed(MouseEvent press)
{
}
});
}
}
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
new PixelPainter().setVisible(true);
}
});
}
}
Ideally I would be using the 2d array JFrame when filling in the colours, but apparently they must be final or effectively final.
I rearranged your code to group like things together.
Here's the GUI I created.
I made the following changes to your code.
I had the main class implement Runnable. Since the EventQueue invokeLater method expects a Runnable, you might as well make the main class a Runnable.
I moved the JPanel creation into the createPixels method. Your methods should do one thing and do that one thing well.
The initGUI method now just creates the JFrame.
I moved the sizing integers into the new PixelPanel class. The class that extends a JPanel has to provide a preferred size. The JFrame pack method then creates a JFrame of the correct size.
In the paintComponent method of the PixelPanel class, all I do is paint. You shouldn't do anything else but paint in the paintComponent method.
I made the pixels bigger, so I could left click and right click on a pixel easier. The left click makes the pixel blue, and the right click erases the blue (makes the pixel white).
Because of the model / view / controller pattern, I pulled the mouse adapter code into its own class. Separating the concerns makes getting each part of the GUI working properly much easier.
And here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PixelPainter implements Runnable {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new PixelPainter());
}
#Override
public void run() {
initGUI();
}
public void initGUI() {
frame = new JFrame("Pixel Art");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createPixels());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createPixels() {
int width = 30;
int height = 20;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(height, width, 0, 0));
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
PixelPanel pixelPanel = new PixelPanel();
pixelPanel.addMouseListener(new ColorListener(pixelPanel));
panel.add(pixelPanel);
}
}
return panel;
}
public class PixelPanel extends JPanel {
private static final long serialVersionUID = 8465814529701152253L;
private static final int PIXEL_SIZE = 20;
private Color backgroundColor;
public PixelPanel() {
this.backgroundColor = Color.WHITE;
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
this.setPreferredSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ColorListener extends MouseAdapter {
private PixelPanel panel;
public ColorListener(PixelPanel panel) {
this.panel = panel;
}
#Override
public void mousePressed(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
panel.setBackgroundColor(Color.BLUE);
panel.repaint();
} else if (event.getButton() == MouseEvent.BUTTON3) {
panel.setBackgroundColor(Color.WHITE);
panel.repaint();
}
}
}
}
Your code is creating a new pixel Array inside the loop. The idea is to create the Array out side the loo and then create a new JPanel to add to the Array inside the loop.
Something like:
int[][] pixGrid = new int [width][height];
JPanel pixel[][] = new JPanel[width][height];
and
//JPanel pixel[][] = new JPanel[width][height];
pixel[row][col] = new JPanel();
Now inside the listener because you add the listener to every panel you can access the panel directly without worrying about the mouse point:
//JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
JPanel selectedPixel = (JPanel)click.getSource();
In fact you can create a single MouseListener to add to each panel instead of creating a different listener for each panel because the above code is generic.
I'm trying to resize a label on MouseEnter, but on MouseExit, I want it back to the previous state. How would I do this?
I want the label to be bigger when it is moused over, but when the mouse exits, the label will back to normal size.
Can anybody explain to me how to do that?
If it's possible, I want to see the resize slowly.
This is the code:
package kk
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class ScrollGroup extends JPanel {
private static final int N = 8;
private static final int NN = N * N;
private static final int GAP = 5;
private static final int SIZE = 100;
public ScrollGroup() {
this.setLayout(new GridLayout(N, N, GAP, GAP));
for (int i = 0; i < NN; i++) {
final JLabel label = new JLabel();
label.setOpaque(true);
label.setBackground(Color.getHSBColor((float) i / NN, 1, 1));
label.setPreferredSize(new Dimension(SIZE, SIZE));
this.add(label);
label.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e){
}
public void mouseExited(MouseEvent e) {
}
});
}
}
private void display() {
JFrame f = new JFrame("ScrollGroup");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane sp = new JScrollPane(this);
GroupLayout layout = new GroupLayout(f.getContentPane());
f.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup().addComponent(sp)));
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup().addComponent(sp)));
f.pack();
f.setSize(N * SIZE, N * SIZE);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ScrollGroup().display();
}
});
}
}
I'm trying to resize a label in MouseEntred,
Define "resize".
You are adding your JLabels to a panel using a GridLayout. All the labels are already set to the maximum size permitted by the space available to the panel, so what do you expect the resize to do?
If you want it to appear that the label is getting bigger, then maybe you can assign a MatteBorder to each label. You can make the MatteBorder whatever size you want and then set the color equal to the background color of the panel.
If you want to animate then then you can use a Swing Timer. In the mouse#ntered you start the Timer. Every time the Timer fires you change the MatteBorder to be one less pixedl until the size is zero and you stop the Timer. On mouseExited, you just restore the default Border.
See the sections from the Swing tutorial on How to Use Timers and How to Use Borders for more information.
I'm trying to write a solitaire game.
I would like to call repaint() when the game starts and paint the full deck
once only the first time repaint is called, but when I add the if-statement it
no longer paints.
Here is the code with the if-statement:
private void paintInitialDeck(Graphics g, Card card){
card.paintCard(g);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (initialDrawing) {
Card card;
for (int i = 0; i < deck.size(); i++) {
card = deck.get(i);
card.setY((int) (50 + 0.2 * i));
card.setX((int) (250 + 0.2 * i));
paintInitialDeck(g, card);
}
initialDrawing = false;
}
}
It doesn't work, but if I remove the if statement and the initialDrawing = false
then it works.
Problem is, I need the if-statement.
Anyone can help me understand this better?
ps.: initialDrawing is set to true to begin with. Also, it's really the initialDrawing = false
that makes the whole thing not paint.
What's happening is that with initial instantiation of that class with the paintComponent method, the paintComponent method is called once. So the paint will occur once, but after that, not again because the initiaDrawing is set to false after the first call to paintComponent in the initial instantiation. So a call to repaint() within the program would not work, as initialDrawing will be false after the first start up paint process.
I'm not sure if you see the paint occurring when you first start the program, but this is why, it won't allow you to paint again after the program has started.
A possible fix might be to initialize initialDrawing to false so it doesn't initially paint. Then whatever component calls the repaint() method, in the action, first set the inititialDrawing to true, then call repaint(). Disable that component so another call to repaint() cannot be made. (or something of this logical nature).
Here's an example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DisablePaint extends JPanel {
ImagePanel panel;
JButton button;
boolean intialDraw = false;
public DisablePaint() {
button = new JButton("Paint Once");
setLayout(new BorderLayout());
panel = new ImagePanel();
add(panel, BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
intialDraw = true;
panel.repaint();
button.setEnabled(false);
}
});
}
private class ImagePanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (intialDraw) {
g.setColor(Color.BLUE);
g.fillRect(50, 50, 200, 200);
initialDraw = false;
}
}
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame();
frame.add(new DisablePaint());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You can see that initialDraw is first set to false, so it won't draw on initial start of program. I then make a call to repaint() in the actionPerformed, but first setting the intialDrawing to true. Then disable the button, not allowing a another initial painting
With the if statement your cards will only ever be drawn on the first call to paintComponent(). After that, the method will only invoke super.paintComponent() which erases any content that had previously been drawn on the component.
Since you don't have a lot of control over when paintComponent() is invoked it might be best to avoid including any behaviour related to it in your application's logic as you've done with the if-statement.
Here's an arbitrary block of code. Try compiling this example and see how many times "Repainted" is printed.
import javax.swing.*;
import java.awt.*;
public class Test {
public static void main(String args[]) {
JFrame frame = new JFrame();
frame.add(new JPanel() {
public void paintComponent(Graphics g)
{
super.paintComponents(g);
System.out.println("Repainted");
}
});
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
}
}
The straight-forward solution would be
if (initialDrawing) {
Card card;
for (int i = 0; i < deck.size(); i++) {
card = deck.get(i);
card.setY((int) (50 + 0.2 * i));
card.setX((int) (250 + 0.2 * i));
}
initialDrawing = false;
}
for (Card card : deck) {
card.paintCard(g);
}
This however suggests, that the initialisation should be done elsewhere, maybe in the constructor. In general painting code should do just painting and may be called several times.