How to have a Java Spinner with left and right arrows? - java

I've used java Spinner in Java Swing, but it had up and down arrows, is there a way to set it's orientation so that the arrows are left and right ?
Thanks to Jean-François Savard's answer, I'm one step closer, but still not quite right, I have the following lines :
public void installUI(JComponent c)
{
super.installUI(c);
c.removeAll();
FlowLayout FL=new FlowLayout();
FL.setHgap(0);
c.setLayout(FL);
JComponent editor=createEditor();
editor.setPreferredSize(new Dimension(30,16));
c.add(editor);
c.add(createPreviousButton());
c.add(createNextButton());
}
The spacing is not correct, how to fix it ? I hard coded in the above lines, how to automatically provide proper space for the text ?

A short search on google lead me to a custom implementation of JSpinner to do this.
public class LeftRightSpinnerDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LeftRightSpinnerDemo().makeUI();
}
});
}
public void makeUI() {
JSpinner spinner = new JSpinner();
spinner.setUI(new LeftRightSpinnerUI());
JFrame frame = new JFrame();
frame.add(spinner);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class LeftRightSpinnerUI extends BasicSpinnerUI {
public static ComponentUI createUI(JComponent c) {
return new LeftRightSpinnerUI();
}
#Override
protected Component createNextButton() {
Component c = createArrowButton(SwingConstants.EAST);
c.setName("Spinner.nextButton");
installNextButtonListeners(c);
return c;
}
#Override
protected Component createPreviousButton() {
Component c = createArrowButton(SwingConstants.WEST);
c.setName("Spinner.previousButton");
installPreviousButtonListeners(c);
return c;
}
// copied from BasicSpinnerUI
private Component createArrowButton(int direction) {
JButton b = new BasicArrowButton(direction);
Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
if (buttonBorder instanceof UIResource) {
b.setBorder(new CompoundBorder(buttonBorder, null));
} else {
b.setBorder(buttonBorder);
}
b.setInheritsPopupMenu(true);
return b;
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
c.removeAll();
c.setLayout(new BorderLayout());
c.add(createNextButton(), BorderLayout.EAST);
c.add(createPreviousButton(), BorderLayout.WEST);
c.add(createEditor(), BorderLayout.CENTER);
}
}
Make sure to add the correct imports as I removed them to lighten the code.
Refer to this for the original post.

Related

How to set divider location for JSplitPane on start-up

I have JPanel which contains JSplitPane. The JPanel is injected during a runtime into a JFrame using the method invokeAndWait. Then the invokeLater is called to update divider location in SplitPane.
The problem is, when the divider update is invoked, JPanel width is still 0.
When I add sleep or a breakpoint anywhere in the code (except the invokeLater), the code works fine.
final JPanel viewPanel = new JPanel();
viewPanel.setLayout(new BorderLayout());
final JPanel header = getPresenterHeader(getPageTitle(), getPageInstructions());
viewPanel.add(header, BorderLayout.NORTH);
viewPanel.add(getSplitPane(), BorderLayout.CENTER);
toolbar = createActionsToolBar();
toolbar.addAction(new ExitPresenterAction(this));
viewPanel.add(toolbar, BorderLayout.SOUTH);
addContent(viewPanel);
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
splitPane.setDividerLocation(0.5);
}
});
I'm not sure whether it helps for you, but we have a method which mostly helps in this situation:
public static JSplitPane setDividerLocation(final JSplitPane splitter, final double proportion) {
if (splitter.isShowing()) {
if ((splitter.getWidth() > 0) && (splitter.getHeight() > 0)) {
splitter.setDividerLocation(proportion);
} else {
splitter.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent ce) {
splitter.removeComponentListener(this);
setDividerLocation(splitter, proportion);
}
});
}
} else {
splitter.addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if (((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) && splitter.isShowing()) {
splitter.removeHierarchyListener(this);
setDividerLocation(splitter, proportion);
}
}
});
}
return splitter;
}
Use it instead of invokeLater call
setDividerLocation(splitPane, 0.5);

