如何在 JTextArea 中用四个空格替换制表符?
How can I replace a tab with four spaces in JTextArea?
我在 JFrame 中使用 JTextArea。我希望制表键插入四个空格而不是制表符。
方法 setTabSize
不起作用,因为它在文本区域的内容中放置了一个制表符 ('\t')。
如何让 JTextArea 在我按下 Tab 键时插入四个空格而不是一个制表符?这样,getText() 方法将为每个制表符 return 缩进四个空格。
我会避免使用 KeyListeners(作为 JTextComponents 的一般规则)甚至键绑定,因为虽然键绑定适用于键盘输入,但它不适用于 copy-and-paste。
在我看来,最好的方法是使用在 JTextArea 的文档(顺便说一下,它是一个 PlainDocument)上设置的 DocumentFilter。这样,即使您将文本复制并粘贴到带有制表符的 JTextAreas 中,所有制表符也会在插入时自动转换为 4 个空格。
例如:
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
public class TestTextArea {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTextArea textArea = new JTextArea(20, 50);
JScrollPane scrollPane = new JScrollPane(textArea);
int spaceCount = 4;
((PlainDocument) textArea.getDocument()).setDocumentFilter(new ChangeTabToSpacesFilter(spaceCount));
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static class ChangeTabToSpacesFilter extends DocumentFilter {
private int spaceCount;
private String spaces = "";
public ChangeTabToSpacesFilter(int spaceCount) {
this.spaceCount = spaceCount;
for (int i = 0; i < spaceCount; i++) {
spaces += " ";
}
}
@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
string = string.replace("\t", spaces);
super.insertString(fb, offset, string, attr);
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
text = text.replace("\t", spaces);
super.replace(fb, offset, length, text, attrs);
}
}
}
所以现在,即使我将其中包含制表符的文档复制并粘贴到 JTextArea 中,所有制表符都会自动替换为 spaceCount
个空格。
这是“我想知道是否……”的时刻之一。
就我个人而言,我会尝试更直接地从源头上解决问题。这意味着以某种方式“捕获”Tab 事件并“替换”它的功能。
所以,我开始修改 http://www.java2s.com/Tutorial/Java/0260__Swing-Event/ListingtheKeyBindingsinaComponent.htm,它可以列出组件的键绑定,为此,我发现 JTextArea 使用 insert-tab
作为动作映射键。
然后我创建了自己的 Action
旨在在当前插入符位置插入空格,例如……
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
// insert-tab
JTextArea ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
ActionMap am = ta.getActionMap();
am.put("insert-tab", new SpacesTabAction());
}
public class SpacesTabAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JTextArea)) {
return;
}
JTextArea textArea = (JTextArea) e.getSource();
int caretPosition = textArea.getCaretPosition();
Document document = textArea.getDocument();
try {
document.insertString(caretPosition, " ", null);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
}
}
可接受的限制
这不会涵盖将文本粘贴到组件中,否则 DocumentFilter
会涵盖这些内容,但是,我想考虑 DocumentFilter
可能无法使用的场景(例如因为已经安装了一个)
更多调查
The method setTabSize does not work, as it puts a tab ('\t') in the contents of the text area.
这是那些“空格 vs 制表符”的争论之一吗:P
我做了一些摆弄,发现与 setTabSize
的大部分不一致都是因为没有使用固定宽度的字体,例如…
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
Font font = Font.decode("Courier New").deriveFont(12);
JTextArea ta = new JTextArea(10, 40);
ta.setFont(font);
ta.setText("---|----|----|----|----|----|----|----|\n\tHello");
ta.setTabSize(0);
add(new JScrollPane(ta));
DefaultComboBoxModel<Integer> model = new DefaultComboBoxModel<>(new Integer[] {0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24});
JComboBox<Integer> tabSizes = new JComboBox<>(model);
add(tabSizes, BorderLayout.NORTH);
tabSizes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Integer tabSize = (Integer)tabSizes.getSelectedItem();
ta.setTabSize(tabSize);
}
});
}
}
}
当我将字体设置为“Courier New”时,我能够获得与设置的标签大小对齐的一致缩进
我显然遗漏了一些东西...
The tab is four spaces. In this "111\t" string, the tab is expands to 1 space; "22\t" — to 2 spaces; "3\t" — to 3 spaces; finally, "\t" expands to four spaces.
是的,这不是预期的行为吗?!
JTextArea
, 4
的 tabSize
, 等宽字体
Sublime 文本编辑器,tabSize
of 4
,单间距字体
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.util.StringJoiner;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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() {
StringJoiner textJoiner = new StringJoiner("\n");
for (int row = 0; row < 10; row++) {
StringJoiner rowJoiner = new StringJoiner("\t");
for (int col = 0; col < 10; col++) {
rowJoiner.add(Integer.toString(col).repeat(row));
}
textJoiner.add(rowJoiner.toString());
}
setLayout(new BorderLayout());
JTextArea ta = new JTextArea(10, 40);
ta.setFont(new Font("Monospaced", Font.PLAIN, 13));
ta.setText(textJoiner.toString());
add(new JScrollPane(ta));
}
protected String replicate(char value, int times) {
return new String(new char[times]).replace('[=12=]', value);
}
}
}
我认为您考虑的是“制表位”,而不是“制表符大小”for example,在这种情况下,即使 DocumentFilter
也不会达到您的预期.
I assume the use of monospace font is implied in the question
不要“假设”我们什么都知道。在我的实验中,字体不是等宽的。这是提供“预期”和“实际”结果以及 minimal reproducible example 的地方,因为它消除了歧义并且不会浪费每个人的时间(尤其是你的)
It is how it works in any text editor.
我显然在使用不同的文本编辑器
我在 JFrame 中使用 JTextArea。我希望制表键插入四个空格而不是制表符。
方法 setTabSize
不起作用,因为它在文本区域的内容中放置了一个制表符 ('\t')。
如何让 JTextArea 在我按下 Tab 键时插入四个空格而不是一个制表符?这样,getText() 方法将为每个制表符 return 缩进四个空格。
我会避免使用 KeyListeners(作为 JTextComponents 的一般规则)甚至键绑定,因为虽然键绑定适用于键盘输入,但它不适用于 copy-and-paste。
在我看来,最好的方法是使用在 JTextArea 的文档(顺便说一下,它是一个 PlainDocument)上设置的 DocumentFilter。这样,即使您将文本复制并粘贴到带有制表符的 JTextAreas 中,所有制表符也会在插入时自动转换为 4 个空格。
例如:
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
public class TestTextArea {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTextArea textArea = new JTextArea(20, 50);
JScrollPane scrollPane = new JScrollPane(textArea);
int spaceCount = 4;
((PlainDocument) textArea.getDocument()).setDocumentFilter(new ChangeTabToSpacesFilter(spaceCount));
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static class ChangeTabToSpacesFilter extends DocumentFilter {
private int spaceCount;
private String spaces = "";
public ChangeTabToSpacesFilter(int spaceCount) {
this.spaceCount = spaceCount;
for (int i = 0; i < spaceCount; i++) {
spaces += " ";
}
}
@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
string = string.replace("\t", spaces);
super.insertString(fb, offset, string, attr);
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
text = text.replace("\t", spaces);
super.replace(fb, offset, length, text, attrs);
}
}
}
所以现在,即使我将其中包含制表符的文档复制并粘贴到 JTextArea 中,所有制表符都会自动替换为 spaceCount
个空格。
这是“我想知道是否……”的时刻之一。
就我个人而言,我会尝试更直接地从源头上解决问题。这意味着以某种方式“捕获”Tab 事件并“替换”它的功能。
所以,我开始修改 http://www.java2s.com/Tutorial/Java/0260__Swing-Event/ListingtheKeyBindingsinaComponent.htm,它可以列出组件的键绑定,为此,我发现 JTextArea 使用 insert-tab
作为动作映射键。
然后我创建了自己的 Action
旨在在当前插入符位置插入空格,例如……
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
// insert-tab
JTextArea ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
ActionMap am = ta.getActionMap();
am.put("insert-tab", new SpacesTabAction());
}
public class SpacesTabAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JTextArea)) {
return;
}
JTextArea textArea = (JTextArea) e.getSource();
int caretPosition = textArea.getCaretPosition();
Document document = textArea.getDocument();
try {
document.insertString(caretPosition, " ", null);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
}
}
可接受的限制
这不会涵盖将文本粘贴到组件中,否则 DocumentFilter
会涵盖这些内容,但是,我想考虑 DocumentFilter
可能无法使用的场景(例如因为已经安装了一个)
更多调查
The method setTabSize does not work, as it puts a tab ('\t') in the contents of the text area.
这是那些“空格 vs 制表符”的争论之一吗:P
我做了一些摆弄,发现与 setTabSize
的大部分不一致都是因为没有使用固定宽度的字体,例如…
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
Font font = Font.decode("Courier New").deriveFont(12);
JTextArea ta = new JTextArea(10, 40);
ta.setFont(font);
ta.setText("---|----|----|----|----|----|----|----|\n\tHello");
ta.setTabSize(0);
add(new JScrollPane(ta));
DefaultComboBoxModel<Integer> model = new DefaultComboBoxModel<>(new Integer[] {0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24});
JComboBox<Integer> tabSizes = new JComboBox<>(model);
add(tabSizes, BorderLayout.NORTH);
tabSizes.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Integer tabSize = (Integer)tabSizes.getSelectedItem();
ta.setTabSize(tabSize);
}
});
}
}
}
当我将字体设置为“Courier New”时,我能够获得与设置的标签大小对齐的一致缩进
我显然遗漏了一些东西...
The tab is four spaces. In this "111\t" string, the tab is expands to 1 space; "22\t" — to 2 spaces; "3\t" — to 3 spaces; finally, "\t" expands to four spaces.
是的,这不是预期的行为吗?!
JTextArea
, 4
的 tabSize
, 等宽字体
Sublime 文本编辑器,tabSize
of 4
,单间距字体
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.util.StringJoiner;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
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() {
StringJoiner textJoiner = new StringJoiner("\n");
for (int row = 0; row < 10; row++) {
StringJoiner rowJoiner = new StringJoiner("\t");
for (int col = 0; col < 10; col++) {
rowJoiner.add(Integer.toString(col).repeat(row));
}
textJoiner.add(rowJoiner.toString());
}
setLayout(new BorderLayout());
JTextArea ta = new JTextArea(10, 40);
ta.setFont(new Font("Monospaced", Font.PLAIN, 13));
ta.setText(textJoiner.toString());
add(new JScrollPane(ta));
}
protected String replicate(char value, int times) {
return new String(new char[times]).replace('[=12=]', value);
}
}
}
我认为您考虑的是“制表位”,而不是“制表符大小”for example,在这种情况下,即使 DocumentFilter
也不会达到您的预期.
I assume the use of monospace font is implied in the question
不要“假设”我们什么都知道。在我的实验中,字体不是等宽的。这是提供“预期”和“实际”结果以及 minimal reproducible example 的地方,因为它消除了歧义并且不会浪费每个人的时间(尤其是你的)
It is how it works in any text editor.
我显然在使用不同的文本编辑器