输入文本时叠加Swing文本标签

我程序中的各种表单使用JTables,我已经能够使用Key Listener在用户输入时选择一行。 这很好用,但我想向用户提供一些反馈,以显示正在输入的文本。

我尝试过创建框架/标签,但无法正确显示它们。

我的基本想法是 – 创建框架(如果它尚不存在),创建标签并设置文本。 例如:

private void showSearchLabel(String search) { if (null == searchTextFrame) { searchTextFrame = new JFrame("searchTextFrame"); searchTextFrame.setBackground(new Color(0, 0, 0, 0)); searchTextFrame.setUndecorated(true); searchTextFrame.setAlwaysOnTop(true); searchTextFrame.getContentPane().setLayout(new java.awt.BorderLayout()); searchTextLabel = new JLabel(); searchTextFrame.getContentPane().add(searchTextLabel); searchTextFrame.pack(); searchTextFrame.setVisible(true); } searchTextLabel.setText(search); } 

showSearchLabel由Key Listener调用,它将最近的按键添加到搜索字符串中。 退格键清除字符串(并删除框架/标签)。 Enter键选择表中的项目,还应删除框架/标签。

我错过了什么?

编辑:澄清 – 使用上面的代码,什么都没有显示。

如果我在创建标签时设置文本,则第一个字符是可见的(在该点可以预期,用户只键入一个字符)。 在此之后调用.setText(搜索) ,文本不会更新。 注意 – 这在屏幕的顶部/左上角是可见的,这不是我想要的地方(理想情况下,它希望它在JTable中显示)。

在过去的几个月中,我不得不多次重复这样的事情,使用一些不同的UI方法(隐藏一个字段直到用户输入,然后通过BorderLayout使其可见;屏幕上的静态字段;等…)并试图构建一个更简洁和可重用的库机制,这将允许我配置任何JTable具有简单的可过滤支持。

我在JLabel使用JTextField的原因之一是,它比你或我能够生成的任何东西(在合理的时间内)更好地处理用户输入,但那就是我;)

就个人而言,我不想打开任何新窗口,因为这会引入其他问题,例如焦点相关问题以及窗口隐藏在某些平台上的当前窗口后面等等。

同样地,我几次“取消隐藏”一个字段,布局被迫改变,这看起来很丑陋,并且并不总是需要让滤镜字段始终可见。 相反,我试图实现的是一个“弹出”字段,它与表本身一起显示,但是能够在父JScrollPane的可视区域内的任何位置显示…

现在,这种方法可能不适合您的需求,但这是我一直努力的方向……

过滤

同样,核心概念是提供不需要我扩展JTable ,但可能包含现有的JTable 。 核心function由实用程序类提供,并通过两个interfaces进行管理,这两个interfaces提供对某些核心function的外部控制(filter如何应用于JTable以及取消操作如何工作)。 实用程序类还提供了配置取消筛选字段的键盘操作的方法

