2009-02-19 15 views
8

Mam wiele niestandardowych edytorów dla JTable i jest to mało powiedziane stwierdzenie, że brakuje użyteczności, szczególnie w odniesieniu do edycji za pomocą klawiatury.JTable ze złożonym edytorem

Głównym powodem jest to, że moi redaktorzy są zawsze tworzone z podobną (choć często bardziej skomplikowanej sytuacji) to:

@Override 
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 
    JPanel container = new JPanel(); 
    container.setLayout(new BorderLayout()); 
    container.add(field, BorderLayout.CENTER); 
    field.setText((String) value); 
    container.add(new JButton("..."), BorderLayout.EAST); 
    return container; 
} 

tj panel z więcej niż jednego składnika wewnątrz. Faktyczny edytor tekstu jest potomkiem komponentu zwracanego jako edytor. Odkładając problemy na bok, z tego co wiem, JTable skupia komponent zwracany przez metodę getTableCellEditorComponent, więc po naciśnięciu klawisza z podświetloną komórką przechodzi fokus, a klawisz naciska na panel, myśląc, że to jest redaktor.
Czy mimo to mogę poinformować JTable, że "prawdziwym" edytorem jest JTextfield? Dodanie hacky requestFocusInWindow na właściwym komponencie jest niewystarczające, ponieważ naciśnięcie klawisza nie zostanie przekazane.

Odpowiedz

4

Sprawdź powiązane artykuły here i here.

Kolejna good article o ogólnej edycji JTable.

+0

Tak, dziękuję. Właśnie otrzymałem podobne porady na forum słońca. http://forums.sun.com/thread.jspa?threadID=5368525 –

2

Jeśli poprawnie przeczytam twoje pytanie, chcesz, aby użytkownik mógł natychmiast wpisać komórkę, bez wcześniejszego aktywowania edytora komórek, tzn. Chcesz, aby naciśnięcie klawisza aktywowało komórkę jako pierwszy znak wprowadzony do tekstu pole.

Moja pierwsza próba polegała na dodaniu właściwości propertyChangeListener do właściwości focusOwner menedżera KeyboardFocusManager, aby zauważyć, że fokus nigdy nie opuszcza tabeli JTable. Pewnie też w to wpadłeś. Czas na plan B.

Mam do czynienia z tym "pierwszym naciśnięciem klawisza", dodając KeyListener do tabeli, która rejestruje ostatnie KeyEvent dla metody keyPressed() w polu instancji. Metoda getTableCellEditorComponent() odczytuje stamtąd. Potrzebowałem również tego hackowanego requestFocusInWindow() zadzwonić wspomnieć, jeśli użytkownik ma nadal pisać dowolne znaki po pierwszym.

Dla mojej przykładowej aplikacji utworzyłem podklasę JTable, która dodaje do siebie KeyListener. Lepiej jest sprawić, aby instancja CellEditor implementowała KeyListener i dodawała go do zwykłej JTable, ale zostawię to tobie.

Oto fragment kodu, jak ja go modyfikować:

@Override 
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 
    JPanel container = new JPanel(); 
    container.setLayout(new BorderLayout()); 
    container.add(field, BorderLayout.CENTER); 

    // Will want to add an instanceof check as well as a check on Character.isLetterOrDigit(char). 
    char keypressed = ((StickyKeypressTable)table).getLastKeyPressed(); 
    field.setText(String.valueOf(keypressed)); 

    container.add(new JButton("..."), BorderLayout.EAST); 

    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      // This needs to be in an invokeLater() to work properly 
      field.requestFocusInWindow(); 
     } 
    }); 
    return container; 
} 

Jeśli chodzi o złośliwość idzie to siedzi gdzieś tam z Vogon Poezji, ale powinno rozwiązać natychmiastowej problem.

+0

Dzięki za odpowiedź. Efektywnie to robimy (na różne sposoby dla różnych edytorów) od jakiegoś czasu i wpędza nas w różnego rodzaju kłopoty (np. Edytor aktywowany jest za pomocą myszy, a słuchacz klawiszy w tabeli wciąż ma lastKeyPressed). Coś szuka lepszego rozwiązania. –

+0

Zajmowałem się tym nieco dalej i ostatecznie skończyłem w klasie wewnętrznej Handler w BasicTableUI. Jednak nie ma jeszcze próbek kodu. – Barend

0

Naprawiłem coś podobnego w 2 etapach

pierwsze zastępują na editCellAt z JTable i nazywają requestFocus po przygotowaniu edytora:

