2017-07-09 25 views
1

我正在研究一個EditText,它可以採用此question中提到的粗體,斜體和下劃線字符。我的自定義EditText工作不正常

我擴展了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); 

    } 
} 

回答

1

你的問題是,你是重新使用跨度的實例。每setSpan(重點煤礦)的文檔:

連接指定的標記對象的範圍開始...文本,末或移動對象來表示範圍,如果它已經在別處連接。

因此,您只需在每次要屬性文本時創建新的Spans。

希望有幫助!

+0

解決了這個問題,但之前的問題重新出現了。昨天我問了這個問題https://stackoverflow.com/q/44984121/5045878,問題的解決方案是重新使用跨度。 –

+0

當然,如果每次文本更改時重新分配每個跨度,事情會變得很慢。你的邏輯需要做的是確定你是否在跨度中,然後重新使用現有的活動。如果您正在開始一個新跨度,請爲該用例創建一個新跨度,然後重新使用它,直至完成使用。然後,當您刪除字符時,您可以重新使用不再使用的跨度。這很複雜,超出了問題的範圍。 – dominicoder

+0

這對我來說有點超出範圍。請問這[答案](https://stackoverflow.com/a/21972806/5045878)的工作? –

相關問題