Added components are not painted until parents are repainted from another source - java

When I modify a component in a component tree of any depth, the modifications usually show automatically, immediately, without need for me to take any action to that end.
Not so when the modification is an addition of a new child.
Furthermore, if I want to force the repaint using any of the methods appropriate (as far as I understand the API), this has no tangible effect as well.
Only when a new modification to the existing tree - including the added, but still invisible component - is made, does the added child appear.
Here is an example, that will render a black window with an "Add" button at the bottom. Clicking the button will have no effect. Resizing or minimising the window will cause as many white "XX" strings to appear, as the button has been pressed beforehand.
I would, of course, very much like to have the additions appear immediately one by one, whenever the button is pressed.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Applikation
{
public static void main(String[] argumente)
{
Box dummy = new Box(BoxLayout.Y_AXIS);
JFrame window = new JFrame();
JPanel panel = new JPanel();
panel.setBackground(Color.black);
window.add(dummy);
dummy.add(panel);
JButton button = new JButton("Add");
button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e)
{
JLabel white = new JLabel("XX");
white.setBackground(Color.white);
white.setForeground(Color.white);
panel.add(white); // only visible after resizing window or switching focus to another program and back
panel.invalidate(); // does nothing
panel.repaint(); // does nothing
panel.repaint(200); // does nothing
} });
dummy.add(button);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
What am I missing?
Note that this is essentially a duplicate of this question, but as can bee seen in my example code, none of its answers do apply: They empirically do not work.

Related

Java swing Button highlighting effect gone due to increased border size

I have a panel of buttons that represent an inventory, and in order to give emphasis to one button in particular I increased its border size. The border increases inward, however, which would be totally fine it didn't cover up the highlight effect buttons have when the mouse hovers over them. Is there any way to make the button highlight effect still show up in some way if it's covered up by the border? Maybe by increasing the highlight effect's border size?
This picture is an example of what I'm talking about. The top button is the emphasized button with the increased border size. The second button is a normal button being highlighted by the cursor(the mouse cursor doesn't show up in snip snapshots). As the picture shows, the increased border size is bigger than the highlight effect so when you try to highlight the first button seemingly nothing happens. The third/fourth are just normal buttons with no effect.
Sorry if the wording here is a little confusing but I'm not sure what the best way to phrase my probelm is.
Here is a simple example. The button with the red border no longer highlights when your mouse cursor hovers over it.
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
public class Test {
JFrame window;
JPanel screen1;
Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
public Test(){
//Frame Window
window = new JFrame();
window.setSize(800,600);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(String[] args){
new Test();
}
}
JButtons come with their own default Border which is the one you are seeing by default (which comes with hover effects, press effects, etc). If you change the border to provide your own, then the default border is gone.
To fix this, one way is to implement your own border which will listen for hover events, which is easier done by listening to the JButton's model changes, like so:
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class Custom {
private static void createAndShowGUI() {
JFrame window;
JPanel screen1;
Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
//Frame Window
window = new JFrame();
window.setSize(400,300);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
//Changes:
Border borderThickRollover = BorderFactory.createLineBorder(Color.CYAN, 3);
buttonThickBorder.getModel().addChangeListener(e -> {
final ButtonModel model = (ButtonModel) e.getSource();
if (model.isRollover())
buttonThickBorder.setBorder(borderThickRollover);
else
buttonThickBorder.setBorder(borderThick);
});
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Custom::createAndShowGUI);
}
}
Similarly you can listen for other events, like the button being pressed, armed, selected or enabled (all those via the button's model) and change the border accordingly. For even more events, you can use listeners on the button itself (like FocusListener for example).
One other way to fix the issue you are seeing is to use the default border itself, along with your custom border, by constructing a CompoundBorder, for example like so:
import java.awt.Color;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
public class Default {
private static void createAndShowGUI() {
JFrame window;
JPanel screen1;
//Border borderThick = BorderFactory.createLineBorder(Color.red, 3);
//Frame Window
window = new JFrame();
window.setSize(400,300);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().setBackground(Color.blue);
//Screen 1
screen1 = new JPanel();
screen1.setBounds(100, 100, 600, 205);
screen1.setBackground(Color.blue);
JButton buttonThickBorder = new JButton("Thick Button");
//buttonThickBorder.setBorder(borderThick);
screen1.add(buttonThickBorder);
//Changes:
Border borderThickRollover = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.CYAN, 5), buttonThickBorder.getBorder());
buttonThickBorder.setBorder(borderThickRollover);
JButton buttonNormalButton = new JButton("NormalButton");
screen1.add(buttonNormalButton);
window.add(screen1);
window.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Default::createAndShowGUI);
}
}
This way you are getting the default border plus your extra border. This way you don't necessarily need to listen for model changes.
Notice, in the second example, that when you hover over the outside border of the compound one (ie the cyan sub-border) the button model changes to rolled over which makes the inside (default) border to be notified. This happens because the compound border is maintained by the button, so hovering the compound border anywhere will make the button's model rolled over. If this is not desired, then the simplest workaround I can think of right now is that you can just add the button inside a JPanel which will have a cyan LineBorder and install the button on that panel.
Generally speaking, you can obtain a JButton's default Border, without the need to actually construct a JButton, through the UIManager like so:
Border buttonDefaultBorder = javax.swing.UIManager.getBorder("Button.border");
...unless of course you install your own Look And Feel (L&F) which behaves differently, in which case you can obtain the default border after constructing the button, but that's a different story (and probably an erroneous L&F implementation).

