突出显示 JTextPane 中的当前行

Posted

技术标签:

【中文标题】突出显示 JTextPane 中的当前行【英文标题】:Highlight current row in JTextPane 【发布时间】:2011-07-21 07:18:33 【问题描述】:

我尝试了超过 2 天的时间来实现对文本编辑器窗口的特定要求...不幸的是到目前为止没有成功:(

目标是获得一个文本编辑器窗口,该窗口将突出显示当前行,就像其他文本编辑器一样。对于当前行,我的意思是当前光标/插入符号所在的行。

我已经找到了两种不同的方法,但遗憾的是我无法采用它们,因此它们按预期工作。

第一种方法是覆盖DefaultHighlighter (http://snippets.dzone.com/posts/show/6688)。 在第二种方法中,HighlighterPainter 将被覆盖 (http://www.jroller.com/santhosh/date/20050622)。

现在我正在尝试在我的项目中采用第一种方法,但正如我所说的那样,它没有按预期工作。

在这篇文章的最后,我发布了一个演示问题的小示例应用程序。

如果我启动程序,插入符号会放在第一行的开头。但是,该行没有突出显示。 现在我输入一些字符。这些字符将突出显示,但那些字符不是完整的行 我按回车键移动到下一行。第一行不再突出显示什么是正确的。第二行也没有突出显示,这是不正确的。同样,当我输入一些字符时,这些字符将被突出显示,但不是完整的行。 当我现在通过光标向上键或鼠标单击将插入符号移回第一行时,将突出显示完整的第一行,而不仅仅是现有的字符。这是我从一开始就想要的行为。

我希望任何人都可以告诉我我在这里做错了什么......或者解释为什么根本无法解决这个问题。任何我可以实现线条突出显示的替代解决方案也非常感谢!

提前非常感谢 干杯 传教士

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;

public class HighlightProblem extends JFrame 
    private static final long serialVersionUID = 1L;
    private final JTextPane textPane;
    private final Highlighter.HighlightPainter cyanPainter;

    public HighlightProblem() 
        cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.CYAN);

        textPane = new JTextPane();
        textPane.setPreferredSize(new Dimension(500, 300));
        textPane.setHighlighter(new LineHighlighter());
        textPane.addCaretListener(new CaretListener() 
            @Override
            public void caretUpdate(CaretEvent e) 
                setHighlight(e);
            
        );
        getContentPane().add(textPane);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
    

    public static void main(String[] args) 
        new HighlightProblem();
    

    public void setHighlight(CaretEvent e) 
        textPane.getHighlighter().removeAllHighlights();
        int currentLine = getLineFromOffset(textPane, e.getDot());
        int startPos = getLineStartOffsetForLine(textPane, currentLine);
        int endOffset = getLineEndOffsetForLine(textPane, currentLine);

        try 
            textPane.getHighlighter().addHighlight(startPos, endOffset, cyanPainter);           
         catch (Exception ex) 
            ex.printStackTrace();
        
        textPane.repaint();
    

    public int getLineFromOffset(JTextComponent component, int offset) 
        return component.getDocument().getDefaultRootElement().getElementIndex(offset);
    

    public int getLineStartOffsetForLine(JTextComponent component, int line) 
        return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
    

    public int getLineEndOffsetForLine(JTextComponent component, int line) 
        return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
    

    public class LineHighlighter extends DefaultHighlighter 
        private JTextComponent component;

        @Override
        public final void install(final JTextComponent c) 
            super.install(c);
            this.component = c;
        

        @Override
        public final void deinstall(final JTextComponent c) 
            super.deinstall(c);
            this.component = null;
        

        @Override
        public final void paint(final Graphics g) 
            final Highlighter.Highlight[] highlights = getHighlights();
            final int len = highlights.length;
            for (int i = 0; i < len; i++) 
                Highlighter.Highlight info = highlights[i];
                if (info.getClass().getName().indexOf("LayeredHighlightInfo") > -1) 
                    // Avoid allocing unless we need it.
                    final Rectangle a = this.component.getBounds();
                    final Insets insets = this.component.getInsets();
                    a.x = insets.left;
                    a.y = insets.top;
                    // a.width -= insets.left + insets.right + 100;
                    a.height -= insets.top + insets.bottom;
                    final Highlighter.HighlightPainter p = info.getPainter();
                    p.paint(g, info.getStartOffset(), info.getEndOffset(), a, this.component);
                
            
        

        @Override
        public void removeAllHighlights() 
            textPane.repaint(0, 0, textPane.getWidth(), textPane.getHeight());
            super.removeAllHighlights();
        
    

【问题讨论】:

【参考方案1】:

下面是从当前行中提取文本的代码。 您可以使用相同的逻辑来获取所需的索引并突出显示文本

    private String getCurrentEditLine() 
        int readBackChars = 100;
        int caretPosition = scriptEditor.getCaretPosition();

        if (caretPosition == 0) 
            return null;
        

        StyledDocument doc = scriptEditor.getStyledDocument();

        int offset = caretPosition <= readBackChars ? 0 : caretPosition
                - readBackChars;

        String text = null;
        try 
            text = doc.getText(offset, caretPosition);
         catch (BadLocationException e) 
        

        if (text != null) 
            int idx = text.lastIndexOf("\n");
            if(idx != -1) 
                return text.substring(idx);
            else 
                return text;
            
        

        return null;
    

【讨论】:

【参考方案2】:

我认为使用荧光笔可能很难实现这一点 - 我认为这不是它们的设计目的。您可能需要使用自定义绘画代码来完成:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;

public class HighlightLineTest 
    private static class HighlightLineTextPane extends JTextPane 
        public HighlightLineTextPane() 
            // Has to be marked as transparent so the background is not replaced by 
            // super.paintComponent(g);
            setOpaque(false);
        

        @Override
        protected void paintComponent(Graphics g) 
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
            try 
                Rectangle rect = modelToView(getCaretPosition());
                if (rect != null) 
                    g.setColor(Color.CYAN);
                    g.fillRect(0, rect.y, getWidth(), rect.height);
                
             catch (BadLocationException e) 
            
            super.paintComponent(g);
        

        @Override
        public void repaint(long tm, int x, int y, int width, int height) 
            // This forces repaints to repaint the entire TextPane.
            super.repaint(tm, 0, 0, getWidth(), getHeight());
        
    

    public static void main(String[] args) 
        JFrame frame = new JFrame("Highlight test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new HighlightLineTextPane());
        frame.setBounds(100, 100, 300, 400);
        frame.setVisible(true);
    

【讨论】:

【参考方案3】:

http://tips4java.wordpress.com/2008/10/29/line-painter/

我认为这就是您要寻找的。我使用了 LinePainter 类并将你的构造函数复制到一个 main 方法中,取出你的荧光笔部分并添加了一个 new LinePainter(textPane); 就像一个魅力

【讨论】:

非常感谢......它看起来真的像一个魅力。

以上是关于突出显示 JTextPane 中的当前行的主要内容,如果未能解决你的问题,请参考以下文章

如何在禁用突出显示的其他组件中安排透明的 JTextPane

Eclipse 不在远程调试模式下突出显示当前行

如何在 QScintilla 中突出显示当前行

Qt突出显示选定的行会覆盖单个单词的突出显示

vbscript Excel突出显示当前行

用JQuery实现表格隔行变色和突出显示当前行