2012-05-10 17 views
6

Trong thực tế, tôi biết làm thế nào để thực hiện sử dụng CTRL +Z (Undo) và CTRL +Y (Redo) với một JTextField. Nhưng tôi có hàng trăm thành phần văn bản trong ứng dụng Swing của tôi, vì vậy có cách áp dụng cho tất cả thành phần văn bản trong ứng dụng của tôi, vì vậy khi tôi nhấp vào CTRL + Z trong bất kỳ Thành phần văn bản nào, nó sẽ hoàn tác mục nhập cuối cùng trong Trường đó?Làm thế nào để sử dụng Ctrl + Z và Ctrl + Y với tất cả các thành phần văn bản?

Tôi đã cố gắng triển khai nó trong EventQueue, nhưng nó không hoạt động!

+5

Đó sẽ là hành vi thực sự kỳ lạ đối với người dùng. Bạn thực hiện sửa đổi cho một trường, nhấn hoàn tác và đột nhiên tất cả các trường của bạn được hoàn nguyên về giá trị trước đó ... Tôi sẽ không hài lòng với điều đó – Robin

+0

Bạn có thể nhận được trợ giúp tốt hơn sớm hơn nếu bạn hiển thị một số mã bạn đã làm bạn vừa mắc một sai lầm đơn giản hay gì đó. PS: Tôi đã thêm thẻ 'swingx'. Tôi củng cố những kẻ này có thể có một thành phần sẵn sàng cho loại vấn đề này. +1 Tôi rất thú vị trong các giải pháp này. – Boro

+0

@Robin Tôi đồng ý với bạn điều này có thể là một bất ngờ khó chịu cho người dùng nhưng tôi vẫn muốn biết làm thế nào để đi về nó :) Tôi tự hỏi nếu ai đó đã làm một cái gì đó của loại này. Chỉ cần một mặc dù nó sẽ được đơn giản như là để gửi cùng một sự kiện cho tất cả các thành phần bạn muốn thay đổi? – Boro

Trả lời

2
  • bạn có thể gel danh sách keybindings built_in short_cuts thực hiện trong thông báo

  • của API bạn phải kiểm tra hoặc chuẩn bị mã của bạn cho tất cả Look accesible và cảm thấy

  • bạn có thể nhận built_in keybindings short_cuts và thay thế như bạn mong đợi, Hệ thống ClipBoard thường được sửa đổi,

  • chưa bao giờ thử nhưng trên diễn đàn này bạn có thể bật ra thông tin giảm dần Làm thế nào để thay đổi, thay thế KeyBi những phát hiện short_cuts

danh sách keybindings built_in short_cuts

import java.awt.*; 
import java.awt.event.*; 
import java.io.*; 
import java.util.*; 
import javax.swing.*; 
import javax.swing.border.*; 
import javax.swing.table.*; 
import javax.swing.filechooser.*; 

public class KeyBindings implements ItemListener { 

    private static final String PACKAGE = "javax.swing."; 
    private static final String[] COLUMN_NAMES = {"Action", "When Focused", "When In Focused Window", "When Ancestor"}; 
    private static String selectedItem; 
    private JComponent contentPane; 
    private JMenuBar menuBar; 
    private JTable table; 
    private JComboBox comboBox; 
    private Hashtable<String, DefaultTableModel> models; 

    /* 
    * Constructor 
    */ 
    public KeyBindings() { 
     models = new Hashtable<String, DefaultTableModel>(); 
     contentPane = new JPanel(new BorderLayout()); 
     contentPane.add(buildNorthComponent(), BorderLayout.NORTH); 
     contentPane.add(buildCenterComponent(), BorderLayout.CENTER); 
     resetComponents(); 
    } 

    /* 
    * The content pane should be added to a high level container 
    */ 
    public JComponent getContentPane() { 
     return contentPane; 
    } 

    /* 
    * A menu can also be added which provides the ability to switch 
    * between different LAF's. 
    */ 
    public JMenuBar getMenuBar() { 
     if (menuBar == null) { 
      menuBar = createMenuBar(); 
     } 
     return menuBar; 
    } 

