You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 8下实现JTextArea固定悬浮搜索框的问题

Java 8下实现JTextArea固定悬浮搜索框的问题

嘿,我完全懂你的困扰——用JPopupMenu做的搜索框动不动就消失,滚动一下或者点个别的地方就没影了,根本没法像Chrome的搜索框那样稳稳贴在滚动面板的左上角。而且你还不想破坏JScrollPane的原有布局,也不想把搜索框放在BorderLayout的NORTH区域,就得是那种overlay在文本区域上面的效果,对吧?

其实JPopupMenu本身就是为临时弹出的菜单设计的,它自带“失去焦点就自动隐藏”的特性,所以用来做固定悬浮的搜索框天生就不合适。要实现你要的效果,咱们得换个思路,用普通的容器组件来做,然后手动控制它的位置和显示状态。

解决方案思路

  • 把原来的JPopupMenu换成普通的JPanel作为搜索容器,避免自动消失的问题
  • 将这个容器设置为透明,添加到JScrollPane的Viewport的GlassPane上,完全不干扰原有布局
  • 监听滚动和视图大小变化事件,实时调整搜索容器的位置,确保它始终停在Viewport左上角
  • 保留原有的Ctrl+F快捷键逻辑,触发时显示搜索框并自动聚焦

修改后的完整代码

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

public class TextAreaSearchDemo {
    static JPanel mainPanel;
    static JTextField searchField;
    static JPanel searchOverlay; // 替换JPopupMenu为普通JPanel
    static JScrollPane scroller;

    public static void main(String[] args) {
        // 确保所有Swing操作在事件调度线程执行
        SwingUtilities.invokeLater(() -> {
            Container mainPanel = createMainPanel();
            JFrame frame = new JFrame("Text Area Search Demo");
            frame.setContentPane(mainPanel);
            frame.setLocationRelativeTo(null);
            frame.pack();
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            frame.setVisible(true);
        });
    }

    static JPanel createMainPanel() {
        mainPanel = new JPanel(new BorderLayout());
        scroller = createScroller();
        mainPanel.add(scroller);
        createSearchOverlay();
        // 初始隐藏搜索框,按Ctrl+F显示
        searchOverlay.setVisible(false);
        return mainPanel;
    }

    static JScrollPane createScroller() {
        JScrollPane scroller = new JScrollPane();
        scroller.setPreferredSize(new Dimension(500, 400));
        scroller.setViewportView(createTextArea());
        
        // 监听滚动事件,实时更新搜索框位置
        scroller.getViewport().addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                updateSearchOverlayPosition();
            }
        });
        return scroller;
    }

    static JTextArea createTextArea() {
        JTextArea textArea = new JTextArea();
        textArea.setWrapStyleWord(true);
        textArea.setLineWrap(true);
        textArea.setEditable(false);
        textArea.setText(createText());
        textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), "search");
        textArea.getActionMap().put("search", createSearchAction());
        return textArea;
    }

    static private String createText() {
        return "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                "Maecenas bibendum, nulla a tempus volutpat, enim magna scelerisque enim, " +
                "et tempor erat urna vel lorem. Curabitur tristique, tortor sit amet bibendum tempus, " +
                "elit tellus semper justo, a semper ligula eros nec lectus. Donec lorem risus, posuere " +
                "at aliquet eget, aliquet eget mi. Proin viverra erat vehicula fringilla auctor. " +
                "Morbi eleifend nisl vitae eros hendrerit posuere. Ut porta tortor eu quam iaculis, vitae " +
                "luctus risus elementum. Phasellus ac bibendum eros. Nam quis magna non elit semper luctus quis " +
                "non odio. Sed iaculis risus non odio pulvinar, eu maximus felis eleifend. Aliquam aliquam augue " +
                "aliquam leo ullamcorper rhoncus. Suspendisse at nibh ante.";
    }

    private static Action createSearchAction() {
        return new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                searchOverlay.setVisible(true);
                // 延迟聚焦,确保搜索框已经显示
                SwingUtilities.invokeLater(() -> searchField.requestFocus());
            }
        };
    }

    private static void createSearchOverlay() {
        // 创建透明悬浮面板,用null布局手动定位
        searchOverlay = new JPanel(null);
        searchOverlay.setOpaque(false); // 透明不遮挡文本
        searchOverlay.setPreferredSize(scroller.getViewport().getSize());
        
        // 创建搜索框并设置位置
        searchField = new JTextField(10);
        searchField.addActionListener(e -> search(searchField.getText()));
        searchField.setBounds(5, 5, 150, 25); // 左上角偏移5像素,避免贴边
        searchOverlay.add(searchField);
        
        // 添加到Viewport的GlassPane,完全不干扰原有布局
        scroller.getViewport().setGlassPane(searchOverlay);
        
        // 监听视图大小变化,更新悬浮面板尺寸
        scroller.getViewport().addAncestorListener(new AncestorListener() {
            @Override
            public void ancestorAdded(AncestorEvent event) {
                updateSearchOverlaySize();
            }

            @Override
            public void ancestorRemoved(AncestorEvent event) {}

            @Override
            public void ancestorMoved(AncestorEvent event) {
                updateSearchOverlayPosition();
            }
        });
    }

    private static void updateSearchOverlaySize() {
        searchOverlay.setSize(scroller.getViewport().getSize());
    }

    private static void updateSearchOverlayPosition() {
        // 让悬浮面板和视图滚动位置同步,保持搜索框在左上角
        searchOverlay.setLocation(scroller.getViewport().getViewPosition());
    }

    private static void search(String text) {
        System.out.println("Searching for " + text);
        // 这里可以添加实际的文本高亮逻辑,比如:
        // JTextArea textArea = (JTextArea) scroller.getViewport().getView();
        // int pos = textArea.getText().indexOf(text);
        // if (pos != -1) {
        //     textArea.setSelectionStart(pos);
        //     textArea.setSelectionEnd(pos + text.length());
        // }
    }
}

关键细节说明

  • 用透明JPanel替代JPopupMenu,彻底解决了自动消失的问题
  • 通过GlassPane添加悬浮容器,完全不会影响JScrollPane的原有布局和滚动功能
  • 监听滚动事件实时更新位置,确保搜索框始终停在可视区域的左上角
  • 保留了原有的Ctrl+F快捷键逻辑,触发时自动显示并聚焦搜索框
  • 后续要加关闭按钮的话,直接在searchOverlay里添加JButton,绑定searchOverlay.setVisible(false)即可

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.09 03:04:33