Left- and right-justifying with dot leaders in JLabel and JButton
Hi,
I am developing checklists using JLabel and JButton components and want to finish up with the text in each component looking something like the following (each line is a separate button/label):
ACTION No1............................RESPONSE No1
ACTION No2............................RESPONSE No2
ACTION No3............................RESPONSE No3
The idea is to have the 揂ction?text left-justified and the 揜esponse?text right-justified with dot-leaders as shown.
Each text line will be put into its own fixed length label or button.
I plan to construct a string from text blocks 揂CTION No1?and 揜ESPONSE No1?and add the calculated, exact no of dots in between to fill the space.
The text font can be fixed (e.g. Courier) or proportional (e.g. Arial).
My question is how to calculate the number of dots to add to fill the space for different fonts and font sizes.
I think that using FontMetrics could provide some help but am not sure how this would work.
Can anyone help.
Many tks
John
[1073 byte] By [
jpashleya] at [2007-11-26 15:38:50]

# 1
Use a BorderLayout.In the West you add the "Action"In the East you add the "Response"In the Center you add a label with the text set to be "....................". The label in the centre will grow or shrink as required.
# 2
Tks,
I can see how to "add" text strings to a container using a BorderLayout to place the strings exactly within the container but I don't see how you can do this with a component like JLabel or JButton. I didn't find an ".add" method which could be used in either JLabel or JButton.
Sample text to do this in a JPanel (not very pretty, professional but illustrates the point):
JPanel jAddPanel = new JPanel(new BorderLayout());
jAddPanel.setPreferredSize(new Dimension(250,250));
JLabel jAddLabelWest = new JLabel("Label WEST");
JLabel jAddLabelEast = new JLabel("Label EAST");
JLabel jAddLabelCenter = new JLabel("..........");
jAddPanel.add(jAddLabelWest, BorderLayout.WEST);
jAddPanel.add(jAddLabelCenter, BorderLayout.CENTER);
jAddPanel.add(jAddLabelWest, BorderLayout.EAST);
this.add(jAddPanel);
// Now I'd like to add this to a component like JLabel or JButton.
Note: I can't get the .CENTER to expand the dots to fill the space. Should it?
The idea is that the text in the JLabel or JButton is made up of, for example 3 separate text (String) objects, viz. "Action" + dots + "Response" and this concatenated string is then put in (just one) fixed length JLabel or JButton expanding and shrinking the dots as required to make "Action" left-justified, "Response" right-justified and (as many as needed) dots in between.
The checklist itself is then made of of multiple rows of Action/Response routines. Using buttons enables the user to click on the button to access for example any explanatory text underlying each Action/Response routine.
Can you help?
John
# 3
> // Now I'd like to add this to a component like JLabel or JButton.
Well all Swing components are Containers, so just set the LayoutManager of the component and then add the panel. The attached code gives an example.
However, I misunderstood your original question and it is probably better to try and use FontMetrics to manipulate the text as you wish. The example also shows this approach
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ComponentWierd extends JFrame implements ComponentListener
{
public ComponentWierd()
{
getContentPane().setLayout( new GridLayout(0, 1) );
// Using my original suggestion
JLabel label = new JLabel();
label.setLayout(new BorderLayout());
label.add(createWierdComponent());
getContentPane().add(label);
JButton button = new JButton();
button.setLayout(new BorderLayout());
button.add(createWierdComponent());
getContentPane().add(button);
// Using Font Metrics
JLabel label2 = new JLabel("Label2 West...Label2 East");
label2.addComponentListener( this );
getContentPane().add(label2);
JButton button2 = new JButton("Button2 West...Button2 East");
System.out.println(button2.getIconTextGap());
button2.addComponentListener( this );
getContentPane().add(button2);
}
private JComponent createWierdComponent()
{
JPanel panel = new JPanel(new BorderLayout());
panel.setOpaque( false );
panel.add(new JLabel("Label WEST"), BorderLayout.WEST);
panel.add(new JLabel("..............................................."));
panel.add(new JLabel("Label EAST"), BorderLayout.EAST);
return panel;
}
public void componentResized(ComponentEvent e)
{
if (e.getComponent() instanceof JLabel)
{
JLabel component = (JLabel)e.getComponent();
String text = component.getText();
String fitText = fitText(component, text);
component.setText( fitText );
}
if (e.getComponent() instanceof JButton)
{
JButton component = (JButton)e.getComponent();
String text = component.getText();
String fitText = fitText(component, text);
component.setText( fitText );
}
}
private String fitText(JComponent component, String text)
{
// Calculate the total width for painting the text
// (Not sure why I need the -8)
Insets insets = component.getInsets();
int availableWidth = getWidth() - insets.left - insets.right - 8;
// Calculate the minimum width our text will take
String start = text.substring(0, text.indexOf("."));
String middle = "...";
String end = text.substring(text.lastIndexOf(".") + 1);
FontMetrics fm = getFontMetrics( component.getFont() );
int startWidth = fm.stringWidth( start );
int middleWidth = fm.stringWidth( middle );
int endWidth = fm.stringWidth( end );
int minimumWidth = startWidth + middleWidth + endWidth;
// Add dots to fill out the extra space
StringBuffer buffer = new StringBuffer(start);
buffer.append(middle);
if (minimumWidth < availableWidth)
{
String dot = ".";
int dotWidth = fm.stringWidth( dot );
int dots = (availableWidth - minimumWidth) / dotWidth;
for (int i = 0; i < dots; i++)
{
buffer.append( dot );
}
}
buffer.append(end);
String result = buffer.toString();
return result;
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
public static void main(String[] args)
{
ComponentWierd frame = new ComponentWierd();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.setSize(200, 200);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
# 4
Hi,well this seems to be just what I needed!Seems to work just fine and gives me a good sound example in component manipulation, resizing, listeners, etc..Many, many, tks.John
# 5
Hi,
I've developed your suggested code further to enable selected JLabels to be toggled visible(true/false) by pressing a JButton.
I'd like however that when a label is made invisible that the rest of the labels/buttons move up to fill the space left by the now invisible label and vice versa.
How do I do this, can you enlighten me on a solution?
Modified code included below. Not very elegant but serves to illustrate the point in question and only makes the label invisible.
Many tks
John
package componentwierd;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ComponentWierd extends JFrame implements ComponentListener, ActionListener {
boolean isVisible = true;
JLabel label2 = new JLabel();
public ComponentWierd() {
getContentPane().setLayout( new GridLayout(0, 1) );
// Using my original suggestion
JLabel label = new JLabel();
label.setLayout(new BorderLayout());
label.add(createWierdComponent());
getContentPane().add(label);
JButton button = new JButton();
button.setLayout(new BorderLayout());
button.add(createWierdComponent());
button.addActionListener(this);
getContentPane().add(button);
// Using Font Metrics
// JLabel label2 = new JLabel("Label2 West...Label2 East");
label2.setText("Label2 West...Label2 East");
label2.addComponentListener( this );
getContentPane().add(label2);
JButton button2 = new JButton("Button2 West...Button2 East");
System.out.println(button2.getIconTextGap());
button2.addComponentListener( this );
button2.setVisible(true);
getContentPane().add(button2);
}
public void actionPerformed(ActionEvent e) {
if (isVisible == true) {
isVisible = false;
label2.setVisible(false);
} else if (isVisible == false) {
isVisible = true;
label2.setVisible(true);
}
}
private JComponent createWierdComponent() {
JPanel panel = new JPanel(new BorderLayout());
panel.setOpaque( false );
panel.add(new JLabel("Label WEST"), BorderLayout.WEST);
panel.add(new JLabel("..............................................."));
panel.add(new JLabel("Label EAST"), BorderLayout.EAST);
return panel;
}
public void componentResized(ComponentEvent e) {
if (e.getComponent() instanceof JLabel) {
JLabel component = (JLabel)e.getComponent();
String text = component.getText();
String fitText = fitText(component, text);
component.setText( fitText );
}
if (e.getComponent() instanceof JButton) {
JButton component = (JButton)e.getComponent();
String text = component.getText();
String fitText = fitText(component, text);
component.setText( fitText );
}
}
private String fitText(JComponent component, String text) {
// Calculate the total width for painting the text
// (Not sure why I need the -8)
Insets insets = component.getInsets();
int availableWidth = getWidth() - insets.left - insets.right - 8;
// Calculate the minimum width our text will take
String start = text.substring(0, text.indexOf("."));
String middle = "...";
String end = text.substring(text.lastIndexOf(".") + 1);
FontMetrics fm = getFontMetrics( component.getFont() );
int startWidth = fm.stringWidth( start );
int middleWidth = fm.stringWidth( middle );
int endWidth = fm.stringWidth( end );
int minimumWidth = startWidth + middleWidth + endWidth;
// Add dots to fill out the extra space
StringBuffer buffer = new StringBuffer(start);
buffer.append(middle);
if (minimumWidth < availableWidth) {
String dot = ".";
int dotWidth = fm.stringWidth( dot );
int dots = (availableWidth - minimumWidth) / dotWidth;
for (int i = 0; i < dots; i++) {
buffer.append( dot );
}
}
buffer.append(end);
String result = buffer.toString();
return result;
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
public static void main(String[] args) {
ComponentWierd frame = new ComponentWierd();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.setSize(200, 200);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
# 6
You would need to actually remove the component from the container and then revalidate() the container. However when you do this the size of each component will change since you are using a GridLayout. So you could add the removed component to the end of the grid to preserve the size. (Note: if you do this you will probably also need to repaint() the container after the revalidate().
Another option would be to use a BoxLayout and the add glue to the bottome of the box so that when you remove the label the size of the other labels doesn't change.
# 7
Tks,this sounds feasable so I'll give it a try.After removing it I want to add it again at a later stage so the option of adding "glue" is quite a good one.Q: How do I create this "glue" you refer to?John
# 8
[url http://java.sun.com/docs/books/tutorial/uiswing/layout/box.html#filler]How to Use Box Layout[/url]
# 9
Cool, tks,long live glue!John