In connection with my question (may be), I found another exception type that I not able to catch and print-out from SwingWorker thread.
How can I to generate RepaintManager exceptions?
I read this CheckThreadViolationRepaintManager and this approach by Alexander Potochkin, but nothing seems to solve my issues.
If it helps, the example below throws prints multiple variations of the following Exception, mostly for each phase of the frame's UI delegate initialization. I used CheckThreadViolationRepaintManager, but the AspectJ variation looks interesting, too.
java.lang.Exception
at EDTViolation$CheckThreadViolationRepaintManager.checkThreadViolations(EDTViolation.java:43)
at EDTViolation$CheckThreadViolationRepaintManager.addDirtyRegion(EDTViolation.java:37)
at javax.swing.JComponent.repaint(JComponent.java:4734)
at java.awt.Component.repaint(Component.java:3168)
at javax.swing.JComponent.setFont(JComponent.java:2727)
at javax.swing.LookAndFeel.installColorsAndFont(LookAndFeel.java:191)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(BasicPanelUI.java:49)
at javax.swing.plaf.basic.BasicPanelUI.installUI(BasicPanelUI.java:39)
at javax.swing.JComponent.setUI(JComponent.java:662)
at javax.swing.JPanel.setUI(JPanel.java:136)
at javax.swing.JPanel.updateUI(JPanel.java:109)
at javax.swing.JPanel.(JPanel.java:69)
at javax.swing.JPanel.(JPanel.java:92)
at javax.swing.JPanel.(JPanel.java:100)
at javax.swing.JRootPane.createGlassPane(JRootPane.java:528)
at javax.swing.JRootPane.(JRootPane.java:348)
at javax.swing.JFrame.createRootPane(JFrame.java:255)
at javax.swing.JFrame.frameInit(JFrame.java:236)
at javax.swing.JFrame.(JFrame.java:159)
at EDTViolation.main(EDTViolation.java:12)
...
import java.lang.ref.WeakReference;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/questions/7787998 */
public class EDTViolation {
public static void main(String args[]) {
RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
private static class CheckThreadViolationRepaintManager extends RepaintManager {
//http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html
private boolean completeCheck = true;
private WeakReference<JComponent> lastComponent;
public CheckThreadViolationRepaintManager(boolean completeCheck) {
this.completeCheck = completeCheck;
}
public CheckThreadViolationRepaintManager() {
this(true);
}
public boolean isCompleteCheck() {
return completeCheck;
}
public void setCompleteCheck(boolean completeCheck) {
this.completeCheck = completeCheck;
}
#Override
public synchronized void addInvalidComponent(JComponent component) {
checkThreadViolations(component);
super.addInvalidComponent(component);
}
#Override
public void addDirtyRegion(JComponent component, int x, int y, int w, int h) {
checkThreadViolations(component);
super.addDirtyRegion(component, x, y, w, h);
}
private void checkThreadViolations(JComponent c) {
if (!SwingUtilities.isEventDispatchThread() && (completeCheck || c.isShowing())) {
boolean repaint = false;
boolean fromSwing = false;
boolean imageUpdate = false;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement st : stackTrace) {
if (repaint && st.getClassName().startsWith("javax.swing.")
&& // for details see
// https://swinghelper.dev.java.net/issues/show_bug.cgi?id=1
!st.getClassName().startsWith("javax.swing.SwingWorker")) {
fromSwing = true;
}
if (repaint && "imageUpdate".equals(st.getMethodName())) {
imageUpdate = true;
}
if ("repaint".equals(st.getMethodName())) {
repaint = true;
fromSwing = false;
}
if ("read".equals(st.getMethodName()) && "javax.swing.JEditorPane".equals(st.getClassName())) {
// Swing reads html from a background thread
return;
}
}
if (imageUpdate) {
//assuming it is java.awt.image.ImageObserver.imageUpdate(...)
//image was asynchronously updated, that's ok
return;
}
if (repaint && !fromSwing) {
//no problems here, since repaint() is thread safe
return;
}
//ignore the last processed component
if (lastComponent != null && c == lastComponent.get()) {
return;
}
lastComponent = new WeakReference<JComponent>(c);
violationFound(c, stackTrace);
}
}
protected void violationFound(JComponent c, StackTraceElement[] stackTrace) {
System.out.println();
System.out.println("EDT violation detected");
System.out.println(c);
for (StackTraceElement st : stackTrace) {
System.out.println("\tat " + st);
}
}
}
}
Create your own Exception
class RepaintManagerException extends Exception
{
String msg;
RepaintManagerException()
{
msg = new String("type message here");
}
}
Usage
public class My_Exception
{
public static void main (String args [ ])
{
try
{
// your code
if (expression) throw new RepaintManagerException( );
}
catch (RepaintManagerException e)
{
System.out.println (e);
}
}
}
Related
I'm trying to create an example with some graphics for a ´Stack´.
I want the Wagons to "Spawn" and move in one track which are the ´stacks´.
The moving process is made by the ´Wagon´ itself. I already tried to give the Wagon a reference to my ´Frame´ called ´Window´ to repaint it in every loop but it still doesn't show up until it reached it's stop.
Wagon:
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Window extends JFrame {
private Stack stack1 = new Stack(1);
public Window() {
setSize(800, 400);
setResizable(false);
setLayout(null); //Not perfect, but it works for those little things
setVisible(true);
createTracks(); //Tracksgraphic made out of gray rects
}
public void addWagon(Wagon wagon) { //Is called when adding a Wagon
this.add(wagon);
stack1.addWagon(wagon);
}
public static void main(String[] args) { //main
new Window();
}
}
Stack:
public class Stack {
private int gleis; //gleis = german for track
private Wagon first = null;
public Stack(int g) {
gleis = g;
}
public void addWagon(Wagon wagon) {
if (first != null) {
wagon.setNext(first);
}
first = wagon;
first.moveRight(gleis);
}
public void removeWagon(int id, Stack nextStack) {
Wagon speicherFirst = first;
first.moveLeft(null);
first = first.getNext();
while (speicherFirst.getID() != id) {
speicherFirst.setNext(first);
first.moveLeft(speicherFirst);
speicherFirst = first;
first = first.getNext();
}
nextStack.addWagon(speicherFirst);
if (speicherFirst.getNext() != null) {
speicherFirst = speicherFirst.getNext();
while (speicherFirst!= null) {
speicherFirst.moveRight(gleis);
speicherFirst = speicherFirst.getNext();
}
}
}
public boolean hasID(int id) {
return first.isID(id);
}
}
Wagon:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Wagon extends JPanel { //Entspricht einem Canvas aus .awt
private Wagon next;
private int id;
public Wagon(int i) {
id = i;
setSize(50, 20);
Random r = new Random();
setBackground(new Color(r.nextFloat(), r.nextFloat(), r.nextFloat()));
setVisible(true);
}
public boolean isID(int i) {
if (id == i) {
return true;
} else if (next == null) {
return false;
} else {
return next.isID(i);
}
}
public void setNext(Wagon n) {
next = n;
}
public void moveRight(int gleis) {
setLocation(getX(), gleis * 100);
if (next != null) {
while (next.getX() - getX() < 70) {
next.moveRight(gleis);
move(3);
}
} else {
while (getX() < 700) {
move(3);
}
}
}
public Wagon getNext() {
return next;
}
public int getID() {
return id;
}
public void moveLeft(Wagon previous) {
if (previous == null) {
while (getX() > 50) {
move(-3);
}
} else {
while (getX() - previous.getX() > 50) {
move(-3);
}
}
}
public void move(int dir) {
this.setLocation(getX() + dir, getY());
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
}
}
it still doesn't show up until it reached it's stop.
Your code is executing on the Event Dispatch Thread (EDT), which is the thread that is responsible for handling GUI events and repainting the GUI. The Thread.sleep() prevents the GUI from repainting itself until the while/loop is finished executing so you only see the painting at the end. Read the section from the Swing tutorial on Concurrency for more information.
The solution is to use a Swing Timer to provide the animation, not Thread.sleep().
It looks like you don't actually have the replaint(); call anywhere in your code. I would have that in both the moveLeft() and moveRight() commands.
Another thing, you're using JPanels as your wagons. I wouldn't do this, it'll take more resources and is bad practice. Unless there's a specific reason, I'd use a base sprite image, and load it to an int[] array, and paint that array to that screen using BufferedImage, instead of having x number of JPanel's running around.
When I run this class(Try class), it calls the Stacker class completely(with full functions), but when I use other class(I used JFrame with button that has actionlistener that calls Stacker class) to run this(Stacker class), the JFrame(Stacker class) will pop-up but empty and I can't close the program.
I tried to run this(Stacker) from other class like this:
public class Try {
/**
* #param args
*/
public static void main(String[] args) {
run();
}
public static void run(){
new Stacker();
}
}
The Stacker class ran fully(I can interact with it). But when I tried to call the stacker class from an actionlistener of a button in JFrame, it's blank and can't be closed.
Please help me.
here are my codes for the Stacker class:
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Stacker extends JFrame implements KeyListener {
int iteration = 1;
static double time = 200;
static int last = 0;
static int m = 10;
static int n = 20;
JButton b[][];
static int length[] = {5,5};
static int layer = 19;
static int deltax[] = {0,0};
static boolean press = false;
static boolean forward = true;
static boolean start = true;
JPanel panel;
public static void main (String[] args) {
Stacker stack = new Stacker();
stack.setVisible(true);
}
public Stacker() {
panel = new JPanel();
panel.setLayout(new GridLayout(20,10));
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
b = new JButton [m][n];
for (int y = 0;y<n;y++) {
for (int x = 0;x<m;x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.white);
b[x][y].setBorderPainted(false);
panel.add(b[x][y]);
b[x][y].setEnabled(true);
}//end inner for
}
setSize(390, 560);
setLayout(new GridBagLayout());
add(panel);
setFocusable(true);
addKeyListener(this);
pack();
setVisible(true);
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
do {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10-length[1]){
forward = false;
} else if (deltax[1] == 0){
forward = true;
}
draw();
try {
Thread.sleep((long) time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}while(press == false);
if (layer>12) {
time= 150-(iteration*iteration*2-iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
System.exit(0);
}
last = deltax[1];
start = false;
go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last<deltax[1]) {
if (deltax[1]+length[1]-1 <= last+length[0]-1) {
return length[1];
} else {
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
} else if (last>deltax[1]) {
return length[1]-Math.abs(deltax[1]-last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0;x<length[1];x++) {
b[x+deltax[0]][layer].setBackground(Color.white);
}
for (int x = 0;x<length[1];x++) {
b[x+deltax[1]][layer].setBackground(Color.BLUE);
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
press = true;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
Your do-while loop and thread sleep look suspicious in that it is running on the Swing event thread tying it up. The problem is that these longer running tasks lock the Swing Event Dispatch Thread, or EDT, the one thread that is responsible for all Swing graphics and user interactions, preventing your application from drawing itself and all of its widgets and prevents the application from responding to any user input.
Likely the application ran in isolation because the issues that I identified above ran off of the EDT, and doing this code wise is one possible solution to your problem, but I'm betting that there are better solutions available that we can help you with if you tell us more about the details of your problem.
Also you appear to be using recursion in a dangerous way having the go method call itself. Can't really say more since I'm on a cell phone.
Firstly, I would replace:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
with the following:
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
as DISPOSE_ON_CLOSE is a static/class variable.
In fact, I am surprised that your IDE did not pick that up (unless, you aren't using an IDE).
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Stacker stack = new Stacker();
stack.setVisible(true);
return null;
}
};
worker.execute();
after using this method, when I call Stacker class from another JFrame, the game executes perfectly. Just like when calling Stacker class from Try class.
Thank you for all the responses I had. :D
Short:
Is possible to set an unknown value on a JSlider having no knob?
Long:
I'm working on a project that has a desktop client developed in Java Swing where the user is required to measure some parameters using a slider. That's a requirement, it has to be a slider.
Until the user interacts with the slider the value has to be unknown, and that means showing no knob. That's a requirement too.
When using a JSlider the knob shows always and is no way to set any value out of its bounds or set it to null as it uses the primitive type int and not the object Integer.
Is there a way to set it a null fail-value or at least some value that shows no knob? Is there some extension that would allow to do so?
Make your own implementation of BasicSliderUI and override paintThumb(Graphics g) to do what you require.
Also take a look here: How to hide the knob of jSlider?
With the great help of Harry Joy about hiding the knob, finally I've been able to solve the problem.
If you check Harry's answer you can read about overriding the method BasicSliderUI.paintThumb(Graphics) for hiding the knob. It works fine on most L&F's not based on Synth (and that means Nimbus), where the approach would be different, but doable via customizations on the L&F.
Installing it is a bit tricky: having a PropertyChangeListener on the UIManager that checks any change on the L&F and installs a proper UI delegate on the component does the magic (in this solution I'm just showing the one based in BasicSliderUI the others are copy-pastes). Also I've tweaked it a bit to make it set the value to the position where is first clicked.
For the delegate to know if it must paint the knob or not I decided having a client property on the JSlider called "ready", which is to be a boolean. This way, the delegate can be installed on any JSlider, not only the one extended.
Now, how to manage unknown values? Adding another new property "selectedValue", this one is bound to both "value" and "ready" and of Integer type. "ready" is false or true depending on if it's null or not, and viceversa, and both "value" and "selectedValue" hold the same value (one being int and the other Integer) unless not ready, which would set the "selectedValue" to null. It might sound complicated, but it is not.
Also there's a little tweak for clearing the value with [Esc] and propagating properties in the component implemented.
Here's the code:
BasicSliderUIExt
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.plaf.basic.BasicSliderUI;
public class BasicSliderUIExt extends BasicSliderUI {
public BasicSliderUIExt(JSlider slider) {
super(slider);
}
#Override
public void paintThumb(Graphics g) {
if (isReady(super.slider)) {
super.paintThumb(g);
}
}
#Override
protected TrackListener createTrackListener(final JSlider slider) {
return new TrackListener() {
#Override
public void mousePressed(MouseEvent event) {
if (isReady(slider)) {
super.mousePressed(event);
} else {
JSlider slider = (JSlider) event.getSource();
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
slider.setValue(valueForYPosition(event.getY()));
break;
case SwingConstants.HORIZONTAL:
slider.setValue(valueForXPosition(event.getX()));
break;
}
super.mousePressed(event);
super.mouseDragged(event);
}
}
#Override
public boolean shouldScroll(int direction) {
if (isReady(slider)) {
return super.shouldScroll(direction);
}
return false;
}};
}
public static boolean isReady(JSlider slider) {
Object ready = slider.getClientProperty(JSliderExt.READY_PROPERTY);
return (ready == null) || (!(ready instanceof Boolean)) || (((Boolean) ready).booleanValue());
}
}
JSliderExt
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Constructor;
import javax.swing.BoundedRangeModel;
import javax.swing.JSlider;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.SliderUI;
public class JSliderExt extends JSlider {
private static final long serialVersionUID = 1L;
public static final String EXTENT_PROPERTY = "extent";
public static final String MAXIMUM_PROPERTY = "maximum";
public static final String MINIMUM_PROPERTY = "minimum";
public static final String READY_PROPERTY = "ready";
public static final String SELECTED_VALUE_PROPERTY = "selectedValue";
public static final String VALUE_PROPERTY = "value";
public static final boolean READY_DEFAULT_VALUE = false;
protected SliderUI uix = new BasicSliderUIExt(this);
public JSliderExt(BoundedRangeModel model, boolean ready) {
super(model);
init(ready);
}
public JSliderExt(BoundedRangeModel model) {
super(model);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int orientation, int minimmum, int maximum, int value, boolean ready) {
super(orientation, minimmum, maximum, value);
init(ready);
}
public JSliderExt(int orientation, int minimmum, int maximum, int value) {
super(orientation, minimmum, maximum, value);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int minimmum, int maximum, int value, boolean ready) {
super(minimmum, maximum, value);
init(ready);
}
public JSliderExt(int minimmum, int maximum, int value) {
super(minimmum, maximum, value);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int minimmum, int maximum, boolean ready) {
super(minimmum, maximum);
init(ready);
}
public JSliderExt(int minimmum, int maximum) {
super(minimmum, maximum);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(int orientation, boolean ready) {
super(orientation);
init(ready);
}
public JSliderExt(int orientation) {
super(orientation);
init(READY_DEFAULT_VALUE);
}
public JSliderExt(boolean ready) {
super();
init(ready);
}
public JSliderExt() {
super();
init(READY_DEFAULT_VALUE);
}
private void init(boolean ready) {
UIManager.addPropertyChangeListener(new PropertyChangeListener() { // Changes the UI delegate in L&F change
#Override
public void propertyChange(PropertyChangeEvent event) {
if ("lookAndFeel".equals(event.getPropertyName())) {
Object newValue = event.getNewValue();
if ((newValue != null) && (newValue instanceof LookAndFeel)) {
LookAndFeel lookAndFeel = (LookAndFeel) newValue;
try {
if (lookAndFeel instanceof MetalLookAndFeel) {
JSliderExt.this.uix = new MetalSliderUIExt();
} else if (lookAndFeel instanceof com.sun.java.swing.plaf.motif.MotifLookAndFeel) {
JSliderExt.this.uix = new MotifSliderUIExt(JSliderExt.this);
} else if (lookAndFeel instanceof com.sun.java.swing.plaf.windows.WindowsLookAndFeel) {
JSliderExt.this.uix = new WindowsSliderUIExt(JSliderExt.this);
} else {
throw new NullPointerException("Default Look & Feel not matched");
}
} catch (Exception e) {
try {
Package sliderPackage = JSliderExt.this.getClass().getPackage();
String lookAndFeelName = lookAndFeel.getName();
if (lookAndFeelName.equals("CDE/Motif")) {
lookAndFeelName = "Motif";
} else if (lookAndFeelName.equals("Windows Classic")) {
lookAndFeelName = "Windows";
} else if (lookAndFeelName.startsWith("JGoodies")) {
lookAndFeelName = "Basic";
}
Class<?> sliderUiType = Class.forName(sliderPackage.getName() + "." + lookAndFeelName
+ "SliderUIExt");
Constructor<?> constructor1 = null;
try {
constructor1 = sliderUiType.getConstructor(JSlider.class);
} catch (Exception e3) { // Nothing to do here
}
Constructor<?> constructor0 = null;
try {
constructor0 = sliderUiType.getConstructor();
} catch (Exception e3) { // Nothing to do here
}
Object sliderUi = null;
if (constructor1 != null) {
sliderUi = constructor1.newInstance(JSliderExt.this);
} else if (constructor0 != null) {
sliderUi = constructor0.newInstance();
}
if ((sliderUi != null) && (sliderUi instanceof SliderUI)) {
JSliderExt.this.setUI((SliderUI) sliderUi);
}
} catch (Exception e2) {
JSliderExt.this.uix = new BasicSliderUIExt(JSliderExt.this);
}
}
} else {
JSliderExt.this.uix = new BasicSliderUIExt(JSliderExt.this);
}
updateUI();
}
}});
addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent event) {
String propertyName = event.getPropertyName();
if (READY_PROPERTY.equals(propertyName)) {
Object newValue = event.getNewValue();
if ((newValue == null) || (!(newValue instanceof Boolean)) || (((Boolean) newValue).booleanValue())) {
setSelectedValue(Integer.valueOf(getValue()));
} else {
setSelectedValue(null);
}
} else if (SELECTED_VALUE_PROPERTY.equals(propertyName)) {
Object newValue = event.getNewValue();
System.out.println(newValue);
if ((newValue != null) && (newValue instanceof Integer)) {
int value = getValue();
int newSelectedValue = ((Integer) newValue).intValue();
if (value != newSelectedValue) {
setValue(newSelectedValue);
}
setReady(true);
} else {
setReady(false);
}
repaint();
} else if (VALUE_PROPERTY.equals(propertyName)) {
setReady(true);
Object newValue = event.getNewValue();
if ((newValue != null) && (newValue instanceof Integer)) {
setSelectedValue((Integer) newValue);
}
}
}});
addKeyListener(new KeyAdapter() { // Enables escape key for clearing value
#Override
public void keyPressed(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.VK_ESCAPE) {
JSliderExt.this.setReady(false);
}
}});
setReady(ready);
}
#Override
public void setValue(int value) {
int oldValue = getValue();
super.setValue(value);
firePropertyChange(VALUE_PROPERTY, Integer.valueOf(oldValue), Integer.valueOf(value));
}
#Override
public void setExtent(int extent) {
int oldExtent = getExtent();
super.setExtent(extent);
firePropertyChange(EXTENT_PROPERTY, Integer.valueOf(oldExtent), Integer.valueOf(extent));
}
#Override
public void setMinimum(int minimum) {
int oldMinimum = getMinimum();
super.setMinimum(minimum);
firePropertyChange(MINIMUM_PROPERTY, Integer.valueOf(oldMinimum), Integer.valueOf(minimum));
}
#Override
public void setMaximum(int maximum) {
int oldMaximum = getMaximum();
super.setMaximum(maximum);
firePropertyChange(MAXIMUM_PROPERTY, Integer.valueOf(oldMaximum), Integer.valueOf(maximum));
}
public Integer getSelectedValue() {
return (Integer) getClientProperty(SELECTED_VALUE_PROPERTY);
}
public void setSelectedValue(Integer selectedValue) {
putClientProperty(SELECTED_VALUE_PROPERTY, selectedValue);
}
public boolean isReady() {
Object ready = getClientProperty(READY_PROPERTY);
return ((ready != null) && (ready instanceof Boolean)) ? ((Boolean) ready).booleanValue()
: READY_DEFAULT_VALUE;
}
public void setReady(boolean waiting) {
putClientProperty(READY_PROPERTY, Boolean.valueOf(waiting));
}
#Override
public void updateUI() {
setUI(this.uix);
updateLabelUIs();
}
}
Please note that using this code, might require some changes on selecting the delegates depending on your application, since this is intended for a Windows system. As said, Synth/Nimbus has to be worked in a different manner, but also any custom L&F or for OSX, needs the proper delegate to be extended and added on the listener.
Why is it that when I wrap a SwingWorker around this code it no longer reports an exception being thrown?
import java.security.InvalidParameterException;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
IntegerString s = new IntegerString("EIGHT");
return null;
}
}.execute();
}
});
}
}
class IntegerString {
public IntegerString(String s) {
if (!isInteger(s)) {
System.out.println("...throwing exception.");
throw new InvalidParameterException("Thrown.");
}
// ...
}
static boolean isInteger(String str) {
if (str == null) {
return false;
}
int length = str.length();
if (length == 0) {
return false;
}
int i = 0;
if (str.charAt(0) == '-') {
if (length == 1) {
return false;
}
i = 1;
}
for (; i < length; i++) {
char c = str.charAt(i);
if (c <= '/' || c >= ':') {
return false;
}
}
return true;
}
}
You have to call get() to retrieve any exceptions that occurred in doInBackground(). For example, you can do it in done() method, ie:
#Override
protected void done() {
try {
get();
} catch (Exception e) {
e.printStackTrace();
}
}
I have a simple JComboBox filter code like this :
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class FilterComboBox extends JComboBox {
private List<String> array;
public FilterComboBox(List<String> array) {
super(array.toArray());
this.array = array;
this.setEditable(true);
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
comboFilter(textfield.getText());
}
});
}
});
}
public void comboFilter(String enteredText) {
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
this.setModel(new DefaultComboBoxModel(filterArray.toArray()));
this.setSelectedItem(enteredText);
this.showPopup();
}
else {
this.hidePopup();
}
}
/* Testing Codes */
public static List<String> populateArray() {
List<String> test = new ArrayList<String>();
test.add("");
test.add("Mountain Flight");
test.add("Mount Climbing");
test.add("Trekking");
test.add("Rafting");
test.add("Jungle Safari");
test.add("Bungie Jumping");
test.add("Para Gliding");
return test;
}
public static void makeUI() {
JFrame frame = new JFrame("Adventure in Nepal - Combo Filter Test");
FilterComboBox acb = new FilterComboBox(populateArray());
frame.getContentPane().add(acb);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception {
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
makeUI();
}
}
The performance of the combo filter is not so good but it is fine for few data set. My problem is - when I remove the comment UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); to change look and feel, the filter doesn't work. In WindowsLookAndFeel, the combo box only takes single character in it by replacing the previously entered character.
Can you please tell me whats going on? Manoj Shrestha's answer below helps in some way but , can you please provide some other suggestions to achieve combo box filter in Java?
Firstly you are creating new model everytime and then invoking show popup from code which leads to flickering etc. We can modify the model itself. Secondly you set the currently entered text as selected item which seems to have selectAll behavior as noted by others. I have modified the code as follows:
public void comboFilter(String enteredText) {
if (!this.isPopupVisible()) {
this.showPopup();
}
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
DefaultComboBoxModel model = (DefaultComboBoxModel) this.getModel();
model.removeAllElements();
for (String s: filterArray)
model.addElement(s);
JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.setText(enteredText);
}
}
Hope it works for you.
very long answer, I think that exelent example about how different Look and Feel have got implemented methods in API and works
KeyListener isn't proper Listener for Swing JComponents, you really to have bothering with KeyBindings,
KeyListener is simple asynchronous,
JComboBox is Compound JComponent, then there is required override internal JComponents, all output from KeyListener must be wrapped into invokeLater(), notice I can create event from coumpond JComponents that twice invokeLater() doesn't returns expected output to the GUI, only Swing Timer with Swing Action can do that correctly, simple why to bothering wiht that example about wrong way,
code
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class ComboBoxHoverOver {
private JComboBox combo = new JComboBox();
public ComboBoxHoverOver() {
combo.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXX");
combo.setRenderer(new ComboToolTipRenderer(combo));
combo.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
//System.out.println(combo.getSelectedItem().toString());
}
});
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//System.out.println(combo.getSelectedItem().toString());
}
});
combo.addItem("");
combo.addItem("Long text 4");
combo.addItem("Long text 3");
combo.addItem("Long text 2");
combo.addItem("Long text 1");
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(combo);
f.pack();
f.setVisible(true);
}
private class ComboToolTipRenderer extends BasicComboBoxRenderer {
private static final long serialVersionUID = 1L;
private JComboBox combo;
private JList comboList;
ComboToolTipRenderer(JComboBox combo) {
this.combo = combo;
}
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (comboList == null) {
comboList = list;
KeyAdapter listener = new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_UP) {
int x = 5;
int y = comboList.indexToLocation(comboList.getSelectedIndex()).y;
System.out.println(comboList.getSelectedIndex());
}
}
};
combo.addKeyListener(listener);
combo.getEditor().getEditorComponent().addKeyListener(listener);
}
if (isSelected) {
//System.out.println(value.toString());
}
return this;
}
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ComboBoxHoverOver comboBoxHoverOver = new ComboBoxHoverOver();
}
});
}
}
JComboBox is Compound JComponent, then there is required override BasicComboBoxUI, please sorry I lazy to write and simulating too much longer code as code from first point
otherwise all effort from above two point are useless and contraproductive, nothing else, only DOT
please can someone to test follows code in *nix and apple OS X
from my Java6 WinXP compo (all important is hidden in the used methods, enless kudos for anonymous author from former Sun Microsystems)
Substance L&F
WindowsLookAndFeel L&F
Nimbus L&F
Metal L&F
from Java Classes
main
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import org.pushingpixels.substance.api.skin.SubstanceOfficeSilver2007LookAndFeel;
public class AutoCompleteTextField {
private static JFrame frame = new JFrame();
private ArrayList<String> listSomeString = new ArrayList<String>();
private Java2sAutoTextField someTextField = new Java2sAutoTextField(listSomeString);
private ArrayList<String> listSomeAnotherString = new ArrayList<String>();
private Java2sAutoComboBox someComboBox = new Java2sAutoComboBox(listSomeAnotherString);
public AutoCompleteTextField() {
listSomeString.add("-");
listSomeString.add("Snowboarding");
listSomeString.add("Rowing");
listSomeString.add("Knitting");
listSomeString.add("Speed reading");
listSomeString.add("Pool");
listSomeString.add("None of the above");
//
listSomeAnotherString.add("-");
listSomeAnotherString.add("XxxZxx Snowboarding");
listSomeAnotherString.add("AaaBbb Rowing");
listSomeAnotherString.add("CccDdd Knitting");
listSomeAnotherString.add("Eee Fff Speed reading");
listSomeAnotherString.add("Eee Fff Pool");
listSomeAnotherString.add("Eee Fff None of the above");
//
someTextField.setFont(new Font("Serif", Font.BOLD, 16));
someTextField.setForeground(Color.black);
someTextField.setBackground(Color.orange);
someTextField.setName("someTextField");
someTextField.setDataList(listSomeString);
//
someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
someComboBox.setForeground(Color.black);
someComboBox.setBackground(Color.YELLOW);
someComboBox.getEditor().selectAll();
someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
((JTextField) someComboBox.getEditor().getEditorComponent()).setDisabledTextColor(Color.black);
someComboBox.setName("someComboBox");
someComboBox.setDataList(listSomeAnotherString);
//
frame.setLayout(new GridLayout(0, 1, 10, 10));
frame.add(someTextField);
frame.add(someComboBox);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
//
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
someTextField.setText("-");
someComboBox.getEditor().setItem(0);
someComboBox.getEditor().selectAll();
someTextField.grabFocus();
someTextField.requestFocus();
someTextField.setText(someTextField.getText());
someTextField.selectAll();
}
});
}
public static void main(String[] args) {
/*SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(new SubstanceOfficeSilver2007LookAndFeel());
SwingUtilities.updateComponentTreeUI(frame);
} catch (UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}
}
});*/
/*try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
System.out.println(info.getName());
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (UnsupportedLookAndFeelException e) {
// handle exception
} catch (ClassNotFoundException e) {
// handle exception
} catch (InstantiationException e) {
// handle exception
} catch (IllegalAccessException e) {
// handle exception
}*/
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AutoCompleteTextField aCTF = new AutoCompleteTextField();
}
});
}
}
AutoComboBox
import java.awt.event.ItemEvent;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.plaf.basic.BasicComboBoxEditor;
public class Java2sAutoComboBox extends JComboBox {
private static final long serialVersionUID = 1L;
private AutoTextFieldEditor autoTextFieldEditor;
private boolean isFired;
private class AutoTextFieldEditor extends BasicComboBoxEditor {
private Java2sAutoTextField getAutoTextFieldEditor() {
return (Java2sAutoTextField) editor;
}
AutoTextFieldEditor(java.util.List<String> list) {
editor = new Java2sAutoTextField(list, Java2sAutoComboBox.this);
}
}
public Java2sAutoComboBox(java.util.List<String> list) {
isFired = false;
autoTextFieldEditor = new AutoTextFieldEditor(list);
setEditable(true);
setModel(new DefaultComboBoxModel(list.toArray()) {
private static final long serialVersionUID = 1L;
#Override
protected void fireContentsChanged(Object obj, int i, int j) {
if (!isFired) {
super.fireContentsChanged(obj, i, j);
}
}
});
setEditor(autoTextFieldEditor);
}
public boolean isCaseSensitive() {
return autoTextFieldEditor.getAutoTextFieldEditor().isCaseSensitive();
}
public void setCaseSensitive(boolean flag) {
autoTextFieldEditor.getAutoTextFieldEditor().setCaseSensitive(flag);
}
public boolean isStrict() {
return autoTextFieldEditor.getAutoTextFieldEditor().isStrict();
}
public void setStrict(boolean flag) {
autoTextFieldEditor.getAutoTextFieldEditor().setStrict(flag);
}
public java.util.List<String> getDataList() {
return autoTextFieldEditor.getAutoTextFieldEditor().getDataList();
}
public void setDataList(java.util.List<String> list) {
autoTextFieldEditor.getAutoTextFieldEditor().setDataList(list);
setModel(new DefaultComboBoxModel(list.toArray()));
}
void setSelectedValue(Object obj) {
if (isFired) {
return;
} else {
isFired = true;
setSelectedItem(obj);
fireItemStateChanged(new ItemEvent(this, 701, selectedItemReminder, 1));
isFired = false;
return;
}
}
#Override
protected void fireActionEvent() {
if (!isFired) {
super.fireActionEvent();
}
}
}
AutoTextField
import java.util.List;
import javax.swing.JTextField;
import javax.swing.text.*;
public class Java2sAutoTextField extends JTextField {
private static final long serialVersionUID = 1L;
private List<String> dataList;
private boolean isCaseSensitive;
private boolean isStrict;
private Java2sAutoComboBox autoComboBox;
public class AutoDocument extends PlainDocument {
private static final long serialVersionUID = 1L;
#Override
public void replace(int i, int j, String s, AttributeSet attributeset)
throws BadLocationException {
super.remove(i, j);
insertString(i, s, attributeset);
}
#Override
public void insertString(int i, String s, AttributeSet attributeset)
throws BadLocationException {
if (s == null || "".equals(s)) {
return;
}
String s1 = getText(0, i);
String s2 = getMatch(s1 + s);
int j = (i + s.length()) - 1;
if (isStrict && s2 == null) {
s2 = getMatch(s1);
j--;
} else if (!isStrict && s2 == null) {
super.insertString(i, s, attributeset);
return;
}
if (autoComboBox != null && s2 != null) {
autoComboBox.setSelectedValue(s2);
}
super.remove(0, getLength());
super.insertString(0, s2, attributeset);
setSelectionStart(j + 1);
setSelectionEnd(getLength());
}
#Override
public void remove(int i, int j) throws BadLocationException {
int k = getSelectionStart();
if (k > 0) {
k--;
}
String s = getMatch(getText(0, k));
if (!isStrict && s == null) {
super.remove(i, j);
} else {
super.remove(0, getLength());
super.insertString(0, s, null);
}
if (autoComboBox != null && s != null) {
autoComboBox.setSelectedValue(s);
}
try {
setSelectionStart(k);
setSelectionEnd(getLength());
} catch (Exception exception) {
}
}
}
public Java2sAutoTextField(List<String> list) {
isCaseSensitive = false;
isStrict = true;
autoComboBox = null;
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
init();
return;
}
}
Java2sAutoTextField(List<String> list, Java2sAutoComboBox b) {
isCaseSensitive = false;
isStrict = true;
autoComboBox = null;
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
autoComboBox = b;
init();
return;
}
}
private void init() {
setDocument(new AutoDocument());
if (isStrict && dataList.size() > 0) {
setText(dataList.get(0).toString());
}
}
private String getMatch(String s) {
for (int i = 0; i < dataList.size(); i++) {
String s1 = dataList.get(i).toString();
if (s1 != null) {
if (!isCaseSensitive
&& s1.toLowerCase().startsWith(s.toLowerCase())) {
return s1;
}
if (isCaseSensitive && s1.startsWith(s)) {
return s1;
}
}
}
return null;
}
#Override
public void replaceSelection(String s) {
AutoDocument _lb = (AutoDocument) getDocument();
if (_lb != null) {
try {
int i = Math.min(getCaret().getDot(), getCaret().getMark());
int j = Math.max(getCaret().getDot(), getCaret().getMark());
_lb.replace(i, j - i, s, null);
} catch (Exception exception) {
}
}
}
public boolean isCaseSensitive() {
return isCaseSensitive;
}
public void setCaseSensitive(boolean flag) {
isCaseSensitive = flag;
}
public boolean isStrict() {
return isStrict;
}
public void setStrict(boolean flag) {
isStrict = flag;
}
public List<String> getDataList() {
return dataList;
}
public void setDataList(List<String> list) {
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
return;
}
}
}
EDIT
output from Win7 64b / Java7
Metal L&F
Windows L&F (funny empty white space near Button in JComboBox)
Nimbus L&F
feel free for edit(s)
This component is called autocomplete and is included in a so called swing extensions porject.
Just have a look at: http://swingx.java.net/
There is a webstart: http://swinglabs-demos.java.net/demos/swingxset6/swingxset.jnlp
Autocomplete is the Menu to select. Have fun and less error prone code :)
obviously the glitch is in the textfield component used. What happens is, when windows look and feel is used , the text in this component is selected just as using the line "textfield.selectAll();", and hence when you type anything else the selected text is cleared form the textfield component. So in the below code the caret position of this component is adjusted. This is how it works, first store the current caret position of text field in a variable "currentCaretPosition", then move the caret position to the beginning in the text in the component. After filtering restoring the caret position back to the "currentCaretPosition" variable value. I hope it works as you want it to.
The refined code is given below:-
/****Beginning of code****/
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class FilterComboBox extends JComboBox {
private List<String> array;
private int currentCaretPosition=0;
public FilterComboBox(List<String> array) {
super(array.toArray());
this.array = array;
this.setEditable(true);
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
currentCaretPosition=textfield.getCaretPosition();
if(textfield.getSelectedText()==null)
{
textfield.setCaretPosition(0);
comboFilter(textfield.getText());
textfield.setCaretPosition(currentCaretPosition);
}
}
});
}
});
}
public void comboFilter(String enteredText) {
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
this.setModel(new DefaultComboBoxModel(filterArray.toArray()));
this.setSelectedItem(enteredText);
this.showPopup();
}
else {
this.hidePopup();
}
}
/* Testing Codes */
public static List<String> populateArray() {
List<String> test = new ArrayList<String>();
test.add("");
test.add("Mountain Flight");
test.add("Mount Climbing");
test.add("Trekking");
test.add("Rafting");
test.add("Jungle Safari");
test.add("Bungie Jumping");
test.add("Para Gliding");
return test;
}
public static void makeUI() {
JFrame frame = new JFrame("Adventure in Nepal - Combo Filter Test");
FilterComboBox acb = new FilterComboBox(populateArray());
frame.getContentPane().add(acb);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
makeUI();
}
}
/******* End of code**********/
It looks like, as you mentioned, when user inputs any texts in combobox, the Windows Look & Feel selects (highlights) the entered text. So, when you press another key, it replaces the previous one. So, the solution is not to highlight the entered texts. You can achieve this by adding any one of the following statements in your keylistener.
textfield.setCaretPosition(textfield.getText().length());
OR
textfield.setSelectionStart(textfield.getText().length());
So, your keylistener should look like this :
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
comboFilter(textfield.getText());
textfield.setCaretPosition(textfield.getText().length());
}
});
}
});