i coded a little annotator in Java with a simple GUI based on Swing but i faced with a problem that is freaking me out.
The problem is: i have a jlist and two jbuttons modifying such jlist, the two buttons have the SAME listener but they don’t work the same.
All it’s supposed to work like this: once you select a row in the jlist you can tag it as ON TOPIC or OFF TOPIC (with the two buttons), the row changes color and then selects the next row but; even if the selection is on the right row (the next one) it highlights the next just for the OFF Topic button, why?
Here’s the code:
public class TweetsAnnotator {
static Boolean[] annotations = null;
@SuppressWarnings("rawtypes")
static JList jl;
static JButton offbutton = new JButton("OFF Topic");
static JButton onbutton = new JButton("ON Topic");
static String file = "inception_TweetList";
public TweetsAnnotator() {
}
/**
* @param args
* @throws IOException
* @throws FileNotFoundException
* @throws ClassNotFoundException
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// Read Tweets from file
ObjectInputStream load = new ObjectInputStream(new FileInputStream(file));
ArrayList<String> list = (ArrayList<String>) load.readObject();
load.close();
System.out.println(list.size() + " Tweets read from: " + file);
// Check and read annotations
File fileannot = new File(file + "Annotations");
if (fileannot.exists()) {
System.out.println("esiste, leggo");
ObjectInputStream loadannot = new ObjectInputStream(new FileInputStream(file + "Annotations"));
annotations = (Boolean[]) loadannot.readObject();
loadannot.close();
} else {
System.out.println("non esiste, creo poi leggo");
ObjectOutputStream save = new ObjectOutputStream(new FileOutputStream(file + "Annotations"));
Boolean[] creatannotations = new Boolean[list.size()];
for (int i=0; i<list.size(); i++) {
creatannotations[i] = (Boolean) null;
}
save.writeObject(creatannotations);
save.close();
ObjectInputStream loadannot = new ObjectInputStream(new FileInputStream(file + "Annotations"));
annotations = (Boolean[]) loadannot.readObject();
loadannot.close();
}
System.out.println(annotations.length + " Annotations loaded");
// Buttons
offbutton.setActionCommand("off");
offbutton.addActionListener(new ButtonListener());
offbutton.setEnabled(false);
onbutton.setActionCommand("on");
onbutton.addActionListener(new ButtonListener());
onbutton.setEnabled(false);
// ButtonPanel
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
buttonPane.add(onbutton);
buttonPane.add(offbutton);
// JList
jl = new JList((Object[])list.toArray());
jl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jl.setLayoutOrientation(JList.VERTICAL);
jl.setVisibleRowCount(-1);
jl.setCellRenderer(new MyCellRenderer());
ListSelectionListener listSelectionListener = new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (jl.getSelectedIndex() == -1) {
//No selection, disable buttons.
onbutton.setEnabled(false);
offbutton.setEnabled(false);
} else {
//Selection, enable buttons.
onbutton.setEnabled(true);
offbutton.setEnabled(true);
}
}
}
};
jl.addListSelectionListener(listSelectionListener);
// JScrollPane
JScrollPane listScroller = new JScrollPane(jl);
// JFrame
JFrame frame = new JFrame(file);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setExtendedState(frame.getExtendedState()|JFrame.MAXIMIZED_BOTH);
frame.addWindowListener(new WindowCloseHandler());
// Add and show
frame.getContentPane().add(listScroller, BorderLayout.CENTER);
frame.getContentPane().add(buttonPane, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
private static class MyCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;
@SuppressWarnings("rawtypes")
public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus ) {
Component c = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
if ( annotations[index] == null ) {
c.setBackground( Color.white );
}
else if (annotations[index] == true) {
c.setBackground( Color.green );
} else {
c.setBackground( Color.red);
}
return c;
}
}
private static class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
int ind = jl.getSelectedIndex() +1;
if (e.getActionCommand().equals("on")) {
System.out.println("ON");
annotations[jl.getSelectedIndex()] = true;
}
if (e.getActionCommand().equals("off")) {
System.out.println("OFF");
annotations[jl.getSelectedIndex()] = false;
}
jl.clearSelection();
jl.setSelectedIndex(ind);
}
}
private static class WindowCloseHandler extends WindowAdapter {
public void windowClosing(WindowEvent evt) {
ObjectOutputStream save = null;
try {
save = new ObjectOutputStream(new FileOutputStream(file + "Annotations"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
save.writeObject(annotations);
} catch (IOException e) {
e.printStackTrace();
}
try {
save.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Saved.");
}
}
}
Even though jl.requestFocus(); works fine, i tried another thing that make it works: i swapped these two lines
buttonPane.add(onbutton);
buttonPane.add(offbutton);
but, why?
Sorry if i ask again but it’s really strange, isn’t it?
In your cell render, try this instead
For something a “little” extended, you could also try
Basically, what you are doing is changing the selection highlight color, hiding the selection. As to why you did get a paint artifact that “seemed” to highlight the selected row is still a slight mystery to me 😛
UPDATED with Knowing why
The paint artifact you are sometimes getting is a result of the
hasFocusparameter of the cell renderer, painting a focus rectangle.Now, if you want to keep your existing cell renderer, then try this in the ActionEvent
From what I can tell, it seems to a slight qwerk with the repaint manager. If I added
jl.repaint()into you existing code (under thesetSelectedIndexcall) I can get it to never paint the focus rectangle 😛