public boolean editCellAt(int row, int column, EventObject e) 
{ 
    if (cellEditor != null && !cellEditor.stopCellEditing()) 
    { 
    return false; 
    } 

    if (row < 0 || row >= getRowCount() || 
     column < 0 || column >= getColumnCount()) 
    { 
    return false; 
    } 

    if (!isCellEditable(row, column)) 
    return false; 

    TableCellEditor editor=getCellEditor(row, column); 
    if (editor != null && editor.isCellEditable(e)) 
    { 
    editorComp=prepareEditor(editor, row, column); 
    if (editorComp == null) 
     { 
     removeEditor(); 
     return false; 
     } 
    //aangepast 
    Rectangle rect=getCellRect(row, column, false); 
    if (datamodel_.useAdaptedEditorRect()) 
     rect=datamodel_.changeRectangle(rect, editorComp); 
    editorComp.setBounds(rect); 
    add(editorComp); 
    editorComp.validate(); 

    setCellEditor(editor); 
    setEditingRow(row); 
    setEditingColumn(column); 
    editor.addCellEditorListener(this); 
    //NEXT LINE ADDED 
    editorComp.requestFocus(); 
    return true; 
    } 
    return false; 
} 

Następnie dodatkowego obciążenia requestFocus ze swoimi JPanel i upewnić się, że TextField umieścić jak editorComponent:

public class EditorPanel extends JPanel { 
    JComponent editorComponent; 

    public boolean isRequestFocusEnabled() 
    { 
    return true; 
    } 

    public void requestFocus() 
    { 
    editorComponent.requestFocus(); 
    } 
} 

zawsze można chwycić keyEvent i ustawić go sobie pytanie:

AWTEvent event = EventQueue.getCurrentEvent(); 
if (event instanceof KeyEvent) 
    { 
    char newSelection = ((KeyEvent) event).getKeyChar(); 
    int keyCode = ((KeyEvent) event).getKeyCode(); 
    editorComponent.requestFocus(); 
    if (editorComponent instanceof JTextField) 
    { 
    if ((newSelection >= (char) FIRST_ALLOWED_CHAR) && (newSelection != (char) LAST_ALLOWED_CHAR)) //comes from DefaultKeyTypedAction 
     ((JTextField) editorComponent).setText(Character.toString(newSelection)); 
    if (keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE) 
     ((JTextField) editorComponent).setText("");   
    } 
    } 
else 
    editorComponent.requestFocus(); 
+0

Tak, próbowałem już tego podejścia. Próbowałem go na moim kodzie i, jak się spodziewałem, pierwsze naciśnięcie klawisza, które aktywuje edytor, nie jest przekazywane (tak jak ma to miejsce, gdy używasz prostego pola edycyjnego), tzn. Jeśli klikniesz zakładkę i klikniesz "123 "only" 23 "pojawia się w komponencie. –

0

Myślę, że to rozwiązałem.
Prawdę mówiąc, nie wiem, co rozwiązało problem, ponieważ używam niestandardowego edytora, niestandardowego renderera i innych rzeczy ...

Po zaznaczeniu komórki i naciśnięciu klawisza "abc" , 3 litery pojawiają się na ekranie (w tym przypadku komórka).

grid.addKeyListener(new KeyAdapter() { 
    public void keyTyped(KeyEvent ke) { 
     int l = grid.getSelectedRow(); 
     int c = grid.getSelectedColumn(); 
     grid.editCellAt(l, c); 
    } 
}); 

Cóż ... Próbowałem ... =)
(nie wiem, czy to to samo, bo mój JTable wykorzystuje JTextField i JComboBox jako redaktorzy).

0

Miałem bardzo podobny problem. W moim przypadku miałem kompleks TableCellEditor, który składa się z JSpinner i kilku innych komponentów. Problem polegał na tym, że po uruchomieniu edytora komórek chciałem przenieść fokus na jego wewnętrzny komponent. Naprawiłem to, dzwoniąc pod numer panel.transferFocusDownCycle(), ale to z kolei spowodowało, że zdarzenia na klawiaturze przestały działać - kiedy mój wewnętrzny komponent się skupił i nacisnąłem klawisz, oczekiwałem, że ten komponent przechwyci to zdarzenie i zmieni jego wartość. Zamiast tego tabela zmieniła fokus w wierszu na powyższy ... Naprawiłem to, dodając KeyListener i wysyłając wszystkie kluczowe zdarzenia bezpośrednio do komponentu wewnętrznego.

To jest klasa opakowania na podstawie JPanel Napisałem, aby ułatwić sobie życie.

public class ContainerPanel extends JPanel implements KeyListener, FocusListener { 

    private JComponent component = null; 

    public ContainerPanel(JComponent component) { 
     this.component = component; 
     addKeyListener(this); 
     addFocusListener(this); 
     setFocusCycleRoot(true); 
     setFocusTraversalPolicy(new ContainerOrderFocusTraversalPolicy()); 
     add(component); 
    } 

    @Override 
    public void keyTyped(KeyEvent e) { 
     component.dispatchEvent(e); 
    } 

    @Override 
    public void keyPressed(KeyEvent e) { 
     component.dispatchEvent(e); 
    } 

    @Override 
    public void keyReleased(KeyEvent e) { 
     component.dispatchEvent(e); 
    } 

    @Override 
    public void focusGained(FocusEvent e) { 
     component.transferFocusDownCycle(); 
    } 

    @Override 
    public void focusLost(FocusEvent e) { 
    } 
}