How to set selected index JComboBox by value - java

I want to set the selected index in a JComboBox by the value not the index. How to do that? Example
public class ComboItem {
private String value;
private String label;
public ComboItem(String value, String label) {
this.value = value;
this.label = label;
}
public String getValue() {
return this.value;
}
public String getLabel() {
return this.label;
}
#Override
public String toString() {
return label;
}
}
JComboBox test = new JComboBox();
test.addItem(new ComboItem(0, "orange"));
test.addItem(new ComboItem(1, "pear"));
test.addItem(new ComboItem(2, "apple"));
test.addItem(new ComboItem(3, "banana"));
test.setSelectedItem("banana");
Ok, I have modified my question a bit. I forgot that i have a custom item inside my JComboBox that makes it a bit more difficult. i cant do setSelectedItem as i have a ComboItem inside each item. So still, how do i get this done?

setSelectedItem("banana"). You could have found it yourself by just reading the javadoc.
Edit: since you changed the question, I'll change my answer.
If you want to select the item having the "banana" label, then you have two solutions:
Iterate through the items to find the one (or the index of the one) which has the given label, and then call setSelectedItem(theFoundItem) (or setSelectedIndex(theFoundIndex))
Override equals and hashCode in ComboItem so that two ComboItem instances having the same name are equal, and simply use setSelectedItem(new ComboItem(anyNumber, "banana"));

You should use model
comboBox.getModel().setSelectedItem(object);

public static void setSelectedValue(JComboBox comboBox, int value)
{
ComboItem item;
for (int i = 0; i < comboBox.getItemCount(); i++)
{
item = (ComboItem)comboBox.getItemAt(i);
if (item.getValue().equalsIgnoreCase(value))
{
comboBox.setSelectedIndex(i);
break;
}
}
}
Hope this help :)

Why not take a collection, likely a Map such as a HashMap, and use it as the nucleus of your own combo box model class that implements the ComboBoxModel interface? Then you could access your combo box's items easily via their key Strings rather than ints.
For instance...
import java.util.HashMap;
import java.util.Map;
import javax.swing.ComboBoxModel;
import javax.swing.event.ListDataListener;
public class MyComboModel<K, V> implements ComboBoxModel {
private Map<K, V> nucleus = new HashMap<K, V>();
// ... any constructors that you want would go here
public void put(K key, V value) {
nucleus.put(key, value);
}
public V get(K key) {
return nucleus.get(key);
}
#Override
public void addListDataListener(ListDataListener arg0) {
// TODO Auto-generated method stub
}
// ... plus all the other methods required by the interface
}

for example
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class ComboboxExample {
private JFrame frame = new JFrame("Test");
private JComboBox comboBox = new JComboBox();
public ComboboxExample() {
createGui();
}
private void createGui() {
comboBox.addItem("One");
comboBox.addItem("Two");
comboBox.addItem("Three");
JButton button = new JButton("Show Selected");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Selected item: " + comboBox.getSelectedItem());
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.requestFocusInWindow();
}
});
}
});
JButton button1 = new JButton("Append Items");
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
appendCbItem();
}
});
JButton button2 = new JButton("Reduce Items");
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
reduceCbItem();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(4, 1));
frame.add(comboBox);
frame.add(button);
frame.add(button1);
frame.add(button2);
frame.setLocation(200, 200);
frame.pack();
frame.setVisible(true);
selectFirstItem();
}
public void appendCbItem() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
comboBox.addItem("Four");
comboBox.addItem("Five");
comboBox.addItem("Six");
comboBox.setSelectedItem("Six");
requestCbFocus();
}
});
}
public void reduceCbItem() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
comboBox.removeItem("Four");
comboBox.removeItem("Five");
comboBox.removeItem("Six");
selectFirstItem();
}
});
}
public void selectFirstItem() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
comboBox.setSelectedIndex(0);
requestCbFocus();
}
});
}
public void requestCbFocus() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.requestFocusInWindow();
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComboboxExample comboboxExample = new ComboboxExample();
}
});
}
}

http://docs.oracle.com/javase/6/docs/api/javax/swing/JComboBox.html#setSelectedItem(java.lang.Object)
test.setSelectedItem("banana");
There are some caveats or potentially unexpected behavior as explained in the javadoc. Make sure to read that.

The right way to set an item selected when the combobox is populated by some class' constructor (as #milosz posted):
combobox.getModel().setSelectedItem(new ClassName(parameter1, parameter2));
In your case the code would be:
test.getModel().setSelectedItem(new ComboItem(3, "banana"));

