I have created a number of JLabels dynamically and have added them to a JPanel with the following code (code should be enough to understand the problem I hope!).
JPanel textPanel = new JPanel();
map = new HashMap<Integer,JLabel>();
vec = new Vector<JLabel>();
for(int i = 0; i<getCount();i++){ // getCount() returns int
JLabel label = new JLabel(getItemText(i)); // getItemText() returns String
map.put(i, label);
vec.add(label);
textPanel.add(map.get(i));
}
Now I am trying to access the Location of these labels but get nothing but java.awt.Point[x=296,y=63] for them when trying to access them via the following code.
System.out.println("Component position [1]: " +
textPanel.getComponent(1).getLocationOnScreen());
I get the same position for all Components, not just for the one.
Also (more importantly) I get the position java.awt.Point[x=0,y=0] for the following code.
System.out.println("Position of Component 1: " + map.get(1).getLocation());
I'm guessing this is to do with the fact that the JLabels are created dynamically. I really need to create them dynamically, however, and also really need to be able to get their Location via getLocation().
Please help! Perhaps there is another way to create them or a way to access their Location(s) differently?
When you create a component it has a default location of (0, 0);
Adding a component to a panel does NOT change this location.
After adding all the labels to the panel you need to do:
panel.revalidate();
This will invoke the layout manager used by the panel and each label will then be assigned a proper location based on the rules of the layout manager.
Here is an SSCCE.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class WhereIsMyComponent {
public static void showComponentLocations(Container parent) {
Component[] all = parent.getComponents();
System.out.println("Show locations of children..");
for (Component c : all) {
System.out.println(c.getLocation());
}
}
public static void main(String[] args) {
String msg = "Hello World!";
final JPanel p = new JPanel(new FlowLayout());
for (int ii=0; ii<6; ii++) {
p.add(new JLabel(msg));
}
ComponentListener cl = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent ce) {
showComponentLocations(p);
}
};
p.addComponentListener(cl);
JFrame f = new JFrame("Where Is My Component?");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(p);
f.pack();
f.setSize(400,300);
f.setLocationByPlatform(true);
f.setVisible(true);
}
}
Typical output
Show locations of children..
java.awt.Point[x=16,y=5]
java.awt.Point[x=89,y=5]
java.awt.Point[x=162,y=5]
java.awt.Point[x=235,y=5]
java.awt.Point[x=308,y=5]
java.awt.Point[x=162,y=26]
Show locations of children..
java.awt.Point[x=16,y=5]
java.awt.Point[x=89,y=5]
java.awt.Point[x=162,y=5]
java.awt.Point[x=235,y=5]
java.awt.Point[x=308,y=5]
java.awt.Point[x=162,y=26]
Show locations of children..
java.awt.Point[x=26,y=5]
java.awt.Point[x=99,y=5]
java.awt.Point[x=172,y=5]
java.awt.Point[x=26,y=26]
java.awt.Point[x=99,y=26]
java.awt.Point[x=172,y=26]
Press any key to continue . . .
Related
I have some components which I need to use setBounds() on, hence the reason why I'm using the setLayout(null).
But some of my components are out the window(below the Y-axis). I was wondering if there is a way to add a scrollbar to navigate down the window so as to see all the remaining components. A screenshot of my window is below.
Output of my window image:
That GUI would be simple to produce using layouts. Put the component displaying the list (which looks well suited to being a JTable, given the two pieces of data per row / line) into a JScrollPane. Put the scroll pane into the CENTER of a BorderLayout. Put the red label into the PAGE_START of the border layout. Then .. oh wait, the job is done!
This is what it might look like (using a JTextArea instead of a table).
can u please post a copy of this code.
Try implementing it based on the instructions above. If there is a problem, post a minimal reproducible example of your attempt.
Since you are refering to the items in the scrolling area as components, and not as texts in a JTextArea, please have a look at the below.
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Mainframe {
private JFrame f;
Box box;
JScrollPane scrollPane;
Random rand = new Random();
public static void main(String[] args) {
new Mainframe().go();
}
private void go() {
box = new Box(BoxLayout.Y_AXIS);
JLabel label = new JLabel("Possible Paths and Total Distances");
label.setForeground(Color.RED);
for (int i = 0; i < 200; i++) {
box.add(Box.createRigidArea(new Dimension(0, 2)));// creates space between the components
box.add(new JLabel(i + " : " + rand.nextInt(10000)));
}
scrollPane = new JScrollPane(box);
Dimension dim = new Dimension(box.getComponent(0).getPreferredSize());
scrollPane.getVerticalScrollBar().setUnitIncrement(dim.height * 2); // adjusts scrolling speed
//scrollPane.getViewport().setBackground(Color.WHITE);
f = new JFrame();
f.getContentPane().add(label, BorderLayout.NORTH);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(640, 480);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
I am trying to make a fix-width label in Java, which I find a solution here.
But I fail whenever I put any inside a label -- while the label is create inside a method.
my code is here :
public class testingGui
JFrame myframe = new JFrame("Some title here");
Container pane_ctn = myframe.getContentPane();
public static void main(String[] args){
testingGui gui = new testingGui();
gui.init();
}
private void init(){
myframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myframe.setSize(320, 480);
myframe.setVisible(true);
pane_ctn.setLayout(new BoxLayout(pane_ctn, BoxLayout.PAGE_AXIS));
JLabel lable = new JLabel("<html>Java is a general-purpose computer programming language</html>");
pane_ctn.add(lable);
}
}
The line JLabel lable = new JLabel("<html>Java is a general-purpose computer programming language</html>"); will never run. (and making pane_ctn into blank even if there's other UI element added)
However I found that it works while the label is create as a field, like this :
public class testingGui {
JFrame myframe = new JFrame("Some title here");
Container pane_ctn = myframe.getContentPane();
JLabel lable = new JLabel("<html>Java is a general-purpose computer programming language</html>");
// I just cut the whole line and paste here, nothing else has changed.
/* ... */
}
So here is my question :
How is the correct way to create a label with html inside a method call ? I need it created on the fly. Thank you.
Edit :
Thank you ybanen giving me a good answer, and others helpers, too.
Now I can creating Label that looking good.
It happens because you try to modify the GUI after it has been displayed. In your case, the best is to create the whole GUI and then to show the frame:
public class TestingGui {
public static void main(final String[] args) {
final JLabel label = new JLabel("<html>Java is a general-purpose computer programming language</html>");
final JFrame frame = new JFrame("Test");
final Container contentPane = frame.getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.PAGE_AXIS));
frame.getContentPane().add(label);
frame.setSize(320, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
However, if you really need to add the label after the GUI has been displayed, you have to follow several rules:
Any modification of the GUI must be done in the Event Dispatching Thread (EDT).
After a container has been displayed, it has been laid out. So if you want to add a new component inside, you have to force a layout of its content (using revalidate() and then repaint()).
I need it created on the fly.
Why? Or rather, why not create and add the label at start-up, then set the text when needed?
My example is a To-Do list, I have a array to store the data, the label is create inside a for loop.
Use a JList!
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class EditableList {
private JComponent ui = null;
String[] items = {
"Do 100 push ups.",
"Buy a book.",
"Find a cat.",
"Java is a general purpose computer language that is concurrent, "
+ "class based, object oriented and specifically designed to have "
+ "as few implementation dependencies as possible.",
"Conquer the world."
};
EditableList() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
JList<String> list = new JList<String>(items);
list.setCellRenderer(new ToDoListRenderer());
list.getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
ui.add(new JScrollPane(list));
JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER));
controls.add(new JButton("Edit Selected"));
controls.add(new JButton("Delete Selected"));
ui.add(controls, BorderLayout.PAGE_END);
}
class ToDoListRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(
JList<? extends Object> list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
Component c = super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
JLabel l = (JLabel)c;
l.setText("<HTML><body style='width: 250px;'>" + value.toString());
return l;
}
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
EditableList o = new EditableList();
JFrame f = new JFrame("To Do List");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I know this might seem like a duplicate, but I'm not getting anywhere with by experimenting on invalidate/validate/revalidate/repaint, so please bear with me. My panel structure looks something like this:
JPanel/GridBagLayout (1)
+-- JPanel/BorderLayout
+-- JPanel/GridBagLayout (2)
| +-- JTextField (3)
| +-- JComboBox
+-- JPanel/WhateverLayout
...
... and so forth. In my sub panel (2) I change the insets (right and bottom), and I want to layout the whole top panel (1). Is there some easy way to layout everything (preferably from top panel (1) and down, but anything that works is okey). Right now I've tried combinations of invalidate/validate/revalidate/repaint on all levels, but nothing seems to work (in fact nothing changes at all). Thanks!
Edit: I found out that GridBagLayout clones the GridBagConstraints as components are added, so the reason my code didn't work by running revalidate and friends was that I updated the wrong constraints. I found and added a solution to this problem below.
for re_layout whole JFrame(JDialog e.i.) is there JFrame#pack()
for fill available area in the container (after remove / add) is there revalidate() and repaint()
you have to decide for which of containers in the Components hierarchy
code example about pack() & (re)validate() & repaint()
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
public class AddComponentsAtRuntime {
private JFrame f;
private JPanel panel;
private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;
public AddComponentsAtRuntime() {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel = new JPanel(new GridLayout(0, 1));
panel.add(b);
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel, "Center");
f.add(getCheckBoxPanel(), "South");
f.setLocation(200, 200);
f.pack();
f.setVisible(true);
}
private JPanel getCheckBoxPanel() {
checkValidate = new JCheckBox("validate");
checkValidate.setSelected(false);
checkReValidate = new JCheckBox("revalidate");
checkReValidate.setSelected(false);
checkRepaint = new JCheckBox("repaint");
checkRepaint.setSelected(false);
checkPack = new JCheckBox("pack");
checkPack.setSelected(false);
JButton addComp = new JButton("Add New One");
addComp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel.add(b);
makeChange();
System.out.println(" Components Count after Adds :" + panel.getComponentCount());
}
});
JButton removeComp = new JButton("Remove One");
removeComp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int count = panel.getComponentCount();
if (count > 0) {
panel.remove(0);
}
makeChange();
System.out.println(" Components Count after Removes :" + panel.getComponentCount());
}
});
JPanel panel2 = new JPanel();
panel2.add(checkValidate);
panel2.add(checkReValidate);
panel2.add(checkRepaint);
panel2.add(checkPack);
panel2.add(addComp);
panel2.add(removeComp);
return panel2;
}
private void makeChange() {
if (checkValidate.isSelected()) {
panel.validate();
}
if (checkReValidate.isSelected()) {
panel.revalidate();
}
if (checkRepaint.isSelected()) {
panel.repaint();
}
if (checkPack.isSelected()) {
f.pack();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
}
});
}
}
GridBagLayout clones the GridBagConstraints as components are added (that's why changing my original once had no effect on the layout), so I extended GridBagLayout to be able to update the actual constraints runtime. The code below sets the layout margins depending on type of component, and if "expanded", which is what I use to toggle between two modes:
public class ExpandableGridBagLayout extends GridBagLayout {
public void setExpand(boolean expanded) {
for (Map.Entry<Component, GridBagConstraints> entry : comptable.entrySet()) {
setExpandedMargin(entry.getKey(), entry.getValue(), expanded);
}
}
private void setExpandedMargin(Component component, GridBagConstraints constraints, boolean expanded) {
constraints.insets.right = 2;
if (component instanceof JLabel) {
constraints.insets.top = expanded ? 3 : 0;
constraints.insets.bottom = expanded ? 3 : 0;
} else {
constraints.insets.bottom = expanded ? 8 : 5;
}
}
}
Then all I had to do was to call panel.revalidate() on (1) and layouting works as expected.
Still not entirely sure about your exact context, but judging from your "answer" you seem to be changing component constraints on the fly. To trigger a re-layout of the effected parts, you need to
invalidate the child/ren whose constraints had been changed (note: it's not enough to invalidate the parent), this will bubble up the hierarchy as needed
validate an appropriate container up the hierarchy
Snippet:
ExpandableGridBagLayout bag = panel2.getLayout();
bag.setExpand(true);
for(Component child: panel2.getComponents())
child.invalidate();
panel1.validate();
There is no public api to recursively invalidate everything below a given container (invalidateTree is package private). A quick hack is to temporarily toggle the font of the parent container (which internally messages invalidateTree)
/**
* Invalidates the component hierarchy below the given container.<p>
*
* This implementation hacks around package private scope of invalidateTree by
* exploiting the implementation detail that the method is internally
* used by setFont, so we temporary change the font of the given container to trigger
* its internal call.<p>
*
* #param parent
*/
protected void invalidateTree(Container parent) {
Font font = parent.getFont();
parent.setFont(null);
parent.setFont(font);
}
EDIT
Don't know which part of this answer you exactly mean by incorrect - obviously I couldn't solve your problem without any detailed knowledge about it ;-)
Curious me is wondering how a revalidate up in the hierarchy would lead to a re-layout of valid grand/children: validate/Tree clearly stops at a valid component. Below is a code-snippet to play with
it's a two-level hierarchy, a parent with two children
the action changes layout-effecting properties of the sister under its feet (we change v/hgap of the layout) and revalidate the parent
The outcome varies, depending f.i. on the LayoutManager of the parent
with FlowLayout nothing happens ever,
with BoxLayout it may be validated, depending on
whether the horizontal or vertical gap was changed and
the direction of the Box
Looks like a relayout of a valid child might be (or not) a side-effect of a relayout higher up - without any guarantee to happen and hard to predict. Nothing I want to rely on ;-)
final JComponent sister = new JPanel();
final Border subBorder = BorderFactory.createLineBorder(Color.RED);
sister.setBorder(subBorder);
sister.add(new JTextField(20));
sister.add(new JButton("dummy - do nothing"));
JComponent brother = new JPanel();
brother.setBorder(BorderFactory.createLineBorder(Color.GREEN));
brother.add(new JTextField(20));
// vary the parent's LayoutManager
final JComponent parent = Box.createVerticalBox();
// final JComponent parent = Box.createHorizontalBox();
// final JComponent parent = new JPanel();
parent.add(sister);
parent.add(brother);
// action to simulate a change of child constraints
Action action = new AbstractAction("change sub constraints") {
#Override
public void actionPerformed(ActionEvent e) {
FlowLayout layout = (FlowLayout) sister.getLayout();
layout.setHgap(layout.getHgap() * 2);
// layout.setVgap(layout.getVgap() * 2);
// sister.invalidate();
parent.revalidate();
}
};
brother.add(new JButton(action));
JFrame frame = new JFrame("play with validation");
frame.add(parent);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
I am not able to use scroll bars with absolute layout in Swing.
I don't wish to use this layout but I have to display dynamic objects on my panel on click of a button and align them using setBounds which can be done using this layout only (I guess).
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class clothes2 extends javax.swing.JFrame {
JTextField n=null;
JButton m=null;
public clothes2(){
initComponents();
}
public void initComponents() {
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
final JPanel jp = new JPanel();
contentPane.setPreferredSize(new Dimension(320,200));
jp.setLayout(null);
m=new JButton("add");
m.setBounds(0,0,50,50);
jp.add(m);
m.addMouseListener( new MouseAdapter() {
int x=0;
int y=0;
public void mouseClicked(MouseEvent me){
x+=100;
y+=100;
jp.add(n=new JTextField("Name"));
n.setBounds(x, y, 50, 50);
jp.add(n=new JTextField("code"));
x+=100;
n.setBounds(x,y, 50, 50);
jp.revalidate();
jp.repaint();
x=0;
}
});
int v = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS;
int h = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS;
JScrollPane jsp = new JScrollPane(jp, v, h);
contentPane.add(jsp, BorderLayout.CENTER);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f= new clothes2();
f.setVisible(true);
f.setSize(640,320);
}
});
}
}
Set preferred size of the container.
JScrollBar uses the preferred size of the component inside it to determine how large the scroll bars should be, and if they should be displayed.
Usually, the layout manager handles this using the preferredLayoutSize method. This can be overriden by explicitly setting the preferred size of the component.
So either you have to set the preferred size, or use a custom layout manager that calculates it for you.
see also here
might help you.
display dynamic objects .. which can be done using this layout only (I guess).
You guess wrong.
See this GUI, that can not only change PLAFs at run-time, but also dynamically add new components1. Click to..
Add Another Label
This example adds the new labels to a GridLayout - but the principle is the same for any layout (or any component).
add layout
jp.setLayout(new FlowLayout());
Why my JFrame 'frame' is diplaying empty window, when it should give me 3 menu buttons and my own painted JComponent below ? What am I missing here ?
import java.awt.*;
import javax.swing.*;
public class Eyes extends JFrame {
public static void main(String[] args) {
final JFrame frame = new JFrame("Eyes");
frame.setPreferredSize(new Dimension(450, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel players = new JPanel(new GridLayout(1, 3));
players.add(new JButton("Eyes color"));
players.add(new JButton("Eye pupil"));
players.add(new JButton("Background color"));
JPanel eyes = new JPanel();
eyes.add(new MyComponent());
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(players);
content.add(eyes);
frame.getContentPane();
frame.pack();
frame.setVisible(true);
}
}
class MyComponent extends JComponent {
public MyComponent(){
}
#Override
public void paint(Graphics g) {
int height = 120;
int width = 120;
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke bs = new BasicStroke(3.0f);
g2d.setStroke(bs);
g2d.setColor(Color.yellow);
g2d.fillOval(200, 200, height, width);
g2d.setColor(Color.black);
g2d.drawOval(60, 60, height, width);
}
}
Your line:
frame.getContentPane();
doesnt do anything but access the content pane of the frame. Instead of getting the content pane, you should set your content pane, like this:
frame.setContentPane(content);
EDIT:
alternatively, as #trashgod points out, you could use the getContentPane method to access the default content pane and add your content component to that:
frame.getContentPane().add(content);
I think you are attempting to use nested JPanels. This is certainly a way to organize your components, but downside to is the fact that it gets difficult to manage in some cases. You could try this snippet of code below. In the program you will find:
1) An array of JLabel
2) An array of JTextField
3) Nested JPanels
At the end of the program I use the Container to add the final product of these object to my Graphics Window.
The most efficient way I can think of is to define these components at the top of my program so that I can reuse them later as I need to.
To achieve this you can try this snippet of code:
import javax.swing.*; //Required to use Swing Components
public class TestGUI extends JFrame
{
JLabel[] label; //Define this with an array
JTextField[] textField; //Define this with an array as well
private int nLabels; //Number of labels preferred
private int nTextFields; //Number of text fields preferred
public testGUI(int amt)
{
//Assuming that you want equal amounts of each,
//set these two variables to the "ant" input parameter
nLabels = amt;
nTextFields = amt;
//Set component attributes
label = new JLabel[2]; //Label compared text fields
textField = new JTextField[2]; //Use two of these for comparison
textField[0].setEnabled(false); //Disabled editing
//Do nothing with the second text field
JPanel labels = new JPanel(); //Place JLabels here
//Use this to align the labels vertically
labels.setLayout(new GridLayout(2, 1));
//Use this for loop to add the labels to this JPanel
for(int i = 0; i < nLabels; i++)
{
labels.add(label[i]);
//You can also define and apply additional properties
//to labels inside this loop. TIP: You can do this in
//any loop
}
JPanel txtFields = new JPanel(); //Place JTextFields here
//Use this to align the text fields vertically
txtFields.setLayout(new GridLayout(2, 1));
//Use this for loop to add the labels to this JPanel
for(int i = 0; i < nTextFields; i++)
{
textFields.add(textField[i]);
//You can also define and apply additional properties
//to text fields inside this loop. TIP: You can do
//this in any loop
}
//Now we have the two components, you asked for help with, set up
//Next, we will need another JPanel to add these to panels to.
//This JPanel will be added to the JFrame Container
//You probably know how to run this via the "main" method
JPanel window = new JPanel();
//Place the JPanel for the labels and text fields
//This requires a horizontal grid
window.setLayout(new GridLayout(1, 2));
//Add the the two JPanels: "labels" and "txtFields"
window.add(labels);
window.add(txtFields);
//Define the Container object to set up the GUI
Container container = getContentPane();
//Apply the "window" JPanel object to the container
container.add(window, BorderLayout.CENTER);
//Center this in the Graphics Window when displayed
}
//Any other methods and/or functions can be added as well
//If they are, they must placed in the constructor method above
}
This is the approach that I would use when trying to go at making and manipulating my Graphics Windows that I write. Sometimes I write applets, but only after making sure that I have everything functioning properly in a plain Graphics Window.
I hope this helps.
If you have any other questions, just let me know and I will answer the to the best of my ability, thanks.