FocusTraversalPolicy losing focus on getComponentBefore()

I'm trying to create a custom focus traversal policy for my swing app by passing a list of components (I do this as the traversal required is not the same as the Containers component order). Like this example:
public class ComponentTraversal extends FocusTraversalPolicy
{
private final java.util.List<Component> componentList;
public ComponentTraversal(java.util.List<Component> aList)
{
componentList = aList;
}
#Override
public Component getComponentAfter(Container aContainer, Component aComponent)
{
int index = componentList.indexOf(aComponent);
if (index == (componentList.size() - 1))
index = -1;
return componentList.get(index + 1);
}
#Override
public Component getComponentBefore(Container aContainer, Component aComponent)
{
int index = componentList.indexOf(aComponent);
if (index == 0)
index = componentList.size();
return componentList.get(index - 1);
}
#Override
public Component getFirstComponent(Container aContainer)
{
return componentList.get(0);
}
#Override
public Component getLastComponent(Container aContainer)
{
return componentList.get(componentList.size() - 1);
}
#Override
public Component getDefaultComponent(Container aContainer)
{
return componentList.get(0);
}
}
And run with this test class:
public class C extends JFrame
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(
new Runnable()
{
#Override
public void run()
{
new C();
}
}
);
}
public C()
{
super("A Test");
JPanel jp = new JPanel(new GridLayout(2,1));
JTextField jtf1 = new JTextField();
JTextField jtf2 = new JTextField();
jp.add(jtf1);
jp.add(jtf2);
java.util.List<Component> comps = new ArrayList<Component>();
comps.add(jtf1);
comps.add(jtf2); //This line
this.add(jp);
this.setFocusTraversalPolicy(new ComponentTraversal(comps));
this.setSize(200,100);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setVisible(true);
}
}
In most cases this works fine, as posted will work in both directions. If I comment out the line with the comment This line (i.e. adding only one component to the traversal list) it will work in the forwards direction (Tab)... retaining focus on itself.
But if I do it in reverse direction (Shift+Tab), the component loses focus and no exceptions are reported.
This isn't really a problem in terms of my application, as there will always be more than one component, but I tried this out and it bugs me and I was wondering if anyone had encountered this before, or knows what I'm doing wrong.

JFrame Components not showing up