Just call comboBox.updateUI() after doing comboBox.setSelectedItem or comboBox.setSelectedIndex or comboModel.setSelectedItem

public boolean preencherjTextCombox (){
int x = Integer.parseInt(TableModelo.getModel().getValueAt(TableModelo.getSelectedRow(),0).toString());
modeloobj = modelosDAO.pesquisar(x);
Combmarcass.getModel().setSelectedItem(modeloobj.getMarca());
txtCodigo.setText(String.valueOf(modeloobj.getCodigo()));
txtDescricao.setText(String.valueOf(modeloobj.getDescricao()));
txtPotencia.setText(String.valueOf(modeloobj.getPotencia()));
return true;
}

In my case build class Item(key, value) as item of combobox
SanPhamDTO currentProd = prodDao.getDetailById(id);
Item item = new Item(currentProd.getCategory().getId(),
currentProd.getCategory().getName());
cbdanhmuc.getModel().setSelectedItem(item)

Related

How to pass a variable between JPanel classes?

I have a JTabbedPane with two JPanels that need to stay in seperate classes. In PageOne, I want to be able to increment MyInteger by clicking the add button, and I then want to be able to print that integer in PageTwo by clicking the button there. It prints the correct value in PageOne, but prints 0 when I pass it to the PageTwo class and print it there.
How can I pass the value in such a way that it prints the correct value when clicking the button in both JPanels? I figure it has something to do with how I inherit from PageOne, but couldn't find a way of changing it on SO that solved my problem.
Main class:
import javax.swing.*;
public class MyJFrame {
PageOne pageOne;
PageTwo pageTwo;
public MyJFrame() {
JFrame f = new JFrame();
pageOne = new PageOne();
pageTwo = new PageTwo();
JTabbedPane jTabbedPane = new JTabbedPane();
jTabbedPane.addTab("Page One", pageOne);
jTabbedPane.addTab("Page Two", pageTwo);
f.add(jTabbedPane);
f.setSize(200,120);
f.setVisible(true);
}
public static void main(String[] args) throws InterruptedException {
new MyJFrame();
}
}
JPanel One:
import javax.swing.*;
public class PageOne extends JPanel {
public Integer myInteger = 0;
public JButton add;
public PageOne() {
add = new JButton();
add.setText("Increment number");
add(add);
add.addActionListener(actionEvent -> {
myInteger++;
printOne();
});
}
public void printOne() {
System.out.println("Page One:" + myInteger);
}
}
JPanel Two:
import javax.swing.*;
public class PageTwo extends JPanel {
PageOne pageOneRef = new PageOne();
public JButton button;
public PageTwo() {
JPanel panel = new JPanel();
button = new JButton("Click me");
panel.add(button);
add(panel);
button.addActionListener(e -> printTwo());
}
public void printTwo() {
System.out.println("Page Two:" + pageOneRef.myInteger);
}
}
The basic answer is, you need some kind of "container" which can be shared between the two components. This is commonly achieved through the use of a "model" of some kind.
See:
Model-View-Controller
Observer Pattern
Writing Event Listeners
for an overview of the concepts presented below
Runnable example
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
DefaultIntegerModel model = new DefaultIntegerModel();
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Page One", new PageOne(model));
tabbedPane.addTab("Page Two", new PageTwo(model));
frame.add(tabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface IntegerModel {
public interface Observer {
public void valueDidChange(IntegerModel source, int value);
}
public int getValue();
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
}
public interface MutableIntegerModel extends IntegerModel {
public void setValue(int value);
}
public class DefaultIntegerModel implements MutableIntegerModel {
private int value;
private List<Observer> observers;
public DefaultIntegerModel() {
this(0);
}
public DefaultIntegerModel(int value) {
this.value = value;
observers = new ArrayList<Observer>(8);
}
#Override
public void setValue(int value) {
this.value = value;
fireValueDidChange(value);
}
#Override
public int getValue() {
return value;
}
#Override
public void addObserver(Observer observer) {
observers.add(observer);
}
#Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
protected void fireValueDidChange(int value) {
for (Observer observer : observers) {
observer.valueDidChange(this, value);
}
}
}
public class PageOne extends JPanel {
public JButton add;
private MutableIntegerModel model;
public PageOne(MutableIntegerModel model) {
this.model = model;
add = new JButton();
add.setText("Increment number");
add(add);
add.addActionListener(actionEvent -> {
model.setValue(model.getValue() + 1);
printOne();
});
}
public void printOne() {
System.out.println("Page One:" + model.getValue());
}
}
public class PageTwo extends JPanel {
private JButton button;
private JLabel label;
private IntegerModel model;
public PageTwo(IntegerModel model) {
this.model = model;
model.addObserver(new IntegerModel.Observer() {
#Override
public void valueDidChange(IntegerModel source, int value) {
System.out.println("Page two value did change to " + value);
label.setText(Integer.toString(model.getValue()));
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
label = new JLabel(Integer.toString(model.getValue()));
add(label, gbc);
button = new JButton("Click me");
button.addActionListener(e -> printTwo());
add(button, gbc);
}
public void printTwo() {
System.out.println("Page Two:" + model.getValue());
}
}
}
But why are there two models
Stop for a second and think about the responsibilities of each component.
PageOne want's to update the model, in order to do so, it also needs to know the value of the model. The model makes no assumption about "how" the consumer of this model will do that (so I didn't provide a increment method), it just allows the consumer to set the value it wants
PageTwo just wants to display the value (and be notified when some change occurs), so it doesn't need a mutable version of the model.
This restricts what consumers maybe able to do to the model rather the exposing functionality to parties which don't need it (and might be tempted to abuse it)
This is a demonstration and your needs may differ, but I'm bit of a scrooge when I design these kinds of things, I need the consumers to prove to me that they need functionality, rather then "assuming" what functionality they "might" require 😉
This is a practice known is "information hiding", which is supported by Polymorphism in OO languages

How to change the icon of a dynamically generated JButton

I have this java swing program, and im trying to figure out how can i create a button that upon clicking it will clear the text areas & change the icon of the person to put their hand down.
The buttons are dynamically generated using a for loop
And this
// To create buttons
for(int i=0 ; i < list.length; i++){
Participant pa = list[i];
JButton b = new JButton(pa.getNameButton(),participant);
b.addActionListener(e ->
{
String s = pa.toString() + questionPane.getText();
final ImageIcon raise = resizeIcon(new ImageIcon("src/raise.png"),30,30);
b.setIcon(raise);
JOptionPane.showMessageDialog(null,s,"Welcome to Chat Room",JOptionPane.INFORMATION_MESSAGE,pa.getImage());
});
p.add(b);
}
// Clear button logic
clearButton.addActionListener(e ->{
questionPane.setText("");
hostPane.setText("");
});
Okay, this is going to be a bit of fun.
The following example decouples much of the concept and makes use of a basic "observer pattern" to notify interested parties that the state has changed (ie, the chat's been cleared).
This is a basic concept where by you decouple the "what" from the "how", ie, "what" it is you want done (update the model) from the "how" it gets done (ie, button push). This makes it easier to adapt to more complex systems.
The example contains a ChatService, which has a single listener, which, for this example, simple tells interested parties that the chat has been cleared.
A more complex solution might have the ChatService generating events for when a user "raises" their hand, which allows the interested parties to deal with it in what ever way is relevant to them.
The example makes use of the Action API to decouple the work performed by each action from the UI itself. This helps create a single unit of work which is easier to deal with when you have a dynamic data set.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
ChatService chatService = new ChatService();
JPanel panel = new JPanel();
String[] names = new String[] {"Bryan", "Alan", "George", "Henry"};
List<PeopleAction> actions = new ArrayList<>(names.length);
for (String name : names) {
PeopleAction action = new PeopleAction(chatService, name, false);
actions.add(action);
}
Random rnd = new Random();
actions.get(rnd.nextInt(names.length)).setRaised(true);
for (Action action : actions) {
JButton btn = new JButton(action);
panel.add(btn);
}
setLayout(new GridLayout(2, 1));
add(panel);
JPanel hostPane = new JPanel();
JButton clearButton = new JButton(new ClearAction(chatService));
hostPane.add(clearButton);
add(hostPane);
}
}
public class ChatService {
private List<ChatListener> listeners = new ArrayList<>(25);
public void addChatListeners(ChatListener listener) {
listeners.add(listener);
}
public void removeChatListener(ChatListener listener) {
listeners.remove(listener);
}
protected void fireChatCleared() {
if (listeners.isEmpty()) {
return;
}
for (ChatListener listener : listeners) {
listener.chatCleared();
}
}
public void clear() {
// Do what's required
fireChatCleared();
}
}
public interface ChatListener {
public void chatCleared();
}
public class PeopleAction extends AbstractAction implements ChatListener {
private String name;
private boolean raised;
public PeopleAction(ChatService chatService, String name, boolean raised) {
// You can use either LARGE_ICON_KEY or SMALL_ICON to set the icon
this.name = name;
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
chatService.addChatListeners(this);
}
public void setRaised(boolean raised) {
if (raised) {
putValue(NAME, "* " + name);
} else {
putValue(NAME, name);
}
}
public boolean isRaised() {
return raised;
}
#Override
public void actionPerformed(ActionEvent evt) {
// Do what ever needs to be done
setRaised(!isRaised());
}
#Override
public void chatCleared() {
setRaised(false);
}
}
public class ClearAction extends AbstractAction {
private ChatService chatService;
public ClearAction(ChatService chatService) {
this.chatService = chatService;
putValue(NAME, "Clear");
}
#Override
public void actionPerformed(ActionEvent evt) {
chatService.clear();
}
}
}

