On most systems, the content in my JLabel just shows fine. It is also resided in a way that it should be always big enough to show its content text because I basically do this:
label.setText(text);
label.setFont(new Font(fontName, 0, 12));
int width = label.getFontMetrics(label.getFont()).stringWidth(text);
int height = 21; // this should always be enough
label.setBounds(new Rectangle(x, y, width, height));
But on some systems (not my own so I cannot really debug it that easy), it cuts the text and shows "..." at the end.
You can see the full code here and you can see the example here (Abbildungen_Bijektiv_X3).
I also have some similar case for JButton.
How can I force Swing to not do that? (Even if it thinks that the component is too small.)
Where exactly does Swing handle this? I browsed through the code of JButton and some related classes but I didn't really found the code where it cuts the text and adds the ellipsis.
There should be no need to set the bounds of the label.
That is the job of a layout manager. Learn to use layout managers and you won't have this problem.
Edit:
Layout managers use:
label.setSize( label.getPreferredSize() );
I am doing this now (for buttons but you could do it in a similar way for other controls):
static public class ButtonUI extends MetalButtonUI {
public static ComponentUI createUI(JComponent c) {
return new ButtonUI();
}
#Override public void paint(Graphics g, JComponent c) {
JSimpleLabel.activateAntiAliasing(g);
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
String text = b.getText();
clearTextShiftOffset();
// perform UI specific press action, e.g. Windows L&F shifts text
if (model.isArmed() && model.isPressed()) {
paintButtonPressed(g,b);
}
FontMetrics metrics = g.getFontMetrics();
Rectangle2D stringBounds = metrics.getStringBounds(text, g);
g.drawString(text,
(b.getWidth() - (int)stringBounds.getWidth()) / 2,
metrics.getLeading() + metrics.getMaxAscent() + (b.getHeight() - (int)stringBounds.getHeight()) / 2);
if (b.isFocusPainted() && b.hasFocus()) {
Rectangle viewRect = new Rectangle();
final int inset = 1;
viewRect.x = inset;
viewRect.y = inset;
viewRect.width = b.getWidth() - (inset + viewRect.x) - 1;
viewRect.height = b.getHeight() - (inset + viewRect.y) - 1;
g.setColor(getFocusColor());
g.drawRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
}
}
}
public void init() {
try {
UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel() {
private static final long serialVersionUID = 1L;
#Override public UIDefaults getDefaults() {
UIDefaults table = super.getDefaults();
table.put("ButtonUI", ButtonUI.class.getName());
return table;
}
});
} catch (Exception e) {
e.printStackTrace();
}
// ...
}
You could use a cross platform look and feel (Like Nimbus) to stop this occuring
Related
I'm using Java Swing and MigLayout (What a wonderful tool!) to create a project in java, and I got a problem.
To display every string as big as I could, I created a sub-class of JLabel, that changes the font according to the size of the component, I'll attach the code in the example I'll provide.
The project is really big and there are a lot of panels nested, I also change the content of the main window on the fly, validating everything after.
But, if I try to use the cell disposition of components within MigLayout, evrything is wrong.
If i use the same layout, with the same constraint, but instead of using my custom label, i use an ordinary JLabel, everything works like a charm.
Here the gist of the example:
https://gist.github.com/bracco23/c47975ede0d857ac3b134f197c4371a2
The code is in two files:
JAdaptiveLabel.java, the custom component that just recalculate the optimal font size whenever text is changed or on demand.
test.java, a mock example. Changing CUSTOM you can switch between my component and a plain JLabel and see the difference. The intended layout is the one with the plain JLabel.
Can anybody give me a clue of what's wrong and how could I fix it?
Ok, after trying hard I solved.
After the test, I came to the conclusion (obvious) that somethig was wrong with my JAdaptiveLabel. So I searched online for another version, to see if it was my implementation or the adaptivity itself the problem.
I came to this answer: #Warren K
I used his class as it was and it worked, so my implementation was bugged.
I started from his version and changed the resizing algorithm, since his was iterative (change size till you find the perfect one) and mine was mathematical (just get the involved measures and calculate the perfect size).
It worked. Now the layout get disposed properly and the label changes font size if I resize the window.
Here the code modified:
package it.bracco23.util;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
// Improved version of http://java-sl.com/tip_adapt_label_font_size.html
public class JAdaptiveLabel extends JLabel {
private Graphics g;
private boolean init = false;
public JAdaptiveLabel(String text, Icon icon, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
init();
}
public JAdaptiveLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
init();
}
public JAdaptiveLabel(String text) {
super(text);
init();
}
public JAdaptiveLabel(Icon image, int horizontalAlignment) {
super(image, horizontalAlignment);
init();
}
public JAdaptiveLabel(Icon image) {
super(image);
init();
}
public JAdaptiveLabel() {
init();
}
protected void init() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
adaptLabelFont(JAdaptiveLabel.this);
}
});
init = true;
}
protected void adaptLabelFont(JLabel l) {
if (g==null) {
return;
}
Rectangle r = l.getBounds();
Insets ins = l.getInsets();
r.x = 0;
r.y = 0;
Font f = l.getFont();
Dimension dim = getTextSize(l, f);
//0.9f is a scale factor to don't let the text take too much space
//without it will work, but the text may appear to close to the border
float xFactor = ((r.width - ins.left - ins.right) * 0.9f) / dim.width;
float yFactor = ((r.height - ins.top - ins.bottom) * 0.9f) / dim.height;
/*the next lines assure the scaling factors are not zero (can happen)
and are different enough from 1. Without this last check, it might happen
that the font starts to cycle between two sizes. */
xFactor = (xFactor != 0 && Math.abs(xFactor - 1)>0.1) ? xFactor : 1;
yFactor = (yFactor != 0 && Math.abs(xFactor - 1)>0.1) ? yFactor : 1;
float fontSize = f.getSize() * Math.min(xFactor, yFactor);
setFont(f.deriveFont(f.getStyle(), fontSize));
repaint();
}
private Dimension getTextSize(JLabel l, Font f) {
Dimension size = new Dimension();
FontMetrics fm = g.getFontMetrics(f);
size.width = fm.stringWidth(l.getText());
size.height = fm.getHeight();
return size;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.g=g;
}
#Override
public void setText(String text) {
super.setText(text);
if(init){
adaptLabelFont(this);
}
}
}
If you use this version of the class inside the example I gave, everything works fine!
P.S. I also added a call to the resizing method in setText, since you must change the size when the label resize or change its content.
So i've come across this problem that i simply can't manage to sort out.
I'm making a game with the help of LibGdx and am trying to create a chat bubble functionality. The problem is, when i try to change the background of the label style to a 9patch drawable, it doesn't scale it well, or at all?
public class ChatBubble
{
private Label textLabel;
private BitmapFont font;
private Label.LabelStyle lStyle;
private int scaledWidth = 0;
private int scaledHeight = 0;
private Timer.Task currentTask;
private Texture bkg;
public ChatBubble()
{
font = new BitmapFont();
font.setColor(Color.BLACK);
bkg = new Texture("data/ui/chatb.9.png");
NinePatch np = new NinePatch(bkg,11,11,9,10);
NinePatchDrawable npd = new NinePatchDrawable(np);
lStyle = new Label.LabelStyle(font,font.getColor());
lStyle.background = npd;
textLabel = new Label("",lStyle);
textLabel.setVisible(false);
textLabel.setAlignment(Align.center);
currentTask = new Timer.Task() {
#Override
public void run() {
textLabel.setVisible(false);
}};
}
public void show(String text, float duration)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,duration);
}
public void show(String text)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,(float)(text.length()*0.1));
}
public void draw(SpriteBatch batch, float x, float y)
{
if(!textLabel.isVisible())return;
textLabel.setPosition(x - scaledWidth, y + scaledHeight);
batch.begin();
textLabel.draw(batch, 1);
batch.end();
}
}
How it looks ingame:
How the 9batch looks:
Any help would be appreciated!
Update:
I've found out that my 9patch scales ok, the problem being in label not updating it's size when setText() is called, thus having it width and height 0 since constructor was "".. calling layout() on label doesn't solve this either.
Call .pack() on the label after .setText() to tell it to size itself to its text (plus whatever padding there is in the background drawable). You don't need to call layout() since that's handled automatically.
I'm not sure the exact reason you have to manually call pack(), but this is generally the case with Widgets that you are not children of a WidgetGroup subclass (i.e. Table, VerticalGroup, etc.).
Edit: I have solved the underlying problem. I used SwingUtilities.invokeLater() to solve the issue. My other question provides more information for those who are interested.
I have a GUI that displays an image on a JPanel in paintComponent() with g.drawImage(). I wrote a subclass of JPanel called CanvasPanelView to override paintComponent() and do a few other things, like set the bounds of where the image is drawn. The problem is that I need to get the JPanel's width and height and when I call this.getWidth() and this.getHeight() in the class that extends JPanel, they both return 0.
The process starts in an action listener inner class:
class MenuBarFileOpenListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
File fileChooserReturnValue = view.showAndGetValueOfFileChooser();
if (fileChooserReturnValue != null) {
try {
DocumentModel newDocument = new DocumentModel(ImageIO.read(fileChooserReturnValue), fileChooserReturnValue.getAbsolutePath(), fileChooserReturnValue.getName());
model.addDocument(newDocument);
view.addDocument(newDocument);
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
}
Then, addDocument() is called:
public void addDocument(DocumentModel document) {
menuBar_file_close.setEnabled(true);
DocumentView newDocumentView = new DocumentView(document.getTitle(), documentsTabbedPaneCloseButtonListener);
documentViews.add(newDocumentView); // add newDocumentView to ArrayList<DocumentView>
newDocumentView.setDocument(document);
documentsTabbedPane.add(newDocumentView.getCanvasPanelView());
int newDocumentIndex = documentsTabbedPane.indexOfComponent(newDocumentView.getCanvasPanelView());
documentsTabbedPane.setTabComponentAt(newDocumentIndex, newDocumentView.getTabPanel());
documentsTabbedPane.setSelectedIndex(newDocumentIndex);
newDocumentView.setBounds(document.getImageWidth(), document.getImageHeight());
}
public DocumentView(String title, ActionListener listener) {
canvas = new CanvasPanelView();
// more code...
}
setBounds() is called:
public void setBounds(int imageWidth, int imageHeight) {
sourceX1 = 0;
sourceY1 = 0;
sourceX2 = imageWidth;
sourceY2 = imageHeight;
// some math...
destinationX1 = 0 + xMargin;
destinationY1 = 0 + yMargin;
destinationX2 = drawWidth - xMargin;
destinationY2 = drawHeight - yMargin;
}
DocumentView is a wrapper class for CanvasPanel and a few other things - it just groups together things that go with each open document.
Everything seems to be instantiated and used or added to the JTabbedPane, so I don't know why this.getWidth() and this.getHeight() return 0. Maybe something is happening between the end of setBounds() and paintComponent().
Why do this.getWidth() and this.getHeight() return 0?
Instead of doing a 'setBounds', why don't you put your documentView inside a Panel(BorderLayout), at a position such as BorderLayout.CENTER?
Alternatively, you can set the minimum and preferred sizes of your documentview based on the image dimensions.
You have written a class, CanvasPanelView. This class extends JPanel. It appears, that in your implementation of setBounds you make no reference to the members of JPanel that getHeight() and getWidth() refer to. Hence, the underlying object is of 0 height, and 0 width.
If you wish to use the JPanel values for height and width, then you simply need to ensure that you are assigning values to them.
I have two problems while searching for a text in a JTable:
1) For example, in JTextField I must initially have a 'Search Text' in transparent manner and if I click on it, the textfield must become blank and we can enter text there. How to achieve this in Java Swing?
2) My search coding is,
private void search8()
{
String target8 = sear8.getText();
for(int row = 0; row < table8.getRowCount(); row++)
for(int col = 0; col < table8.getColumnCount(); col++)
{
String next8 = (String)table8.getValueAt(row, col);
if(next8.equals(target8))
{
showSearchResults(row, col);
return;
}
}
But it is case-sensitive. I want it to be case-insensitive search. Where should I make changes in this? Also, for eg, if there is a text 'abc' and now I need to type the entire word 'abc'. Is there any way such that, if I type 'a' or 'bc' it would take me to that cell?
Kindly guide me. Thanks in advance.
1. Create a hint for your JTextView. See this example http://code.google.com/p/xswingx/
2. Use equalsIgnoreCase( ) for comparison with case-Insensitivity...
////////////////////EDITED PART//////////////////////
3. If you dont want to implement a hint as i mentioned in point 1, then use FocusListener.
Eg:
JTextField textField = new JTextField("A TextField");
textField.addFocusListener(this);
public void focusGained(FocusEvent e) {
textField = "" ;
}
See this for more details:
http://docs.oracle.com/javase/tutorial/uiswing/events/focuslistener.html
I use a custom paint method
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String label = getLabel();
if (label != null && (getText() == null || getText().length() == 0)) {
Insets insets = getInsets();
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.top + insets.bottom);
// This buffer should be created when the label is changed
// or the size of the component is changed...
BufferedImage buffer = ImageUtilities.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(getForeground());
g2d.setFont(getFont());
FontMetrics fm = g2d.getFontMetrics();
Composite comp = g2d.getComposite();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f));
int textHeight = fm.getHeight();
int x = insets.left;
int y = ((height - textHeight) / 2) + fm.getAscent();
g2d.drawString(label, 0, y);
g2d.dispose();
g.drawImage(buffer, insets.left, insets.top, this);
}
}
I've had some issues with it running on MacOS, hence the use of BufferedImage but it should work fine.
I typically wait until the user has typed in the field before clearing the label, but you could use a focus listener and flag to trigger the process instead
UPDATED with FOCUS LISTENER
public class MyTextField extents JTextField implements FocusListener {
private boolean hasFocus = false;
public void addNotify() {
super.addNotify();
addFocusListener(this);
}
public void removeNotify() {
removeFocusListener(this);
super.removeNotify();
}
public void focusGained(FocusEvent evt) {
hasFocus = true;
}
public void focusLost(FocusEvent evt) {
hasFocus = false;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String label = getLabel();
if (!hasFocus && label != null && (getText() == null || getText().length() == 0)) {
// As above...
}
}
}
Or something to that effect
For the case sensitive part, you can use String.compareToIgnoreCase().
You should use next8.equalsIgnoreCase(target8) in place of next8.equals(target8) for your search to be case insensitive.
yeah - I'm aware that developers love to re-invent the wheel :-) Biased me prefers to use my favourite framework SwingX which already has all necessary building blocks:
automatic search support working the same way across all collection components
WYMIWYS (what-you-match-is-what-you-see), that is it uses the actual string representation in the renderering component instead of a dumb toString
search components are pluggable
That's the theory, at least, so on to eating my own dog food: the default findbar - that's the component to use for incremental search, that is searching the target while typing - uses a plain text field instead of the required prompt field. A custom implementation:
/**
* A custom JXFindBar which uses a JXTextField instead of a plain as super.
*/
public static class PromptSearchBar extends JXFindBar {
/**
* Overridden to replace the plain text field in super
* with a JXTextField (which supports prompts).
*/
#Override
protected void initComponents() {
super.initComponents();
searchField = new JXTextField() {
#Override
public Dimension getMaximumSize() {
Dimension superMax = super.getMaximumSize();
superMax.height = getPreferredSize().height;
return superMax;
}
};
searchField.setColumns(getSearchFieldWidth());
((JXTextField) searchField).setPrompt(getUIString(SEARCH_FIELD_LABEL));
}
/**
* Overridden to update the prompt in addition to super
*/
#Override
protected void updateLocaleState(Locale locale) {
super.updateLocaleState(locale);
((JXTextField) searchField).setPrompt(getUIString(SEARCH_FIELD_LABEL, locale));
}
/**
* Overridden to not add the search label.
*/
#Override
protected void build() {
setLayout(new FlowLayout(SwingConstants.LEADING));
add(searchField);
add(findNext);
add(findPrevious);
}
}
Installing in custom code:
SearchFactory factory = new SearchFactory() {
#Override
public JXFindBar createFindBar() {
return new PromptSearchBar();
}
};
SearchFactory.setInstance(factory);
factory.setUseFindBar(true);
That's it - focus a JXTable, JXTree, JXList, ... press ctr-f and type away in the searchfield: the next matching cell will be highlighted.
On most systems, the content in my JLabel just shows fine. It is also resided in a way that it should be always big enough to show its content text because I basically do this:
label.setText(text);
label.setFont(new Font(fontName, 0, 12));
int width = label.getFontMetrics(label.getFont()).stringWidth(text);
int height = 21; // this should always be enough
label.setBounds(new Rectangle(x, y, width, height));
But on some systems (not my own so I cannot really debug it that easy), it cuts the text and shows "..." at the end.
You can see the full code here and you can see the example here (Abbildungen_Bijektiv_X3).
I also have some similar case for JButton.
How can I force Swing to not do that? (Even if it thinks that the component is too small.)
Where exactly does Swing handle this? I browsed through the code of JButton and some related classes but I didn't really found the code where it cuts the text and adds the ellipsis.
There should be no need to set the bounds of the label.
That is the job of a layout manager. Learn to use layout managers and you won't have this problem.
Edit:
Layout managers use:
label.setSize( label.getPreferredSize() );
I am doing this now (for buttons but you could do it in a similar way for other controls):
static public class ButtonUI extends MetalButtonUI {
public static ComponentUI createUI(JComponent c) {
return new ButtonUI();
}
#Override public void paint(Graphics g, JComponent c) {
JSimpleLabel.activateAntiAliasing(g);
AbstractButton b = (AbstractButton) c;
ButtonModel model = b.getModel();
String text = b.getText();
clearTextShiftOffset();
// perform UI specific press action, e.g. Windows L&F shifts text
if (model.isArmed() && model.isPressed()) {
paintButtonPressed(g,b);
}
FontMetrics metrics = g.getFontMetrics();
Rectangle2D stringBounds = metrics.getStringBounds(text, g);
g.drawString(text,
(b.getWidth() - (int)stringBounds.getWidth()) / 2,
metrics.getLeading() + metrics.getMaxAscent() + (b.getHeight() - (int)stringBounds.getHeight()) / 2);
if (b.isFocusPainted() && b.hasFocus()) {
Rectangle viewRect = new Rectangle();
final int inset = 1;
viewRect.x = inset;
viewRect.y = inset;
viewRect.width = b.getWidth() - (inset + viewRect.x) - 1;
viewRect.height = b.getHeight() - (inset + viewRect.y) - 1;
g.setColor(getFocusColor());
g.drawRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height);
}
}
}
public void init() {
try {
UIManager.setLookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel() {
private static final long serialVersionUID = 1L;
#Override public UIDefaults getDefaults() {
UIDefaults table = super.getDefaults();
table.put("ButtonUI", ButtonUI.class.getName());
return table;
}
});
} catch (Exception e) {
e.printStackTrace();
}
// ...
}
You could use a cross platform look and feel (Like Nimbus) to stop this occuring