Эффективное применение форматирования текста на лету, установив Spans на EditText - PullRequest
0 голосов
/ 14 февраля 2020

Я использую элемент управления EditText, который разрешает форматирование текста (полужирный, курсив и т. Д. c.).

Чтобы применить форматирование, в обработчике событий AfterTextChanged моего TextWatcher я определяю, используется ли стиль форматирования, например, жирный, был включен через пользовательский интерфейс. Если это так, я пробовал два разных подхода, ни один из которых не является удовлетворительным по разным причинам:

Подход 1

textView.EditableText.SetSpan(new StyleSpan(TypefaceStyle.Bold), start, end, SpanTypes.ExclusiveExclusive);

Для начала значение, я пытался использовать _textView.SelectionStart - 1 или начальную позицию, когда StyleSpan был впервые применен. А для значения end _textView.SelectionStart .

Несмотря на то, что текст выглядит хорошо отформатированным с использованием этого метода, он создает ненужные StyleSpans, когда достаточно только одного. Это ясно, когда я пытаюсь сохранить текст в моем локальном БД через преобразование Html:

string html = Html.ToHtml(new SpannableString(Fragment_Textarea.Instance().Textarea().EditableText));

Например, вместо <b>this is bold text</b> я получаю <b><b><b><b><b><b><b><b><b><b><b><b><b><b><b><b><b>this is bold text</b></b></b></b></b></b></b></b></b></b></b></b></b></b></b></b></b>. Итак, ясно, что я делаю что-то не так / неэффективен в этом подходе. Очевидно, что это приводит к возможному замедлению как при вводе текста, так и при получении при запуске.

Что-то, что я рассмотрел, это проверить, есть ли Span на предыдущем символе (_textView.SelectionStart - 1), и, если да, чтобы удалить диапазон, а затем добавить диапазон, который начинается в этой точке вплоть до _textView.SelectionStart, т.е. гарантирует, что существует только один диапазон, постоянно проверяя / удаляя / добавляя необходимый диапазон. Но это похоже на еще один неэффективный способ справиться с этим.

Подход 2

textView.EditableText.SetSpan(new StyleSpan(TypefaceStyle.Bold), start, end, SpanTypes.ExclusiveInclusive);

Таким образом, это не приводит к той же неэффективности, что и выше, а потому что из флага SpanTypes.ExclusiveInclusive, я не могу остановить форматирование стиля, чтобы закончить, когда я отключаю его через пользовательский интерфейс. Другими словами, когда я включаю стиль Bold, весь последующий текст будет отформатирован жирным шрифтом, даже если я выключил его.

Из этих двух мне кажется, что это правильный общий подход, и поэтому мне интересно, могу ли я сделать что-нибудь, чтобы остановить применение стиля, как только я выключу его переключение. Или есть другой способ, который я совсем пропустил как лучшую практику для обработки такого рода требований.

1 Ответ

0 голосов
/ 21 февраля 2020

Итак, я решил использовать совершенно другой подход, перенеся ответственность за установку диапазона на то, когда кнопка на панели инструментов для активации стиля переключается (в отличие от любого текста, изменяющего слушателей).

Например, когда включен жирный стиль, его обработчик событий запускает следующий код:

int start = _textarea.SelectionStart - 1;
var spanType = SpanTypes.ExclusiveInclusive;
_textarea.EditableText.SetSpan(new StyleSpan(TypefaceStyle.Bold), start, _textarea.SelectionStart, spanType);

Тип диапазона должен быть ExclusiveInclusive, как предложено выше. Хитрость заключается в том, чтобы изменить это, как только стиль был отключен. Это относительно просто, если вы печатаете жирным шрифтом, а затем выключаете стиль (просто вопрос поиска диапазона, его удаления, а затем добавления нового диапазона с теми же начальными / конечными точками, но это ExcEx c). Но мне нужно было, чтобы код был более гибким и учитывал ситуацию, в которой вы можете позже решить ввести текст span другого стиля. Например, допустим, я начинаю с:

Это жирный текст

Но затем я редактирую и хочу изменить его на:

Это жирный шрифт да текст

В таком сценарии мне нужно убедиться, что я создал исключительный жирный интервал ExclusiveExclusive по обе стороны от "да". :

int start = -1;
int end = -1;
List<Tuple<int, int>> respans = new List<Tuple<int, int>>(); 

// go through all relevant spans that start from -1 indices ago
var spans = _textarea.EditableText.GetSpans(_textarea.SelectionStart - 1, _textarea.SelectionStart, Class.FromType(typeof(StyleSpan)));
if (spans.Length > 0)
{
    for (int u = 0; u < spans.Length; u++)
    {
        // found a matching span!
        if (((StyleSpan)spans[u]).Style == TypefaceStyle.Bold)
        {
            // get the starting and ending indices for the iterated span
            start = _textarea.EditableText.GetSpanStart(spans[u]);
            end = _textarea.EditableText.GetSpanEnd(spans[u]);

            // remove the span
            _textarea.EditableText.RemoveSpan(spans[u]);

            // if the current index is less than when this iterated span ended
            // and greater than when it started
            // then that means non-bold text is being inserted in the middle of a bold span
            // that needs to be split into 2 (before current index + after current index)
            if (_textarea.SelectionStart > start && _textarea.SelectionStart < end)
            {
                respans.Add(new Tuple<int, int>(start, _textarea.SelectionStart - 1));
                for(int c = _textarea.SelectionStart + 1; c < _textarea.Length(); c++ )
                {
                    if(_textarea.Text[c] != ' ' )
                    {
                        respans.Add(new Tuple<int, int>(c, end));
                        break;
                    }
                }
            }
            // otherwise, the recreated span needs to start and end when the iterated did
            // with one important change in relation to its span type
            else
            {
                respans.Add(new Tuple<int, int>(start, end));
            }
        }
    }

    // if there are 1 or more spans that need to be restored,
    // go through them and add them back according to start/end points set on their creation
    // as an ExclusiveExclusive span type
    if( respans.Count > 0 )
    {
        foreach( Tuple<int,int> tp in respans )
        {
            _textarea.EditableText.SetSpan(new StyleSpan(TypefaceStyle.Bold), tp.Item1, tp.Item2, SpanTypes.ExclusiveExclusive);
        }
    }
}

Похоже, что это делает работу: интервалы создаются / управляются, когда пользовательский интерфейс взаимодействует (а текст не изменяется) ?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...