8

我正在开发 Android 中的富文本编辑器。基本上,它具有与 EditText 相关联的粗体、斜体和链接按钮,以更改内容的样式。如果您先选择要设置样式的文本,然后使用以下方法选择按钮,我会很好地工作:http: //developer.android.com/guide/appendix/faq/commontasks.html#selectingtext

我想要做的是让它像富文本编辑器一样工作,您可以在其中使用按钮作为切换来设置文本样式,只要您愿意,然后再次单击切换以停止使用该样式。所以如果我想输入'请注意这个!' 以粗体显示,我会单击“B”按钮,然后开始输入文本,我输入的所有内容都会是粗体,直到我再次单击“B”按钮。

关于如何解决这个问题的任何想法?我希望我已经足够清楚了:)

4

5 回答 5

12

对于那些感兴趣的人,我通过在按下 ToggleButton 时保存光标位置(下面的“styleStart”变量)来实现这一点,然后当用户键入更多字符时,我实际上用 removeSpan() 删除了匹配的 StyleSpan,然后重新通过使用保存的原始光标位置 + 当前键入的字符长度,使用 setSpan() 将其添加回来。

您还需要跟踪用户是否更改了光标位置,这样您就不会为您不想要的文本设置样式。这是 TextWatcher 代码:

final EditText contentEdit = (EditText) findViewById(R.id.content);
        contentEdit.addTextChangedListener(new TextWatcher() { 
            public void afterTextChanged(Editable s) { 
                //add style as the user types if a toggle button is enabled
                ToggleButton boldButton = (ToggleButton) findViewById(R.id.bold);
                ToggleButton emButton = (ToggleButton) findViewById(R.id.em);
                ToggleButton bquoteButton = (ToggleButton) findViewById(R.id.bquote);
                ToggleButton underlineButton = (ToggleButton) findViewById(R.id.underline);
                ToggleButton strikeButton = (ToggleButton) findViewById(R.id.strike);
                int position = Selection.getSelectionStart(contentEdit.getText());
                if (position < 0){
                    position = 0;
                }

                if (position > 0){

                    if (styleStart > position || position > (cursorLoc + 1)){
                        //user changed cursor location, reset
                        styleStart = position - 1;
                    }

                    cursorLoc = position;

                    if (boldButton.isChecked()){  
                        StyleSpan[] ss = s.getSpans(styleStart, position, StyleSpan.class);

                        for (int i = 0; i < ss.length; i++) {
                            if (ss[i].getStyle() == android.graphics.Typeface.BOLD){
                                s.removeSpan(ss[i]);
                            }
                        }
                        s.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), styleStart, position, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    if (emButton.isChecked()){
                        StyleSpan[] ss = s.getSpans(styleStart, position, StyleSpan.class);

                        boolean exists = false;
                        for (int i = 0; i < ss.length; i++) {
                            if (ss[i].getStyle() == android.graphics.Typeface.ITALIC){
                                s.removeSpan(ss[i]);
                            }
                        }
                        s.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), styleStart, position, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    if (bquoteButton.isChecked()){

                        QuoteSpan[] ss = s.getSpans(styleStart, position, QuoteSpan.class);

                        for (int i = 0; i < ss.length; i++) {
                                s.removeSpan(ss[i]);
                        }
                        s.setSpan(new QuoteSpan(), styleStart, position, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    if (underlineButton.isChecked()){
                        UnderlineSpan[] ss = s.getSpans(styleStart, position, UnderlineSpan.class);

                        for (int i = 0; i < ss.length; i++) {
                                s.removeSpan(ss[i]);
                        }
                        s.setSpan(new UnderlineSpan(), styleStart, position, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                    if (strikeButton.isChecked()){
                        StrikethroughSpan[] ss = s.getSpans(styleStart, position, StrikethroughSpan.class);

                        for (int i = 0; i < ss.length; i++) {
                                s.removeSpan(ss[i]);
                        }
                        s.setSpan(new StrikethroughSpan(), styleStart, position, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                }
            } 
            public void beforeTextChanged(CharSequence s, int start, int count, int after) { 
                    //unused
            } 
            public void onTextChanged(CharSequence s, int start, int before, int count) { 
                        //unused
                } 
});

这是 ToggleButton 单击操作之一:

final ToggleButton boldButton = (ToggleButton) findViewById(R.id.bold);   

            boldButton.setOnClickListener(new Button.OnClickListener() {
                public void onClick(View v) {

                    EditText contentText = (EditText) findViewById(R.id.content);

                    int selectionStart = contentText.getSelectionStart();

                    styleStart = selectionStart;

                    int selectionEnd = contentText.getSelectionEnd();

                    if (selectionStart > selectionEnd){
                        int temp = selectionEnd;
                        selectionEnd = selectionStart;
                        selectionStart = temp;
                    }


                    if (selectionEnd > selectionStart)
                    {
                        Spannable str = contentText.getText();
                        StyleSpan[] ss = str.getSpans(selectionStart, selectionEnd, StyleSpan.class);

                        boolean exists = false;
                        for (int i = 0; i < ss.length; i++) {
                            if (ss[i].getStyle() == android.graphics.Typeface.BOLD){
                                str.removeSpan(ss[i]);
                                exists = true;
                            }
                        }

                        if (!exists){
                            str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                        }

                        boldButton.setChecked(false);
                    }
                }
        });

可能有更好的解决方案,如果你有一个,很高兴听到它!

于 2010-06-23T16:42:27.377 回答
5

TextWatcher您可以在他们使用 a和方法键入的每个字符之后更新样式addTextChangedListener()

好的,这只是简单的示例代码。

int mStart = -1;

// Bold onClickListener
public void onClick(View view)
{
    if(mStart == -1) mStart = mEditText.getText().length();
    else mStart = -1;
}

// TextWatcher
onTextChanged(CharSequence s, int start, int before, int count)
{
    if(mStart > 0)
    {
        int end = mEditText.getText().length();
        mEditText.getText().setSpan(new StyleSpan(android.graphics.Typeface.BOLD), mStart, end - mStart, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}
于 2010-06-22T21:25:22.937 回答
2

我知道这是一个旧线程,但我的同事在网络上发现了DroidWriter。我已经用它玩了一点,它很容易解释,很容易使用和修改。希望这可以帮助任何想要创建富文本编辑器的人。

于 2014-09-05T17:17:07.877 回答
1

有一个开源的 EditText 富文本编辑器,叫做android-richtexteditor但代码在 r5 中被莫名其妙地删除了。旧版本中的代码仍然存在;只需使用 svn 签出 r4 或更早的版本即可访问它。该代码似乎支持斜体、下划线、颜色选择器、文本大小等。

还回答了以下问题1、2

于 2011-12-15T22:20:34.610 回答
0

只有一种可能性,不确定它是不是最好的解决方案,但它是我想到的,谁知道呢,但它会激发你找到一个更优雅的解决方案:

也许您可以考虑在内部跟踪“文档”的分层样式结构及其所有样式标签,并用键入的每个字符重建/替换最终输出?当然,您也必须跟踪光标位置。

于 2010-06-23T14:39:37.157 回答