    /* 
    * This panel is added to the North of the content pane 
    */ 
    private JComponent buildNorthComponent() { 
     comboBox = new JComboBox(); 
     JLabel label = new JLabel("Select Component:"); 
     label.setDisplayedMnemonic('S'); 
     label.setLabelFor(comboBox); 
     JPanel panel = new JPanel(); 
     panel.setBorder(new EmptyBorder(15, 0, 15, 0)); 
     panel.add(label); 
     panel.add(comboBox); 
     return panel; 
    } 

    /* 
    * Check the key name to see if it is the UI property 
    */ 
    private String checkForUIKey(String key) { 
     if (key.endsWith("UI") && key.indexOf('.') == -1) { 
      String componentName = key.substring(0, key.length() - 2);// Ignore these components 
      if (componentName.equals("PopupMenuSeparator") || componentName.equals("ToolBarSeparator") || componentName.equals("DesktopIcon")) { 
       return null; 
      } else { 
       return componentName; 
      } 
     } 
     return null; 
    } 

    /* 
    ** Build the emtpy table to be added in the Center 
    */ 
    private JComponent buildCenterComponent() { 
     DefaultTableModel model = new DefaultTableModel(COLUMN_NAMES, 0); 
     table = new JTable(model) { 

      private static final long serialVersionUID = 1L; 

      @Override 
      public boolean isCellEditable(int row, int column) { 
       return false; 
      } 
     }; 
     table.setAutoCreateColumnsFromModel(false); 
     table.getColumnModel().getColumn(0).setPreferredWidth(200); 
     table.getColumnModel().getColumn(1).setPreferredWidth(200); 
     table.getColumnModel().getColumn(2).setPreferredWidth(200); 
     table.getColumnModel().getColumn(3).setPreferredWidth(200); 
     Dimension d = table.getPreferredSize(); 
     d.height = 350; 
     table.setPreferredScrollableViewportSize(d); 
     table.getTableHeader().setFocusable(true); 
     return new JScrollPane(table); 
    } 

    /* 
    * When the LAF is changed we need to reset all the items 
    */ 
    private void resetComponents() { 
     models.clear(); 
     ((DefaultTableModel) table.getModel()).setRowCount(0); 
     Vector<String> comboBoxItems = new Vector<String>(50);//  buildItemsMap(); 
     UIDefaults defaults = UIManager.getLookAndFeelDefaults(); 
     for (Object key : defaults.keySet()) { // All Swing components will have a UI property. 
      String componentName = checkForUIKey(key.toString()); 
      if (componentName != null) { 
       comboBoxItems.add(componentName); 
      } 
     } 
     Collections.sort(comboBoxItems); 
     comboBox.removeItemListener(this); 
     comboBox.setModel(new DefaultComboBoxModel(comboBoxItems)); 
     comboBox.setSelectedIndex(-1); 
     comboBox.addItemListener(this); 
     comboBox.requestFocusInWindow(); 
     if (selectedItem != null) { 
      comboBox.setSelectedItem(selectedItem); 
     } 
    } 

    /** 
    * Create menu bar 
    */ 
    private JMenuBar createMenuBar() { 
     JMenuBar menuBar1 = new JMenuBar(); 
     menuBar1.add(createFileMenu()); 
     menuBar1.add(createLAFMenu()); 
     return menuBar1; 
    } 

    /** 
    * Create menu items for the Application menu 
    */ 
    private JMenu createFileMenu() { 
     JMenu menu = new JMenu("Application"); 
     menu.setMnemonic('A'); 
     menu.addSeparator(); 
     menu.add(new ExitAction()); 
     return menu; 
    } 

