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.
Related
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
so i'm trying to make it where if these two shapes touch each other the window closes. Here is the first part
public class Mayflower {
JFrame f = new JFrame();
public static void main(String[] args) {
Mayflower bob = new Mayflower();
bob.Start();
}
private void Start(int clothes, int food, int repair, int money) {
int complete = 0;
Mayflower bob = new Mayflower();
//JOptionPane.showMessageDialog(null, "Your equipment:\nClothes - " + clothes + "\nFood - " + food + "\nrepair equipment - " + repair + "\nMoney left - $" + money);
bob.epic(complete);
}
public void epic(int complete) {
if (complete == 0){
Iceberg Tim = new Iceberg();
f.add(Tim);
f.setVisible(true);
f.setSize(600, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("SAILIN BABEEEEY");
f.setLocation(600, 200);
}
if(complete == 1){
System.out.println("odeyladoeijoo");
f.dispose();
}
}
}
Then it calls to the constructor iceberg where the minigame is, I deleted all the movement input because it wasn't relevant:
package mayflower;
public class Iceberg extends JPanel implements ActionListener, KeyListener {
Timer time = new Timer(5, this);
int x = 260;
int y = 500;
int velx = 0;
int vely = 0;
int hitscany = -4000;
int hitscanvely = -1;
public Iceberg() {
time.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setColor(MyColor1);
g.fillRect(x, y, 40, 60);
g.setColor(Color.GRAY);
g.fillRect(0, hitscany, 650, 0);
if (y == hitscany) {
int complete = 1;
Mayflower bob = new Mayflower();
bob.epic(complete);
}
time.start();
}
So i made it to where The "hitscan" object moves down the screen and when it touches the object the window is supposed to close. When my if statement (for if the y coordinates of the two objects are equal) calls the public void epic its supposed to "activate" the if statement for if complete is == 1 and dispose of the frame but for some reason it doesn't
So, I assume that, this (previously, now removed) code goes some where in your Iceberg key handler code...
if ((((x - icex)) >= -40 && ((x - icex) - 180) <= -130) && (((y - icey)) >= -60 && ((y - icey) - 180) <= -130)) {
int complete = 1;
Mayflower bob = new Mayflower();
bob.epic(complete);
}
This highlights a number of issues. First, you are creating another instance of Mayflower, which is creating another instance of JFrame, which is what's getting disposed, not the original frame.
Iceberg really has no need to interact with Mayflower, it's beyond it's realm of responsibility. Instead, Iceberg "should" be generating event notifications to interested parties about its change in state.
For that, we need an observer pattern!
Let's start with a simple interface which describes all the notifications Iceberg is willing to make...
public interface GameListener {
public void completed(Iceberg berg);
}
Next, we need some way to manage these listeners in Iceberg...
public class Iceberg extends JPanel implements ActionListener, KeyListener {
private List<GameListener> listeners = new ArrayList<>(25);
public void addGameListener(GameListener listener) {
listeners.add(listener);
}
public void removeGameListener(GameListener listener) {
listeners.remove(listener);
}
And finally, some way to generate the notifications...
public class Iceberg extends JPanel implements ActionListener, KeyListener {
//...
protected void fireCompleted() {
for (GameListener listener : listeners) {
listener.completed(this);
}
}
Now, when you have a "completed" state, you can notify the interested parties...
if ((((x - icex)) >= -40 && ((x - icex) - 180) <= -130) && (((y - icey)) >= -60 && ((y - icey) - 180) <= -130)) {
fireCompleted();
}
Now, in your start method, you simply need to create an instance of Iceberg, register a GameListener and get it all started...
private void Start(int clothes, int food, int repair, int money) {
Iceberg Tim = new Iceberg();
Tim.addGameListener(new GameListener() {
#Override
public void completed(Iceberg berg) {
f.dispose();
}
});
f.add(Tim);
f.setVisible(true);
f.setSize(600, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("SAILIN BABEEEEY");
f.setLocation(600, 200);
}
Observations...
Okay, there is plenty about your code sample to be worried about, but let's start with...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.RED);
g.fillRect(x, y, 40, 60);
g.setColor(Color.GRAY);
g.fillRect(0, hitscany, 650, 0);
if (y == hitscany) {
int complete = 1;
Mayflower bob = new Mayflower();
bob.epic(complete);
}
time.start();
}
paintComponent should never be public, no-one should ever be calling it directly.
You declare but never use g2
This...
if (y == hitscany) {
int complete = 1;
Mayflower bob = new Mayflower();
bob.epic(complete);
}
is a bad idea on a number of levels. Paint should paint the current state of the component, nothing else, it should not be making decisions about the state of the component. This belongs in your main loop
And...
time.start();
I can't begin to tell you how horrible this is. paintComponent will be called often (if you're performing animation), meaning you are continuously reseting the Timer. The Timer's state should never be modified inside paintComponent. Instead, it should control through other means, like the constructor or start/stop methods
KeyListener is a poor choice now days. It suffers from a number of well known and documented short comings. A better, all round solution, is to use the Key Bindings API which has been designed to help solve these issues in a reliable and robust way
You could use
f.setVisible(false)
This just hides the window, and f.dispose() deletes the actual object.
If you want it to act like you clicked the X button, then use this:
f.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
(got from How to programmatically close a JFrame)
(f being your JFrame)
I have 2 Components (which are both derived from jComponent). One is a Map and the other one are Territories within this map as you can see at the picture below.
The Map is drawn on a fixed size of 1200x800 so far. And for each territorys I have a list of points, which will connected be a the shapes of each country.
Drawing everything working quite good so far. But when I click on a country always the last drawn territory gets called. It seems like every territory is an layer over the whole map. How can I make it work, that if I click on a single territory that only this gets clicked. So that if I click on China, the click event of china gets called?
Below is the Code of the 2 Components in case it helps:
Map.java
public class Map extends JComponent {
private Collection<Territory> territories;
public Map(Collection<Territory> territories) {
super();
this.territories = territories;
}
#Override
public void paintComponent(Graphics graphics)
{
graphics.setColor(new Color(8, 114, 200));
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
for (Territory tmp : this.territories) {
TerritoryComponent territoryComponent = new TerritoryComponent(tmp);
this.add(territoryComponent);
}
this.updateUI();
}
}
TerritoryComponent.java
public class TerritoryComponent extends JComponent implements MouseListener {
private Territory territory;
public TerritoryComponent(Territory tmp) {
super();
this.setSize(this.getMaximumSize());
this.addMouseListener(this);
this.territory = tmp;
}
#Override
public void paintComponent(Graphics graphics)
{
for (Patch patch : this.territory.getPatches()) {
ArrayList<Point> points = patch.getPoints();
int x[] = new int[points.size() + 1];
int y[] = new int[points.size() + 1];
for (int i = 0; i < points.size(); i++) {
x[i] = (int) points.get(i).getX();
y[i] = (int) points.get(i).getY();
}
x[points.size()] = (int) points.get(0).getX();
y[points.size()] = (int) points.get(0).getY();
graphics.setColor(new Color(255, 255, 255));
graphics.fillPolygon(x, y, points.size() + 1);
graphics.setColor(new Color(52, 52, 52));
graphics.drawPolyline(x, y, points.size() + 1);
}
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("clicked " + this.territory.getName());
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
And for each territorys I have a list of points,
Create a Polygon Object to represent each of your territories.
Add each Polygon to an ArrayList
In the paintComponent() method you just iterate through the ArrayList to paint each Polygon
if I click on a single territory that only this gets clicked.
In the mouseClicked(...) event you then get the mouse point and your iterate through the ArrayList containing all your Polygons. You can then use the Polygon.contains(...) method to determine which territory you clicked on.
Also don't use the updateUI() method. The method should only be invoked when the LAF is changed. It should especially not be invoked in a painting method as this will cause the component to potentially repaint itself.
I created a custom ListCellRenderer that extends Jidesoft's StyledLabel. The JList my renderer is used in is a fixed width, so in my renderer I attempt to shorten text based on this width. I call setText in getListCellRendererComponent, which sometimes works. When I select a cell, the text acts as if I had never shortened it at all. There is no branching related to the setText and text-shortening.
I attempted to use the solutions in this somewhat-related answer discussing custom rendering and cell heights, but it didn't work consistently on select either.
edit: some code
public class CustomListCellRenderer extends StyledLabel implements ListCellRenderer {
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
setText(shortenName(value.toString(), 150));
return this;
}
private String shortenName(String name, int width) {
if(this.getGraphics() != null) {
final FontMetrics fontMetrics = this.getFontMetrics(this.getFont());
String end = name.substring(name.length()/2);
String beginning = name.substring( 0, name.length()/2);
int stringWidth = SwingUtilities.computeStringWidth(fontMetrics, name);
if(stringWidth < width)
return name;
do {
end = end.substring(1);
beginning = beginning.substring(0, beginning.length() - 1);
stringWidth = SwingUtilities.computeStringWidth(fontMetrics, beginning + "..." + end);
} while (stringWidth > width);
return beginning + "..." + end;
}
return name;
}
public static void main(String[] args) {
JFrame frame = new JFrame();
JList list = new JList(new String[] {
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"});
list.setCellRenderer(new CustomListCellRenderer());
frame.getContentPane().add(list);
frame.setVisible(true);
}
}
In your implementation of ListCellRenderer you're relying on the getGraphics() of the label. Sometimes, getGraphics() is null which is OK, but you're not entering the if(this.getGraphics() != null) condition and simply returning the unmodified string. That is why you get inconsistent results. Commenting out this condition solved the problem in the posted code. You should not rely on getGraphics() its value is out of your control.
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