I have to extend the size of JTextField on KeyPressed event as user enter the text in textfield.please give me some idea how to achieve this?
thanks in advance
Okay, jumping for it :-)
Let's assume the question is
How to adjust a JTextField's width to always fit its content width?
Usual collaborators
a LayoutManager which sizes its children at their pref size, f.i. FlowLayout
JTextField reports its pref size fitted to content
auto-magical resize expected
Quick example:
JTextField field = new JTextField("something");
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger
frame.setSize(400, 400);
type ... and nothing happens: size remains at initial size. That's surprise: for some reason, the field's auto-validation simply doesnt't happen. In fact, a manual revalidate of the field on receiving a change notification (Note: the only correct listener type here is a DocumentListener) doesn't change the field as well:
final JTextField field = new JTextField("something");
DocumentListener l = new DocumentListener() {
private void updateField(JTextField field)
// has no effect
field.revalidate();
}
#Override
public void removeUpdate(DocumentEvent e) {
updateField(field);
}
#Override
public void insertUpdate(DocumentEvent e) {
updateField(field);
}
#Override
public void changedUpdate(DocumentEvent e) {
}
};
field.getDocument().addDocumentListener(l);
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger
frame.setSize(400, 400);
#Gagandeep Bali found out that it's the parent that needs to be revalidated:
private void updateField(JTextField field) {
field.getParent().revalidate();
}
Unexpected, so the next question is the notorious why? Here: why doesn't the invalidate bubble up the container hierarchy until it finds a validateRoot? The answer is in the api doc:
Calls to revalidate that come from within the textfield itself will be handled by validating the textfield, unless the textfield is contained within a JViewport, in which case this returns false.
Or in other words: it's not bubbled up because the field itself is a validateRoot. Which leaves the other option to override and unconditionally return false:
JTextField field = new JTextField("something") {
#Override
public boolean isValidateRoot() {
return false;
}
};
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger
frame.setSize(400, 400);
The price to pay for this, is that the scrolling doesn't work - which isn't a big deal in this context, as the text always fits into the field. Or implement slightly more intelligent, and return true if the actual width is less than the pref.
The best way I can think of is to add one CaretListener to the JTextField concerned, and with the change in the length of the Document, you Increase/Decrease the columns of the said JTextField by calling it's setColumns(...), which inturn will Increase/Decrease the size of the JTextField
Here is one example to show you, how to achieve this :
import java.awt.*;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
public class JTextFieldColumnExample
{
private int columns = 1;
private JTextField tfield;
private void displayGUI()
{
JFrame frame = new JFrame("JTextField Columns Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel contentPane = new JPanel();
tfield = new JTextField();
tfield.setColumns(columns);
tfield.addCaretListener(new CaretListener()
{
public void caretUpdate(CaretEvent ce)
{
int len = tfield.getDocument().getLength();
if (len > columns)
tfield.setColumns(++columns);
else
{
if (--columns != 0)
tfield.setColumns(columns);
else
{
columns = 1;
tfield.setColumns(columns);
}
}
contentPane.revalidate();
contentPane.repaint();
}
});
contentPane.add(tfield);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JTextFieldColumnExample().displayGUI();
}
});
}
}
1) never to use KeyListener for JTextComponent, use DocumentListener, nothing else
2) you can to use FontMetrics, TextLayout, SwingUtilities
3) after resize you have to notify LayoutManager,
4) if is there only JTextField then to use pack() for Top-Level Container,
5) otherwise (in the case that there is more than one JPanel nested other JComponents) you have to re_layout whole Container with revalidate()and repaint() then
call pack() to the Top-Level Container if you want to resize continiously with its contens
don't call pack() to the Top-Level Container if you don't want to resize, but required usage of JScrollPane
6) in the case that value of String could be very long, then to use proper JTextComponent with supporting multiline output to the GUI, to use JTextArea (in JScrollPane) rather than plain JTextField
Depends if you are using a LayoutManager or not. If not, attach a KeyListener to the JTextField and on keyRelease you need to calculate the length of the String (in pixels) and determine if the field needs to be updated
addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
updateField();
}
public void insertUpdate(DocumentEvent e) {
updateField();
}
public void removeUpdate(DocumentEvent e) {
updateField();
}
public void updateField() {
FontMetrics fm = getFontMetrics(getFont());
String text = getText();
int length = fm.stringWidth(text);
Dimension size = getPreferredSize();
Insets insets = getInsets();
if (length < min) {
size.width = min;
} else {
size.width = length + (insets.left + insets.right);
}
setSize(size);
invalidate();
repaint();
}
});
Possibly a more sensible solution might be:
addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
updateField();
}
public void insertUpdate(DocumentEvent e) {
updateField();
}
public void removeUpdate(DocumentEvent e) {
updateField();
}
public void updateField() {
setColumns(getText().length());
}
});
I would also pay attention to what kleopatra & mKorbel have to say. While KeyListener might seem like a good idea, there are just to many situation's where it won't be notified - setText is the major one.
Related
i am developing an application in which Brightness of image change as per user change the value of JSlider.
Jslider display on window but image is not loaded and i also don't know how to pass value of JSlider to paintComponent() method.
my code :
public class Neo_2010_Slider1 extends JFrame
{
private static final long serialVersionUID = 1L;
private Container container ;
private JSlider slider1 ;
private JLabel lbl1 ;
private JPanel panel1 ;
private JTextField txt1 ;
public Neo_2010_Slider1() {
super("Slider");
setAlwaysOnTop(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(new Color(14555));
setSize(new Dimension(400,400));
setResizable(true);
container = getContentPane();
BorderLayout containerLayout = new BorderLayout();
container.setLayout(containerLayout);
lbl1 = new JLabel("Slider 1");
/****************** TextField Properties ********************************/
txt1 = new JTextField(4);
slider1 = new JSlider(JSlider.HORIZONTAL,0,1000,0);//direction , min , max , current
slider1.setFont(new Font("Tahoma",Font.BOLD,12));
slider1.setMajorTickSpacing(100);
slider1.setMinorTickSpacing(25);
slider1.setPaintLabels(true);
slider1.setPaintTicks(true);
slider1.setPaintTrack(true);
slider1.setAutoscrolls(true);
slider1.setPreferredSize(new Dimension(500,500));
slider1.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
txt1.setText(String.valueOf(slider1.getValue()));
repaint();
}
});
txt1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try
{
slider1.setValue(Integer.parseInt(txt1.getText()));
}
catch(Exception ex)
{
txt1.setText("ERROR");
txt1.setToolTipText("Set Value in Range between 0 - 1000 ") ;
}
}
});
this.addFocusListener(new FocusListener() {
#Override
public void focusLost(FocusEvent e){
}
#Override
public void focusGained(FocusEvent e) {
txt1.setText(String.valueOf(slider1.getValue()));
}
});
panel1 = new JPanel();
container.add(panel1, BorderLayout.WEST);
panel1.add(lbl1);
panel1.add(txt1);
panel1.add(slider1);
setVisible(true);
}
public void paintComponent(Graphics g){
Graphics2D g2d=(Graphics2D)g;
try{
BufferedImage src=ImageIO.read(new File("src.jpg"));
BufferedImage dest=changeBrightness(src,0.5f);
g2d.drawImage(dest,0,0,this);
ImageIO.write(dest,"jpeg",new File("dest.jpg"));
}catch(Exception e){
e.printStackTrace();
}
}
public BufferedImage changeBrightness(BufferedImage src,float val){
RescaleOp brighterOp = new RescaleOp(val, 0, null);
return brighterOp.filter(src,null); //filtering
}
public static void main(String args[])
{
new Neo_2010_Slider1();
}
}
i also don't know that where to pass paintComponent method..
if anyone knows then please guide me.
I'm not sure what it is you how to gain, but JFrame doesn't have a paintComponent method, so it will never be called.
What I would do, instead is
Load the source image as soon as you can and store it in variable (masterImage for example)
Use a JLabel to show the image
Use a javax.swing.Timer with a short delay (so 125 milliseconds) set so it won't repeat. Each time the JSlider's stateChanged event is raised, I would restart this timer.
When the timer finally triggers, I would update the "master" image's brightness and apply teh result to "image" JLabel's as it's icon.
The reason for the timer is the fact that the operation of applying the brightness can not only take time, but can increase the amount of memory the application consumes. You will want to reduce this to the absolute minimum if you can
to get the value of a JSlider, you should add a changelistener.
slider.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent e) {
// handle change
JSlider source = (JSlider)e.getSource();
methodeToHandleChange( (int) source.getValue() );
}
});
see also how to use a sliders
paintComponent
the paintComponent method is a protected method, so it can not be called from the outside. However it is part of the painting mechanisme that can be invoked by calling the repaint method.
slider.repaint();
I have a JFrame in which I remove and add components and each time I do this, I resize the frame accordingly. I added a Component Listener but of course it gets triggered both by the user resizing the window and also by my setSize() methods.
Is there any way to see from the ComponentEvent whether the user resized the window or I did through setSize()?
The posible solutions I found are:
1. Use a flag - boolean resizing - which I set to true before setSize() and to false after that.
2. Add a mouseDragged listener and compare sizes before and after the drag.
The second one is definitely not a good choice. The first one would work but I would like to know if I can find in a simple way whether the user is the one who resized the window or not.
I resize the frame accordingly
Whats wrong with using pack()?
I remove and add components and each time I do this,
Then this is where you should set your Boolean value:
programResize == true:
panel.add(...);
frame.setSize(...); // this should cause the ComponentListener to fire
// the ComponentListener would then use programResize == false;
Or a better option option could be:
component.removeComponentListener(...);
panel.add(...);
frame.setSize(...);
component.addComponentListener(...);
I like this approach better because all the logic based on the manual update is self contained in one place and there is no need to define a Boolean variable.
Is there any way to see from the ComponentEvent whether the user
resized the window or I did through setSize()?
yes it is, use boolean flag reseted by Swing Timer
in the case that user resized window then ComponentListener firing a new event per every pixel, bunch of events
by determine the contianer set(Xxx)Size is this event fired only once time
example about events from ComponentListner
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
public class ComponentEventDemo extends JPanel
implements ComponentListener, HierarchyListener,
ItemListener {
private JFrame frame;
private static final long serialVersionUID = 1L;
private JTextArea display;
private JLabel label;
private JButton button = new JButton("Change Size");
private String newline = "\n";
public ComponentEventDemo() {
display = new JTextArea();
display.setEditable(false);
JScrollPane scrollPane = new JScrollPane(display);
scrollPane.setPreferredSize(new Dimension(350, 200));
label = new JLabel("This is a label", JLabel.CENTER);
label.addComponentListener(this);
JCheckBox checkbox = new JCheckBox("Label visible", true);
checkbox.addItemListener(this);
checkbox.addComponentListener(this);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension dim = frame.getPreferredSize();
if (!dim.equals(new Dimension(800, 600))) {
frame.setPreferredSize(new Dimension(800, 600));
frame.pack();
} else {
frame.setPreferredSize(new Dimension(400, 300));
frame.pack();
}
}
});
JPanel panel = new JPanel(new GridLayout(1, 3));
panel.add(label);
panel.add(checkbox);
panel.add(button);
panel.addComponentListener(this);
frame = new JFrame("ComponentEventDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane, BorderLayout.CENTER);
frame.add(panel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
label.setVisible(true);
label.revalidate();
label.repaint();
} else {
label.setVisible(false);
}
}
protected void displayMessage(String message) {
//If the text area is not yet realized, and
//we tell it to draw text, it could cause
//a text/AWT tree deadlock. Our solution is
//to ensure that the text area is realized
//before attempting to draw text.
// if (display.isShowing()) {
display.append(message + newline);
display.setCaretPosition(display.getDocument().getLength());
//}
}
#Override
public void componentHidden(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Hidden");
}
#Override
public void componentMoved(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Moved");
}
#Override
public void componentResized(ComponentEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Resized ");
}
#Override
public void componentShown(ComponentEvent e) {
//displayMessage(e.getComponent().getClass().getName() + " --- Shown");
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComponentEventDemo componentEventDemo = new ComponentEventDemo();
}
});
}
#Override
public void hierarchyChanged(HierarchyEvent e) {
displayMessage(e.getComponent().getClass().getName() + " --- Hierarchy changed");
}
}
Good evening ladies and gentlemen,
I have a problem with Java Swing that I cannot solve, maybe you can help me. Here it is:
I have one JFrame which uses BorderLayout, and many JPanels.
Every time I need to put up a new screen (i.e. from the Main Menu, when Search button is clicked, go to the Search Menu), I simply remove the component (JPanel) which is located in the center, and put the new screen (new JPanel) in the center instead.
This way, I don't call all my header and footer objects every time I want to put up a new screen.
Everything works fine with this system except this little problem: I want to trigger some methods everytime I put up a new JPanel or change back to an existing JPanel (generally speaking, everytime a JPanel appears).
In order to do that, I tried to implement ComponentListener's componentShown(ComponentEvent e) method, and added a ComponentListener to a JPanel which I put up in the center of my JFrame, and it did NOT work. After this, I did some research and found out that this componentShown (#ComponentListener) method only works when the visibilty of the JPanel is changed (from invisible to visible or the opposite). Unfortunately, I'm not changing the visibility of a JPanel, just replacing it with another one: removing the current one, and adding the new one. Below code illustrates how I replace the JPanels.
// Get the JPanel located in the center of our JFrame
JPanel currentView = (JPanel) myFrame.getContentPane().getComponent( 2 );
if ( currentView != null )
{
// Remove it from the JPanel
myFrame.getContentPane().remove( currentView );
}
// Add the new JPanel
myFrame.getContentPane().add( otherView, BorderLayout.CENTER );
// Pack the JFrame and show it
myFrame.pack();
So here is what I have. I would really appreciate it if you could help me out.
I think that this issue corresponding with HierarchyListener, for comparing
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ContainerListener extends JFrame {
private static final long serialVersionUID = 1L;
public ContainerListener() {
super("Test");
setContentPane(new TestPanel());
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] parameters) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ContainerListener containerListener = new ContainerListener();
}
});
}
private class TestPanel extends JPanel {
private static final long serialVersionUID = 1L;
TestPanel() {
setLayout(new FlowLayout(FlowLayout.LEFT));
add(new JButton(new AbstractAction("Add label") {
private static final long serialVersionUID = 1L;
private int n = 0;
#Override
public void actionPerformed(ActionEvent event) {
TestPanel.this.add(new JLabel("Label " + ++n));
validate();
}
}));
addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
System.out.println("Components Change: " + e.getChanged());
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
if (e.getComponent().isDisplayable()) {
System.out.println("Components: " + e.getChanged());
} else {
System.out.println("Components: " + e.getChanged());
}
}
}
});
addContainerListener(new ContainerAdapter() {
#Override
public void componentAdded(ContainerEvent event) {
System.out.println("componentAdded : " + event.getChild() + "containerName" + " was added");
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
I highly recommend listening to the advice given by #Jeffrey, but if you do proceed with this design, then perhaps implementing the ContainerListener interface may prove useful.
When in doubt, consult the API.
I have a Swing app with a large panel which is wrapped in a JScrollPane. Users normally move between the panel's subcomponents by tabbing, so when they tab to something out view, I want the scroll pane to autoscroll so the component with input focus is always visible.
I've tried using KeyboardFocusManager to listen for input focus changes, and then calling scrollRectToVisible.
Here's an SSCCE displaying my current strategy (just copy/paste and run!):
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class FollowFocus {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final int ROWS = 100;
final JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(new JLabel(
"Thanks for helping out. Use tab to move around."));
for (int i = 0; i < ROWS; i++) {
JTextField field = new JTextField("" + i);
field.setName("field#" + i);
content.add(field);
}
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("focusOwner",
new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (!(evt.getNewValue() instanceof JComponent)) {
return;
}
JComponent focused = (JComponent) evt.getNewValue();
if (content.isAncestorOf(focused)) {
System.out.println("Scrolling to " + focused.getName());
focused.scrollRectToVisible(focused.getBounds());
}
}
});
JFrame window = new JFrame("Follow focus");
window.setContentPane(new JScrollPane(content));
window.setSize(200, 200);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
});
}
}
If you run this example, you'll notice it doesn't work very well. It does get the focus change notifications, but the call to scrollRectToVisible doesn't appear to have any effect. In my app (which is too complex to show here), scrollRectToVisible works about half the time when I tab into something outside of the viewport.
Is there an established way to solve this problem? If it makes any difference, the Swing app is built on Netbeans RCP (and most of our customers run Windows).
My comment to the other answer:
scrollRectToVisible on the component itself is the whole point of that
method ;-) It's passed up the hierarchy until a parent doing the
scroll is found
... except when the component itself handles it - as JTextField does: it's implemented to scroll horizontally to make the caret visible. The way out is to call the method on the field's parent.
Edit
just for clarity, the replaced line is
content.scrollRectToVisible(focused.getBounds());
you have to take Rectangle from JPanel and JViewPort too, then compare, for example
notice (against down-voting) for final and nice output required some work for positions in the JViewPort
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
//http://stackoverflow.com/questions/8245328/how-do-i-make-jscrollpane-scroll-to-follow-input-focus
public class FollowFocus {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final int ROWS = 100;
final JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(new JLabel(
"Thanks for helping out. Use tab to move around."));
for (int i = 0; i < ROWS; i++) {
JTextField field = new JTextField("" + i);
field.setName("field#" + i);
content.add(field);
}
final JScrollPane scroll = new JScrollPane(content);
KeyboardFocusManager.getCurrentKeyboardFocusManager().
addPropertyChangeListener("focusOwner", new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (!(evt.getNewValue() instanceof JComponent)) {
return;
}
JViewport viewport = (JViewport) content.getParent();
JComponent focused = (JComponent) evt.getNewValue();
if (content.isAncestorOf(focused)) {
System.out.println("Scrolling to " + focused.getName());
Rectangle rect = focused.getBounds();
Rectangle r2 = viewport.getVisibleRect();
content.scrollRectToVisible(new Rectangle(rect.x, rect.y, (int) r2.getWidth(), (int) r2.getHeight()));
}
}
});
JFrame window = new JFrame("Follow focus");
window.setContentPane(new JScrollPane(content));
window.setSize(200, 200);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
});
}
}
Here my short summary.
Add this to your Tools class:
public static void addOnEnter(Component c, Consumer<FocusEvent> onEnter) {
FocusListener fl = new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
onEnter.accept(e);
}
#Override
public void focusLost(FocusEvent e) { }
};
c.addFocusListener(fl);
}
public static void scrollToFocus(FocusEvent e) {
((JComponent) e.getComponent().getParent()).scrollRectToVisible(
e.getComponent().getBounds());
}
and use it like this:
Tools.addOnEnter(component, Tools::scrollToFocus);
component can be JTextField, JButton, ...
One major issue in your code is:
focused.scrollRectToVisible(focused.getBounds());
You are calling scrollRectToVisible on the component itself! Presumably a typo.
Make your JScrollPane a final variable and call
scrollPane.getViewport().scrollRectToVisible(focused.getBounds());
Here jtextbox is the component you want to focus and jscrollpane is your scrollpane:
jScrollpane.getVerticalScrollBar().setValue(jtextbox.getLocation().x);
Current I can add a bunch of customed component objects to the JPanel by pressing "add" JButton. I also got a "delete" JButton which I wish to do the opposite of "add".
My intention is that I can select a component with a mouse and click the delete button and pressto!, the component is gone.
I hook a MouseListener to the panel, and use MouseEvent, e.getComponent() to get w/e current component the mouse clicks on. So if it returns a custom component then a variable "private myComponent current" (already set to null) will point to that component. Then I can just click on "delete" button to remove it. An actionListener already added in "delete" button and in the body it calls this.remove(current) (if current is not null).
However, this doesn't work as I can't remove a component! Any pointer?
If there is an elegant way to managing add/remove components please suggest!
public class MainDisplayPanel extends JPanel implements ActionListener, MouseListener{
private JButton newClassButton;
private JButton deleteButton;
private Resizable current;
private Resizable resizer;
public MainDisplayPanel(LayoutManager layout) {
super(layout);
newClassButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addResizer();
}
});
deleteButton = new JButton("Delete");
deleteButton.addActionListener(this);
this.addMouseListener(this);
this.add(newClassButton);
this.add(deleteButton);
}
public void addResizer() {
//JPanel panel = new JPanel();
//panel.setBackground(Color.white);
resizer = new Resizable( new ClassBox());
this.add(resizer);
this.revalidate();
this.repaint();
}
public void actionPerformed(ActionEvent e) {
if(current!=null)
{
this.remove(current);
this.revalidate();
this.repaint();
}
}
public void mouseClicked(MouseEvent e) {
System.out.println(e);
Component component = e.getComponent();
if(component instanceof Resizable)
current= (Resizable) e.getComponent();
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.add(new MainDisplayPanel(null));
jframe.setSize(new Dimension(600,400));
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Doh!
Now, in the addResizer() method. Every time I press the add button to add a new Resizable object, what'd happen to the previously added objects? I'm certain that they become null because resizer variable no longer referring to it them??? Even if this is the case, they are still displayed on the panel...And if I pressed delete only the newly added Resizable object gets removed. So am I on the right track here?
Edit: to sum up my problem, I hooked the MouseListener to wrong object. It should be Resizable object instead of the panel. Therefore, variable current is always null.
Your problem is your MouseLisetener. You are listening to the MainDisplayPanel, and so when you click on the JPanel, the MouseEvent#getComponent method returned by, e, in your mousePressed method will return the MainDisplayPanel instance since that is what is being listened to, not the Resizable instance that is under the mouse.
Solutions include:
creating one MouseListener object and adding this same object to each Resizable as a MouseListener for the Resizable, or
using your current set up, but hold your Resizable's in an ArrayList and then iterating through the array list in the mousePressed method to see if any Resizable has been clicked by using the componentAt(...) method.
Note that I had to create my own SSCCE to solve this. Again in the future, please do us all a favor and do this for us as it really is in your and our best interest, and shows that you respect our time and our help.
Edit 1
My SSCCE:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MainDisplayPanel extends JPanel {
private static final int RESIZABLE_COUNT = 40;
private JButton deleteButton;
private Resizable current;
private Resizable resizer;
private List<Resizable> resizableList = new ArrayList<Resizable>();
public MainDisplayPanel(LayoutManager layout) {
super(layout);
deleteButton = new JButton("Delete");
deleteButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
deleteButtonActionPerformed(e);
}
});
this.addMouseListener(new MyMouseAdapter());
this.add(deleteButton);
for (int i = 0; i < RESIZABLE_COUNT; i++) {
addResizer();
}
}
private void deleteButtonActionPerformed(ActionEvent e) {
if (current != null) {
this.remove(current);
resizableList.remove(current);
current = null;
this.revalidate();
this.repaint();
}
}
public void addResizer() {
resizer = new Resizable();
this.add(resizer);
resizableList.add(resizer);
this.revalidate();
this.repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
current = null;
Component c = getComponentAt(e.getPoint());
for (Resizable resizable : resizableList) {
if (resizable == c) {
current = resizable;
resizable.setFill(true);
} else {
resizable.setFill(false);
}
}
}
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
// !! jframe.add(new MainDisplayPanel(null));
jframe.add(new MainDisplayPanel(new FlowLayout()));
jframe.setSize(new Dimension(600, 400));
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
#SuppressWarnings("serial")
class Resizable extends JPanel {
private static final int RESIZE_WIDTH = 50;
private static final int RESIZE_HEIGHT = 40;
private static final int THICKNESS = 5;
private static final Color FILL_COLOR = Color.pink;
public Resizable() {
Random rand = new Random();
// different color border so we can see that it was the clicked one that was deleted.
Color color = new Color(
rand.nextInt(255),
rand.nextInt(255),
rand.nextInt(255));
setBorder(BorderFactory.createLineBorder(color, THICKNESS));
}
#Override // so we can see it
public Dimension getPreferredSize() {
return new Dimension(RESIZE_WIDTH, RESIZE_HEIGHT);
}
public void setFill(boolean fill) {
Color fillColor = fill ? FILL_COLOR : null;
setBackground(fillColor);
repaint();
}
}
it very crazy idea, but everything is possible, but
1) in case that you Layed JComponent by using some of LayoutManager you can remove JComponents from Container, and thenafter you must/have to call revalidate() + repaint(), but this actions has side effect -> ReLayout Container and then Container's contents could be look very ***
2) in case that you layed Container with AbsoluteLayout, that should be maybe nicest but question is what with emtpy space inside Container
there is very easy way how to do it, you need to add JPopupMenu to the Container,
on RightMouseClick you have to finding JComponent under the MouseCursor
then call Container#remove(myComponent), thenafter you have to call revalidate() + repaint() for refresh GUI
or is same for me
call myComponent.setVisible(false), no re-layout, no revalidate + repaint, JComponent waiting on same place for (eventually) reusing
excelent thread about how to LayoutManagers add/remove JComponents + revalidate + repaint
I believe the problem is you need to force Swing to layout the components again after removing one. After you remove(current), call revalidate().