How To Add JLabel to Already Existing Jframe?

lol i dont even know if i worded that right
i am a really new programmer and this is my code for the main class:
package culminatingnew;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class CulminatingNew {
public static void main(String[] args) {
Container container = null;
JFrame jframe = new JFrame("Math Adventure");
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setLocationRelativeTo(null);
jframe.setBounds (150, 0, 1000, 1000);
jframe.setBackground(Color.blue);
jframe.setVisible(true);
JLabel labelText = new JLabel("Welcome!");
jframe.getContentPane().add(new CharacterChoose());//
jframe.setVisible(true);
jframe.getContentPane().add(labelText);
jframe.setVisible(true);
So basically, I'm making a game. In another class in the assignment package is CharacterChoose, where the user is greeted with a picture of their character. All I want to do is add text to this same screen saying "Welcome", but whenever I try this it just ignores the CharacterChoose screen and opens a new blank frame that says "Welcome". Is there any way to fix this?
GUI's are not linear/procedural programs, the are event driven, that is, something happens and you respond to it.
Instead, maybe consider using a button saying "Continue" or something, which the user must press, once pressed, you can then present the next view.
I'd recommend having a look at CardLayout for easier management of switching between views.
See How to Use Buttons, Check Boxes, and Radio Buttons, How to Write an Action Listeners and How to Use CardLayout for more details

How to make JSplitPane auto expand on mouse hover?

I want to make a Java program with Swing layout where there is a menu on the left that is expanded when a mouse hovers over the menu area, but auto contracts after your mouse leaves.
I am trying to imitate the effect of something like mobile Youtube for Android, or Weebly's editor. Fro those who don't know, both layouts have menus on the left that expand when your mouse hovers over them. Then after your mouse leaves the area, the menu contracts again and is out of view.
I was able to create the JSplitPane containing my menu successfully, but I have no idea how to make it automatically expand when the user's mouse hovers over the JSplitPane component, or how to make it contract after the mouse leaves the area.
In case anyone is wondering why: This type of menu is easy for the user to use but because it hides away when unneeded, allows me to have more space for the main part of the program.
Any help would be appreciated thanks!
Well make use of jSplitPan.setDividerLocation(location);: Sets the location of the divider. location - an int specifying a UI-specific value (typically a pixel count).
jSplitPane1.addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent evt) {
// use evt.getSource() if needed
jSplitPan1.setDividerLocation(location);
}
});
You will probably need to compute the divider location a little bit intelligently. hints: by computing the relevant preferred-size hints. The part relevant should be discovered by you.
Take a look into official tutorial page to know: How to use MouseMotionListeners including other event listeners.
There are two basic problems...
You need to detect when the mouse hovers over the divider and
When it exists the "menu"
The second is relatively easy, you can use a MouseListener and monitor the mouseExit event, setting the position of the split pane divider as the mouse leaves.
This is complicated though, as if the user exists the "menu" by crossing over the divider, this may trigger the "menu" to made visible again...
The first problem is more complicated, as the JSplitPane contains three components, the left and right components, but also a divider component. JSplitPane doesn't actually allow access to the divider, which is rather annoying and because it uses it's own mouse listener, it blocks mouse events going to the split itself.
Instead, you need to gain access to it via the UI delegate.
Once you've set up the mouse listener, you need to ensure that the menu will only be shown when the menu is hidden, for this, I simply checked the size of the "menu" component
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
public class TestSpltPane {
public static void main(String[] args) {
new TestSpltPane();
}
public TestSpltPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final JPanel left = new JPanel();
left.setBackground(Color.RED);
left.setPreferredSize(new Dimension(100, 100));
JPanel right = new JPanel();
right.setBackground(Color.BLUE);
right.setPreferredSize(new Dimension(100, 100));
final JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
sp.setDividerLocation(0);
BasicSplitPaneDivider divider = ((BasicSplitPaneUI) sp.getUI()).getDivider();
divider.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
if (left.getWidth() == 0) {
sp.setDividerLocation(100);
}
}
});
left.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
sp.setDividerLocation(0);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

