java - 我的自定义 EditText 无法正常工作

标签 java android android-edittext

我正在开发一个 EditText,它可以采用粗体、斜体和下划线字符,如 question 中所述.

我已经扩展了 EditText 并覆盖了 onTextChanged() 方法。 我的代码在第一次出现任何粗体、斜体、带下划线的输入时工作正常,但在我第二次出现后,第一次出现的情况被更改为普通文本。

Here is a gif depecting the problem

这是 MainActivity.java

    package com.example.syed.andtexteditor;

import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    int helperCounterB = 0;
    int helperCounterI = 0;
    int helperCounterU = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button boldButton = (Button) findViewById(R.id.bold_button);
        boldButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Button boldButton = (Button) findViewById(R.id.bold_button);
                helperCounterB++;

                if (helperCounterB % 2 != 0)
                //The EditText is in Bold mode when the Bold button is pressed odd-th time
                    boldButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.black));
                else

                    boldButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.grey));

                TextArea t = (TextArea) findViewById(R.id.textInput);
                t.applyTypeface(helperCounterI, helperCounterB, helperCounterU);

            }

        });

        Button italicsButton = (Button) findViewById(R.id.italics_button);
        italicsButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Button italicsButton = (Button) findViewById(R.id.italics_button);
                helperCounterI++;
                if (helperCounterI % 2 != 0) //The EditText is in Italics mode when the Italics button is pressed odd-th time
                    italicsButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.black));
                else
                    italicsButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.grey));

                TextArea t = (TextArea) findViewById(R.id.textInput);
                t.applyTypeface(helperCounterI, helperCounterB, helperCounterU);

            }

        });

        Button underlineButton = (Button) findViewById(R.id.underline_button);
        underlineButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Button underlineButton = (Button) findViewById(R.id.underline_button);
                helperCounterU++;
                if (helperCounterU % 2 != 0)//The EditText is in Underline mode when the Underline button is pressed odd-th time

                    underlineButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.black));
                else

                    underlineButton.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.grey));

                TextArea t = (TextArea) findViewById(R.id.textInput);
                t.applyTypeface(helperCounterI, helperCounterB, helperCounterU);

            }
        });

    }
}

这里是扩展的 EditText 类,即 TextArea.java

package com.example.syed.andtexteditor;


import android.content.Context;
import android.graphics.Typeface;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.util.AttributeSet;
import android.util.Log;


/**
 * Created by Syed on 29-05-2017.
 */

public class TextArea extends AppCompatEditText {
    Context c;
    public static final int TYPEFACE_NORMAL = 0;
    public static final int TYPEFACE_BOLD = 1;
    public static final int TYPEFACE_ITALICS = 2;
    public static final int TYPEFACE_BOLD_ITALICS = 3;
    public static final int TYPEFACE_UNDERLINE = 4;
    public static final int TYPEFACE_BOLD_UNDERLINE = 5;
    public static final int TYPEFACE_ITALICS_UNDERLINE = 6;
    public static final int TYPEFACE_BOLD_ITALICS_UNDERLINE = 7;

    private int currentTypeface;
    private int lastCursorPosition;

    private StyleSpan normalspan = new StyleSpan(Typeface.NORMAL);
    private StyleSpan boldspan = new StyleSpan(Typeface.BOLD);
    private StyleSpan italicspan = new StyleSpan(Typeface.ITALIC);
    private StyleSpan boldItalicspan = new StyleSpan(Typeface.BOLD_ITALIC);
    private UnderlineSpan underlinespan = new UnderlineSpan();

    public TextArea(Context context) {
        super(context);
        c = context;
        lastCursorPosition = this.getSelectionStart();

    }

