Как форматировать и отображать длинный текст с помощью Garmin ConnectIQ SDK
Я пытаюсь отобразить сообщение, которое может быть длиннее ширины экрана. Как лучше всего отформатировать сообщение, чтобы оно отображалось как многострочное?
Обратите внимание, что сообщения приходят с сервера, поэтому это не жестко закодированная строка ресурса.
Я не вижу никаких инструментов для этого, и ни Toybox:: WatchUi:: Text drawable, ни Dc.drawText, похоже, не поддерживают форматирование абзаца.
Dc.getTextDimensions позволяет определять ширину и высоту текста, так что это потенциально полезно, но нативные приложения (например, средство уведомления о сообщениях) действительно отображают правильно отформатированные абзацы, поэтому у меня сложилось впечатление, что я что-то упустил.
5 ответов
В итоге я использовал эвристику для вычисления максимального количества символов, которые помещаются в строку, и, при необходимости, обтекание строки:
function formatText(dc, text, width, font) {
// characters below worked better than "EeeTtaAooiNshRdlcum "
// which I used initially and is based on frequency of characters
// in English words
var chars = "AbCdEfGhIj";
var oneCharWidth = dc.getTextWidthInPixels(chars, font) / chars.length();
var charPerLine = mListWidth / oneCharWidth;
if (text.length() > charPerLine) {
var result = text.substring(0, charPerLine);
result += "\n";
result += text.substring(charPerLine, text.length());
return [result, 0];
} else {
return [text, 1];
}
}
Фактическая функция в моем приложении IQ Connect Garminello с открытым исходным кодом находится здесь.
Начиная с уровня API 3.1.0 существует очень привлекательныйfitTextToArea()
который можно использовать для размещения текстовой строки в указанной области.
Параметры:
-
text
— (Lang.String) — Текст, помещающийся в заданную область, который может включать символы новой строки. -
font
— (Graphics.FontType) — шрифт для использования при определении места разрыва строки -
width
— (Lang.Number) — Ширина области, в которую помещается -
height
— (Lang.Number) — Высота области, в которую помещается - — (Lang.Boolean) — Если значение равно true, результирующая строка может быть усечена, чтобы соответствовать предоставленной области, с использованием предоставленного шрифта.
Возвращает:
Возвращает подходящий для отображения в заданной области. String
будет усечен, еслиtruncate
параметр имеет значение true, и строка не может поместиться в указанную область. В противном случае,null
будет возвращен.
Например:
//! Draw the item's label and bitmap
//! @param dc Device context
public function draw(dc as Dc) as Void {
var font = Graphics.FONT_XTINY;
dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT);
dc.drawText(dc.getWidth() / 2,
dc.getHeight() / 2,
font,
Graphics.fitTextToArea(_description, font, dc.getWidth()-10, dc.getHeight(), true),
Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
}
Ссылка
Я придумал довольно хорошее рекурсивное решение, которое учитывает немало возможностей (наличие в строке "\n" или "", а если слово слишком длинное, оно разделяет слово):
function convertTextToMultiline(dc, text){
var extraRoom = 0.8;
var oneCharWidth = dc.getTextWidthInPixels("EtaoiNshrd",Graphics.FONT_SMALL)/10;
var charPerLine = extraRoom * dc.getWidth()/oneCharWidth;
return convertTextToMultilineHelper(text, charPerLine);
}
function convertTextToMultilineHelper(text, charPerLine) {
if (text.length() <= charPerLine) {
return text;
} else {
var i = charPerLine + 1;
for (; i >= 0; i--) {
if (text.substring(i, i + 1).equals("\n")) {
break;
}
}
if (i >= 0) {
var line = text.substring(0, i);
var textLeft = text.substring(i + 1, text.length());
var otherLines = convertTextToMultilineHelper(textLeft, charPerLine);
return line + "\n" + otherLines;
} else {
var lastChar = charPerLine + 1;
while (!(text.substring(lastChar, lastChar + 1).equals(" ") || text.substring(lastChar, lastChar + 1).equals("\n"))&& lastChar >= charPerLine/2) {
lastChar--;
}
if (lastChar >= charPerLine/2) {
var line = text.substring(0, lastChar + 1);
var textLeft = text.substring(lastChar + 1, text.length());
var otherLines = convertTextToMultilineHelper(textLeft, charPerLine);
return line + "\n" + otherLines;
} else {
var line = text.substring(0, charPerLine) + "-";
var textLeft = text.substring(charPerLine, text.length());
var otherLines = convertTextToMultilineHelper(textLeft, charPerLine);
return line + "\n" + otherLines;
}
}
}
}
Это может быть не самое красивое или самое эффективное решение, но оно, безусловно, работает!
Я предполагаю, что вы должны сделать некоторые ручные вычисления по ширине текста, используя https://developer.garmin.com/downloads/connect-iq/monkey-c/doc/Toybox/Graphics/Dc.html
Я сталкиваюсь почти с той же проблемой, хотя для моего решения я обрежу символы, которые не вписываются в ширину экрана / ширину текстового поля
Я делаю вспомогательную функцию для вычисления строки, которую я могу вписать в определенную ширину, которая будет выглядеть так:
function getTextWidthMaxWidth(dc, text, font, maxWidth) {
var dispText = text;
//chunk off a char until it fits maxWidth
while (dc.getTextWidthInPixels(dispText, font) > maxWidth) {
dispText = dispText.subString(0, dispText.Length() - 1);
}
return dispText;
}
В настоящее время TextArea предназначена именно для этого.
using Toybox.WatchUi;
using Toybox.Graphics as Gfx;
class ErrorView extends WatchUi.View {
hidden var _message;
hidden var _textArea;
function initialize(message) {
_message = message;
View.initialize();
}
function onLayout(dc) {
_textArea = new WatchUi.TextArea({
:text=>_message.toString(),
:color=>Graphics.COLOR_WHITE,
:font=>[Graphics.FONT_XTINY],
:locX =>WatchUi.LAYOUT_HALIGN_CENTER,
:locY=>WatchUi.LAYOUT_VALIGN_CENTER,
:width=>dc.getWidth()* 2/3,
:height=>dc.getHeight() * 2/3
});
}
function onUpdate(dc) {
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
dc.clear();
_textArea.draw(dc);
}
}