How to show Modified contents in Applet without resizing

I've created an applet game, but when I modify some of the contents, I need to (maximise or minimise) resize the window to show my modified applet.
even when I add a label, or anything, it needs resizing since I've not used the paint method.(no use of repaint).
Help me with this, how to show modified contents without resizing...
here's a sample code that have same problem.
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
public class Appl extends JApplet implements ActionListener{
Button b = new Button();
public void init()
{
setLayout(new FlowLayout());
setSize(300,300);
setVisible(true);
add(b);
b.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource()==b)
{
add(new Label("Button clicked"));
repaint();
}
}
}
If I remember correctly you just call the repaint method after the modifications of your content, then it should show up.
Repaint is always implicitly called when you resize the Applet.
Edit: Applying the validate medthod on the Japplet Container works for me in the given example. This also redraws added components, repaint just calls the paint method. try it :-)

Fire mouse event on underlying components

I'm looking for a way to pass mouse events to components covered by other components. To illustrate what I mean, here's a sample code. It contains two JLabels, one is twice smaller and entirely covered with a bigger label. If you mouse over the labels, only the bigger one fires mouseEntered event however.
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
import javax.swing.border.LineBorder;
public class MouseEvtTest extends JFrame {
public MouseEvtTest() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLayout(null);
setSize(250, 250);
MouseAdapter listener = new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
System.out.printf("Mouse entered %s label%n", e.getComponent().getName());
}
};
LineBorder border = new LineBorder(Color.BLACK);
JLabel smallLabel = new JLabel();
smallLabel.setName("small");
smallLabel.setSize(100, 100);
smallLabel.setBorder(border);
smallLabel.addMouseListener(listener);
add(smallLabel);
JLabel bigLabel = new JLabel();
bigLabel.setName("big");
bigLabel.setBorder(border);
bigLabel.setSize(200, 200);
bigLabel.addMouseListener(listener);
add(bigLabel, 0); //Add to the front
}
public static void main(String[] args) {
new MouseEvtTest().setVisible(true);
}
}
What would be the best way to fire mouse entered event on the smaller label when cursor is moved to the coordinates above it? How would it work in case where there would be multiple components stacked on top of each other? What about the remaining mouse events, like mouseClicked, mousePressed, mouseReleased, etc.?
Take a look at Alexander Potochkin's blog entry on A Well-Behaved GlassPane
In your listener:
bigLabel.dispatchEvent(mouseEvent);
Of course, you will have to define bigLabel as final
Well to understand whats happening you need to understand how Z-Ordering works. As a quick overview, the component that was added last is painted first. So in your case you want to add the small component before the big component.
// add(bigLabel, 0); //Add to the front
add(bigLabel); // add to the end so it is painted first
The OverlayLayout might help explain this better and give you another option.

Categories