显示的JTextField实际上已添加到JTable本身。 我曾想过试图在视图中强制“偏移”,所以所有的行都被按下了,但我知道怎么做的唯一方法是扩展JTable本身,这就是我试图避免的

 public class TableUtilities { private static WeakHashMap mapFilters = new WeakHashMap(25); public static void installFilterSupport(JTable table, IFilterListener listener, KeyStroke escapeKey) { FilterSupport support = new FilterSupport(table, listener, escapeKey); mapFilters.put(table, support); } public static void uninstallFilterSupport(JTable table) { FilterSupport support = mapFilters.remove(table); if (support != null) { support.uninstall(); } } protected static class FilterSupport implements IFilterSupport { private JViewport viewport; private JTable table; private JTextField searchField; private Timer filterTimer; private HierarchyListener hierarchyListener; private ChangeListener changeListener; private IFilterListener filterListener; public FilterSupport(JTable table, IFilterListener listener, KeyStroke escapeKey) { this.table = table; this.filterListener = listener; table.setFillsViewportHeight(true); hierarchyListener = new HierarchyListener() { @Override public void hierarchyChanged(HierarchyEvent e) { long flags = e.getChangeFlags(); if ((flags & HierarchyEvent.PARENT_CHANGED) != 0) { if (e.getChanged().equals(table)) { JTable table = (JTable) e.getChanged(); if (e.getChangedParent() instanceof JViewport) { if (table.getParent() == null) { uninstall(); } else { install(); } } } } } }; changeListener = new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JViewport viewport = (JViewport) e.getSource(); Rectangle viewRect = viewport.getViewRect(); searchField.setSize(searchField.getPreferredSize()); int x = (viewRect.x + viewRect.width) - searchField.getWidth(); int y = viewRect.y; searchField.setLocation(x, y); } }; table.addHierarchyListener(hierarchyListener); searchField = new JTextField(20); searchField.setVisible(false); searchField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { filterChanged(); } @Override public void removeUpdate(DocumentEvent e) { filterChanged(); } @Override public void changedUpdate(DocumentEvent e) { filterChanged(); } }); searchField.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { cancelField(); } }); filterTimer = new Timer(250, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { applyFilter(); } }); filterTimer.setRepeats(false); table.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { if (Character.isLetterOrDigit(e.getKeyChar())) { searchField.setVisible(true); table.revalidate(); table.repaint(); // ?? Should this maintain the current filter value? searchField.setText(null); searchField.requestFocusInWindow(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { searchField.dispatchEvent(e); } }); } } }); Action escapeAction = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { cancelField(); } }; bindKeyStrokeTo(table, JComponent.WHEN_FOCUSED, "clear", escapeKey, escapeAction); bindKeyStrokeTo(searchField, JComponent.WHEN_FOCUSED, "clear", escapeKey, escapeAction); } protected void cancelField() { searchField.setVisible(false); table.requestFocusInWindow(); table.revalidate(); table.repaint(); if (filterListener != null) { filterListener.filterCancelled(table, this); } } public void filterChanged() { filterTimer.restart(); } protected void applyFilter() { if (filterListener != null) { filterListener.filterChanged(table, searchField.getText()); } } protected void uninstall() { filterTimer.stop(); if (viewport != null) { if (changeListener != null) { viewport.removeChangeListener(changeListener); } table.remove(searchField); searchField.setVisible(false); } viewport = null; } protected void install() { if (viewport != null) { uninstall(); } Container parent = table.getParent(); if (parent instanceof JViewport) { viewport = (JViewport) parent; viewport.addChangeListener(changeListener); table.add(searchField); } } @Override public String getFilter() { return searchField.getText(); } @Override public void setFilter(String filter) { searchField.setText(filter); } } public static void bindKeyStrokeTo(JComponent parent, int condition, String name, KeyStroke keyStroke, Action action) { InputMap im = parent.getInputMap(condition); ActionMap am = parent.getActionMap(); im.put(keyStroke, name); am.put(name, action); } public static interface IFilterSupport { public String getFilter(); public void setFilter(String filter); } public static interface IFilterListener { public void filterChanged(JTable table, String filter); public void filterCancelled(JTable table, IFilterSupport support); } } 

而我的考试class……

 import java.awt.BorderLayout; import java.awt.Container; import java.awt.EventQueue; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.WeakHashMap; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.RowFilter; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableRowSorter; public class TestSearchTable { public static void main(String[] args) { new TestSearchTable(); } public TestSearchTable() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { setLayout(new BorderLayout()); DefaultTableModel model = new DefaultTableModel( new Object[][]{ {"Tiana", "Wilmer"}, {"Twana", "Wingate"}, {"Cody", "Baumgarten"}, {"Venus", "Espy"}, {"Savanna", "Buckmaster"}, {"Adrien", "Edgecomb"}, {"Lauretta", "Sassman"}, {"Vivienne", "Glasco"}, {"Cassy", "Merryman"}, {"Mitchel", "Jarvie"}, {"Kelsi", "Casebeer"}, {"Rosy", "Rizzi"}, {"Bernice", "Capote"}, {"Tijuana", "Launius"}, {"Jeffie", "Crownover"}, {"Selena", "Leavy"}, {"Damon", "Tulloch"}, {"Norris", "Devitt"}, {"Cecil", "Burgio"}, {"Queen", "Mechling"}}, new Object[]{"First Name", "Last name"} ) { @Override public boolean isCellEditable(int row, int column) { return false; } }; JTable table = new JTable(model); table.setAutoCreateRowSorter(true); TableUtilities.installFilterSupport(table, new TableUtilities.IFilterListener() { @Override public void filterChanged(JTable table, String filter) { TableRowSorter sorter = (TableRowSorter) table.getRowSorter(); if (filter == null || filter.trim().length() == 0) { filter = "*"; } if (!filter.startsWith("*") || !filter.endsWith("*")) { filter = "*" + filter + "*"; } filter = wildcardToRegex(filter); filter = "(?i)" + filter; sorter.setRowFilter(RowFilter.regexFilter(filter)); } @Override public void filterCancelled(JTable table, TableUtilities.IFilterSupport support) { // support.setFilter(null); } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); add(new JScrollPane(table)); } } public static String wildcardToRegex(String wildcard) { StringBuilder s = new StringBuilder(wildcard.length()); s.append('^'); for (int i = 0, is = wildcard.length(); i < is; i++) { char c = wildcard.charAt(i); switch (c) { case '*': s.append(".*"); break; case '?': s.append("."); break; // escape special regexp-characters case '(': case ')': case '[': case ']': case '$': case '^': case '.': case '{': case '}': case '|': case '\\': s.append("\\"); s.append(c); break; default: s.append(c); break; } } s.append('$'); return (s.toString()); } } 

新的JLabel()具有“标题的空字符串”。 它的首选大小为零。 您可以

  • 在构造函数中添加所需的空间。

     searchTextLabel = new JLabel(" "); 
  • 在调用setVisible()之后,在封闭框架上调用pack() setVisible()

     f.setVisible(true); … searchTextFrame.pack(); 
  • 在构造函数中添加一个空格,以建立高度,并在封闭的Container上调用validate()

     searchTextLabel = new JLabel(" "); … f.setVisible(true); … searchTextLabel.setText(search); searchTextLabel.validate();