通过将Spans设置到EditText上,即时有效地应用文本格式

问题描述 投票:0回答:1

我正在使用允许文本格式设置为(粗体,斜体等)的EditText控件。

要应用格式设置,在TextWatcher的AfterTextChanged事件处理程序中,我检测是否已通过UI启用了格式设置样式(如粗体)。如果是这样,我尝试了两种不同的方法,但由于不同的原因,两种方法都不令人满意:

方法1

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

对于start值,我尝试使用_ 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>。因此,很明显,我在这种方法中做错了/效率低下。显然,这导致输入文本以及在启动时进行检索时最终变慢。

我考虑过的事情是检查前面的字符(_textView.SelectionStart - 1)是否有跨度,如果是,则删除跨度,然后添加一个从该点开始直到_textView.SelectionStart的跨度,即通过不断检查/删除/添加必要的跨度,确保只有一个跨度。但这似乎是处理此问题的另一种无效方法。

方法2

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

因此,这不会导致与上面相同的低效率,但是由于SpanTypes.ExclusiveInclusive标志,当我通过UI将其关闭时,我无法停止样式格式的结束。换句话说,当我打开“粗体”样式时,后面的所有文本都将以粗体样式设置格式,即使我已关闭其切换。

在这两者中,在我看来这是一种正确的general方法,因此,我想知道是否可以在关闭样式切换后立即采取任何措施来停止应用样式。还是我完全错过了作为处理此类需求的最佳实践的另一种方法。

android android-edittext formatting spannablestring spannablestringbuilder
1个回答
0
投票

因此,我最终采取了一种完全不同的方法,将设置跨度的责任转移到工具栏上的用于激活样式的按钮被切换时(与任何文本更改的侦听器相对)。

例如,当打开粗体样式时,其事件处理程序将运行,并运行以下代码:

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

如上所述,跨度类型必须为ExclusiveInclusive。诀窍是在切换样式后立即更改此设置。如果您以粗体键入然后关闭样式(这只是查找跨度,将其删除然后以相同的起点/终点添加一个新跨度,但这就是ExcExc),则这相对简单。但是我需要代码更加灵活,并考虑到您以后可能决定在另一种样式的跨度文本中键入内容的情况。例如,假设我开始于:

这是粗体文本

但是后来我编辑并将其更改为:

此为粗体文本

在这种情况下,我需要确保在“是”的任一边都创建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);
        }
    }
}

这似乎可以完成工作:与UI交互(而不更改文本)时,将创建/管理范围👍

© www.soinside.com 2019 - 2024. All rights reserved.