Change variables in ChangeListener?

I am making a matching card program and I want to make sure the user only selects two card. So I have made Changelisteners and inside those changelisteners I would like to have an integer that would increase when there is a change in the state of the button. I have tried to use int, but it gave me the error where it says to use a final or effectively final. Is there some way that I can use an int inside of the changelistener method.
Here is an example:
card1Button.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
//int increases here
}
});
You have two basic choices to solve the immediate issue
You could...
Make the counter an instance field
public class MyAwesomeCardGame extends ... {
private int counter;
//...
public MyAwesomeCardGame() {
//...
card1Button.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
counter++;
}
});
}
}
You could...
Make the counter a instance of field of the anonymous class
public class MyAwesomeCardGame extends ... {
//...
public MyAwesomeCardGame() {
//...
card1Button.addChangeListener(new ChangeListener() {
private int counter;
public void stateChanged(ChangeEvent e) {
counter++;
}
});
}
}
Alternatively
Depending on what you're doing, you could use two ButtonGroups instead, it would ensure that only one button from each group can be selected at a time
You could change the scope of your variable
public class CardGame {
private int x;
public CardGame() {
card1Button.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
x++;
}
});
}
}
Here is an example that shows how you can do this when using a JCheckBox:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class CheckBoxGroup
{
private Set<GroupButtonModel> models = new HashSet<GroupButtonModel>();
private int groupSize;
public CheckBoxGroup(int groupSize)
{
this.groupSize = groupSize;
}
public void register(JCheckBox checkBox)
{
ButtonModel groupModel = new GroupButtonModel();
groupModel.setSelected ( checkBox.getModel().isSelected() );
checkBox.setModel( groupModel );
}
private class GroupButtonModel extends JToggleButton.ToggleButtonModel
{
#Override
public void setSelected(boolean selected)
{
if (!selected)
{
models.remove( this );
super.setSelected( selected );
return;
}
// Check number of currently selected check boxes
if (models.size() == groupSize)
{
System.out.println("Only " + groupSize + " items can be selected");
}
else
{
models.add( this );
super.setSelected( selected );
}
}
}
private static void createAndShowGUI()
{
JPanel panel = new JPanel();
CheckBoxGroup group = new CheckBoxGroup(3);
for (int i = 0; i < 10; i++)
{
JCheckBox checkBox = new JCheckBox( String.valueOf(i) );
panel.add( checkBox );
group.register( checkBox );
}
JFrame frame = new JFrame("Check Box Group");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
It adds a custom model to the component to check the number of currently selected components before allowing you to select another component.
It will also work for a JToggleButton. Just change the register(...) method to register toggle buttons.

Filtering a JList from text field input

I have a text field stick to a JList and I need to filter JList entities based on values entered by user in the text field.
For example, if the JList values are {"one","two","three"...} and the user types "three" in the text field, the JList should select the row with "three" value.
private void jTbandnoActionPerformed(java.awt.event.ActionEvent evt)
{
// TODO add your handling code here:
String tXT=jTbandno.getText();
jList2.collections.equal(tXt);
int index = jList2.getSelectedIndex();
JOptionPane.showMessageDialog(null, index);
}
Well you can create method to filter out the values by using
DefaultListModel#removeElement, and
DefaultListModel#addElement
Generally when dealing with data or components that use models, we should interact with the model and not the component. So you can have a method something like
private String[] defaultValues = {
"a", "ab", "abc", "abcd", "abcde", "abcdef", "absdefg", "abcdefgh",
"abcdefghi", "abcdefghij", "abcdefghijk", "abcdefghijkl", "abcdefghijklm",
};
public void filterModel(DefaultListModel<String> model, String filter) {
for (String s : defaultValues) {
if (!s.startsWith(filter)) {
if (model.contains(s)) {
model.removeElement(s);
}
} else {
if (!model.contains(s)) {
model.addElement(s);
}
}
}
}
When you need to filter, for example in the DocumentListener of a text field, just get the text of the text field, and call this method, passing in the model and the filter string. Something like
private void filter() {
String filter = field.getText();
filterModel((DefaultListModel<String>)jList.getModel(), filter);
}
Here's a complete example. You may want to implement some more complicated filtering such as including sorting into search filter. This is just a simple case example.
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class DynamicListDemo {
private String[] defaultValues = {
"a", "ab", "abc", "abcd", "abcde", "abcdef", "absdefg", "abcdefgh",
"abcdefghi", "abcdefghij", "abcdefghijk", "abcdefghijkl", "abcdefghijklm",
};
private JList jList = createJList();
public DynamicListDemo() {
JFrame frame = new JFrame();
frame.add(new JScrollPane(jList));
frame.add(createTextField(), BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JTextField createTextField() {
final JTextField field = new JTextField(15);
field.getDocument().addDocumentListener(new DocumentListener(){
#Override public void insertUpdate(DocumentEvent e) { filter(); }
#Override public void removeUpdate(DocumentEvent e) { filter(); }
#Override public void changedUpdate(DocumentEvent e) {}
private void filter() {
String filter = field.getText();
filterModel((DefaultListModel<String>)jList.getModel(), filter);
}
});
return field;
}
private JList createJList() {
JList list = new JList(createDefaultListModel());
list.setVisibleRowCount(6);
return list;
}
private ListModel<String> createDefaultListModel() {
DefaultListModel<String> model = new DefaultListModel<>();
for (String s : defaultValues) {
model.addElement(s);
}
return model;
}
public void filterModel(DefaultListModel<String> model, String filter) {
for (String s : defaultValues) {
if (!s.startsWith(filter)) {
if (model.contains(s)) {
model.removeElement(s);
}
} else {
if (!model.contains(s)) {
model.addElement(s);
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new DynamicListDemo();
}
});
}
}
UPDATE: As mKorbel pointed out the obvious, this may be a better use case for a JTable with a single column, where sorting and filtering is already part of the API.
See How to Use Tables: Sorting and Filtering

How to make part of a JTextField uneditable

I wanted to develop a console-like interface, similar to IDLE. That involved determining how to prevent a certain part of the text in a JTextField from being edited. For example:
>>> help
Where the ">>> " is uneditable. The caret must never move behind a certain position, and the text behind that position cannot be edited in any way.
I looked at NavigationFilter, but it doesn't seem to prevent keyboard driven manipulation of the caret.
This shows how to do it with a NavigationFilter:
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter
{
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
{
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
#Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.setDot(Math.max(dot, prefixLength), bias);
}
#Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() > prefixLength)
{
deletePrevious.actionPerformed( null );
}
}
}
private static void createAndShowUI()
{
JTextField textField = new JTextField("Prefix_", 20);
textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Spent a little while figuring this out, so I thought I would share my solution for anyone else with the same dilemma. I don't know if it's optimal, but it does seem to work.
It prevents the user from using backspace behind the postion n. It also moves the caret back to n for any other events, such as (illegally) changing the caret position with the arrow keys or mouse. Finally, it resets the text and caret position after a entry is processed.
EDIT: While I'm leaving this answer here for posterity, see the accepted answer for the best way to solve this problem.
JTextField in = new JTextField();
final String protectMe = ">>> "; //protect this text
final int n = protectMe.length();
in.setText(protectMe);
in.setCaretPosition(n);
in.addCaretListener(new CaretListener()
{
#Override
public void caretUpdate(CaretEvent e)
{
if (e.getDot() < n)
{
if (!(in.getText().length() < n))
in.getCaret().setDot(n);
}
}
});
in.addKeyListener(new KeyListener()
{
#Override
public void keyPressed(KeyEvent arg0)
{
if (in.getCaret().getDot() <= n)
{
in.setText(protectMe + in.getText().substring(n));
arg0.consume();
}
}
#Override
public void keyReleased(KeyEvent arg0){}
#Override
public void keyTyped(KeyEvent arg0){}
});
in.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
String input = in.getText().substring(n).trim();
//do something
in.setText(protectMe);
in.setCaretPosition(n);
}
});
As usual, let me know if there's anything I missed!

Categories