问题
If a selected index on a JList is clicked, I want it to de-select. In other words, clicking on the indices actually toggles their selection. Didn't look like this was supported, so I tried
list.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
java.awt.Point point = evt.getPoint();
int index = list.locationToIndex(point);
if (list.isSelectedIndex(index))
list.removeSelectionInterval(index, index);
}
});
The problem here is that this is being invoked after JList has already acted on the mouse event, so it deselects everything. So then I tried removing all of JList's MouseListeners, adding my own, and then adding all of the default listeners back. That didn't work, since JList would reselect the index after I had deselected it. Anyway, what I eventually came up with is
MouseListener[] mls = list.getMouseListeners();
for (MouseListener ml : mls)
list.removeMouseListener(ml);
list.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
java.awt.Point point = evt.getPoint();
final int index = list.locationToIndex(point);
if (list.isSelectedIndex(index))
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
list.removeSelectionInterval(index, index);
}
});
}
});
for (MouseListener ml : mls)
list.addMouseListener(ml);
... and that works. But I don't like it. Is there a better way?
回答1:
Looking at the Example "ListSelectionModel: Enabling Toggle Selection Mode" here: http://java.sun.com/products/jfc/tsc/tech_topics/jlist_1/jlist.html
I have modified it slightly for multi-select list boxes (changed setSelectionInterval to addSelectionInterval) and eliminated a problem with re-selection if you click to de-select and move your mouse while the mouse is down (moved the gestureStarted check for both add and remove).
objList.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
boolean gestureStarted = false;
@Override
public void setSelectionInterval(int index0, int index1) {
if(!gestureStarted){
if (isSelectedIndex(index0)) {
super.removeSelectionInterval(index0, index1);
} else {
super.addSelectionInterval(index0, index1);
}
}
gestureStarted = true;
}
@Override
public void setValueIsAdjusting(boolean isAdjusting) {
if (isAdjusting == false) {
gestureStarted = false;
}
}
});
回答2:
How about this?
import javax.swing.DefaultListSelectionModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
public class A {
public static void main(String[] args) {
JFrame f = new JFrame("Test");
final JList list = new JList(new String[] {"one","two","three","four"});
list.setSelectionModel(new DefaultListSelectionModel(){
@Override
public void setSelectionInterval(int index0, int index1) {
if (index0==index1) {
if (isSelectedIndex(index0)) {
removeSelectionInterval(index0, index0);
return;
}
}
super.setSelectionInterval(index0, index1);
}
@Override
public void addSelectionInterval(int index0, int index1) {
if (index0==index1) {
if (isSelectedIndex(index0)) {
removeSelectionInterval(index0, index0);
return;
}
super.addSelectionInterval(index0, index1);
}
}
});
f.getContentPane().add(list);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
It works but note one side effect... If you set the mode to multi selction like this for instance:
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION );
you cannot select multiple objects via mouse drag. Ctrl (or shift) click works. I'm sure it can be fixed but i assume you asked this for single selection lists... If not modify your question and we can start thinking for solutions to the multiple selection problem.
回答3:
I know that this question already has an accepted answer, but I thought that I'd expand a bit, since I ended up stuck on this task for a few hours.
I was trying to implement a click-to-deselect action for selected items, but my list implementation requires the use of Single-Selection mode, specified by
JList jlist = new JList(new DefaultListModel());
jlist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Unfortunately, this led to exceptions and redundant calls for many of the solutions for the click-to-deselect problem on many SO questions, including this answer by FuryComptuers above. Due to code in DefaultListSelectionModel.class
, specifically in the addSelectionInterval(int index0, int index1)
and removeSelectionInterval(int index0, int index1)
methods, which calls back to the setSelectionInterval(int index0, int index1)
method, caused a circular call that leads to (obviously) exceptions. This "problem" code can be seen below.
// If we only allow a single selection, channel through
// setSelectionInterval() to enforce the rule.
if (getSelectionMode() == SINGLE_SELECTION) {
setSelectionInterval(index0, index1);
return;
}
Sawas Dalkitsis' answer solved this problem, but would still act weird when dragging the mouse on a selected item (the selected item will select and de-select itself over and over while dragging the mouse). This wouldn't seem like a problem, but (apparently) I have a shaky hand, and minor mouse movements while clicked resulted in unwanted behavior. I combined Sawas Dalkitsis answer and FuryComptuers's answer to get the following code, which seems to work as desired:
JList jlist = new JList(new DefaultListModel());
jList.setSelectionModel(new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
boolean gestureStarted = false;
@Override
public void setSelectionInterval(int index0, int index1) {
if(!gestureStarted){
if (index0==index1) {
if (isSelectedIndex(index0)) {
removeSelectionInterval(index0, index0);
return;
}
}
super.setSelectionInterval(index0, index1);
}
gestureStarted = true;
}
@Override
public void addSelectionInterval(int index0, int index1) {
if (index0==index1) {
if (isSelectedIndex(index0)) {
removeSelectionInterval(index0, index0);
return;
}
super.addSelectionInterval(index0, index1);
}
}
@Override
public void setValueIsAdjusting(boolean isAdjusting) {
if (isAdjusting == false) {
gestureStarted = false;
}
}
});
jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Note: I didn't check this against the ListSelectionModel.SINGLE_INTERVAL_SELECTION
, as Sawas Dalkitsis did, so use caution if implementing it in that case.
回答4:
You could always the ListSelectionListener instead of deciphering the point clicked and then translating it to the item selected.
http://java.sun.com/docs/books/tutorial/uiswing/examples/events/index.html#ListSelectionDemo
http://java.sun.com/docs/books/tutorial/uiswing/events/listselectionlistener.html
http://java.sun.com/docs/books/tutorial/uiswing/examples/events/ListSelectionDemoProject/src/events/ListSelectionDemo.java
In the above link for java file there is an implementation which can be easily improved to do "deselection" :)
回答5:
I extended FuryComptuers answer to support multiple selection, and fixed an issue where setSelectionInterval
didn't work if it was called directly.
public class ToggleableListSelectionModel extends DefaultListSelectionModel {
private static final long serialVersionUID = 1L;
private boolean mGestureStarted;
@Override
public void setSelectionInterval(int index0, int index1) {
// Toggle only one element while the user is dragging the mouse
if (!mGestureStarted) {
if (isSelectedIndex(index0)) {
super.removeSelectionInterval(index0, index1);
} else {
if (getSelectionMode() == SINGLE_SELECTION) {
super.setSelectionInterval(index0, index1);
} else {
super.addSelectionInterval(index0, index1);
}
}
}
// Disable toggling till the adjusting is over, or keep it
// enabled in case setSelectionInterval was called directly.
mGestureStarted = getValueIsAdjusting();
}
@Override
public void setValueIsAdjusting(boolean isAdjusting) {
super.setValueIsAdjusting(isAdjusting);
if (isAdjusting == false) {
// Enable toggling
mGestureStarted = false;
}
}
}
回答6:
Nick Dandoulakis' answer didn't quite work for me when selecting multiple items at once using a mouse click while pressing Shift.
The following ListSelectionModel
behaves as I'd expect it when selecting items using mouseclicks with Shift or Ctrl.
Also, holding down Shift + Ctrl and pressing either → or ← selects items the way I want it to.
public static class ToggleableListSelectionModel extends DefaultListSelectionModel {
private static final long serialVersionUID = 1L;
@Override
public void setSelectionInterval(int startIndex, int endIndex) {
if (startIndex == endIndex) {
if (multipleItemsAreCurrentlySelected()) {
clearSelection();
}
if (isSelectedIndex(startIndex)) {
clearSelection();
}
else {
super.setSelectionInterval(startIndex, endIndex);
}
}
// User selected multiple items
else {
super.setSelectionInterval(startIndex, endIndex);
}
}
private boolean multipleItemsCurrentlyAreSelected() {
return getMinSelectionIndex() != getMaxSelectionIndex();
}
}
来源:https://stackoverflow.com/questions/2528344/jlist-deselect-when-clicking-an-already-selected-item