    /** 
    * Create menu items for the Look & Feel menu 
    */ 
    private JMenu createLAFMenu() { 
     ButtonGroup bg = new ButtonGroup(); 
     JMenu menu = new JMenu("Look & Feel"); 
     menu.setMnemonic('L'); 
     String lafId = UIManager.getLookAndFeel().getID(); 
     UIManager.LookAndFeelInfo[] lafInfo = UIManager.getInstalledLookAndFeels(); 
     for (int i = 0; i < lafInfo.length; i++) { 
      String laf = lafInfo[i].getClassName(); 
      String name = lafInfo[i].getName(); 
      Action action = new ChangeLookAndFeelAction(laf, name); 
      JRadioButtonMenuItem mi = new JRadioButtonMenuItem(action); 
      menu.add(mi); 
      bg.add(mi); 
      if (name.equals(lafId)) { 
       mi.setSelected(true); 
      } 
     } 
     return menu; 
    } 

    /* 
    * Implement the ItemListener interface 
    */ 
    @Override 
    public void itemStateChanged(ItemEvent e) { 
     String componentName = (String) e.getItem(); 
     changeTableModel(getClassName(componentName)); 
     selectedItem = componentName; 
    } 

    /* 
    * Use the component name to build the class name 
    */ 
    private String getClassName(String componentName) { 
     if (componentName.equals("TableHeader")) {// The table header is in a child package 
      return PACKAGE + "table.JTableHeader"; 
     } else { 
      return PACKAGE + "J" + componentName; 
     } 
    } 