I have a problem that the JFrame is not showing upmy components.
When i opened the GasStationPanel in WindowBuilder it show well, but the MainFrame is show as a blank windows.
Please, I need you help here.
Thanks!
The JFrame code is:
public class MainFrame extends JFrame {
private GasStationPanel pnlMainGasStation;
public MainFrame() throws SecurityException, IOException {
try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
}
getContentPane().setLayout(new BorderLayout());
this.pnlMainGasStation = new GasStationPanel("all cars","pumps","coffee");
this.add(pnlMainGasStation, BorderLayout.CENTER);
setLocationRelativeTo(null);
setTitle("GasStation");
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
Utils.closeApplication(MainFrame.this);
}
});
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = new Dimension();
frameSize.setSize(screenSize.width*0.7, screenSize.height*0.9);
setSize(frameSize);
setVisible(true);
}
public GasStationPanel getMainPanel() {
return pnlMainGasStation;
}
}
The GasStationPanel Code:
public class GasStationPanel extends JPanel {
private JSplitPane splinterRight, splinterLeft;
private AllCarsPanel allCarsPanel;
private FuelPumpListPanel fuelPumpsListPanel;
private CoffeeHousePanel coffeeHousePanel;
private List<GasStationController> allListeners;
public AllCarsPanel getAllCarsPanel() {
return allCarsPanel;
}
public FuelPumpListPanel getFuelPumpsListPanel() {
return fuelPumpsListPanel;
}
public CoffeeHousePanel getCoffeeHousePanel() {
return coffeeHousePanel;
}
public GasStationPanel(String allCarsStr, String fuelPumpsListStr,
String coffeeHousePanelStr) throws SecurityException, IOException {
// Init Listeners List
this.allListeners = new ArrayList<GasStationController>();
// Layout and size
setLayout(new BorderLayout());
// Build panels
allCarsPanel = new AllCarsPanel();
fuelPumpsListPanel = new FuelPumpListPanel();
coffeeHousePanel = new CoffeeHousePanel();
// Split the screen to three
splinterRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splinterLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splinterLeft.setLeftComponent(allCarsPanel);
splinterLeft.setRightComponent(fuelPumpsListPanel);
splinterRight.setLeftComponent(splinterLeft);
splinterRight.setRightComponent(coffeeHousePanel);
}
public void registerListener(GasStationController gasStationController) {
this.allListeners.add(gasStationController);
}
In short, you never add any components to your container. For example, in your GasStationPanel code, perhaps you should try invoking add(Component) by passing in your JSplitPanes as an argument. For example:
add(splinterLeft, BorderLayout.CENTER);

JEditorPane, JScrollPane and accessibility

I have a simple Swing application with a JEditorPane wrapped in a JScrollPane.
Unfortunately screen reader software like JAWS or NVDA does not behave correctly.
When focus enters the JEditorPane it only reads the accessible name followed by "text" and then stops, when the expected behavior is to continue reading the contents of the JEditorPane.
If I do not wrap the JEditorPane in the JScrollPane it works as expected.
I have tried inspecting the accessible tree using Monkey, but I cannot see any relevant difference between a JEditorPane wrapped in a JScrollPane and one that is not wrapped.
Any ideas?
Here is a brief sample that demonstrates the problem. If focus enters the first JEditorPane, JAWS reads "first editorpane - edit". If focus enters the second JEditorPane, JAWS reads "second editorpane - edit - bar".
public final class SmallExample {
public static void main(String... aArgs){
JFrame frame = new JFrame("Test Frame");
JPanel panel = new JPanel();
JEditorPane editorPane1 = new JEditorPane();
editorPane1.setText("Foo");
editorPane1.getAccessibleContext().setAccessibleName("first editorpane");
editorPane1.getAccessibleContext().setAccessibleDescription("");
JScrollPane scrollPane = new JScrollPane( editorPane1, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
panel.add(scrollPane);
JEditorPane editorPane2 = new JEditorPane();
panel.add(editorPane2);
editorPane2.setText("Bar");
editorPane2.getAccessibleContext().setAccessibleName("second editorpane");
editorPane2.getAccessibleContext().setAccessibleDescription("");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
I found a workaround myself:
If I
modify the accessible tree to skip the JScrollPane and JViewPort
avoid sending accessible property-changed events from the JEditorPane
then it works.
I would still very much appreciate any insight into why this works (I found the workaround by replacing the AccessibleContext for editorPane2 with the one for editorPane1 and gradually switching the methods back until I found the ones that I needed to override).
Here is a working example (it is not so brief anymore):
public final class Example {
public static void main(String... aArgs){
JFrame frame = new JFrame("Test Frame");
final JPanel panel = new JPanel(){
public AccessibleContext getAccessibleContext() {
if(accessibleContext==null){
accessibleContext = new AccessibleContextWrapper(super.getAccessibleContext()){
public Accessible getAccessibleChild(int i) {
Accessible accessibleChild = super.getAccessibleChild(i);
while(accessibleChild!=null && (accessibleChild instanceof JScrollPane || accessibleChild instanceof JViewport)){
accessibleChild = accessibleChild.getAccessibleContext().getAccessibleChild(0);
}
return accessibleChild;
}
};
}
return accessibleContext;
}
};
final JEditorPane editorPane = new JEditorPane(){
public AccessibleContext getSuperAccessibleContext() {
return super.getAccessibleContext();
}
#Override
public AccessibleContext getAccessibleContext() {
return new AccessibleContextWrapper(super.getAccessibleContext()){
public Accessible getAccessibleParent() {
Accessible parent = super.getAccessibleParent();
while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
parent = parent.getAccessibleContext().getAccessibleParent();
}
return parent;
}
public int getAccessibleIndexInParent() {
int res = super.getAccessibleIndexInParent();
Accessible parent = super.getAccessibleParent();
while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
res = parent.getAccessibleContext().getAccessibleIndexInParent();
parent = parent.getAccessibleContext().getAccessibleParent();
}
return res;
}
public void addPropertyChangeListener(
PropertyChangeListener listener) {
}
public void removePropertyChangeListener(
PropertyChangeListener listener) {
}
};
}
};
editorPane.setText("Foo");
editorPane.getAccessibleContext().setAccessibleName("first editorpane");
editorPane.getAccessibleContext().setAccessibleDescription("");
editorPane.getAccessibleContext();
JScrollPane scrollPane = new JScrollPane( editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
panel.add(scrollPane);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static class AccessibleContextWrapper extends AccessibleContext {
private final AccessibleContext inner;
public AccessibleContextWrapper(AccessibleContext inner) {
this.inner = inner;
}
public String getAccessibleName() {
return inner.getAccessibleName();
}
public void setAccessibleName(String s) {
inner.setAccessibleName(s);
}
public String getAccessibleDescription() {
return inner.getAccessibleDescription();
}
public void setAccessibleDescription(String s) {
inner.setAccessibleDescription(s);
}
public AccessibleRole getAccessibleRole() {
return inner.getAccessibleRole();
}
public AccessibleStateSet getAccessibleStateSet() {
return inner.getAccessibleStateSet();
}
public Accessible getAccessibleParent() {
return inner.getAccessibleParent();
}
public void setAccessibleParent(Accessible a) {
inner.setAccessibleParent(a);
}
public int getAccessibleIndexInParent() {
return inner.getAccessibleIndexInParent();
}
public int getAccessibleChildrenCount() {
return inner.getAccessibleChildrenCount();
}
public Accessible getAccessibleChild(int i) {
return inner.getAccessibleChild(i);
}
public Locale getLocale() throws IllegalComponentStateException {
return inner.getLocale();
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
inner.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
inner.removePropertyChangeListener(listener);
}
public AccessibleAction getAccessibleAction() {
return inner.getAccessibleAction();
}
public AccessibleComponent getAccessibleComponent() {
return inner.getAccessibleComponent();
}
public AccessibleSelection getAccessibleSelection() {
return inner.getAccessibleSelection();
}
public AccessibleText getAccessibleText() {
return inner.getAccessibleText();
}
public AccessibleEditableText getAccessibleEditableText() {
return inner.getAccessibleEditableText();
}
public AccessibleValue getAccessibleValue() {
return inner.getAccessibleValue();
}
public AccessibleIcon[] getAccessibleIcon() {
return inner.getAccessibleIcon();
}
public AccessibleRelationSet getAccessibleRelationSet() {
return inner.getAccessibleRelationSet();
}
public AccessibleTable getAccessibleTable() {
return inner.getAccessibleTable();
}
public void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
inner.firePropertyChange(propertyName, oldValue, newValue);
}
}
}

How would I go about highlighting an item in a JList? [duplicate]

I am trying to change JList rows dynamically. I need change nth row colour, highlight it(n is unknown during compilation). I saw a lot of examples with custom ListCellRenderer, but all were "static".
In other words I have JList with x rows. During runtime my "business logic" detects nth row is important. So I want make its background green, wait one second, and then make it white again. One more thing, don't wan change row selection.
What is the best way to do so?
Simple, set a custom ListCellRenderer to your JList using:
list.setCellRenderer(myListCellrenderer);
Now inside the overridden method getListCellRendererComponent() do something like this:
public Component getListCellRendererComponent(.....) {
Component c = super.getListCellRendererComponent();
c.setBackGround(Color.blue)
return c;
}
The above example assumed that your custom renderer overrid DefaultListCellRenderer
Based on ListDemo sample from SUN.
If you enter some text in the textfield which isn't in the list and you hit highlight it gets added.
If the text is in the list and you hit highlight the entry in the list gets temporarily highlighted blue.
Note the solution here with the match field is just for demo. For more correct implementation consider the other ideas proposed and consider using javax.swing.Timer
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ListDemo extends JPanel {
private JList list;
private DefaultListModel listModel;
public String match = null;
private static final String hireString = "Highlight";
private JTextField employeeName;
public ListDemo() {
super(new BorderLayout());
listModel = new DefaultListModel();
listModel.addElement("Test1");
listModel.addElement("Test2");
listModel.addElement("Test3");
list = new JList(listModel);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.setVisibleRowCount(5);
list.setCellRenderer(new MyListCellRenderer());
JScrollPane listScrollPane = new JScrollPane(list);
JButton hireButton = new JButton(hireString);
HireListener hireListener = new HireListener(hireButton);
hireButton.setActionCommand(hireString);
hireButton.addActionListener(hireListener);
hireButton.setEnabled(false);
employeeName = new JTextField(10);
employeeName.addActionListener(hireListener);
employeeName.getDocument().addDocumentListener(hireListener);
listModel.getElementAt(list.getSelectedIndex()).toString();
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(employeeName);
buttonPane.add(hireButton);
buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
add(listScrollPane, BorderLayout.CENTER);
add(buttonPane, BorderLayout.PAGE_END);
}
class MyListCellRenderer extends JLabel implements ListCellRenderer {
public MyListCellRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(JList paramlist, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
if (value.toString().equals(match)) {
setBackground(Color.BLUE);
SwingWorker worker = new SwingWorker() {
#Override
public Object doInBackground() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) { /*Who cares*/ }
return null;
}
#Override
public void done() {
match = null;
list.repaint();
}
};
worker.execute();
} else
setBackground(Color.RED);
return this;
}
}
class HireListener implements ActionListener, DocumentListener {
private boolean alreadyEnabled = false;
private JButton button;
public HireListener(JButton button) {
this.button = button;
}
public void actionPerformed(ActionEvent e) {
String name = employeeName.getText();
if (listModel.contains(name)) {
match = name;
list.repaint();
employeeName.requestFocusInWindow();
employeeName.selectAll();
return;
}
if (name.equals("")) {
Toolkit.getDefaultToolkit().beep();
employeeName.requestFocusInWindow();
employeeName.selectAll();
return;
}
int index = list.getSelectedIndex();
if (index == -1)
index = 0;
else
index++;
listModel.insertElementAt(employeeName.getText(), index);
employeeName.requestFocusInWindow();
employeeName.setText("");
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
public void insertUpdate(DocumentEvent e) {
enableButton();
}
public void removeUpdate(DocumentEvent e) {
handleEmptyTextField(e);
}
public void changedUpdate(DocumentEvent e) {
if (!handleEmptyTextField(e))
enableButton();
}
private void enableButton() {
if (!alreadyEnabled)
button.setEnabled(true);
}
private boolean handleEmptyTextField(DocumentEvent e) {
if (e.getDocument().getLength() <= 0) {
button.setEnabled(false);
alreadyEnabled = false;
return true;
}
return false;
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ListDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ListDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() { createAndShowGUI(); }
});
}
}
Your custom ListCellRenderer, which implements the method getListCellRendererComponent, will have access to both the JList and the value that it is redering. This gives you a couple options for how to determine when to paint the nth row green:
You could subclass JList and have the renderer ask it which color to use for the bg. The JList subclass could trigger a repaint when the business logic determines that it is time for the nth row to be green, and then start an Swing Timer to trigger a repaint returning the bg back to normal
When the business logic determines when you should show the row as green, you also have the option of setting state on the backing object of the row, and test it for that state within getListCellRendererComponent, setting the bg green if the state is correct. Again, you have the option of setting an Swing Timer to revert the state on the backing object.

Categories