JTextComponent :如何更新现有的 StyledDocument 元素
JTextComponent : how to update an existing StyledDocument elements
我使用两个 DefaultStyledDocument 编写了两个窗格差异 class。
现在我想让用户就地更改用于显示删除和插入的颜色(以及粗体),无需重新比较或保留中间差异结果。
我面临的问题是 insertString 复制了属性,所以我不能事后只更新 Style 实例。
我尝试了 DefaultStyledDocument.setLogicalStyle(int, Style) 但视觉上没有任何变化。
您是否看到一种 (simple/cheap) 无需重新创建整个文档即可进行样式更新的方法?
逻辑样式适用于整个段落。据我所知,无法将样式应用于段落中的字符并让 Swing 自动侦听样式中的更改。
您可以做的是将 Style 用作常规 AttributeSet,然后使用 ElementIterator 扫描样式元素,因为 Style 的名称存储在 AttributeSet.NameAttribute 属性键下:
void updateHighlight(StyledDocument doc,
AttributeSet newStyle) {
Object styleName = newStyle.getAttribute(AttributeSet.NameAttribute);
ElementIterator i = new ElementIterator(doc);
for (Element e = i.first(); e != null; e = i.next()) {
AttributeSet attr = e.getAttributes();
Object name = attr.getAttribute(AttributeSet.NameAttribute);
if (styleName.equals(name)) {
int start = e.getStartOffset();
int end = e.getEndOffset();
doc.setCharacterAttributes(start, end - start,
newStyle, false);
}
}
}
是否符合“simple/cheap”由您决定。
这是一个演示:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
public class StyleChanger {
public static void main(String[] args) {
EventQueue.invokeLater(() -> show());
}
private static final String HIGHLIGHT =
StyleChanger.class.getName() + ".highlight";
static void show() {
JTextPane textPane = new JTextPane();
Style normal = textPane.getStyle(StyleContext.DEFAULT_STYLE);
Style highlight = textPane.addStyle(HIGHLIGHT, normal);
StyleConstants.setBackground(highlight, Color.YELLOW);
class Fragment {
final AttributeSet style;
final String text;
Fragment(AttributeSet style,
String text) {
this.style = style;
this.text = text;
}
}
Fragment[] fragments = {
new Fragment(highlight, "Space"),
new Fragment(normal, ": the final frontier. These are the "),
new Fragment(highlight, "voyages"),
new Fragment(normal, " of the starship "),
new Fragment(highlight, "Enterprise"),
new Fragment(normal, ". Its five-year "),
new Fragment(highlight, "mission"),
new Fragment(normal, ": to explore strange "),
new Fragment(highlight, "new worlds"),
new Fragment(normal, "; to seek out "),
new Fragment(highlight, "new life"),
new Fragment(normal, " and new civilizations; to "),
new Fragment(highlight, "boldly go"),
new Fragment(normal, " where no man has gone before!"),
};
for (Fragment fragment : fragments) {
textPane.setCharacterAttributes(fragment.style, true);
textPane.replaceSelection(fragment.text);
}
Action change = new AbstractAction("Change Highlight\u2026") {
private static final long serialVersionUID = 1;
@Override
public void actionPerformed(ActionEvent event) {
Color color = JColorChooser.showDialog(
textPane.getTopLevelAncestor(),
"Highlight Color",
textPane.getBackground());
if (color != null) {
StyleConstants.setBackground(highlight, color);
updateHighlight(textPane.getStyledDocument(), highlight);
}
}
};
JButton changeButton = new JButton(change);
JPanel buttonPane = new JPanel();
buttonPane.add(changeButton);
JFrame frame = new JFrame("Style Changer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(textPane));
frame.getContentPane().add(buttonPane, BorderLayout.PAGE_END);
frame.setSize(450, 300);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private static void updateHighlight(StyledDocument doc,
AttributeSet newStyle) {
Object styleName = newStyle.getAttribute(AttributeSet.NameAttribute);
ElementIterator i = new ElementIterator(doc);
for (Element e = i.first(); e != null; e = i.next()) {
AttributeSet attr = e.getAttributes();
Object name = attr.getAttribute(AttributeSet.NameAttribute);
if (styleName.equals(name)) {
int start = e.getStartOffset();
int end = e.getEndOffset();
doc.setCharacterAttributes(start, end - start,
newStyle, false);
}
}
}
}
我使用两个 DefaultStyledDocument 编写了两个窗格差异 class。
现在我想让用户就地更改用于显示删除和插入的颜色(以及粗体),无需重新比较或保留中间差异结果。
我面临的问题是 insertString 复制了属性,所以我不能事后只更新 Style 实例。
我尝试了 DefaultStyledDocument.setLogicalStyle(int, Style) 但视觉上没有任何变化。
您是否看到一种 (simple/cheap) 无需重新创建整个文档即可进行样式更新的方法?
逻辑样式适用于整个段落。据我所知,无法将样式应用于段落中的字符并让 Swing 自动侦听样式中的更改。
您可以做的是将 Style 用作常规 AttributeSet,然后使用 ElementIterator 扫描样式元素,因为 Style 的名称存储在 AttributeSet.NameAttribute 属性键下:
void updateHighlight(StyledDocument doc,
AttributeSet newStyle) {
Object styleName = newStyle.getAttribute(AttributeSet.NameAttribute);
ElementIterator i = new ElementIterator(doc);
for (Element e = i.first(); e != null; e = i.next()) {
AttributeSet attr = e.getAttributes();
Object name = attr.getAttribute(AttributeSet.NameAttribute);
if (styleName.equals(name)) {
int start = e.getStartOffset();
int end = e.getEndOffset();
doc.setCharacterAttributes(start, end - start,
newStyle, false);
}
}
}
是否符合“simple/cheap”由您决定。
这是一个演示:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
public class StyleChanger {
public static void main(String[] args) {
EventQueue.invokeLater(() -> show());
}
private static final String HIGHLIGHT =
StyleChanger.class.getName() + ".highlight";
static void show() {
JTextPane textPane = new JTextPane();
Style normal = textPane.getStyle(StyleContext.DEFAULT_STYLE);
Style highlight = textPane.addStyle(HIGHLIGHT, normal);
StyleConstants.setBackground(highlight, Color.YELLOW);
class Fragment {
final AttributeSet style;
final String text;
Fragment(AttributeSet style,
String text) {
this.style = style;
this.text = text;
}
}
Fragment[] fragments = {
new Fragment(highlight, "Space"),
new Fragment(normal, ": the final frontier. These are the "),
new Fragment(highlight, "voyages"),
new Fragment(normal, " of the starship "),
new Fragment(highlight, "Enterprise"),
new Fragment(normal, ". Its five-year "),
new Fragment(highlight, "mission"),
new Fragment(normal, ": to explore strange "),
new Fragment(highlight, "new worlds"),
new Fragment(normal, "; to seek out "),
new Fragment(highlight, "new life"),
new Fragment(normal, " and new civilizations; to "),
new Fragment(highlight, "boldly go"),
new Fragment(normal, " where no man has gone before!"),
};
for (Fragment fragment : fragments) {
textPane.setCharacterAttributes(fragment.style, true);
textPane.replaceSelection(fragment.text);
}
Action change = new AbstractAction("Change Highlight\u2026") {
private static final long serialVersionUID = 1;
@Override
public void actionPerformed(ActionEvent event) {
Color color = JColorChooser.showDialog(
textPane.getTopLevelAncestor(),
"Highlight Color",
textPane.getBackground());
if (color != null) {
StyleConstants.setBackground(highlight, color);
updateHighlight(textPane.getStyledDocument(), highlight);
}
}
};
JButton changeButton = new JButton(change);
JPanel buttonPane = new JPanel();
buttonPane.add(changeButton);
JFrame frame = new JFrame("Style Changer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(textPane));
frame.getContentPane().add(buttonPane, BorderLayout.PAGE_END);
frame.setSize(450, 300);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private static void updateHighlight(StyledDocument doc,
AttributeSet newStyle) {
Object styleName = newStyle.getAttribute(AttributeSet.NameAttribute);
ElementIterator i = new ElementIterator(doc);
for (Element e = i.first(); e != null; e = i.next()) {
AttributeSet attr = e.getAttributes();
Object name = attr.getAttribute(AttributeSet.NameAttribute);
if (styleName.equals(name)) {
int start = e.getStartOffset();
int end = e.getEndOffset();
doc.setCharacterAttributes(start, end - start,
newStyle, false);
}
}
}
}