    /* 
    * Change the TabelModel in the table for the selected component 
    */ 
    private void changeTableModel(String className) { 
     DefaultTableModel model = models.get(className); // Check if we have already built the table model for this component 
     if (model != null) { 
      table.setModel(model); 
      return; 
     } 
     model = new DefaultTableModel(COLUMN_NAMES, 0); // Create an empty table to start with 
     table.setModel(model); 
     models.put(className, model); 
     JComponent component = null; // Create an instance of the component so we can get the default Action map and Input maps 
     try { 
      if (className.endsWith("JFileChooser")) {// Hack so I don't have to sign the jar file for usage in Java Webstart 
       component = new JFileChooser(new DummyFileSystemView()); 
      } else { 
       Object o = Class.forName(className).newInstance(); 
       component = (JComponent) o; 
      } 
     } catch (Exception e) { 
      Object[] row = {e.toString(), "", "", ""}; 
      model.addRow(row); 
      return; 
     } 
     ActionMap actionMap = component.getActionMap(); // Not all components have Actions defined 
     Object[] keys = actionMap.allKeys(); 
     if (keys == null) { 
      Object[] row = {"No actions found", "", "", ""}; 
      model.addRow(row); 
      return; 
     } 
     // In some ActionMaps a key of type Object is found (I have no idea why) 
     // which causes a ClassCastExcption when sorting so we will ignore it 
     // by converting that entry to the empty string 
     for (int i = 0; i < keys.length; i++) { 
      Object key = keys[i]; 
      if (key instanceof String) { 
       continue; 
      } else { 
       keys[i] = ""; 
      } 
     } 
     Arrays.sort(keys); 
     for (int i = 0; i < keys.length; i++) { // Create a new row in the model for every Action found 
      Object key = keys[i]; 
      if (key != "") { 
       Object[] row = {key, "", "", ""}; 
       model.addRow(row); 
      } 
     } 
     // Now check each InputMap to see if a KeyStroke is bound the the Action 
     updateModelForInputMap(model, 1, component.getInputMap(JComponent.WHEN_FOCUSED)); 
     updateModelForInputMap(model, 2, component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)); 
     updateModelForInputMap(model, 3, component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)); 
    } 

    /* 
    * The model is potentially update for each of the 3 different InputMaps 
    */ 
    private void updateModelForInputMap(TableModel model, int column, InputMap inputMap) { 
     if (inputMap == null) { 
      return; 
     } 
     KeyStroke[] keys = inputMap.allKeys(); 
     if (keys == null) { 
      return; 
     } 
     // The InputMap is keyed by KeyStroke, however we want to be able to 
     // access the action names that are bound to a KeyStroke so we will create 
     // a Hashtble that is keyed by action name. 
     // Note that multiple KeyStrokes can be bound to the same action name. 
     Hashtable<Object, String> actions = new Hashtable<Object, String>(keys.length); 
     for (int i = 0; i < keys.length; i++) { 
      KeyStroke key = keys[i]; 
      Object actionName = inputMap.get(key); 
      Object value = actions.get(actionName); 
      if (value == null) { 
       actions.put(actionName, key.toString().replace("pressed ", "")); 
      } else { 
       actions.put(actionName, value + ", " + key.toString().replace("pressed ", "")); 
      } 
     } 
     for (int i = 0; i < model.getRowCount(); i++) { 
      // Now we can update the model for those actions that have KeyStrokes mapped to them 
      String o = actions.get(model.getValueAt(i, 0)); 
      if (o != null) { 
       model.setValueAt(o.toString(), i, column); 
      } 
     } 
    } 

    /* 
    * Change the LAF and recreate the UIManagerDefaults so that the properties 
    * of the new LAF are correctly displayed. 
    */ 
    private class ChangeLookAndFeelAction extends AbstractAction { 

     private static final long serialVersionUID = 1L; 
     private String laf; 

     protected ChangeLookAndFeelAction(String laf, String name) { 
      this.laf = laf; 
      putValue(Action.NAME, name); 
      putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      try { 
       JMenuItem mi = (JMenuItem) e.getSource(); 
       JPopupMenu popup = (JPopupMenu) mi.getParent(); 
       JRootPane rootPane = SwingUtilities.getRootPane(popup.getInvoker()); 
       Component c = rootPane.getContentPane().getComponent(0); 
       rootPane.getContentPane().remove(c); 
       UIManager.setLookAndFeel(laf); 
       KeyBindings bindings = new KeyBindings(); 
       rootPane.getContentPane().add(bindings.getContentPane()); 
       SwingUtilities.updateComponentTreeUI(rootPane); 
       rootPane.requestFocusInWindow(); 
      } catch (Exception ex) { 
       System.out.println("Failed loading L&F: " + laf); 
       System.out.println(ex); 
      } 
     } 
    } 

    private class ExitAction extends AbstractAction { 

     private static final long serialVersionUID = 1L; 

     public ExitAction() { 
      putValue(Action.NAME, "Exit"); 
      putValue(Action.SHORT_DESCRIPTION, getValue(Action.NAME)); 
      putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X)); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      System.exit(0); 
     } 
    } 

    private class DummyFileSystemView extends FileSystemView { 

     @Override 
     public File createNewFolder(File containingDir) { 
      return null; 
     } 

     @Override 
     public File getDefaultDirectory() { 
      return null; 
     } 

     @Override 
     public File getHomeDirectory() { 
      return null; 
     } 
    } 

    private static void createAndShowGUI() { 
     KeyBindings application = new KeyBindings(); 
     JFrame.setDefaultLookAndFeelDecorated(true); 
     JFrame frame = new JFrame("Key Bindings"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setJMenuBar(application.getMenuBar()); 
     frame.getContentPane().add(application.getContentPane()); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     //UIManager.put("swing.boldMetal", Boolean.FALSE); 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       createAndShowGUI(); 
      } 
     }); 
    } 
} 
1

Nếu bạn muốn chạy "sự kiện toàn cầu" trên ứng dụng của bạn không phân biệt nơi bạn trọng tâm hiện nay của bạn, bạn sẽ cần phải làm việc với các KeyboardFocusManager:

KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 
    kfm.addKeyEventDispatcher(new KeyEventDispatcher() { 

     @Override 
     public boolean dispatchKeyEvent(KeyEvent e) { 
      // do your stuff here 
      return done; 
     } 
    }); 

Hope this helps.

5

Để làm cho tất cả các thành phần văn bản của bạn "không thể nén", bạn chỉ có thể tạo ra chúng bằng lớp con riêng của bạn như:

public class MyTextField extends JTextField { 
    public MyTextField() { 
     final UndoManager undoMgr = new UndoManager(); 

     // Add listener for undoable events 
     getDocument().addUndoableEditListener(new UndoableEditListener() { 
      public void undoableEditHappened(UndoableEditEvent pEvt) { 
       undoMgr.addEdit(pEvt.getEdit()); 
      } 
     }); 

     // Add undo/redo actions 
     getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) { 
      public void actionPerformed(ActionEvent pEvt) { 
       try { 
        if (undoMgr.canUndo()) { 
         undoMgr.undo(); 
        } 
       } catch (CannotUndoException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
     getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) { 
      public void actionPerformed(ActionEvent pEvt) { 
       try { 
        if (undoMgr.canRedo()) { 
         undoMgr.redo(); 
        } 
       } catch (CannotRedoException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

     // Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y) 
     getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), 
      UNDO_ACTION); 
     getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), 
      REDO_ACTION); 
    } 
} 

Sau đó, thay vì tạo JTextField s, hãy tạo MyTextField s. Giới hạn là bạn cũng có thể muốn tạo một lớp con khác cho JTextArea và cứ thế cho các số khác JTextComponent s. Vì vậy, nó cũng có thể sử dụng một phương pháp hữu ích để thêm Undo/Redo các tính năng cho bất kỳ hiện JTextCompoent:

public final static String UNDO_ACTION = "Undo"; 

public final static String REDO_ACTION = "Redo"; 

public static void makeUndoable(JTextComponent pTextComponent) { 
    final UndoManager undoMgr = new UndoManager(); 

    // Add listener for undoable events 
    pTextComponent.getDocument().addUndoableEditListener(new UndoableEditListener() { 
     public void undoableEditHappened(UndoableEditEvent evt) { 
      undoMgr.addEdit(evt.getEdit()); 
     } 
    }); 

    // Add undo/redo actions 
    pTextComponent.getActionMap().put(UNDO_ACTION, new AbstractAction(UNDO_ACTION) { 
     public void actionPerformed(ActionEvent evt) { 
      try { 
       if (undoMgr.canUndo()) { 
        undoMgr.undo(); 
       } 
      } catch (CannotUndoException e) { 
       e.printStackTrace(); 
      } 
     } 
    }); 
    pTextComponent.getActionMap().put(REDO_ACTION, new AbstractAction(REDO_ACTION) { 
     public void actionPerformed(ActionEvent evt) { 
      try { 
       if (undoMgr.canRedo()) { 
        undoMgr.redo(); 
       } 
      } catch (CannotRedoException e) { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    // Create keyboard accelerators for undo/redo actions (Ctrl+Z/Ctrl+Y) 
    pTextComponent.getInputMap().put(
     KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK), UNDO_ACTION); 
    pTextComponent.getInputMap().put(
     KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_DOWN_MASK), REDO_ACTION); 
} 

Các giải pháp cuối cùng và hoàn toàn minh bạch sẽ được thực hiện lớp UI của riêng bạn sử dụng Lnf features, nhưng bạn có thể muốn hãy suy nghĩ kỹ trước khi làm cho tất cả TextComponents có thể hoàn tác, vì lý do bộ nhớ tiêu thụ chẳng hạn nếu bạn thường thực hiện sửa đổi văn bản lớn cho các thành phần này ...

+0

Nó hoạt động tốt đối với tôi, nhưng tôi đã phải bỏ qua những thay đổi phong cách: 'public void undoableEditHappened (UndoableEditEvent evt) { if (evt.getEdit() instanceof AbstractDocument.DefaultDocumentEvent) { AbstractDocument.DefaultDocumentEvent ad = (AbstractDocument.DefaultDocumentEvent) evt.getEdit(); nếu (ad.getType() == DocumentEvent.EventType.CHANGE) { trả lại; } } undoMgr.addEdit (evt.getEdit()); } ' –

+0

UNDO_ACTION và REDO_ACTION là gì? Hằng số của bất kỳ loại nào mà bạn xác định? –

+1

@ChrisK Nó có thể là bất kỳ 'Chuỗi' bạn muốn. Xem câu trả lời đã chỉnh sửa của tôi. Tôi đã sử dụng hai hằng số. – xav