Как форматировать и отображать длинный текст с помощью 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);
    }

}
Другие вопросы по тегам