Я пытался получить простой форматированный текст в JavaFX: мне нужен непрерывный текст, где некоторые символы выделены жирным шрифтом, нижним или верхним индексом. Это невозможно в обычных классах Text или Label. Я попытался WebView
безуспешно, так как, хотя он будет отображать такой текст, он не изменяет размер своего содержимого и, таким образом, занимает неконтролируемо большую часть экрана.
Теперь я пытаюсь использовать TextFlow
. Я могу успешно связать вместе Text
объекты, некоторые из которых можно выделить жирным шрифтом. Однако нижний и верхний индексы оказываются более сложными. Подстрочный индекс можно эмулировать, просто уменьшая размер шрифта, однако верхний индекс требует, чтобы объект Text
был поднят над остальными. Я не могу найти способ сделать это: TextFlow
специально игнорирует свойства перевода объектов Text
, и я не могу переопределить getBaselineOffset()
для рассматриваемого Text
, так как он является окончательным.
Должен ли я поместить Text
в HBox? Неужели в JavaFX такой поддержки нет? То, что я пытаюсь сделать, не сложно; кажется ошеломляющим, что нет встроенной поддержки нижнего и верхнего индексов.
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextFlow;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class TextFlowBuilder
{
private static final String BOLD = "<b>";
private static final String UN_BOLD = "</b>";
private static final String SUPERSCRIPT = "<sup>";
private static final String UN_SUPERSCRIPT = "</sup>";
private static final String SUBSCRIPT = "<sub>";
private static final String UN_SUBSCRIPT = "</sub>";
private static final Pattern NOT_JUST_WHITESPACE = Pattern.compile("\\S");
private static final Pattern CHARACTER_CODE = Pattern.compile("&#(\\d+);");
public static TextFlow htmlToTextFlow(final String html, final int fontSize, final TextAlignment alignment)
{
final String[] split = html.split("(?<=>)|(?=<)"); //Split before and after tags, splitting it into a series of tags and tag contents.
final List<Text> texts = new LinkedList<>();
boolean b = false;
boolean sup = false;
boolean sub = false;
for (String segment : split)
{
switch (segment)
{
case BOLD:
b = true;
break;
case UN_BOLD:
b = false;
break;
case SUPERSCRIPT:
sup = true;
break;
case UN_SUPERSCRIPT:
sup = false;
break;
case SUBSCRIPT:
sub = true;
break;
case UN_SUBSCRIPT:
sub = false;
break;
default:
//Add as text if string is not a tag, and is more than just whitespace.
if (segment.length() > 0
&& NOT_JUST_WHITESPACE.matcher(segment).find()
&& !segment.startsWith("<"))
{
final Matcher m = CHARACTER_CODE.matcher(segment);
while (m.find())
{
final String specialChar = Character.toString((char)Integer.parseInt(m.group(1)));
segment = m.replaceFirst(specialChar);
}
final Text t = new Text(segment);
String style = "";
if (b)
style += "-fx-font-weight: bold; ";
if (sup)
{
style += String.format("-fx-font-size: %f.3; ", fontSize/1.75);
//Need to move text to above the rest
}
else if (sub)
{
style += String.format("-fx-font-size: %f.3; ", fontSize/1.75);
}
else
{
style += String.format("-fx-font-size: %d; ", fontSize);
}
t.setStyle(style);
texts.add(t);
}
}
}
final Text[] textsAsArray = new Text[texts.size()];
final TextFlow tf = new TextFlow(texts.toArray(textsAsArray));
tf.setTextAlignment(alignment);
return tf;
}
}