    public TextArea(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void changeTypeface(int tfId) {
        currentTypeface = tfId;
        lastCursorPosition = this.getSelectionStart();
    }

    public void applyTypeface(int helperCounterI, int helperCounterB, int helperCounterU) {
        int min = 0;
        int max = this.getText().length();
        if (this.isFocused()) {
            final int selStart = this.getSelectionStart();
            final int selEnd = this.getSelectionEnd();

            min = Math.max(0, Math.min(selStart, selEnd));
            max = Math.max(0, Math.max(selStart, selEnd));
        }
        Spannable s = this.getText();
        Editable selectedText = new SpannableStringBuilder(s, min, max);

        SpannableStringBuilder s1 = new SpannableStringBuilder(s, 0, min);

        String selectedTextString = selectedText.toString();
        SpannableStringBuilder selectedSpannedString = new SpannableStringBuilder(selectedTextString);
        Log.d(VIEW_LOG_TAG, "Helper Counter I: " + helperCounterI + " Helper Counter B: " + helperCounterB);
        if (helperCounterI % 2 != 0 && helperCounterB % 2 != 0 && helperCounterU % 2 == 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {
                this.changeTypeface(TextArea.TYPEFACE_BOLD_ITALICS);

            }
            //ignore this part as there are no issues with this
            else {

                StyleSpan styleSpan = new StyleSpan(Typeface.BOLD_ITALIC);
                selectedSpannedString.setSpan(styleSpan, min, max, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }

        } else if (helperCounterI % 2 != 0 && helperCounterB % 2 == 0 && helperCounterU % 2 == 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {
                this.changeTypeface(TextArea.TYPEFACE_ITALICS);

            }
            //ignore this part as there are no issues with this
            else {
                StyleSpan styleSpan = new StyleSpan(Typeface.ITALIC);
                selectedSpannedString.setSpan(styleSpan, min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        } else if (helperCounterI % 2 == 0 && helperCounterB % 2 != 0 && helperCounterU % 2 == 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {

                this.changeTypeface(TextArea.TYPEFACE_BOLD);

            }
            //ignore this part as there are no issues with this
            else {
                StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
                selectedSpannedString.setSpan(styleSpan, min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        } else if (helperCounterB % 2 == 0 && helperCounterI % 2 == 0 && helperCounterU % 2 == 0) {

            this.changeTypeface(TextArea.TYPEFACE_NORMAL);

        } else if (helperCounterU % 2 != 0 && helperCounterI % 2 == 0 && helperCounterB % 2 == 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {
                this.changeTypeface(TYPEFACE_UNDERLINE);

            }
            //ignore this part as there are no issues with this
            else {
                selectedSpannedString.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        } else if (helperCounterU % 2 != 0 && helperCounterI % 2 == 0 && helperCounterB % 2 != 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {
                this.changeTypeface(TYPEFACE_BOLD_UNDERLINE);

            }
            //ignore this part as there are no issues with this
            else {
                selectedSpannedString.setSpan(new StyleSpan(Typeface.BOLD), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                selectedSpannedString.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        } else if (helperCounterU % 2 != 0 && helperCounterI % 2 != 0 && helperCounterB % 2 == 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {

                this.changeTypeface(TYPEFACE_ITALICS_UNDERLINE);

            } else
            //ignore this part as there are no issues with this
            {
                selectedSpannedString.setSpan(new StyleSpan(Typeface.ITALIC), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                selectedSpannedString.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        } else if (helperCounterU % 2 != 0 && helperCounterI % 2 != 0 && helperCounterB % 2 != 0) {
            if (this.getSelectionEnd() == this.getSelectionStart()) {
                this.changeTypeface(TYPEFACE_BOLD_ITALICS_UNDERLINE);

            }
            //ignore this part as there are no issues with this
            else {
                selectedSpannedString.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                selectedSpannedString.setSpan(new UnderlineSpan(), min, max, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
                SpannableStringBuilder s3 = new SpannableStringBuilder(s, max, s.length());
                CharSequence finalSpannable = TextUtils.concat(s1, selectedSpannedString, s3);
                this.setText(finalSpannable);
            }
        }
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {


        Log.d(VIEW_LOG_TAG, "Start: " + start + " Length before: " + lengthBefore + " Length After: " + lengthAfter + " TextLength: " + text.length());

        CharacterStyle ss = null;
        UnderlineSpan ss1 = null;
        int endLength = text.toString().length();


        switch (currentTypeface) {
            case TYPEFACE_NORMAL:
                ss = normalspan;
                break;
            case TYPEFACE_BOLD:
                ss = boldspan;
                break;
            case TYPEFACE_ITALICS:
                ss = italicspan;
                break;
            case TYPEFACE_BOLD_ITALICS:
                ss = boldItalicspan;
                break;
            case TYPEFACE_UNDERLINE:
                ss = underlinespan;
                break;
            case TYPEFACE_BOLD_UNDERLINE:
                ss = boldspan;
                ss1 = underlinespan;
                break;
            case TYPEFACE_ITALICS_UNDERLINE:
                ss = italicspan;
                ss1 = underlinespan;
                break;
            case TYPEFACE_BOLD_ITALICS_UNDERLINE:
                ss = boldItalicspan;
                ss1 = underlinespan;
                break;
        }
        if (lastCursorPosition > endLength)
            return;
        Log.d(TextArea.class.getSimpleName(), new Integer(lastCursorPosition).toString() + new Integer(endLength).toString());
        this.getText().setSpan(ss, lastCursorPosition, endLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        this.getText().setSpan(ss1, lastCursorPosition, endLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

    }
}

最佳答案

您的问题是您正在重复使用 span 的实例。根据 setSpan 的文档(强调我的):

Attach the specified markup object to the range start…end of the text, or move the object to that range if it was already attached elsewhere.

因此,您只需在每次想要属性文本时创建新的 Span。

希望对您有所帮助!

关于java - 我的自定义 EditText 无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44998227/

相关文章:

java - 我如何绘制从我的 android 应用程序发送到 tomcat 服务器的 lat ,lng 并在那里绘制谷歌地图?

java - 带附件的 Web 服务在 2 路 SSL 连接上失败

java - 响应监听器内部的字符串数组在监听器类外部提供空值

安卓 : How to prevent users from entering numbers with more than 3 decimal places

android - addTextChangedListener 动态获取EditText

java 模拟发牌者 - ArrayIndexOutOfBoundsException

android - OpenCV 图像识别应用程序 : very good results on desktop but not on android

java - Android 文本转语音抛出 ActivityNotFoundException

java - 我希望当用户使用 GPS 进入 10 米半径时收到通知

android - 如何在动画之后将更改应用到 View 位置?