出于学习目的,我正在用 Java 从头开始制作一个所见即所得编辑器。现在我想让用户可以知道什么样式将应用于下一个键入的字符,通过按下/样式化按钮来指示它们存在,但它现在不起作用。
我当前的 JToolBar 代码 -
public class ToolBar extends JToolBar {
private final JTextPane textPane;
private final StyledEditorKit.BoldAction boldAction = new StyledEditorKit.BoldAction();
private final StyledEditorKit.UnderlineAction underlineAction = new StyledEditorKit.UnderlineAction();
private final StyledEditorKit.ItalicAction italicAction = new StyledEditorKit.ItalicAction();
private JButton boldBtn;
private JButton italicsBtn;
private JButton underlineBtn;
private final Color defaultColor = new Color(238,238,238);
private final Color selectedColor = new Color(100,100,100);
public ToolBar(JTextPane textPane) {
super("WYSIWYG Tools");
this.textPane = textPane;
setFloatable(false);
setupButtons();
updateButtonStates();
textPane.addCaretListener(e -> updateButtonStates());
}
private void setupButtons() {
boldBtn = createImageButton("src/resources/images/bold.png");
boldBtn.addActionListener(this::boldStylize);
italicsBtn = createImageButton("src/resources/images/italics.png");
italicsBtn.addActionListener(this::italicStylize);
underlineBtn = createImageButton("src/resources/images/underline.png");
underlineBtn.addActionListener(this::underlineStylize);
JButton strikeBtn = createImageButton("src/resources/images/strike.png");
add(boldBtn);
add(italicsBtn);
add(underlineBtn);
add(strikeBtn);
}
private static JButton createImageButton(String imagePath) {
JButton button = new JButton();
try {
BufferedImage image = ImageIO.read(new File(imagePath));
button.setIcon(new ImageIcon(image));
} catch (IOException e) {
e.printStackTrace();
}
return button;
}
private void boldStylize(ActionEvent e) {
textPane.requestFocusInWindow();
boldAction.actionPerformed(new ActionEvent(textPane, ActionEvent.ACTION_PERFORMED, null));
updateButtonStates();
}
private void underlineStylize(ActionEvent e) {
textPane.requestFocusInWindow();
underlineAction.actionPerformed(new ActionEvent(textPane, ActionEvent.ACTION_PERFORMED, null));
updateButtonStates();
}
private void italicStylize(ActionEvent e) {
textPane.requestFocusInWindow();
italicAction.actionPerformed(new ActionEvent(textPane, ActionEvent.ACTION_PERFORMED, null));
updateButtonStates();
}
private void updateButtonStates() {
System.out.println("updating button states");
StyledDocument doc = textPane.getStyledDocument();
int caretPosition = textPane.getCaretPosition();
System.out.println("Caret positions " + caretPosition);
Element paragraphElement = doc.getParagraphElement(caretPosition);
AttributeSet attrs = paragraphElement.getAttributes();
//The above will tell us the current style of things
boolean isBold = StyleConstants.isBold(attrs);
boolean isItalics = StyleConstants.isItalic(attrs);
boolean isUnderline = StyleConstants.isUnderline(attrs);
System.out.println(String.format("bold %b italics %b underline %b", isBold, isItalics, isUnderline));
if (isBold) {
boldBtn.setBackground(selectedColor);
} else {
boldBtn.setBackground(defaultColor);
}
if (isItalics) {
italicsBtn.setBackground(selectedColor);
} else {
italicsBtn.setBackground(defaultColor);
}
if (isUnderline) {
underlineBtn.setBackground(selectedColor);
} else {
underlineBtn.setBackground(defaultColor);
}
}
}
我相信问题是我的
updateStateButtons
函数的前几行,attrs
总是将我的所有样式返回为 false,尽管在我的 TextPane 中输入时我看到了样式。
我之前尝试过
StyledDocument doc = textPane.getStyledDocument();
AttributeSet attrs = doc.getCharacterElement(textPane.getSelectionStart()).getAttributes();
这种方法有效 - 如果我突出显示一个选择并应用粗体,则粗体按钮将突出显示。然后,如果我继续打字,它就会保持突出显示状态。但如果我转到文本末尾,它会取消突出显示,但我要输入的下一个字符仍然是粗体。
我考虑过手动切换,但我认为可能会不同步,最好从文档中读取样式,但我显然没有完全正确地做到这一点。有谁知道我如何修改它,以便我可以向用户展示下一个字符的样式?
您想使用
CaretListener
检查属性的当前状态。
JTextPane
确实有一个getInputAttributes()
方法,对我来说应该是使用的方法。但是,它对我不起作用。当插入符号位于第一个风格化字符之前时,这意味着要键入的下一个字符应该风格化,但事实并非如此。
因此,以下代码将插入符号位置调整 1,这似乎给出了正确的结果:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class TextPaneCaret
{
private static void createAndShowGUI()
{
JButton bold = new JButton( new StyledEditorKit.BoldAction() );
JTextPane textPane = new JTextPane();
textPane.setText("Now is the time for...");
JScrollPane scrollPane = new JScrollPane( textPane );
scrollPane.setPreferredSize( new Dimension( 200, 200 ) );
textPane.addCaretListener((e) ->
{
StyledDocument doc = textPane.getStyledDocument();
int offset = textPane.getCaretPosition();
if (offset > 0)
offset--;
Element characterElement = doc.getCharacterElement(offset);
AttributeSet attributes = characterElement.getAttributes();
System.out.println( attributes.containsAttribute(StyleConstants.Bold, Boolean.TRUE) );
});
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(bold, BorderLayout.PAGE_START);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}